使用ASP.NET清除缓存的最有效方法

时间:2011-06-02 09:27:24

标签: c# asp.net entity-framework caching cache-control

我正在构建一个ASP.NET/Umbraco驱动的网站,它是通过实体框架驱动的非常自定义的数据,我们不得不缓存相当多的数据查询(例如按关键字搜索),因为它是一个繁忙的网站。 / p>

但是当用户创建新数据条目时,我需要清除所有缓存的查询(搜索等...),以便结果中有新条目。

因此,在我的创建,删除和更新方法中,我调用以下方法:

public static void ClearCacheItems()
{
    var enumerator = HttpContext.Current.Cache.GetEnumerator();

    while (enumerator.MoveNext())
    {
        HttpContext.Current.Cache.Remove(enumerator.Key.ToString());
    }
}

这真的很糟糕吗?我看不出我应该如何清除缓存的项目?

5 个答案:

答案 0 :(得分:11)

您使用的方法实际上是清除缓存的正确方法,代码中只有一个小错误。只要原始集合保持不变,枚举器仅有效。因此,虽然代码可能在大多数情况下都可以工作,但在某些情况下可能会出现小错误。最好是使用以下代码,它基本上相同,但不直接使用枚举器。

List<string> keys = new List<string>();
IDictionaryEnumerator enumerator = Cache.GetEnumerator();

while (enumerator.MoveNext())
  keys.Add(enumerator.Key.ToString());

for (int i = 0; i < keys.Count; i++)
  Cache.Remove(keys[i]);

答案 1 :(得分:7)

仅清除一个特定功能域的整个ASP.NET缓存似乎有点矫枉过正。

您可以创建一个中间对象,并将所有缓存的查询存储在那里。该对象可能只是字典对象的包装器。所有查询都应使用此对象,而不是直接使用ASP.NET缓存。

然后在需要时将此对象添加到ASP.NET缓存中。当您需要清除查询时,只需访问此对象并清除基础字典。这是一个示例实现:

    public sealed class IntermediateCache<T>
    {
        private Dictionary<string, T> _dictionary = new Dictionary<string, T>();

        private IntermediateCache()
        {
        }

        public static IntermediateCache<T> Current
        {
            get
            {
                string key = "IntermediateCache|" + typeof(T).FullName;
                IntermediateCache<T> current = HttpContext.Current.Cache[key] as IntermediateCache<T>;
                if (current == null)
                {
                    current = new IntermediateCache<T>();
                    HttpContext.Current.Cache[key] = current;
                }
                return current;
            }
        }

        public T Get(string key, T defaultValue)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            T value;
            if (_dictionary.TryGetValue(key, out value))
                return value;

            return defaultValue;
        }

        public void Set(string key, T value)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            _dictionary[key] = value;
        }

        public void Clear()
        {
            _dictionary.Clear();
        }
    }

如果我的查询表示如下:

    public class MyQueryObject
    {
       ....
    }

然后,我会像这样使用“区域”缓存:

// put something in this intermediate cache
IntermediateCache<MyQueryObject>.Current.Set("myKey", myObj);

// clear this cache
IntermediateCache<MyQueryObject>.Current.Clear();

答案 2 :(得分:6)

Cache类的设计人员可以很容易地为其添加Clear方法。但他们没有,而且是设计 - 因此你的代码糟糕

如果修改了集合,则一个问题是通过集合枚举的线程影响。这会引发错误。

你真的永远不需要清除整个缓存。如果服务器需要内存,它将清除它。缓存访问是按键(出于某种原因),因此您需要知道您要访问的内容。因此,如果您需要删除和项目,请使用密钥进行操作。

更新

我的建议是以一种清除缓存非常容易的方式设计缓存。例如,按ID对您的缓存项目(创建一个用于保存相关缓存结果的类)进行分组,并使用ID作为密钥。每当更改与该ID相关的内容时,请清除该ID的缓存。 轻松,轻松。

答案 3 :(得分:3)

您是否考虑过使用缓存依赖Here's the MSDN explanation和那里的一些花絮:

  

在ASP.NET应用程序的Cache对象中存储的项与文件,缓存键,其中一个数组或另一个CacheDependency对象之间建立依赖关系。 CacheDependency类监视依赖关系,以便当它们中的任何一个发生更改时,将自动删除缓存的项目。

// Insert the cache item.
CacheDependency dep = new CacheDependency(fileName, dt);
cache.Insert("key", "value", dep);

// Check whether CacheDependency.HasChanged is true.
if (dep.HasChanged)
  Response.Write("<p>The dependency has changed.");  
else Response.Write("<p>The dependency has not changed.");

this very enthusiastic fellow更多地解释了它。

答案 4 :(得分:3)

是的,永远不要遍历缓存并删除项目。它会给你一个痛苦的世界(我最近经历过这个)。因为您将收到错误“集合已被修改,枚举可能无法执行”。

我也有一个搜索缓存,我没有设置到期时间 - 我需要时手动无效。

技巧是将数据保存到“桶”中。那个“桶”通常是父标识符,或者如果你想进入数据库术语,那就是外键。

因此,如果您需要清除给定“客户”的所有“订单”,则缓存密钥应为CustomerId。在我的情况下,我缓存ReadOnlyCollection<T>。所以我不需要循环,只需删除它,然后添加新的副本。

另外,为了双重安全 - 我使用 ReaderWriterLockSlim 来使缓存无效。当我知道它需要被无效时,我首先调用数据库,获取写锁,刷新并刷新相关的缓存,然后关闭写锁。

这样就完全无缝了。