90

我想知道 和 之间是否有任何区别MemoryCacheHttpRuntime.Cache在 ASP.NET MVC 项目中首选哪一个?

据我了解,两者都是线程安全的,API 乍一看或多或少相同,那么何时使用哪个有什么区别?

4

4 回答 4

85

HttpRuntime.Cache获取Cache当前应用程序的。

该类MemoryCache类似于 ASP.NETCache类。

MemoryCache如果您使用过 ASP.NET 类,该类具有许多用于访问缓存的属性和方法,这些属性和方法您会很熟悉Cache

HttpRuntime.Cache和之间的主要区别在于MemoryCache后者已被更改,以使其可供非 ASP.NET 应用程序的 .NET Framework 应用程序使用。

如需补充阅读:

更新 :

根据用户反馈,Jon davis 博客有时无法正常工作。因此,我将整篇文章作为图像。请看。

注意:如果不清楚,只需单击图像。之后它将在浏览器上打开。然后再次单击它以放大 :)

在此处输入图像描述

于 2012-12-04T13:48:35.120 回答
26

这是乔恩戴维斯的文章。为了保持可读性,我删除了现在已经过时的 EntLib 部分、介绍和结论。


ASP.NET 缓存

ASP.NET 或 System.Web.dll 程序集确实具有缓存机制。它从未打算在 Web 上下文之外使用,但它可以在 Web 之外使用,并且它确实在各种哈希表中执行所有上述过期行为。

在搜索了 Google 之后,似乎很多讨论过 .NET 中内置缓存功能的人都在他们的非 Web 项目中使用了 ASP.NET 缓存。这不再是 .NET 中最可用、最受支持的内置缓存系统;.NET 4 有一个 ObjectCache,我稍后会介绍。Microsoft 一直坚持认为 ASP.NET 缓存不适合在 Web 之外使用。但是很多人仍然停留在 .NET 2.0 和 .NET 3.5 中,需要一些东西来工作,而这恰好适用于很多人,尽管 MSDN 明确表示:

注意:Cache 类不适合在 ASP.NET 应用程序之外使用。它是为在 ASP.NET 中使用而设计和测试的,以便为 Web 应用程序提供缓存。在其他类型的应用程序中,例如控制台应用程序或 Windows 窗体应用程序,ASP.NET 缓存可能无法正常工作。

ASP.NET 缓存的类是 System.Web.dll 中的 System.Web.Caching.Cache。但是,您不能简单地新建一个 Cache 对象。您必须从 System.Web.HttpRuntime.Cache 获取它。

Cache cache = System.Web.HttpRuntime.Cache;

使用 ASP.NET 缓存记录在 MSDN here上。

优点:

  1. 它是内置的
  2. 尽管有 .NET 1.0 语法,但它使用起来相当简单
  3. 在 Web 环境中使用时,它是经过充分测试的. 在网络环境之外,根据谷歌搜索,尽管微软不建议这样做,但通常并不知道它会导致问题,只要您使用的是 .NET 2.0 或更高版本。
  4. 当项目被删除时,您可以通过委托通知您,如果您需要保持它处于活动状态并且您无法提前设置项目的优先级,这是必要的。
  5. 在本文顶部的移除方法列表中,单个项目具有 (a)、(b) 或 (c) 到期和移除方法中的任何一种的灵活性。您还可以将过期行为与物理文件的存在相关联。

