System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache: ¿hay alguna diferencia?

84

Me pregunto si hay alguna diferencia entre MemoryCachey HttpRuntime.Cache, ¿cuál se prefiere en los proyectos ASP.NET MVC?

Por lo que tengo entendido, ambos son seguros para subprocesos, la API es, a primera vista, más o menos la misma, por lo que ¿hay alguna diferencia cuando usar cuál?

Giedrius
fuente

Respuestas:

80

HttpRuntime.Cacheobtiene el Cachepara la aplicación actual.

La MemoryCacheclase es similar a la Cacheclase ASP.NET .

La MemoryCacheclase tiene muchas propiedades y métodos para acceder a la caché que le resultarán familiares si ha utilizado la Cacheclase ASP.NET .

La principal diferencia entre HttpRuntime.Cachey MemoryCachees que este último se ha cambiado para que sea utilizable por aplicaciones .NET Framework que no son aplicaciones ASP.NET.

Para lectura adicional:

Actualización:

Según los comentarios de los usuarios, a veces el blog de Jon davis no funciona, por lo que he puesto todo el artículo como una imagen.

Nota: Si no está claro, simplemente haga clic en la imagen, luego se abrirá en un navegador, luego haga clic nuevamente para hacer zoom :)

ingrese la descripción de la imagen aquí

Sampath
fuente
Ese artículo de John Davis es realmente una buena lectura: pros y contras claros en un solo lugar.
Giedrius
absolutamente todo está en un solo lugar. Además, Davis también mencionó 4 métodos de almacenamiento en caché diferentes.
Sampath
2
@Spikeh Me está cargando bien.
user247702
1
@Stijn Gracias, no cargaba el otro día, pero ya está de vuelta :)
Spikeh
1
@sampath funciona ahora. Ayer parecía que el sitio había sido pirateado. ¡Gracias por tu ayuda!
Baga
24

Aquí está el artículo de Jon Davis. Para preservar la legibilidad, estoy eliminando la sección EntLib ahora obsoleta, la introducción y la conclusión.


Caché ASP.NET

ASP.NET, o el ensamblado System.Web.dll, tiene un mecanismo de almacenamiento en caché. Nunca tuvo la intención de usarse fuera de un contexto web, pero puede usarse fuera de la web, y realiza todos los comportamientos de expiración anteriores en una especie de tabla hash.

Después de buscar en Google, parece que bastantes personas que han discutido la funcionalidad de almacenamiento en caché incorporada en .NET han recurrido al uso de la caché ASP.NET en sus proyectos que no son web. Este ya no es el sistema de almacenamiento en caché integrado más disponible y más compatible en .NET; .NET 4 tiene un ObjectCache al que entraré más adelante. Microsoft siempre ha insistido en que la caché ASP.NET no está diseñada para usarse fuera de la web. Pero muchas personas todavía están atrapadas en .NET 2.0 y .NET 3.5, y necesitan algo con qué trabajar, y esto funciona para muchas personas, aunque MSDN dice claramente:

Nota: La clase Cache no está diseñada para usarse fuera de las aplicaciones ASP.NET. Fue diseñado y probado para su uso en ASP.NET para proporcionar almacenamiento en caché para aplicaciones web. En otros tipos de aplicaciones, como aplicaciones de consola o aplicaciones de Windows Forms, es posible que el almacenamiento en caché de ASP.NET no funcione correctamente.

La clase para el caché ASP.NET es System.Web.Caching.Cache en System.Web.dll. Sin embargo, no puede simplemente renovar un objeto Cache. Debe adquirirlo de System.Web.HttpRuntime.Cache.

Cache cache = System.Web.HttpRuntime.Cache;

El trabajo con la caché de ASP.NET se documenta en MSDN aquí .

