HTTPCache和MemoryCache交叉依赖

时间:2014-04-17 09:53:18

标签: c# .net memorycache

我的项目主要是使用.net MemoryCache,但我确实有一个使用HTTPCache的组件。这使得交叉依赖更难以处理。

无论如何,我可以让两个缓存相互依赖吗?例如,我可以提供给MemoryCache的HTTPCacheChangeMonitor和一个可以提供给HTTPCache的MemoryCacheDependency。

1 个答案:

答案 0 :(得分:0)

自发布此问题以来,我一直致力于实现一些跨缓存依赖项。不确定他们是否采取了最好的方法。

<强> HttpCacheChangeMonitor

允许ObjectCache项依赖于HTTPCache

public class HttpCacheChangeMonitor : ChangeMonitor
{
    private readonly string _uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
    private readonly string[] _httpCacheKeys;

    public override string UniqueId
    {
        get { return _uniqueId; }
    }

    public HttpCacheChangeMonitor(string httpCacheKey)
        : this(new[] { httpCacheKey }) { }

    public HttpCacheChangeMonitor(string[] httpCacheKeys)
    {
        _httpCacheKeys = httpCacheKeys;
        Initialise();
    }

    private void Initialise()
    {
        HttpRuntime.Cache.Add(_uniqueId, _uniqueId, new CacheDependency(null, _httpCacheKeys), DateTime.MaxValue, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, Callback);
        InitializationComplete();
    }

    private void Callback(string key, object value, CacheItemRemovedReason reason)
    {
        OnChanged(null);
    }

    protected override void Dispose(bool disposing)
    {
        Debug.WriteLine(
                _uniqueId + " notifying cache of change.", "HttpCacheChangeMonitor");
        HttpRuntime.Cache.Remove(_uniqueId);
    }
}

<强>测试

public class HttpCacheChangeMonitorTests
{
    [Fact]
    public void ChangeMonitorTest()
    {
        HttpRuntime.Cache.Add("ChangeMonitorTest1", "", null, Cache.NoAbsoluteExpiration, new TimeSpan(0,10,0), CacheItemPriority.Normal, null);
        HttpRuntime.Cache.Add("ChangeMonitorTest2", "", null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 10, 0), CacheItemPriority.Normal, null);
        using (MemoryCache cache = new MemoryCache("TestCache", new NameValueCollection()))
        {

            // Add data to cache
            for (int idx = 0; idx < 10; idx++)
            {
                cache.Add("Key" + idx, "Value" + idx, GetPolicy(idx));
            }

            long middleCount = cache.GetCount();

            // Flush cached items associated with "NamedData" change monitors
            HttpRuntime.Cache.Remove("ChangeMonitorTest1");

            long finalCount = cache.GetCount();

            Assert.Equal(10, middleCount);
            Assert.Equal(5, middleCount - finalCount);
            HttpRuntime.Cache.Remove("ChangeMonitorTest2");
        }
    }

    private static CacheItemPolicy GetPolicy(int idx)
    {
        string name = (idx % 2 == 0) ? "ChangeMonitorTest1" : "ChangeMonitorTest2";

        CacheItemPolicy cip = new CacheItemPolicy();
        cip.AbsoluteExpiration = System.DateTimeOffset.UtcNow.AddHours(1);
        cip.ChangeMonitors.Add(new HttpCacheChangeMonitor(name));
        return cip;
    }
}

<强> MemoryCacheDependency

允许HttpCache项依赖于MemoryCache

public class MemoryCacheDependency : CacheDependency
{
    private readonly string _uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
    private readonly IEnumerable<string> _cacheKeys;
    private readonly MemoryCache _cache;

    public override string GetUniqueID()
    {
        return _uniqueId;
    }

    public MemoryCacheDependency(MemoryCache cache, string cacheKey)
        : this(cache, new[] { cacheKey }) { }
    public MemoryCacheDependency(MemoryCache cache, IEnumerable<string> cacheKeys)
    {
        _cache = cache;
        _cacheKeys = cacheKeys;
        Initialise();
    }

    private void Initialise()
    {
        var monitor = _cache.CreateCacheEntryChangeMonitor(_cacheKeys);
        CacheItemPolicy pol = new CacheItemPolicy{AbsoluteExpiration = DateTime.MaxValue, Priority = CacheItemPriority.NotRemovable};
        pol.ChangeMonitors.Add(monitor);
        pol.RemovedCallback = Callback;
        _cache.Add(_uniqueId, _uniqueId, pol);
        FinishInit();
    }

    private void Callback(CacheEntryRemovedArguments arguments)
    {
        NotifyDependencyChanged(arguments.Source, EventArgs.Empty);
    }

    protected override void DependencyDispose()
    {
        Debug.WriteLine(
                   _uniqueId + " notifying cache of change.", "ObjectCacheDependency");
        _cache.Remove(_uniqueId);
        base.DependencyDispose();
    }
}

<强>测试

public class MemoryCacheDependencyTests
{
    [Fact]
    public void CacheDependencyTest()
    {
        using (MemoryCache cache = new MemoryCache("TestCache", new NameValueCollection()))
        {
            cache.Add("HttpCacheTest1", DateTime.Now, new CacheItemPolicy {SlidingExpiration = new TimeSpan(0, 10, 0)});
            cache.Add("HttpCacheTest2", DateTime.Now, new CacheItemPolicy {SlidingExpiration = new TimeSpan(0, 10, 0)});

            // Add data to cache
            for (int idx = 0; idx < 10; idx++)
            {
                HttpRuntime.Cache.Add("Key" + idx, "Value" + idx, GetDependency(cache, idx), Cache.NoAbsoluteExpiration, new TimeSpan(0,10,0), CacheItemPriority.NotRemovable, null);
            }

            int middleCount = HttpRuntime.Cache.Count;

            // Flush cached items associated with "NamedData" change monitors
            cache.Remove("HttpCacheTest1");

            int finalCount = HttpRuntime.Cache.Count;

            Assert.Equal(10, middleCount);
            Assert.Equal(5, middleCount - finalCount);
        }
    }

    private static CacheDependency GetDependency(MemoryCache cache, int idx)
    {
        string name = (idx % 2 == 0) ? "HttpCacheTest1" : "HttpCacheTest2";

        return new MemoryCacheDependency(cache, name);
    }
}