缓存在不少状况下须要用到,合理利用缓存能够一方面能够提升程序的响应速度,同时能够减小对特定资源访问的压力。本文主要针对本身在Winform方面的缓存使用作一个引导性的介绍,但愿你们可以从中了解一些缓存的使用场景和使用方法。缓存是一个中大型系统所必须考虑的问题。为了不每次请求都去访问后台的资源(例如数据库),咱们通常会考虑将一些更新不是很频繁的,能够重用的数据,经过必定的方式临时地保存起来,后续的请求根据状况能够直接访问这些保存起来的数据。这种机制就是所谓的缓存机制。html
.NET 4.0的缓存功能主要由三部分组成:System.Runtime.Caching,System.Web.Caching.Cache和Output Cache。web
MemoryCache对象:这是在.NET 4.0中新增的缓存框架,在Namespace:System.Runtime.Caching ,Assembly:System.Runtime.Caching.dll中。数据库
System.Web.Caching.Cache这个则是在.NET2.0开始就一直存在的缓存对象,通常主要用在Web中,固然也能够用于Winform里面,不过要引用System.Web.dll。缓存
Output Cache则是Asp.NET里面使用的,在ASP.NET 4.0以前的版本都是直接使用System.Web.Caching.Cache来缓存HTML片断。在ASP.NET 4.0中对它进行了从新设计,提供了一个OutputCacheProvider供开发人员进行扩展,可是它默认状况下,仍然使用System.Web.Caching.Cache来作作缓存。框架
一、自定义Hastable的缓存处理。
除了上面三种的缓存机制,通常咱们还能够在静态对象里面经过HashTable或者Dictionary的方式进行自定义的缓存存储和使用。ide
例如我在我本身所开发的程序里面,都使用了工厂类来建立业务对象,因为建立业务对象以及数据访问层对象,是一个在界面或者中间层反复调用的操做,所以须要把常常调用的对象把它存储起来,下载调用的时候,直接从内存中取出来便可。以下面的BLLFactory类,就是一个基于泛型对象的业务类的建立操做,使用了基于Hashtable的静态对象进行缓存处理。函数
/// <summary> /// 对业务类进行构造的工厂类 /// </summary> /// <typeparam name="T">业务对象类型</typeparam> public class BLLFactory<T> where T : class { private static Hashtable objCache = new Hashtable(); private static object syncRoot = new Object(); /// <summary> /// 建立或者从缓存中获取对应业务类的实例 /// </summary> public static T Instance { get { string CacheKey = typeof(T).FullName; T bll = (T)objCache[CacheKey]; //从缓存读取 if (bll == null) { lock (syncRoot) { if (bll == null) { bll = Reflect<T>.Create(typeof(T).FullName, typeof(T).Assembly.GetName().Name); //反射建立,并缓存 objCache.Add(typeof(T).FullName, bll); } } } return bll; } } }
二、使用.NET4.0的MemoryCache对象实现缓存post
MemoryCache的使用网上介绍的很少,不过这个是.NET4.0新引入的缓存对象,估计主要是替换原来企业库的缓存模块,使得.NET的缓存能够无处不在,而不用基于特定的Windows版本上使用。性能
首先咱们使用来建立一个基于MemoryCache的辅助类MemoryCacheHelper,方便调用进行缓存处理。测试
/// <summary> /// 基于MemoryCache的缓存辅助类 /// </summary> public static class MemoryCacheHelper { private static readonly Object _locker = new object(); public static T GetCacheItem<T>(String key, Func<T> cachePopulate, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null) { if(String.IsNullOrWhiteSpace(key)) throw new ArgumentException("Invalid cache key"); if(cachePopulate == null) throw new ArgumentNullException("cachePopulate"); if(slidingExpiration == null && absoluteExpiration == null) throw new ArgumentException("Either a sliding expiration or absolute must be provided"); if(MemoryCache.Default[key] == null) { lock(_locker) { if(MemoryCache.Default[key] == null) { var item = new CacheItem(key, cachePopulate()); var policy = CreatePolicy(slidingExpiration, absoluteExpiration); MemoryCache.Default.Add(item, policy); } } } return (T)MemoryCache.Default[key]; } private static CacheItemPolicy CreatePolicy(TimeSpan? slidingExpiration, DateTime? absoluteExpiration) { var policy = new CacheItemPolicy(); if(absoluteExpiration.HasValue) { policy.AbsoluteExpiration = absoluteExpiration.Value; } else if(slidingExpiration.HasValue) { policy.SlidingExpiration = slidingExpiration.Value; } policy.Priority = CacheItemPriority.Default; return policy; } }
下面在给个通过改良的版本,供参考吧!