Pros:

  1. Está integrado .
  2. A pesar de la sintaxis de .NET 1.0, es bastante sencillo de usar.
  3. Cuando se usa en un contexto web, está bien probado . Fuera de los contextos web, según las búsquedas de Google, no se sabe que cause problemas, a pesar de que Microsoft recomienda no hacerlo, siempre que utilice .NET 2.0 o posterior.
  4. Puede recibir una notificación a través de un delegado cuando se elimina un elemento, lo cual es necesario si necesita mantenerlo vivo y no pudo establecer la prioridad del elemento por adelantado.
  5. Los artículos individuales tienen la flexibilidad de cualquiera de los métodos (a), (b) o (c) de vencimiento y eliminación en la lista de métodos de eliminación en la parte superior de este artículo. También puede asociar el comportamiento de caducidad con la presencia de un archivo físico.

Contras:

  1. No solo es estático, solo hay uno . No puede crear su propio tipo con su propia instancia estática de caché. Solo puede tener un depósito para toda su aplicación, punto. Puede envolver el depósito con sus propios envoltorios que hacen cosas como preinyectar prefijos en las claves y eliminar estos prefijos cuando extrae los pares clave / valor. Pero todavía hay un solo cubo. Todo está agrupado. Esto puede ser una verdadera molestia si, por ejemplo, tiene un servicio que necesita almacenar en caché tres o cuatro tipos diferentes de datos por separado. Esto no debería ser un gran problema para proyectos patéticamente simples. Pero si un proyecto tiene un grado significativo de complejidad debido a sus requisitos, la caché de ASP.NET normalmente no será suficiente.
  2. Los elementos pueden desaparecer, quiera o no. Mucha gente no es consciente de esto, yo no lo sabía hasta que actualicé mis conocimientos sobre esta implementación de caché. De forma predeterminada, la caché ASP.NET está diseñada para destruir elementos cuando "se siente" así. Más específicamente, consulte (c) en mi definición de una tabla de caché en la parte superior de este artículo. Si otro hilo en el mismo proceso está trabajando en algo completamente diferente y descarga elementos de alta prioridad en la caché, tan pronto como .NET decida que necesita algo de memoria, comenzará a destruir algunos elementos en la caché de acuerdo con sus prioridades, las prioridades más bajas primero. Todos los ejemplos documentados aquí para agregar elementos de caché usan la prioridad predeterminada, en lugar del valor de prioridad NotRemovable que evita que se elimine con fines de borrado de memoria, pero aún así lo eliminará de acuerdo con la política de vencimiento.
  3. La clave debe ser una cadena. Si, por ejemplo, está almacenando en caché registros de datos en los que los registros tienen una clave larga o entera, primero debe convertir la clave en una cadena.
  4. La sintaxis está obsoleta . Es la sintaxis de .NET 1.0, incluso más fea que ArrayList o Hashtable. Aquí no hay genéricos, ni interfaz IDictionary <>. No tiene método Contains (), colección de claves, eventos estándar; solo tiene un método Get () más un indexador que hace lo mismo que Get (), devolviendo nulo si no hay coincidencia, más Add (), Insert () (¿redundante?), Remove () y GetEnumerator () .
  5. Ignora el principio DRY de configurar sus comportamientos predeterminados de expiración / eliminación para que pueda olvidarse de ellos. Tienes que decirle explícitamente al caché cómo quieres que el elemento que estás agregando caduque o se elimine cada vez que agregas un elemento.
  6. No hay forma de acceder a los detalles de almacenamiento en caché de un elemento en caché, como la marca de tiempo de cuando se agregó. La encapsulación se excedió un poco aquí, lo que dificulta el uso de la caché cuando en el código está intentando determinar si un elemento en caché debe invalidarse contra otro mecanismo de almacenamiento en caché (es decir, colección de sesiones) o no.
  7. Eventos de eliminación no se exponen como eventos y se deben rastrear en el momento de agregarlos.
  8. Y si no lo he dicho lo suficiente, Microsoft lo recomienda explícitamente fuera de la web. Y si está maldito con .NET 1.1, se supone que no debe usarlo con confianza de estabilidad fuera de la web, así que no se moleste.

ObjectCache / MemoryCache de .NET 4.0

Microsoft finalmente implementó una clase ObjectCache abstracta en la última versión de .NET Framework, y una implementación de MemoryCache que hereda e implementa ObjectCache para propósitos de memoria en una configuración no web.