缺点:

  1. 它不仅是静态的,而且只有一个。您不能使用自己的静态缓存实例创建自己的类型。您的整个应用程序只能有一个存储桶,期间。您可以使用自己的包装器来包装存储桶,这些包装器可以在键中预注入前缀,并在您拉回键/值对时删除这些前缀。但仍然只有一个桶。一切都集中在一起。例如,如果您有一个服务需要分别缓存三种或四种不同类型的数据,这可能会很麻烦。对于可怜的简单项目来说,这应该不是一个大问题。但是,如果项目由于其要求而具有任何显着程度的复杂性,那么 ASP.NET 缓存通常就不够用了。
  2. 物品可以消失,任性. 很多人没有意识到这一点——我没有,直到我刷新了关于这个缓存实现的知识。默认情况下,ASP.NET 缓存旨在在“感觉”喜欢时销毁项目。更具体地说,请参阅本文顶部我对缓存表的定义中的 (c)。如果同一进程中的另一个线程正在处理完全不同的事情,并且它将高优先级项目转储到缓存中,那么一旦 .NET 决定它需要一些内存,它就会开始销毁缓存中的一些项目。他们的优先级,优先级较低。此处记录的所有用于添加缓存项的示例都使用默认优先级,而不是 NotRemovable 优先级值,该值可防止出于内存清除目的而将其删除,但仍会根据过期策略将其删除。
  3. 键必须是字符串。例如,如果您正在缓存数据记录,其中记录的键是长整数或整数,则必须首先将键转换为字符串。
  4. 语法是陈旧的。它是 .NET 1.0 语法,甚至比 ArrayList 或 Hashtable 还要丑陋。这里没有泛型,没有 IDictionary<> 接口。它没有 Contains() 方法,没有 Keys 集合,没有标准事件;它只有一个 Get() 方法和一个与 Get() 做同样事情的索引器,如果没有匹配则返回 null,再加上 Add()、Insert()(冗余?)、Remove() 和 GetEnumerator() .
  5. 忽略设置默认过期/删除行为的 DRY 原则,以便您忘记它们。您必须明确告诉缓存您希望添加的项目如何过期或在每次添加添加项目时被删除。
  6. 无法访问缓存项的缓存详细信息,例如添加时间的时间戳。封装在这里有点过火了,当你在代码中试图确定一个缓存项是否应该针对另一个缓存机制(即会话集合)无效时,很难使用缓存。
  7. 删除事件不会作为事件公开,必须在添加时进行跟踪。
  8. 如果我说得还不够,微软明确建议不要在网络之外使用它。而且,如果您被.NET 1.1 诅咒,那么您不应该在 Web 之外对使用它有任何稳定性的信心,所以不要打扰。

.NET 4.0 的 ObjectCache / MemoryCache

微软最终在 .NET Framework 的最新版本中实现了一个抽象的 ObjectCache 类,以及一个 MemoryCache 实现,它在非 Web 设置中继承和实现 ObjectCache 以用于内存中的目的。

System.Runtime.Caching.ObjectCache 位于 System.Runtime.Caching.dll 程序集中。它是一个抽象类,它声明了与 ASP.NET 缓存中的基本相同的 .NET 1.0 样式接口。System.Runtime.Caching.MemoryCache是 ObjectCache 的内存实现,与 ASP.NET 缓存非常相似,只是有一些变化。

要添加具有滑动到期的项目,您的代码将如下所示:

var config = new NameValueCollection();  
var cache = new MemoryCache("myMemCache", config);  
cache.Add(new CacheItem("a", "b"),  
    new CacheItemPolicy  
    {  
        Priority = CacheItemPriority.NotRemovable,  
        SlidingExpiration=TimeSpan.FromMinutes(30)  
    }); 

优点:

  1. 它是内置的,现在由 Microsoft 在 Web 之外提供支持和推荐。
  2. 与 ASP.NET 缓存不同,您可以实例化 MemoryCache 对象实例。

    注意:它不一定是静态的,但它应该是——这是Microsoft 的建议(见黄色警告)

  3. 与 ASP.NET 缓存的接口相比,已经进行了一些细微的改进,例如在添加项目时订阅删除事件的能力,删除多余的 Insert(),可以使用 CacheItem 添加项目具有定义缓存策略的初始化程序的对象,并添加了 Contains()。

缺点:

  1. 仍然没有完全加强 DRY。根据我的少量经验,您仍然无法设置一次滑动到期TimeSpan而忘记它。坦率地说,虽然上面 item-add 示例中的策略更具可读性,但它需要非常冗长。
  2. 它仍然不是通用键控;它需要一个字符串作为键。因此,如果您正在缓存数据记录,则不能存储 long 或 int ,除非您转换为字符串。

DIY:自己建造

