緩存在很多情況下需要用到,合理利用緩存可以一方面可以提高程序的響應速度,同時可以減少對特定資源訪問的壓力。本文主要針對自己在Winform方面的緩存使用做一個引導性的介紹,希望大家能夠從中了解一些緩存的使用場景和使用方法。緩存是一個中大型系統所必須考慮的問題。為了避免每次請求都去訪問后台的資源(例如數據庫),我們一般會考慮將一些更新不是很頻繁的,可以重用的數據,通過一定的方式臨時地保存起來,后續的請求根據情況可以直接訪問這些保存起來的數據。這種機制就是所謂的緩存機制。
.NET 4.0的緩存功能主要由三部分組成:System.Runtime.Caching,System.Web.Caching.Cache和Output Cache。
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來做做緩存。
1、自定義Hastable的緩存處理。
除了上面三種的緩存機制,一般我們還可以在靜態對象里面通過HashTable或者Dictionary的方式進行自定義的緩存存儲和使用。
例如我在我自己所開發的程序里面,都使用了工廠類來創建業務對象,由於創建業務對象以及數據訪問層對象,是一個在界面或者中間層反復調用的操作,因此需要把經常調用的對象把它存儲起來,下載調用的時候,直接從內存中取出來即可。如下面的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; } } }
2、使用.NET4.0的MemoryCache對象實現緩存
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的引入,會涉及到更多的知識點,而且熟悉程序還不夠,所以依然采用較為常用的方式來處理緩存的數據。
出處:https://www.cnblogs.com/wuhuacong/p/3526335.html
======================================================================================
摘要
在對winform做的項目優化的時候,首先想到的是對查詢,並不經常變化的數據進行緩存,但對web項目來說有System.Web.Caching.Cache類進行緩存,那么winform端該如何呢?你可能會想到,存到文件中,但那可能有問題,文件操作權限問題,IO操作性能問題。
解決辦法
針對exe的項目,可以使用MemoryCache這個類,進行內存級別的緩存。
輔助類
/// <summary> /// 基於MemoryCache的緩存輔助類 /// </summary> public static class MemoryCacheHelper { private static readonly Object _locker = new object(); public static T FindCacheItem<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]; } /// <summary> /// 移除緩存 /// </summary> /// <param name="key"></param> public static void RemoveCache(string key) { MemoryCache.Default.Remove(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; } }
測試
class Program { static void Main(string[] args) { string cacheKey = "person_key"; Person p = MemoryCacheHelper.FindCacheItem<Person>(cacheKey, () => { return new Person() { Id = Guid.NewGuid(), Name = "wolfy" }; }, new TimeSpan(0, 30, 0)); if (p != null) { Console.WriteLine(p.ToString()); } Console.WriteLine("獲取緩存中數據"); Person p2 = MemoryCacheHelper.FindCacheItem<Person>(cacheKey, () => { return new Person() { Id = Guid.NewGuid(), Name = "wolfy" }; }, new TimeSpan(0, 30, 0)); if (p2 != null) { Console.WriteLine(p2.ToString()); } MemoryCacheHelper.RemoveCache(cacheKey); Person p3 = MemoryCacheHelper.FindCacheItem<Person>(cacheKey, () => { return new Person() { Id = Guid.NewGuid(), Name = "wolfy" }; }, new TimeSpan(0, 30, 0)); if (p3 != null) { Console.WriteLine(p3.ToString()); } Console.Read(); } } public class Person { public Guid Id { set; get; } public string Name { set; get; } public override string ToString() { return Id.ToString() + "\t" + Name; } }
參考:
https://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx
出處:https://www.cnblogs.com/wolf-sun/p/7251343.html