System.Runtime.Caching.ObjectCache está en el ensamblado System.Runtime.Caching.dll. Es una clase abstracta que declara básicamente las mismas interfaces de estilo .NET 1.0 que se encuentran en la caché ASP.NET.System.Runtime.Caching.MemoryCachees la implementación en memoria de ObjectCache y es muy similar a la caché ASP.NET, con algunos cambios.

Para agregar un artículo con un vencimiento deslizante, su código se vería así:

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)  
    }); 

Pros:

  1. Está integrado y ahora es compatible y recomendado por Microsoft fuera de la web.
  2. A diferencia de la caché de ASP.NET, puede crear una instancia de objeto MemoryCache.

    Nota: No tiene por qué ser estático, pero debería serlo; esa es la recomendación de Microsoft (consulte la advertencia amarilla) .

  3. Se han realizado algunas mejoras leves en comparación con la interfaz de la caché de ASP.NET, como la capacidad de suscribirse a eventos de eliminación sin estar necesariamente allí cuando se agregaron los elementos, se eliminó el Insert () redundante, los elementos se pueden agregar con un CacheItem objeto con un inicializador que define la estrategia de almacenamiento en caché, y se agregó Contains ().

Contras:

  1. Aún no refuerza completamente DRY. Desde mi pequeña experiencia, todavía no puede establecer el TimeSpan de vencimiento deslizante una vez y olvidarse de él. Y, francamente, aunque la política del ejemplo de adición de elementos anterior es más legible, requiere una verbosidad horrible.
  2. Todavía no tiene una clave genérica; requiere una cuerda como clave. Por lo tanto, no puede almacenar tanto tiempo como int si está almacenando en caché registros de datos, a menos que los convierta a cadena.

Bricolaje: construye uno tú mismo

En realidad, es bastante simple crear un diccionario de almacenamiento en caché que realice una expiración explícita o deslizante. (Se vuelve mucho más difícil si desea que los elementos se eliminen automáticamente con el fin de borrar la memoria). Esto es todo lo que tiene que hacer:

  1. Cree una clase de contenedor de valor llamada algo como Expiring o Expirable que contendría un valor de tipo T, una propiedad TimeStamp de tipo DateTime para almacenar cuando se agregó el valor a la caché, y un TimeSpan que indicaría qué tan lejos de la marca de tiempo el artículo debe caducar. Para el vencimiento explícito, simplemente puede exponer un establecedor de propiedad que establece el TimeSpan dada una fecha restada por la marca de tiempo.
  2. Cree una clase, llamémosla ExpirableItemsDictionary, que implemente IDictionary. Prefiero convertirlo en una clase genérica definida por el consumidor.
  3. En la clase creada en el n. ° 2, agregue un Dictionary> como propiedad y llámelo InnerDictionary.
  4. La implementación de IDictionary en la clase creada en el n. ° 2 debería usar InnerDictionary para almacenar elementos en caché. La encapsulación ocultaría los detalles del método de almacenamiento en caché a través de instancias del tipo creado en el n. ° 1 anterior.
  5. Asegúrese de que el indexador (este []), ContainsKey (), etc., tenga cuidado de eliminar los elementos caducados y eliminar los elementos caducados antes de devolver un valor. Devuelve nulo en los captadores si se eliminó el elemento.
  6. Utilice bloqueos de hilo en todos los captadores, definidores, ContainsKey () y, en particular, al borrar los elementos caducados.
  7. Genere un evento cada vez que un elemento se elimine debido a su vencimiento.
  8. Agregue una instancia de System.Threading.Timer y manipúlela durante la inicialización para eliminar automáticamente los elementos caducados cada 15 segundos. Este es el mismo comportamiento que el caché ASP.NET.
  9. Es posible que desee agregar una rutina AddOrUpdate () que elimine el vencimiento deslizante reemplazando la marca de tiempo en el contenedor del elemento (instancia de vencimiento) si ya existe.

Microsoft tiene que admitir sus diseños originales porque su base de usuarios ha creado una dependencia de ellos, pero eso no significa que sean buenos diseños.