创建一个执行显式或滑动过期的缓存字典实际上非常简单。(如果您希望出于清除内存的目的而自动删除项目,这将变得更加困难。)这就是您所要做的:

  1. 创建一个名为 Expiring 或 Expirable 的值容器类,它包含一个类型为 T 的值、一个 DateTime 类型的 TimeStamp 属性,用于存储该值何时被添加到缓存中,以及一个 TimeSpan,它指示与时间戳相距多远该项目应该过期。对于显式过期,您可以只公开一个属性设置器,该设置器在给定日期减去时间戳的情况下设置 TimeSpan。
  2. 创建一个实现 IDictionary 的类,我们称之为 ExpirableItemsDictionary。我更喜欢让它成为一个由消费者定义的通用类。
  3. 在#2 中创建的类中,添加一个 Dictionary> 作为属性并将其命名为 InnerDictionary。
  4. 如果在#2 中创建的类中的 IDictionary 的实现应该使用 InnerDictionary 来存储缓存项。封装将通过上面 #1 中创建的类型的实例隐藏缓存方法的详细信息。
  5. 确保索引器 (this[])、ContainsKey() 等在返回值之前小心清除过期项目并移除过期项目。如果项目被删除,则在 getter 中返回 null。
  6. 在所有 getter、setter、 ContainsKey() 上使用线程锁,尤其是在清除过期项目时。
  7. 每当项目因过期而被删除时引发事件。
  8. 添加一个 System.Threading.Timer 实例并在初始化期间对其进行装配,以每 15 秒自动删除过期项目。这与 ASP.NET 缓存的行为相同。
  9. 您可能想要添加一个 AddOrUpdate() 例程,通过替换项目容器(Expiring 实例)上的时间戳(如果它已经存在)来推出滑动过期。

微软必须支持其原始设计,因为它的用户群已经建立了对它们的依赖,但这并不意味着它们是好的设计。

优点:

  1. 您可以完全控制实施。
  2. 可以通过设置默认缓存行为来加强 DRY,然后在每次添加项目时只删除键/值对而不声明缓存细节。
  3. 可以实现现代接口,即IDictionary<K,T>。这使得它更容易使用,因为它的接口作为字典接口更容易预测,而且它更容易被使用 IDictionary<> 的助手和扩展方法访问。
  4. 缓存细节可以是未封装的,例如通过公共只读属性公开您的 InnerDictionary,允许您针对缓存策略编写显式单元测试,并使用基于它的附加缓存策略扩展您的基本缓存实现。
  5. 尽管对于那些已经熟悉 ASP.NET 缓存或缓存应用程序块的 .NET 1.0 样式语法的人来说,它不一定是一个熟悉的界面,但您可以将界面定义为您想要的任何外观。
  6. 可以使用任何类型的键。这是创建泛型的原因之一。并非所有内容都应使用字符串作为键。

缺点:

  1. 不是由 Microsoft 发明或​​认可的,因此不会有相同的质量保证。
  2. 假设只实现了我上面描述的指令,不会“随意”清除优先级清除内存的项目(无论如何这是缓存的一个极端实用功能..在您将使用缓存的地方购买 RAM , RAM 很便宜)。

在所有这四个选项中,这是我的偏好。我已经实现了这个基本的缓存解决方案。到目前为止,它似乎运行良好,没有已知的错误(请在下面的评论或 jon-at-jondavis 联系我!!),我打算在我所有需要的较小的副项目中使用它基本缓存。这里是:

Github 链接:https ://github.com/kroimon/ExpirableItemDictionary

旧链接:ExpirableItemDictionary.zip

值得一提:AppFabric、NoSQL 等

请注意,这篇博客文章的标题表示“简单缓存”,而不是“重型缓存”。如果您想涉足重型领域,您应该考虑专门的横向扩展解决方案。

于 2017-01-24T05:03:53.843 回答
4

如果要将经典的 ASP.NET MVC 应用程序迁移到 ASP.NET Core,MemoryCache.Default 也可以充当“桥梁”,因为 Core 中没有“System.Web.Caching”和“HttpRuntime”。

我还写了一个小基准来存储一个bool项目 20000 次(以及另一个检索它的基准),并且 MemoryCache 似乎慢了两倍(27ms vs 13ms - 这是所有 20k 迭代的总和)但它们都非常快而且这大概可以忽略。

于 2020-03-10T13:35:23.517 回答
3

MemoryCache 就是它所说的那样,一个存储在内存中的缓存

HttpRuntime.Cache(参见http://msdn.microsoft.com/en-us/library/system.web.httpruntime.cache(v=vs.100).aspxhttp://msdn.microsoft.com/en- us/library/system.web.caching.cache.aspx)坚持到您在应用程序中配置的任何内容。

参见例如“ASP.NET 4.0:编写自定义输出缓存提供程序” http://weblogs.asp.net/gunnarpeipman/archive/2009/11/19/asp-net-4-0-writing-custom-output-cache -providers.aspx

于 2012-12-04T13:52:36.430 回答