/// <summary> /// 基于System.Runtime.Caching.dll中的MemoryCache的缓存辅助类 /// </summary> public static class MemoryCacheHelper { private static readonly Object _locker = new object(); /// <summary> /// 若是缓存中存在指定的key,则优先从缓存中返回 /// </summary> /// <typeparam name="T">返回值类型</typeparam> /// <param name="key">缓存关键字</param> /// <param name="cachePopulate">须要执行的方法、Lambda表达式、(匿名)代理等。 /// <code>例如:() => "测试", 或者 /// delegate () { return new aaa("测试"); }, /// </code> /// </param> /// <param name="slidingExpiration">滑动窗口模式的使用过时时间</param> /// <param name="absoluteExpiration">绝对过时时间</param> /// <returns></returns> public static T GetCacheItem<T>(String key, Func<T> cachePopulate, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null) { if (String.IsNullOrWhiteSpace(key)) throw new ArgumentException("Invalid cache key"); if (cachePopulate == null) throw new ArgumentNullException("cachePopulate"); if (slidingExpiration == null && absoluteExpiration == null) throw new ArgumentException("Either a sliding expiration or absolute must be provided"); if (MemoryCache.Default[key] == null) { lock (_locker) { if (MemoryCache.Default[key] == null) { var item = new CacheItem(key, cachePopulate()); var policy = CreatePolicy(slidingExpiration, absoluteExpiration); MemoryCache.Default.Add(item, policy); } } } return (T)((MemoryCache.Default[key] is T) ? MemoryCache.Default[key] : default(T)); //return (T)MemoryCache.Default[key]; } /// <summary> /// 若是缓存中存在指定的key,则优先从缓存中返回 /// </summary> /// <typeparam name="T">返回值类型</typeparam> /// <param name="key">缓存关键字</param> /// <param name="cachePopulate">须要执行的方法、Lambda表达式、(匿名)代理等。 /// <code>例如:() => "测试", 或者 /// delegate () { return new aaa("测试"); }, /// </code> /// </param> /// <param name="expirationTime">缓存过时时间</param> /// <param name="expirationTimeType">缓存过时时间类型</param> /// <param name="enabledCache">是否启用缓存,若是false则每次都是直接执行被调方法cachePopulate,默认启用,</param> /// <returns></returns> public static T GetCacheItem<T>(String key, Func<T> cachePopulate, TimeSpan expirationTime, ExpirationTimeType expirationTimeType, bool enabledCache = true) { if (String.IsNullOrWhiteSpace(key)) throw new ArgumentException("Invalid cache key"); if (cachePopulate == null) throw new ArgumentNullException("cachePopulate"); if (expirationTime == null) throw new ArgumentException("Either a sliding expiration must be provided"); T tmp = default(T); if (enabledCache) { if (MemoryCache.Default[key] == null) { lock (_locker) { if (MemoryCache.Default[key] == null) { Console.WriteLine("MemoryCache is null."); CacheItem item = new CacheItem(key, cachePopulate()); CacheItemPolicy policy = null; if (expirationTimeType == ExpirationTimeType.AbsoluteExpirationTimeType) policy = CreatePolicy(null, DateTime.Now.Add(expirationTime)); else if (expirationTimeType == ExpirationTimeType.SlidingExpirationTimeType) policy = CreatePolicy(expirationTime, null); else policy = CreatePolicy(TimeSpan.Zero, null); MemoryCache.Default.Add(item, policy); } } } tmp = (MemoryCache.Default[key] is T) ? (T)MemoryCache.Default[key] : default(T); //return (MemoryCache.Default[key] is T) ? (T)MemoryCache.Default[key] : default(T); //return (T)MemoryCache.Default[key]; } else { tmp = cachePopulate(); } return tmp; } /// <summary> /// 从缓存中移除知道键值的缓存对象 /// </summary> /// <param name="key">缓存对象的键</param> /// <returns></returns> public static bool RemoveCacheItem(string key) { bool isRemove = false; try { if (MemoryCache.Default[key] != null) { lock (_locker) { if (MemoryCache.Default[key] != null) { MemoryCache.Default.Remove(key); } } } isRemove = true; } catch (Exception ex) { throw ex; } return isRemove; } /// <summary> /// 构造缓存过时时间和优先级 /// </summary> /// <param name="slidingExpiration">滑动窗口模式的使用过时时间</param> /// <param name="absoluteExpiration">绝对过时时间</param> /// <returns></returns> private static CacheItemPolicy CreatePolicy(TimeSpan? slidingExpiration, DateTime? absoluteExpiration) { if (slidingExpiration == null && absoluteExpiration == null) throw new ArgumentException("Either a sliding expiration or absolute must be provided"); CacheItemPolicy policy = new CacheItemPolicy(); if (slidingExpiration.HasValue) { policy.SlidingExpiration = slidingExpiration.Value; } else if (absoluteExpiration.HasValue) { policy.AbsoluteExpiration = absoluteExpiration.Value; } policy.Priority = CacheItemPriority.Default; //List<string> filePaths = new List<string>(); //filePaths.Add("c:\\cache\\example.txt"); //policy.ChangeMonitors.Add(new HostFileChangeMonitor(filePaths)); return policy; } /// <summary> /// 缓存过时时间类型 /// </summary> public enum ExpirationTimeType { /// <summary> /// 滑动窗口模式的使用过时时间 /// </summary> SlidingExpirationTimeType = 1, /// <summary> /// 绝对过时时间 /// </summary> AbsoluteExpirationTimeType = 2 } }
这个辅助类只有一个public方法,就是GetCacheItem,使用的时候,须要指定key和获取数据的处理代理,还有缓存的过时时间,是基于TimeSpan的仍是基于绝对时间的,选择其一。
上面的辅助类,咱们在什么状况下会使用到呢?
假如在一个工做流模块中用到了人员ID,而人员ID须要进行人员名称的转义,人员信息咱们通常知道放在权限系统模块里面,那么若是在工做流里面须要频繁对人员ID进行转义,那么就须要方法调用权限系统的接口模块,这样处理就可使用缓存模块进行优化处理的了。
void gridView1_CustomColumnDisplayText(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e) { if (e.Column.FieldName.Equals("ProcUser") || e.Column.FieldName.Equals("ProcUid") || e.Column.FieldName.Equals("UserId")) { if (e.Value != null) { e.DisplayText = SecurityHelper.GetUserFullName(e.Value.ToString()); } } }
其中的SecurityHelper.GetUserFullName是我对调用进行基于缓存的二次封装,具体逻辑以下所示。
/// <summary> /// 根据用户的ID,获取用户的全名,并放到缓存里面 /// </summary> /// <param name="userId">用户的ID</param> /// <returns></returns> public static string GetUserFullName(string userId) { string key = "Security_UserFullName" + userId; string fullName = MemoryCacheHelper.GetCacheItem<string>(key, delegate() { return BLLFactory<User>.Instance.GetFullNameByID(userId.ToInt32()); }, new TimeSpan(0, 30, 0));//30分钟过时 return fullName; }
MemoryCacheHelper的方法GetCacheItem里面的Func<T>我使用了一个匿名函数用来获取缓存的值。
delegate() { return BLLFactory<User>.Instance.GetFullNameByID(userId.ToInt32()); }
而调用BLLFactory<User>.Instance.GetFullNameByID则是从数据库里面获取对应的数据了。
这样在第一次或者缓存过时的时候,自动调用业务对象类的方法来获取数据了。
最后,在界面上调用GetUserFullName的方法便可实现基于缓存方式的调用,程序第一次使用的,碰到指定的键没有数据,就去数据库里面获取,之后碰到该键,则直接获取缓存的数据了。
下面图形是程序具体的实现效果。
固然,以上两种方式都还能够经过AOP的注入方式实现代码的简化操做,不过因为对AOP的引入,会涉及到更多的知识点,并且熟悉程序还不够,因此依然采用较为经常使用的方式来处理缓存的数据。