C# System.Web.Caching.Cache类 缓存 各种缓存依赖
原文:https://www.cnblogs.com/kissdodog/archive/2013/05/07/3064895.html
Cache类,是一个用于缓存常用信息的类。HttpRuntime.Cache以及HttpContext.Current.Cache都是该类的实例。 一、属性
二、方法
三、静态字段
先来看基本的示例: index.aspx.cs页面代码: namespace WebApplication1 { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Person p = new Person(); p.Id = 1; p.Name = "诸葛亮"; Cache cache = HttpRuntime.Cache; cache.Insert("AA",p); cache.Insert("BB","字符串"); Response.Write(cache.Get("BB").ToString()); //输出 字符串 Person p2 = cache["AA"] as Person; Response.Write(p2.Id + " : " + p2.Name); //输出 1 : 诸葛亮 Response.Write(cache.EffectivePrivateBytesLimit); //-1 这是一个只读属性,那就没什么好说了,只能输出来看看了,但是-1是什么意思呢?无限吗 Response.Write(cache.EffectivePercentagePhysicalMemoryLimit); //98 开始移除项之前可以使用到98% Response.Write(cache.Count); //输出 3 Response.Write(cache["BB"]); //输出 字符串 支持索引器式的读取 cache.Remove("BB"); //从cache中移除一项 Response.Write("~~~" + cache["BB"] + "~~~"); //移除了输出 null,但程序不报错 foreach (var obj in cache) { Response.Write(obj.GetType() + "<br/>"); //输出不知道什么鸟东西 } } } public class Person { public int Id { get; set; } public string Name { get; set; } } } 四、文件缓存依赖当被依赖的文件更改时,缓存会立即被清空: index.aspx.cs代码: public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Cache cache = HttpContext.Current.Cache; //文件缓存依赖 cache.Insert("CC", "依赖项测试", new CacheDependency(@"D:\123.txt")); //这时候在about.aspx页面添加一行代码,当更改一下D:123.txt时,cache["cc"]会立即被清空 } } about.aspx.cs代码: public partial class About : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { //直接打开本页面,输出缓存依赖项测试 //当更改D:\123.txt之后,在刷新,输出空,表明该Cache是依赖于D:\123.txt的 Response.Write(HttpContext.Current.Cache["CC"]); } } 五、NoSlidingExpiration 绝对过期时间NoSlidingExpiration 绝对过期时间,当超过设定时间,立即移除。 下面来看下绝对过期时间的示例,index.aspx.cs: public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Cache cache = HttpContext.Current.Cache; //30秒后就到期,立即移除,没商量 cache.Insert("DD", "绝对过期测试", null, DateTime.Now.AddSeconds(5), System.Web.Caching.Cache.NoSlidingExpiration); } } about.aspx.cs: public partial class About : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { //先打开index.aspx添加到缓存 然后立即打开本页面,输出 绝对过期测试 //持续刷新5秒后,不会再输出 绝对过期测试 Response.Write(HttpContext.Current.Cache["DD"]); } } 六、NoAbsoluteExpiration 滑动过期时间NoAbsoluteExpiration 当超过设定时间没再使用时,才移除缓存 滑动过期测试,index.aspx.cs: public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Cache cache = HttpContext.Current.Cache; //弹性过期时间,当缓存没使用10秒就过期 cache.Insert("DD", "滑动过期测试", null, System.Web.Caching.Cache.NoAbsoluteExpiration,TimeSpan.FromSeconds(10)); } } about.aspx.cs: public partial class About : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { //直接打开本页面,输出弹性过期测试 //如果一直不停地刷新,都会继续输出,但是当超过10秒后再刷新,不会再输出 滑动缓存测试 Response.Write(HttpContext.Current.Cache["DD"]); } } 注意 当设置绝对到期时间时,请使用 DateTime 结构。当设置弹性过期时间时,请使用 TimeSpan 结构。另外,如果您创建的弹性过期时间小于零或大于一年,则将引发 ArgumentOutOfRangeException 类。 七、缓存的优先级设置CacheItemPriority枚举 设置缓存的优先级 public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Cache cache = HttpContext.Current.Cache; //文件权重级别 cache.Add("MyData", "缓存重要级别", null, Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(30), CacheItemPriority.High, null); } } 该枚举一共有六级: //在服务器释放系统内存时,具有该优先级级别的缓存项最有可能被从缓存删除。 Low = 1,//在服务器释放系统内存时,具有该优先级级别的缓存项比分配了 System.Web.Caching.CacheItemPriority.Normal //优先级的项更有可能被从缓存删除。 BelowNormal = 2,//在服务器释放系统内存时,具有该优先级级别的缓存项很有可能被从缓存删除,其被删除的可能性仅次于具有 System.Web.Caching.CacheItemPriority.Low Normal = 3,//缓存项优先级的默认值为 System.Web.Caching.CacheItemPriority.Normal。 Default = 3,//在服务器释放系统内存时,具有该优先级级别的缓存项被删除的可能性比分配了 System.Web.Caching.CacheItemPriority.Normal //优先级的项要小。 AboveNormal = 4,//在服务器释放系统内存时,具有该优先级级别的缓存项最不可能被从缓存删除。 High = 5,//在服务器释放系统内存时,具有该优先级级别的缓存项将不会被自动从缓存删除。但是,具有该优先级级别的项会根据项的绝对到期时间或可调整到期时间与其他项一起被移除 NotRemovable = 6,
八、当缓存被移除时,通知程序这时就要用到Add的最后一个参数,委托了: index.aspx.cs代码如下: public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Cache cache = HttpRuntime.Cache; //文件权重级别 cache.Add("MyData", "缓冲移除通知", null, DateTime.Now.AddSeconds(10) ,Cache.NoSlidingExpiration,CacheItemPriority.Low, Show); } public void Show(string key, object value, CacheItemRemovedReason reason) { Cache cache = HttpRuntime.Cache; Cache.Insert("MyData", "缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!"); } } about.aspx.cs代码如下: public partial class About : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Response.Write(HttpRuntime.Cache["MyData"]); } } 此处实现的效果时:第一次打开index.aspx(让程序加入数据到缓存),然后打开about.aspx显示出“缓存移除通知”,10秒后再刷新,显示 “缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!缓存被清空啦!” 经测试,以上程序的Cache最好还是用HttpRuntime的,否则没有请求时HttpContext会报,“未将对象引用设置到对象的实例”。 这就是被清空时会自动调用委托程序进行处理,你可以再次将数据添加进入缓存,或者记录到日志等等一系列操作。 九、数据库依赖缓存1、配置的方式(sqlserver2000) SqlDependency第一个构造函数。 首先一个WebForm的Web.Config中的配置文件修改如下: <connectionStrings> <add name="ApplicationServices" connectionString="Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <compilation debug="true" targetFramework="4.0" /> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> //此行配置的意思是,开启数据库缓存,轮询时间为1秒,这是为了能够快速看到更改效果 <databases> <add connectionStringName="ApplicationServices" name="con"/> </databases> </sqlCacheDependency> </caching> </system.web> 修改Global.asax.cs文件代码如下: void Application_Start(object sender, EventArgs e) { string connString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString; SqlCacheDependencyAdmin.EnableNotifications(connectionString); //启动数据库的数据缓存依赖功能 Index.aspx.cs文件代码如下: protected void Page_Load(object sender, EventArgs e) { SqlCacheDependency dependency = new SqlCacheDependency("con", "Record"); // 新增或修改一条缓存记录 HttpRuntime.Cache.Insert("MyData", "数据库缓存测试", dependency); } About.aspx.cs文件代码如下: protected void Page_Load(object sender, EventArgs e) { Response.Write(HttpRuntime.Cache["MyData"]); } 次数实现的效果时,打开Index.aspx.cs文件将数据添加到缓存后,然后打开about.asox,页面输出"数据库缓存测试",这时候更改一下数据库的Record表,再刷新about.aspx页面,上一次输出的内容没有了。 System.Data.SqlClient.SqlDependency.Start(connString); System.Data.SqlClient.SqlDependency.Stop(connString); 这两行代码不一定要放在Global.asax.cs里面,在代码执行之前和执行之后就OK了。 注意,在这个例子当中,数据库要开启Service Broker 检测是否已经启用Service Broker Select DATABASEpRoPERTYEX('数据库名称','IsBrokerEnabled') -- 1 表示已经启用 0 表示没有启用 启用Servicce Broker ALTER DATABASE NX SET ENABLE_BROKER; 如果启动时,一直显示正在执行查询,那么用一下这条语句 ALTER DATABASE NX SET NEW_BROKER WITH ROLLBACK IMMEDIATE; ALTER DATABASE NX SET ENABLE_BROKER; 2、编程的方式(sqlserver2008),第二个构造函数SqlDependency(sqlcommand sqlcom) index.aspx.cs文件代码如下: protected void Page_Load(object sender, EventArgs e) { SqlCacheDependency dependency; SqlConnection conn = new SqlConnection(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); using (conn) { string sql = "select name from dbo.record"; SqlCommand cmd = new SqlCommand(sql, conn); dependency = new SqlCacheDependency(cmd); SqlDataAdapter adapter = new SqlDataAdapter(cmd); //这里要特别注意,你的cmd一定要执行过,或者说Execute过,真正从数据库里面取出过数据 DataSet ds = new DataSet(); adapter.Fill(ds); //这里才是cmd真正执行的地方,注意真正执行要在绑定dependency之后 } Response.Write("开始!"); Cache.Insert("MyData", "数据库依赖测试", dependency); Response.Write("完成!"); } about.aspx.cs代码文件如下: public partial class About : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (HttpRuntime.Cache["MyData"] != null) { Response.Write(HttpRuntime.Cache["MyData"]); } else { Response.Write("缓存已不存在!"); } } } 实现的效果一样是,打开index.aspx文件,再打开about.aspx文件,页面输出"缓存测试结果",当更改表record之后,再刷新about.aspx页面,缓存被清空。 再来一个例子,上次又是页面啥的,这次用十几行代码说明,这个东东实现的要点: static void Main(string[] args) { Cache cache = HttpRuntime.Cache; System.Data.SqlClient.SqlDependency.Start(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); // 创建缓存依赖 SqlConnection conn = new SqlConnection(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); SqlCommand command = new SqlCommand("select Id,name from dbo.Record", conn); SqlCacheDependency dependency = new SqlCacheDependency(command); //注意,创建的command与SqlCacheDependency绑定要在command执行之前 SqlDataAdapter adapter = new SqlDataAdapter(command); DataSet ds = new DataSet(); adapter.Fill(ds); //这里要特别注意,Fill才是真正执行 cache.Insert("DD", "数据库依赖测试", dependency); Console.WriteLine(cache["DD"]); Thread.Sleep(20000); //暂停20秒给你更改一下数据库 if (cache["DD"] == null) { Console.WriteLine("数据库已经修改过了!"); } Console.ReadKey(); System.Data.SqlClient.SqlDependency.Stop(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); } 在途中,我会随便修改一下Record表,输出结果如下: 再来一次数据库缓存依赖,回调函数的例子: public class Program { static void Main(string[] args) { Cache cache = HttpRuntime.Cache; System.Data.SqlClient.SqlDependency.Start(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); // 创建缓存依赖 SqlConnection conn = new SqlConnection(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); SqlCommand command = new SqlCommand("select Id,name from dbo.Record", conn); SqlCacheDependency dependency = new SqlCacheDependency(command); //注意,创建的command与SqlCacheDependency绑定要在command执行之前 SqlDataAdapter adapter = new SqlDataAdapter(command); DataSet ds = new DataSet(); adapter.Fill(ds); //这里要特别注意,Fill才是真正执行 CacheItemRemovedCallback callback = new CacheItemRemovedCallback(RemovedCallback); cache.Insert("DD", "数据库依赖测试", dependency, DateTime.Now.AddDays(1), TimeSpan.Zero, CacheItemPriority.Default, callback); Console.WriteLine(cache["DD"]); Thread.Sleep(15000); //暂停15秒给你更改一下数据库 if (cache["DD"] == null) { Console.WriteLine("数据库已经修改过了!"); } Console.ReadKey(); System.Data.SqlClient.SqlDependency.Stop(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); } public static void RemovedCallback(string key, object value, CacheItemRemovedReason reason) { Console.WriteLine("缓存被移除!"); Console.WriteLine(reason.ToString()); } } 输出结果如下: 这里要补充几点:当设置为绝对过期时,后面那个TimeSpan参数要设为TimeSpan.Zero。另外,重载中还有一个方法之前在缓存移除前执行回调函数的,而刚刚那个是缓存数据被移除之后执行的。 CacheItemUpdateCallback 缓存数据移除之前调用; CacheItemRemoceCallback 缓存数据移除之后调用; 再来写一个: public class Program { static void Main(string[] args) { Cache cache = HttpRuntime.Cache; System.Data.SqlClient.SqlDependency.Start(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); // 创建缓存依赖 SqlConnection conn = new SqlConnection(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); SqlCommand command = new SqlCommand("select Id,name from dbo.Record", conn); SqlCacheDependency dependency = new SqlCacheDependency(command); //注意,创建的command与SqlCacheDependency绑定要在command执行之前 SqlDataAdapter adapter = new SqlDataAdapter(command); DataSet ds = new DataSet(); adapter.Fill(ds); //这里要特别注意,Fill才是真正执行 CacheItemUpdateCallback callback = new CacheItemUpdateCallback(RemovedCallback); cache.Insert("DD", "数据库依赖测试", dependency, DateTime.Now.AddDays(1), TimeSpan.Zero, callback); Console.WriteLine(cache["DD"]); Thread.Sleep(15000); //暂停15秒给你更改一下数据库 if (cache["DD"] == null) { Console.WriteLine("数据库已经修改过了!"); } Console.ReadKey(); System.Data.SqlClient.SqlDependency.Stop(@"Server=KISSDODOG-PC;Database=Nx;uid=sa;pwd=123;"); } public static void RemovedCallback(string key, CacheItemUpdateReason reason, out object expensiveObject, out CacheDependency dependency, out DateTime absoluteExpiration, out TimeSpan slidingExpiration) { // key:要从缓存中移除的项的标识符。 // reason:要从缓存中移除项的原因。 // expensiveObject:此方法返回时,包含含有更新的缓存项对象。 // dependency:此方法返回时,包含定义项对象和文件、缓存键、文件或缓存键的数组或另一个 System.Web.Caching.CacheDependency 对象之间的依赖项的对象。 // absoluteExpiration:此方法返回时,包含对象的到期时间。 // slidingExpiration:此方法返回时,包含对象的上次访问时间和对象的到期时间之间的时间间隔。 expensiveObject = null; dependency = null; absoluteExpiration = DateTime.Now; slidingExpiration = TimeSpan.Zero; Console.WriteLine("缓存马上被移除!"); //但是现在还有没有呢?输出试试 Console.WriteLine(HttpRuntime.Cache["DD"]); Console.WriteLine(reason.ToString()); } } 输出结果为: 看到,在Update这个委托执行时,缓存还是存在的。 注意:那个SQL语句还是比较严格的,主要要符合以下要求 a). 必须设置完全限定名称的数据表。即表名前面需要加所有者,如dbo.test。 b). 必须明确设置所访问数据库列名称,不能使用“*”。 c). 必须保证不是聚合函数。如COUNT、MAX等。
很明显,SQL SERVER 2005的缓存机制更加高效。另外,SqlCacheDependency类还特别结合SQL SERVER 2005 进行了优化: a). 使用SQL SERVER 2005 时,SqlCacheDependency类支持与System.Data.SqlClient.SqlDependency类进行集成。应用程序可创建SqlDependency对象,并通过OnChanged事件处理程序接受通知进行注册。这样,应用程序不仅可以使用Sql server 2005的查询通知机制来监测使用SQL查询结果无效的数据更改,并将缓存对象从缓存中移除,而且还可以轻松获取数据更改通知,以便刷新缓存。(从这里可以看出,当触发onRemoveCallback委托的时候,数据已经从缓存里面删除了,这样一来可以手动在委托里面添加缓存,或者干脆设置成null,让他下次调用的时候再缓存。) b). 不仅向应用程序添加缓存依赖项,还可以与@OutputCache指令一起使用,以生成依赖于SqlServer数据库表的输出缓存的页面或用户控件。对于用户控件,@OutputCache指令不支持使用SQL SERVER 2005 的查询通知(即onRemoveCallback委托)。 十、组合依赖缓存 当依赖的内容为多个Dependency时,可以通过AggregateCacheDependency创建依赖的组合,以便在任何一个依赖项发生变化时,使缓存项失效。 组合依赖缓存示例: //创建缓存依赖 System.Web.Caching.SqlCacheDependency product = new System.Web.Caching.SqlCacheDependency("northwindcache","products"); System.Web.Caching.SqlCacheDependency category = new System.Web.Caching.SqlCacheDependency("northwindcache","products") //多个缓存依赖组合成一个依赖对象使用 System.Web.Caching.AggregateCacheDependency acd = new System.Web.Caching.AggregateCacheDependency(); acd.Add(product,category);
十一、System.Web.Caching.Cache Insert和Add方法的区别Add方法 object Add(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback); Insert方法 void Insert(string key, object value); //永不过期 void Insert(string key, object value, CacheDependency dependencies); //依赖 void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration);//绝对时间过期: void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemUpdateCallback onUpdateCallback); //依赖+回调 void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback); //依赖+优先级+回调 比较、区别 a). Insert方法支持5种重载,使用灵活,而Add方法必须提供7个参数; b). Add方法可以返回缓存项的数据对象,Insert 返回Void; c). 添加重复缓存情况下(Key已存在),Insert会替换该项,而Add方法则不执行任何操作,并返回原来保存的object对象(Update 2014-03-18)。 过期策略 a). 永不过期 b). 绝对时间过期 DateTime.Now.AddSeconds(10)表示缓存在10秒后过期,TimeSpan.Zero表示不使用平滑过期策略。 例:Cache.Insert("Data", ds,null, DateTime.Now.AddSeconds(10), TimeSpan.Zero); c). 变化时间过期(平滑过期) DateTime.MaxValue表示不使用绝对时间过期策略,TimeSpan.FromSeconds(10)表示缓存连续10秒没有访问就过期。 例:Cache.Insert("Data", ds, null, DateTime.MaxValue, TimeSpan.FromSeconds(10)); 十二、清空缓存清空缓存主要通过Remove()方法,但是只能通过传入一个Key,清空一个。 GetEnumerator()方法用于获取所有缓存项。MoveNext()用于将位置移动到下一个缓存项。 如果想清空所有缓存,由于Cache类没有提供RemoveAll()方法,所以可以通过以下方式实现: IDictionaryEnumerator DicCache = HttpRuntime.Cache.GetEnumerator(); int count = HttpRuntime.Cache.Count; for (int i = 0; i < count; i++) { DicCache.MoveNext(); HttpRuntime.Cache.Remove(DicCache.Entry.Key.ToString()); } 十三、缓存依赖总结缓存依赖主要有如下技术点 Dependency依赖:.Net中支持两种依赖:CacheDependency和SqlDependency(两者都是Dependency的子类)。从.Net2.0开始,CacheDependency不再是密封的,而是可以继承的。 过期时间:过期时间分绝对过期时间和滑动过期时间两种。 删除通知:.Net提供了一个机制,当被缓存的数据从内存中移除的时候,可以提供一个通知机制,来回调用户定义的方法,方法必须符合CacheItemRemoveCallback委托的定义。该委托定义如下: public delegate void CacheItemRemoveCallback(string key,Object value,CacheItemRemovedReason reason) 其中,CacheItemRemoveReason为缓存项被移除的原因。CacheItemRemovedReason是一个枚举类型,定义了如下原因:
注意:
PS: 1、 CacheDependency是AggregateCacheDependency和SqlCacheDependency的父类。主要用于在应用程序数据缓存对象与文件、缓存键、文件或缓存键的数组或另外一个CacheDependency对象之间建立依赖关系。CacheDependency监视依赖关系比便在任何对象更改时自动移除缓存对象。CacheDependency可以监测一组(到文件或目录的)文件路径的更改情况。 2、 AggregateCacheDependency主要用于实现聚合缓存依赖。如一笔数据同时对两个表进行缓存依赖,一旦其中任何一个表数据更改缓存将失效。 3、 SqlCacheDependency将应用程序数据缓存对象、页面输出缓存、数据源控件等与指定SQL Server数据库表或Sql Server 2005 查询结果之间建立缓存依赖关系,在表发生更改(Sql Server 2005 行级别更改)时,自动从缓存中删除和重新添加与该表关联的缓存对象。一般而言:
有以下几条缓存数据的规则。: 你应该认真分析你的程序。根据实际情况来看哪里该用,哪里不该用。如:Cache用得过多也会增大服务器的压力。整页输出缓存,又会影响数据的更新。 如果真的需要缓存很大量的数据,可以考虑静态技术。 CacheDependency还支持【嵌套】,即:CacheDependency的构造函数中支持传入其它的CacheDependency实例,这样可以构成一种非常复杂的树状依赖关系。 |