[設計案例] 清除Cache物件 #2. Create Custom CacheDependency

上一篇廢話了這麼多,其實重點只有一個,我這次打算利用 CacheDependency 的機制,只要一聲令下,我想移除的 cache item 就會因為 CacheDependency 的關係自動失效,而不用很辛苦的拿著 cache key 一個一個移除。

我的想法是用 tags 的概念,建立起一套靠某個 tag 就能對應到一組 cache item,然後將它移除。開始之前先來想像一下 code 寫好長什麼樣子:

透過 tags 來控制 cache items 的範例程式

static void Main(string[] args)
{
    string[] urls = new string[] {
        "http://columns.chicken-house.net/",
        // 共 50 組網址... 略
    };

    foreach (string url in urls)
    {
        DownloadData(new Uri(url));
    }

    Console.ReadLine();
    TaggingCacheDependency.DependencyDispose("funp.com");
    Console.ReadLine();
}

private static void Info(string key, object value, CacheItemRemovedReason reason)
{
        Console.WriteLine("Remove: {0}", key);
}

private static byte[] DownloadData(Uri sourceURL)
{
    byte[] buffer = (byte[])HttpRuntime.Cache[sourceURL.ToString()];

    if (buffer == null)
    {
        // 直接到指定網址下載。略...
        buffer = null;

        HttpRuntime.Cache.Add(
            sourceURL.ToString(),
            buffer,
            new TaggingCacheDependency(sourceURL.Host, sourceURL.Scheme),
            Cache.NoAbsoluteExpiration,
            TimeSpan.FromSeconds(600),
            CacheItemPriority.NotRemovable,
            Info);
    }

    return buffer;
}
}

這段 sample code 做的事很簡單,程式準備了 50 個網址清單,用 for-loop 一個一個下載。下載的 method: DownloadData(Uri sourceURL) 會先檢查 cache 是否已經有資料,沒有才真正下載 (不過下載的細節不是本篇要講的,所以就直接略過了…)。

而主程式的最後一行,則是想要把指定網站 ( funp.com ) 下載的所有資料,都從 cache 移除。為了方便觀看程式結果,我特地加上了 callback method, 當 cache item 被移除時, 會在畫面顯示資訊:

image

由執行結果來看,果然被移出 cache 的都是來在 funp.com 的網址… 接著來看看程式碼中出現的 TaggingCacheDependecny 是怎麼實作的。相關的 code 如下:

TaggingCacheDependency 的實作

public class TaggingCacheDependency : CacheDependency
{
    private static Dictionary<string, List<TaggingCacheDependency>> _lists = new Dictionary<string, List<TaggingCacheDependency>>();

    public TaggingCacheDependency(params string[] tags)
    {
        foreach (string tag in tags)
        {
            if (_lists.ContainsKey(tag) == false)
            {
                _lists.Add(tag, new List<TaggingCacheDependency>());
            }
            _lists[tag].Add(this);
        }
        this.SetUtcLastModified(DateTime.MinValue);
        this.FinishInit();
    }

    public static void DependencyDispose(string tag)
    {
        if (_lists.ContainsKey(tag) == true)
        {
            foreach (TaggingCacheDependency tcd in _lists[tag])
            {
                tcd.NotifyDependencyChanged(null, EventArgs.Empty);
            }
            _lists[tag].Clear();
            _lists.Remove(tag);
        }
    }
}

30 行不到… 其實程式很簡單,TaggingCacheDependency 繼承自 CacheDependency, 額外宣告一個靜態的 Dictionary<string, List> 來處理各個標簽及 TaggingCacheDependency 的關係,剩下的就沒什麼了。呼叫 DependencyDispose( ) 就可以通知 .NET Cache 機制,將相關的 cache item 移除。

用法很簡單,當你要把任何物件放進 cache 時,只要用 TaggingCacheDependency 物件來標示它的 tag:

把物件加進 Cache, 配上 TaggingCacheDependency …

HttpRuntime.Cache.Add(
    sourceURL.ToString(),
    buffer,
    new TaggingCacheDependency(sourceURL.Host, sourceURL.Scheme),
    Cache.NoAbsoluteExpiration,
    TimeSpan.FromSeconds(600),
    CacheItemPriority.NotRemovable,
    Info);

在這個例子裡 (line 4), 直接在 TaggingCacheDependency 物件的 constructor 上直接標上 tags, 在此例是直接把網址的 hostname, scheme 兩個部份當作 tag, 未來就可以依照這兩種資訊直接讓 cache 裡的相關物件失效。

而要下令讓 Cache 內有標上某個 tag 的 cache item 失效,只要這行:

將標為 “funp.com” 的 cache item 設為失效的 cache item

TaggingCacheDependency.DependencyDispose("funp.com");

結果就會如同上面的程式範例一樣,還留在 cache 的該網址下載資料,在這一瞬間通通都會被清掉…

用這種方式,是不是比拿到 key 再去呼叫 Cache.Remove( key ) 的方式簡單多了呢? 同時也能夠更快速的處理複雜的移除機制。其實運用 tagging 的方式只是一例,需要的話你也可以設計合適的 CacheDependency 類別。

以下是本篇文章的兩個附加參考檔案:

Download File - URL清單

Download File - Visual Studio 2008 Project






Facebook Pages

Edit Post (Pull Request)

Post Directory