Pros:

  1. Tienes el control total sobre la implementación.
  2. Puede reforzar SECO configurando comportamientos de almacenamiento en caché predeterminados y luego simplemente colocando pares clave / valor sin declarar los detalles del almacenamiento en caché cada vez que agrega un elemento.
  3. Puede implementar interfaces modernas , a saber IDictionary<K,T>. Esto hace que sea mucho más fácil de consumir ya que su interfaz es más predecible como interfaz de diccionario, además de que la hace más accesible para los ayudantes y métodos de extensión que funcionan con IDictionary <>.
  4. Los detalles del almacenamiento en caché se pueden desencapsular , por ejemplo, al exponer su InnerDictionary a través de una propiedad pública de solo lectura, lo que le permite escribir pruebas unitarias explícitas contra su estrategia de almacenamiento en caché, así como extender su implementación básica de almacenamiento en caché con estrategias de almacenamiento en caché adicionales que se basan en ella.
  5. Aunque no es necesariamente una interfaz familiar para aquellos que ya se sintieron cómodos con la sintaxis de estilo .NET 1.0 de la caché ASP.NET o el Bloque de aplicaciones de almacenamiento en caché, puede definir la interfaz para que se vea como usted quiera.
  6. Puede usar cualquier tipo para llaves. Esta es una de las razones por las que se crearon los genéricos. No todo debe estar codificado con una cuerda.

Contras:

  1. No está inventado ni respaldado por Microsoft , por lo que no tendrá la misma garantía de calidad.
  2. Suponiendo que solo se implementan las instrucciones que describí anteriormente, no se borran los elementos para borrar la memoria de manera prioritaria (que de todos modos es una función de utilidad de esquina de un caché. COMPRAR RAM donde estaría usando el caché , La RAM es barata).

Entre estas cuatro opciones, esta es mi preferencia. He implementado esta solución básica de almacenamiento en caché. Hasta ahora, parece funcionar perfectamente, no hay errores conocidos (¡contácteme con los comentarios a continuación o en jon-at-jondavis si los hay!), Y tengo la intención de usarlo en todos mis proyectos secundarios más pequeños que necesitan almacenamiento en caché básico. Aquí está:

Enlace de Github: https://github.com/kroimon/ExpirableItemDictionary

Enlace antiguo: ExpirableItemDictionary.zip

Digno de mención: AppFabric, NoSQL, Et Al

Tenga en cuenta que el título de este artículo de blog indica "Almacenamiento en caché simple", no "Almacenamiento en caché de alta resistencia". Si desea dedicarse a las tareas pesadas, debe buscar soluciones dedicadas y escalables.

DeepSpace101
fuente
3

MemoryCache es lo que dice que es, un caché almacenado en la memoria

HttpRuntime.Cache (consulte http://msdn.microsoft.com/en-us/library/system.web.httpruntime.cache(v=vs.100).aspx y http://msdn.microsoft.com/en- us / library / system.web.caching.cache.aspx ) persiste en lo que sea que lo configure en su aplicación.

consulte, por ejemplo, "ASP.NET 4.0: escritura de proveedores de caché de salida personalizados" http://weblogs.asp.net/gunnarpeipman/archive/2009/11/19/asp-net-4-0-writing-custom-output-cache -providers.aspx

Christian Westman
fuente
1
Hm, no estoy seguro de si el segundo enlace no es engañoso, porque se habla de OutputCache y de implementar OutputCacheProvider.
Giedrius
Hm, no puedo encontrar dónde diría, que puede persistir System.Web.Caching.Cache usando una configuración diferente
Giedrius
2

MemoryCache.Default también puede servir como un "puente" si está migrando una aplicación clásica ASP.NET MVC a ASP.NET Core, porque no hay "System.Web.Caching" ni "HttpRuntime" en Core.

También escribí un pequeño punto de referencia para almacenar un boolelemento 20000 veces (y otro punto de referencia para recuperarlo) y MemoryCache parece ser dos veces más lento (27ms vs 13ms - eso es total para las 20k iteraciones) pero ambos son súper rápidos y esto probablemente pueda ser ignorado.

Alex
fuente