[VS2010] ASP.NET 4.0 新功能:自訂輸出快取 (Output Cache) 提供者
輸出快取是 ASP.NET 開發以來重要的功能之一,快取機制是由伺服器的記憶體提供,將網頁或資料的一部份或全部暫存在記憶體中,以提供更快速的回應時間 (response time),並減少伺服器處理與回傳資料的負載,讓應用程式可以服務更多來自用戶端的呼叫,而且輸出快取就單純是暫存快取資料,因此在 ASP.NET 不斷的升級之時,輸出快取沒有做太多的改變,然而這段期間快取的需求卻不斷變化,同時來自大型應用程式的 Load Balancing 架構以及 Web Farm 應用程式的架構下,不同的用戶端會導向到不同的 Server,而快取資料卻只能存在單一台 Server 的記憶體,這會造成快取資料形同虛設,它只能對一小部份的用戶端有作用,沒有連接到存有快取的 Server 的用戶端就無法存取到快取的資料。這促使了微軟必須要將快取機制做修改,以能夠提供除了記憶體以外的快取儲存區,像是檔案或資料庫等,以在不同情況下的快取能夠服務到最多的用戶端,這樣才能夠真正發揮快取的作用,尤其是在大型應用程式中。
ASP.NET 4.0 的輸出快取即具備這樣的能力,ASP.NET 4.0 的 Core Service 將 Output Cache 改寫成使用 Provider Model,讓開發人員可以利用設計自己的 Provider 的方式,讓 ASP.NET 可以輸出快取資料到不同的快取儲存體,而且開發人員也可以將快取與現有具高效率的快取提供者做連接,像是 memcache 這個受歡迎的 Open Source Cache Provider。開發人員可以實作在 System.Web.Caching 命名空間中的 OutputCacheProvider 抽象類別中的各個成員,並且在 Web.config 中加掛這個類別,即可讓 ASP.NET 使用這個類別來提供快取機制。下列程式碼即是一個 OutputCacheProvider 的範例實作:
public class FileCacheProvider : OutputCacheProvider 
{ 
    private string _cachePath; 
    private string CachePath 
    { 
        get 
        { 
            if (!string.IsNullOrEmpty(_cachePath)) 
                return _cachePath; 
            _cachePath = ConfigurationManager.AppSettings["OutputCachePath"]; 
            var context = HttpContext.Current; 
            if (context != null) 
            { 
                _cachePath = context.Server.MapPath(_cachePath); 
                if (!_cachePath.EndsWith("\\")) 
                    _cachePath += "\\"; 
            } 
            return _cachePath; 
        } 
    } 
    public override object Add(string key, object entry, DateTime utcExpiry) 
    { 
        Debug.WriteLine("Cache.Add(" + key + ", " + entry + ", " + utcExpiry +")"); 
var path = GetPathFromKey(key);
        if (File.Exists(path)) 
            return entry; 
        using (var file = File.OpenWrite(path)) 
        { 
            var item = new CacheItem { Expires = utcExpiry, Item = entry }; 
            var formatter = new BinaryFormatter(); 
            formatter.Serialize(file, item); 
        } 
        return entry; 
    } 
    public override object Get(string key) 
    { 
        Debug.WriteLine("Cache.Get(" + key + ")"); 
var path = GetPathFromKey(key);
        if (!File.Exists(path)) 
            return null; 
CacheItem item = null;
        using (var file = File.OpenRead(path)) 
        { 
            var formatter = new BinaryFormatter(); 
            item = (CacheItem)formatter.Deserialize(file);               
        } 
        if (item == null || item.Expires <= DateTime.Now.ToUniversalTime()) 
        { 
            Debug.WriteLine("Expired: " + item.Expires + " <= " + DateTime.Now.ToUniversalTime()); 
            Remove(key); 
            return null; 
        } 
        return item.Item; 
    } 
    public override void Remove(string key) 
    { 
        Debug.WriteLine("Cache.Remove(" + key + ")"); 
        var path = GetPathFromKey(key); 
        if (File.Exists(path)) 
            File.Delete(path); 
    } 
    public override void Set(string key, object entry, DateTime utcExpiry) 
    { 
        Debug.WriteLine("Cache.Set(" + key + ", " + entry + ", " + utcExpiry + ")"); 
        var item = new CacheItem { Expires = utcExpiry, Item = entry }; 
        var path = GetPathFromKey(key); 
        using (var file = File.OpenWrite(path)) 
        { 
            var formatter = new BinaryFormatter(); 
            formatter.Serialize(file, item); 
        } 
    } 
    private string GetPathFromKey(string key) 
    { 
        return CachePath + MD5(key) + ".txt"; 
    } 
    private string MD5(string s) 
    { 
        MD5CryptoServiceProvider provider; 
        provider = new MD5CryptoServiceProvider(); 
        byte[] bytes = Encoding.UTF8.GetBytes(s); 
        StringBuilder builder = new StringBuilder(); 
bytes = provider.ComputeHash(bytes);
        foreach (byte b in bytes) 
            builder.Append(b.ToString("x2").ToLower()); 
        return builder.ToString(); 
    } 
}
然後在 Web.config 中將這個 Provider 加掛上 ASP.NET:
<caching> 
  <outputCache defaultProvider="FileCache"> 
    <providers> 
      <add name="FileCache" type="MyCacheProvider.FileCacheProvider, MyCacheProvider"/> 
    </providers> 
  </outputCache> 
</caching>
如此即可在 ASP.NET 中使用自訂的快取提供者給 ASP.NET Cache 使用:

參考資料與圖片來源: