• Asp.Net 基础知识回顾_Cache


    一、传统缓存方式

    比如将可重复利用的东西放到Application或是Session中去保存。

     Session["Count"] = 100;

     Application["Count"] = 100;

    二、页面输出缓存

    页面输出缓存是最为简单的缓存机制,该机制将整个ASP.NET页面内容保存在服务器内存中。当用户请求该页面时,系统从内存中输出相关数据,直到缓存数据过期。在这个过程中,缓存内容直接发送给用户,而不必再次经过页面处理生命周期。通常情况下,页面输出缓存对于那些包含不需要经常修改内容,需要大量处理才能编译完成的页面特别有用。需要注意的是,页面输出缓存是将页面全部内容都保存在内存中,并用于完成客户端请求。

    设置页面输出缓存可以使用以下两种方式:一种是使用@OutputCache指令,另一种是使用页面输出缓存API。页面输出缓存API主要是指HttpCachePolicy类。

    @ OutputCache以声明的方式控制 ASP.NET 页或页中包含的用户控件的输出缓存策略。

    语法如下:

    <%@ OutputCache Duration="#ofseconds"
       Location="Any | Client | Downstream | Server | None | ServerAndClient "
       Shared="True | False"
       VaryByControl="controlname"
       VaryByCustom="browser | customstring"
       VaryByHeader="headers"
       VaryByParam="parametername" 
       CacheProfile="cache profile name | ''"
       NoStore="true | false"
       SqlDependency="database/table name pair | CommandNotification"
    %>

    Duration

    页或用户控件进行缓存的时间(以秒计)。在页或用户控件上设置该属性为来自对象的 HTTP 响应建立了一个过期策略,并将自动缓存页或用户控件输出。

    Location

    用于指定输出缓存项的位置。其属性值是OutputCacheLocation枚举值,它们是Any、Client、Downstream、None、Server和ServerAndClient。默认值是Any,表示输出缓存可用于所有请求,包括客户端浏览器、代理服务器或处理请求的服务器上。需要注意的是,包含在用户控件中的@OutputCache指令不支持此属性。

    Shared

    一个布尔值,确定用户控件输出是否可以由多个页共享。默认值为 false。

    NoStore

    该属性定义一个布尔值,用于决定是否阻止敏感信息的二级存储。需要注意的是,包含在用户控件中的
    @OutputCache指令不支持此属性。

    SqlDependency

        提供了这样一种能力:当被监测的数据库中的数据发生变化时,SqlDependency会自动触发OnChange事件来通知应用程序,从而达到让系统自动更新数据(或缓存)的目的。

    VaryByControl

    该属性使用一个分号分隔的字符串列表,来更改用户控件的输出缓存。这些字符串代表在用户控件中声明的ASP.NET服务器控件的ID属性值。除非已经包含了VaryByParam属性,否则在@ OutputCache指令中,该属性是必需的。

    VaryByCustom

    用于自定义输出缓存要求的任意文本。如果赋予该属性值是browser,缓存将随浏览器名称和主要版本信息的不同而异。如果输入了自定义字符串,则必须在应用程序的Global.asax文件中重写HttpApplication.GetVaryByCustomString方法。

    VaryByHeader

    该属性中包含由分号分隔的HTTP标头列表,用于使输出缓存发生变化。当将该属性设为多标头时,对于每个指定的标头,输出缓存都包含一个请求文档的不同版本。VaryByHeader属性在所有HTTP 1.1缓存中启用缓存项,而不仅限于ASP.NET缓存。用户控件中的@ OutputCache指令不支持此属性。

    三、页面输出缓存API

    Response类的Cache属性用于获取页面缓存策略,Cache属性的核心是调用System.Web.HttpCachePolicy。主要用于设置缓存特定的HTTP标头的方法和用于控制ASP.NET页面输出缓存的方法。由于HttpCachePolicy类方法众多,下面简要说明几个常用方法。

    SetExpires

    用于设置缓存过期的绝对时间。它的参数是一个DataTime类的实例,表示过期的绝对时间。

    SetLastModified

    用于设置页面的Last-Modified HTTP标头。Last-Modified HTTP标头表示页面上次修改时间,缓存将依靠它来进行计时。如果违反了缓存限制层次结构,此方法将失败。该方法的参数是一个DataTime类的实例。

    SetSlidingExpiration 

    该方法将缓存过期从绝对时间设置为可调时间。其参数是一个布尔值。当参数为true时,Cache-Control HTTP标头将随每个响应而更新。当参数为false时,将保留该设置,且任何启用可调整过期的尝试都将静态失败。此方法不直接映射到HTTP标头,它由后续模块或辅助请求来设置源服务器缓存策略。

    SetCacheability

    用于设置页面的Cache-Control HTTP标头。该标头用于控制在网络上缓存文档的方式。该方法有两种重载方式,所不同的是参数。一种重载方法的参数是HttpCacheability枚举值,包括NoCache、Private、Public、Server、ServerAndNoCache和ServerAndPrivate。另一种方法的参数有两个,一个参数是HttpCacheability枚举值,另一个参数是字符串,表示添加到标头的缓存控制扩展。需要注意的是,仅当与Private或NoCache指令一起使用时,字段扩展名才有效。如果组合不兼容的指令和扩展,则此方法将引发无效参数异常。

    Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));

    Response.Cache.SetExpires(DateTime.Parse("6:00:00PM"));

    Response类的Cache属性用于获取页面缓存策略。该属性的数据类型是HttpCachePolicy。可通过调用Response.Cache来获取HttpCachePolicy实例,进而实现对于当前页面输出缓存的设置。如上代码所示,第一行代码表示输出缓存时间是60秒,并且页面不随任何GET或POST参数改变,等同于“<%@ OutputCache Duration="60" VaryByParam="none" %>”。第二行代码设置缓存过期的绝对时间是当日下午6时整。

    四、页面部分缓存

    有时缓存整个页面是不现实的,因为页的某些部分可能在每次请求时都需要变化。在这些情况下,只能缓存页的一部分。顾名思义,页面部分缓存是将页面部分内容保存在内存中以便响应用户请求,而页面其他部分内容则为动态内容。在页面中经常存在某些部分固定不变(如导航栏,页面右侧的排行榜等信息),而其他内容则需要根据用户请求的不同动态生成。所以我们可以将固定部分的内容缓存在服务器内存中,以减少服务器查询数据时间。

    页面部分缓存也称为控件缓存,这种方式允许将需要缓存的信息包含在一个用户控件内。然后,将该用户控件标记为可缓存的,以此来缓存页面输出的部分内容。这种方式允许缓存页面中的特定内容,而不缓存整个页面。例如,如果要创建一个显示大量动态内容(如股票信息)的页,其中有些部分为静态内容(如每周总结),这时可以将静态部分放在用户控件中,并允许缓存这些内容。

    页面部分缓存示例如下: 

    CacheControl.ascx的页头代码中添加声明语句:

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CacheControl.ascx.cs" Inherits="CacheControl" %>

      <%@ OutputCache Duration="60" VaryByParam="none" %>

    <%=DateTime.Now %>  

    调用该控件的页面代码:

     <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CacheDemo.aspx.cs"%>
    <%@ Register src="CacheControl.ascx" tagname="CacheControl" tagprefix="uc" %>
    <head runat="server">
        <title>CacheDemo</title>
    </head>
    <body>
        <form id="form1" runat="server">
        页面的时间:<%=DateTime.Now %> <br/>
      控件的时间:<uc:CacheControl ID="CacheControl" runat="server" /><br/>
        </form>
    </body> 
    </html>

    当刷新页面时,“页面的时间”每次刷新都变化,而“控件的时间”的数据却是60秒才变化一次。说明对页面的“局部”控件实现了缓存,而页面其它部分不受影响。

    五、 应用程序数据缓存

    应用程序数据缓存提供了一种编程方式,可通过键/值对将任意数据存储在内存中。使用应用程序缓存与使用应用程序状态类似。但是,与应用程序状态不同的是,应用程序数据缓存中的数据是易失的,即数据并不是在整个应用程序生命周期中都存储在内存中。应用程序数据缓存的优点是由ASP.NET管理缓存,它会在项过期、无效,或内存不足时移除缓存中的项。还可以配置应用程序缓存,以便在移除项时通知应用程序。

    如下所示插入一个字符串进缓存:

    Cache["name"]="CacheDemo";

    这个存储的字串值可以像这样得到:

    if (Cache["name"] != null)
    {
    string name = Cache["name"].ToString();
    }

    六、 缓存依赖

    前文的几种方式都可以实现数据缓存功能,但问题是数据有时候是在变化的。这样用户可能在缓存期间查询的数据就是过期的数据,从而导致数据的不一致。那有没有办法在数据变化时,系统能自动更新缓存中的数据,从而让用户得到实时有效的数据。

    .NET已经为我们提供了这样一种非常好的解决方法:SqlCacheDependency数据库缓存依赖。下面就让我们看一下如何实现数据库缓存依赖功能:

    1. 配置SqlCacheDependency 

    修改web.config,让项目启用SqlCacheDependency。

    <?xml version="1.0"?>
    <configuration>
        <appSettings/>
        <connectionStrings>
            <add name="strcodematic" connectionString="data source=dbserver;initial catalog=codematic;user id=sa;password=sa"  providerName="System.Data.SqlClient" />
        </connectionStrings>
        <system.web>
            <caching>
                <sqlCacheDependency enabled="true" pollTime="6000">
                    <databases>
     <add name="codematic" connectionStringName="strcodematic" />
                    </databases>
                </sqlCacheDependency>         
            </caching>
          <compilation debug="true">
            </compilation>     
            <authentication mode="Windows"/>    
        </system.web>
    </configuration> 

    注意:在<databases>节的<add name="codematic" connectionStringName="strcodematic" />中的name属性值必须和第三步的Page_Load代码中System.Web.Caching.SqlCacheDependency("codematic", "P_Product"); 中的第一个参数(数据库名称)相一致。

    这里的connectionStringName指定了在<connectionStrings>中添加的某一个连接字符串。name则是为该SqlCacheDependency起的名字,这个名字将在第三步中用到。

    SqlCacheDependency类会自动完成对此配置节信息的读取,以建立和数据库之间的联系。

    2.启用缓存依赖

    如果要启用SqlCacheDependency,则需要以命令行的方式执行。

    aspnet_regsql.exe工具位于Windows\Microsoft.NET\Framework\[版本]文件夹中。

    aspnet_regsql -C "data source=.;initial catalog=codematic;user id=sa;password=123456" -ed -et -t "BookInfo"

    参数-C后面的字符串是连接字符串(请替换成自己所需要的值),参数-t后面的字符串是数据表的名字。

     运行结果如图所示:

    命令执行后,在指定的数据库中会多出一个AspNet_SqlCacheTablesForChangeNotification表。

    要使得7.0或者2000版本以上的SQLServer支持SqlCacheDependency特性,需要对数据库服务器执行相关的配置。

    有两种方法配置SQLServer:

    一 使用aspnet_regsql命令行工具。

    二 使用SqlCacheDependencyAdmin类。(msdn看到的,没用过

    例如:

    aspnet_regsql -S "server" -E -d "database" –ed

    或者

    aspnet_regsql -S "server" -E -d "database" -et -t "table"

    如果是Sql验证的话要把-E换成,-U (用户名),-P (密码)

    以下是该工具的命令参数说明:

    -? 显示该工具的帮助功能;

    -S 后接的参数为数据库服务器的名称或者IP地址;

    -U 后接的参数为数据库的登陆用户名;

    -P 后接的参数为数据库的登陆密码;

    -E 使用当前登录用户的 Windows 集成认证进行身份验证;

    -d 后接参数为对哪一个数据库采用SqlCacheDependency功能;

    -C 连接数据库的连接字符串。如果您指定服务器(-S)和登录(-U和-P,或 -E)信息,则此选项不是必需的,因为连接字符串已经包含这些信息;
    -t 后接参数为对哪一个表采用SqlCacheDependency功能;
    -ed 允许对数据库使用SqlCacheDependency功能;
    -dd 禁止对数据库采用SqlCacheDependency功能;
    -et 允许对数据表采用SqlCacheDependency功能;
    -dt 禁止对数据表采用SqlCacheDependency功能;
    -lt 列出当前数据库中有哪些表已经采用sqlcachedependency功能;

    3.0使用SqlCacheDependency 

    在代码中使用缓存,并为其设置SqlCacheDependency依赖:

     

    /// <summary>
    /// 获取当前应用程序指定CacheKey的Cache对象值
    /// </summary>
    /// <param name="CacheKey">索引键值</param>
    /// <returns>返回缓存对象</returns>
    public static object GetCache(string CacheKey)
    {
        System.Web.Caching.Cache objCache = HttpRuntime.Cache;
        return objCache[CacheKey];
    }
    /// <summary>
    /// 设置以缓存依赖的方式缓存数据
    /// </summary>
    /// <param name="CacheKey">索引键值</param>
    /// <param name="objObject">缓存对象</param>
    /// <param name="cacheDepen">依赖对象</param>
    public static void SetCache(string CacheKey, object objObject, System.Web.Caching.CacheDependency dep)
    {
        System.Web.Caching.Cache objCache = HttpRuntime.Cache;
        objCache.Insert(
            CacheKey,
            objObject,
            dep,
            System.Web.Caching.Cache.NoAbsoluteExpiration,//从不过期
            System.Web.Caching.Cache.NoSlidingExpiration,//禁用可调过期
            System.Web.Caching.CacheItemPriority.Default,
            null);
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        string CacheKey = "cachetest";
        object objModel = GetCache(CacheKey);//从缓存中获取
        if (objModel == null)//缓存里没有
        {
            objModel = GetData();//把当前时间进行缓存
            if (objModel != null)
            {
                //依赖数据库codematic中的P_Product表变化 来更新缓存
                System.Web.Caching.SqlCacheDependency dep = new System.Web.Caching.SqlCacheDependency("CodeMatic", "P_Product");
                SetCache(CacheKey, objModel, dep);//写入缓存
            }
        } 
        GridView1.DataSource = (DataSet)objModel;
        GridView1.DataBind();
    }

    从以上代码可以看出,和文件依赖基本相同,只是在存放缓存SetCache时存入的依赖对象不同。这里用的是SqlCacheDependency。

    其中,创建SqlCacheDependency的构造方法:

    public SqlCacheDependency (string databaseEntryName,string tableName)
    //databaseEntryName:是在Web.config文件的caching节的sqlCacheDependency的databases元素中定义的数据库的名称。 
    //tableName:与SqlCacheDependency关联的数据库表的名称。
    //这样,只有当P_Product表的内容发生变化时,查询操作才会重新查询数据更新缓存的内容,可以大大减少数据库的重复查询和提高系统的性能和运行效率。

    七、缓存管理类

    首先,缓存管理类外部代码定义了各种过期时间的常量,主要用于方便设定缓存的过期时间

    /// <summary>
        /// 全站缓存类
        /// </summary>
        public sealed class SiteCache
        {
            // 一天
            public const int DayFactor = 17280;
            // 一小时
            public const int HourFactor = 720;
            // 一分钟
            public const int MinuteFactor = 12; 
            // 一秒
            public const double SecondFactor = 0.2;
    
            private static int factor = 5;
            /// <summary>
            /// 时间因子
            /// </summary>
            public static int Factor
            {
                get { return factor; }
                set { factor = value; }
            }
    
     public static Cache CurrentCache {
                get {
                    return HttpRuntime.Cache;
                }
            }
            }
    //Clear方法用于移除所有存储在内存当中的缓存,代码中cacheEnum变量通过GetEnumerator方法获得所有缓存列表,再根据缓存列表来移除缓存。
            public static void Clear()
            {
                //获取缓存中的键设置及其值的字典枚举数
                IDictionaryEnumerator cacheEnum = siteCache.GetEnumerator();
                ArrayList al = new ArrayList();
                //循环添加到数组列表
                while (cacheEnum.MoveNext())
                {
                    al.Add(cacheEnum.Key);
                }
                //循环从缓存中移除
                foreach (string key in al)
                {
                    siteCache.Remove(key);
                }
            }
    //RemoveByPattern方法是通过正则移除对应的缓存。
     public static void RemoveByPattern(string pattern)
            {
                IDictionaryEnumerator cacheEnum = siteCache.GetEnumerator();
                Regex regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
                ArrayList al = new ArrayList();
                //循环从缓存中取出键进行正则条件匹配
                while (cacheEnum.MoveNext())
                {
                    //匹配正确则添加到数组列表
                    if (regex.IsMatch(cacheEnum.Key.ToString()))
                    {
                        al.Add(cacheEnum.Key);
                    }
                }
                //移除匹配正则的缓存
                foreach (string key in al)
                {
                    siteCache.Remove(key);
                }
            }
    //Insert方法是通过Cache的插入方法而实现的,主要能够把缓存键、缓存对象、缓存依赖、缓存过期时间、缓存级别这些缓存项插入到内存中。
        /// <summary>
            /// 插入缓存项
            /// </summary>
            public static void Insert(string key, object value, System.Web.Caching.CacheDependency dep, int seconds, CacheItemPriority priority)
            {
                if (value != null)
                {
                    siteCache.Insert(key, value, dep, DateTime.Now.AddSeconds(Factor * seconds), TimeSpan.Zero, priority, null);
                }
            }
    //Get方法通过指定的键从Cache中获取缓存
            public static object Get(string key)
            {
                return siteCache[key];
            }
        }

     八、缓存的应用规则

    缓存分配的有效期不要太短。
    缓存那些经常被访问,同时变化频率不大的数据。
    缓存整个应用程序都要使用的设置或对象,并且这些设置和对象在生存期内不会频繁变化。
    不要缓存太多项,缓存每个项均有开销。
    不要缓存很少使用的项。
    不要缓存容易重新计算或随时都可能会修改的对象,如购物车。
    不要缓存敏感信息,否则其他人很容易取得这些信息。

    比如:可以缓存网站信息配置

    GetConfig是一个泛型方法,网站信息配置里的所有功能属性都是通过它来获得对象。方法后面跟着一个where子句,用于指定对类型的约束。其中class限制了类型参数必须是引用类型, new()限制了该类型参数必须具有无参数的公共构造函数。

    方法内首先获取了T变量的类型。然后根据类型名拼装成configCacheKey缓存键,通过SiteCache类的Get方法获取缓存的值并存储到configObject变量中。接着判断该缓存是否不存在值,如果不存在,使用GetConfigPath方法获得了T变量的物理文件地址。

    public static T GetConfig<T>() where T : class, new()
    {
         Type configClassType = typeof(T);
         string configCacheKey = "CK_SiteConfigCode_" + configClassType.Name;
         object configObject = SiteCache.Get(configCacheKey);
         if (configObject == null)
         {
              string configFilePath = GetConfigPath<T>();
    //下面检查T变量的物理文件地址是否存在,如果存在即读取T变量的配置信息,并将信息插入到缓存中。其中Insert方法使用了缓存依赖CacheDependency实例监视文件或目录路径,当该资源更改时,缓存的对象将过时,从缓存中移除。
              if (File.Exists(configFilePath))
              {
                  using (XmlTextReader xmlTextReader = new XmlTextReader(configFilePath))
                  {
                       XmlSerializer xmlSerializer = new XmlSerializer(configClassType);
                       configObject = xmlSerializer.Deserialize(xmlTextReader);
                  }
                  SiteCache.Insert(configCacheKey, configObject, new CacheDependency(configFilePath)); 
              }
         }
    //最后,把缓存对象转换为T泛型变量。判断是否为空,如果是则创建新的T实例返回,否则将原有值返回。
         T config = configObject as T;
         if (config == null)
         {
             return new T();
         }
         else
         {
             return config;
         }
    }
     
  • 相关阅读:
    Sicily shortest path in unweighted graph
    Sicily connect components in undirected graph
    Sicily 1931. 卡片游戏
    Sicily 1021. Couples
    c++ 高效文本读写
    Sicily 1129. ISBN
    Sicily 1133. SPAM
    Sicily 1282. Computer Game
    小工具?不,这是小工具的集合!
    .Net Core建站(4):FTP发布项目及连接服务器数据库
  • 原文地址:https://www.cnblogs.com/entclark/p/8654918.html
Copyright © 2020-2023  润新知