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

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

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

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

public static void ClearCacheItems() { var enumerator = HttpContext.Current.Cache.GetEnumerator(); while (enumerator.MoveNext()) { HttpContext.Current.Cache.Remove(enumerator.Key.ToString()); } } 

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

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

 List keys = new List(); IDictionaryEnumerator enumerator = Cache.GetEnumerator(); while (enumerator.MoveNext()) keys.Add(enumerator.Key.ToString()); for (int i = 0; i < keys.Count; i++) Cache.Remove(keys[i]); 

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

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

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

  public sealed class IntermediateCache { private Dictionary _dictionary = new Dictionary(); private IntermediateCache() { } public static IntermediateCache Current { get { string key = "IntermediateCache|" + typeof(T).FullName; IntermediateCache current = HttpContext.Current.Cache[key] as IntermediateCache; if (current == null) { current = new IntermediateCache(); 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.Current.Set("myKey", myObj); // clear this cache IntermediateCache.Current.Clear(); 

Cache类的设计者很容易为它添加一个Clear方法。 但他们没有,而且是设计 – 因此你的代码很糟糕

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

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

UPDATE

我的建议是以一种清除缓存非常容易的方式设计缓存。 例如,按ID分组缓存项(创建一个类来保存相关的缓存结果),并使用ID作为键。 每当更改与该ID相关的内容时,请清除该ID的缓存。 十分简单。

您是否考虑过使用缓存依赖关系 ? 这是MSDN的解释和一些花絮:

建立存储在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("

The dependency has changed."); else Response.Write("

The dependency has not changed.");

这个非常热心的人解释了更多关于它的内容。

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

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

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

因此,如果您需要清除给定“客户”的所有“订单”,则缓存键应为CustomerId。 在我的例子中,我缓存了一个ReadOnlyCollection 。 所以我不需要循环,只需删除它,然后添加新的副本。

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

这样一切都是无缝的。