System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache - 有什么区别吗?

Gie*_*ius 83 c# asp.net-mvc caching c#-4.0

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

据我所知,两者都是线程安全的,API从一开始就或多或少都是一样的,所以在使用哪个时有什么区别?

Sam*_*ath 78

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

MemoryCache班是类似于ASP.NET Cache类.

MemoryCache班有许多属性和访问缓存,如果你已经使用了ASP.NET,这将是你熟悉的方法Cache类.

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

如需额外阅读:

更新:

根据用户的反馈,有时Jon davis博客不起作用.因此我把整篇文章都作为一个图像.请看.

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

在此输入图像描述

  • @Spikeh这对我来说很好. (2认同)

Dee*_*101 23

这是Jon Davis的文章.为了保持可读性,我正在删除现在过时的EntLib部分,介绍和结论.


ASP.NET缓存

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

在搜索谷歌之后,似乎有不少人讨论过.NET中的内置缓存功能,他们已经在他们的非网络项目中使用了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;
Run Code Online (Sandbox Code Playgroud)

与ASP.NET缓存的工作是记录在MSDN上这里.

优点:

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

缺点:

  1. 它不仅是静态的,而且只有一个.您无法使用自己的静态Cache实例创建自己的类型.您的整个应用只能拥有一个存储桶.您可以使用自己的包装器来包装存储桶,这些包装器会在键中预先注入前缀,并在将键/值对拉回时删除这些前缀.但仍然只有一个桶.一切都集中在一起.例如,如果您的服务需要分别缓存三种或四种不同类型的数据,那么这可能会非常麻烦.对于可怜的简单项目来说,这不应该是一个大问题.但是,如果项目由于其要求而具有任何显着程度的复杂性,则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诅咒,那么你不应该在网络之外对任何稳定性充满信心地使用它,所以不要打扰.

.NET 4.0的ObjectCache/MemoryCache

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

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)  
    }); 
Run Code Online (Sandbox Code Playgroud)

优点:

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

    注意:它不必是静态的,但应该是 - 这是微软的建议(参见黄色警告).

  3. 与ASP.NET缓存的界面相比有一些细微的改进,例如订阅删除事件的能力,而不必在添加项目时存在,冗余的Insert()被删除,项目可以添加CacheItem具有定义缓存策略的初始值设定项的对象,并添加了Contains().

缺点:

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

DIY:自己建造一个

实际上,创建一个执行显式或滑动过期的缓存字典非常简单.(如果您希望自动删除项目以进行内存清除,则会变得更加困难.)以下是您需要做的所有事情:

  1. 创建一个名为Expiring或Expirable的值容器类,它包含一个类型为T的值,一个TimeStamp属性,类型为DateTime,当该值被添加到缓存时存储,以及一个TimeSpan,它指示距离时间戳有多远该项目应该过期.对于显式到期,您可以公开一个属性设置器,该设置器设置TimeSpan,给定一个日期减去时间戳.
  2. 创建一个类,我们称之为ExpirableItemsDictionary,它实现了IDictionary.我更喜欢把它变成一个由消费者定义的泛型类.
  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 instance)上的时间戳(如果已存在)来推送滑动到期.

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

优点:

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

缺点:

  1. 不是由Microsoft发明,也不是由Microsoft认可,因此它不会具有相同的质量保证.
  2. 假设只实现了我上面描述的指令,那么"willy-nilly"不会清除优先级清除内存的项目(无论如何都是缓存的角落效用函数..在RAM中你将使用缓存,RAM很便宜).

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

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

旧链接:ExpirableItemDictionary.zip

值得一提的是:AppFabric,NoSQL,Et Al

请注意,此博客文章的标题表示"简单缓存",而不是"重载缓存".如果你想深入了解重要的东西,你应该看看专用的,横向扩展的解决方案.