net mvc enable data cache asp asp.net-mvc database caching

asp.net mvc - enable - Cómo almacenar datos en caché en una aplicación MVC



enable output caching (14)

Aquí hay una clase / servicio de ayuda de caché agradable y simple que uso:

using System.Runtime.Caching; public class InMemoryCache: ICacheService { public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class { T item = MemoryCache.Default.Get(cacheKey) as T; if (item == null) { item = getItemCallback(); MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10)); } return item; } } interface ICacheService { T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class; }

Uso:

cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));

El proveedor de caché verificará si hay algo con el nombre de "id de caché" en el caché, y si no lo hay, llamará a un método delegado para obtener datos y almacenarlo en el caché.

Ejemplo:

var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())

He leído mucha información sobre el almacenamiento en caché de páginas y el almacenamiento parcial de páginas en una aplicación MVC Sin embargo, me gustaría saber cómo almacenaría datos en caché.

En mi escenario, usaré LINQ para Entidades (marco de la entidad). En la primera llamada a GetNames (o cualquiera que sea el método) quiero obtener los datos de la base de datos. Quiero guardar los resultados en caché y en la segunda llamada para usar la versión en caché si existe.

¿Puede alguien mostrar un ejemplo de cómo funcionaría esto, dónde debería implementarse (modelo?) Y si funcionaría.

He visto esto hecho en aplicaciones ASP.NET tradicionales, generalmente para datos muy estáticos.


Aquí hay una mejora en la respuesta de Hrvoje Hudo. Esta implementación tiene un par de mejoras clave:

  • Las claves de caché se crean automáticamente en función de la función para actualizar los datos y el objeto pasado que especifica las dependencias
  • Pase en el lapso de tiempo para cualquier duración de caché
  • Utiliza un candado para la seguridad del hilo.

Tenga en cuenta que esto depende de Newtonsoft.Json para serializar el objeto DependeOn, pero que se puede cambiar fácilmente por cualquier otro método de serialización.

ICache.cs

public interface ICache { T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class; }

InMemoryCache.cs

using System; using System.Reflection; using System.Runtime.Caching; using Newtonsoft.Json; public class InMemoryCache : ICache { private static readonly object CacheLockObject = new object(); public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class { string cacheKey = GetCacheKey(getItemCallback, dependsOn); T item = MemoryCache.Default.Get(cacheKey) as T; if (item == null) { lock (CacheLockObject) { item = getItemCallback(); MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration)); } } return item; } private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class { var serializedDependants = JsonConvert.SerializeObject(dependsOn); var methodType = itemCallback.GetType(); return methodType.FullName + serializedDependants; } }

Uso:

var order = _cache.GetOrSet( () => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId) , new { id = orderId } , new TimeSpan(0, 10, 0) );


Consulte la dll System.Web en su modelo y use System.Web.Caching.Cache

public string[] GetNames() { string[] names = Cache["names"] as string[]; if(names == null) //not in cache { names = DB.GetNames(); Cache["names"] = names; } return names; }

Un poco simplificado pero supongo que funcionaría. Esto no es específico de MVC y siempre he usado este método para almacenar datos en caché.


Diré que implementar Singleton en este problema de datos persistentes puede ser una solución para este asunto en caso de que encuentre soluciones anteriores mucho más complicadas

public class GPDataDictionary { private Dictionary<string, object> configDictionary = new Dictionary<string, object>(); /// <summary> /// Configuration values dictionary /// </summary> public Dictionary<string, object> ConfigDictionary { get { return configDictionary; } } private static GPDataDictionary instance; public static GPDataDictionary Instance { get { if (instance == null) { instance = new GPDataDictionary(); } return instance; } } // private constructor private GPDataDictionary() { } } // singleton


Extendiendo la respuesta de @Hrvoje Hudo ...

Código:

using System; using System.Runtime.Caching; public class InMemoryCache : ICacheService { public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class { TValue item = MemoryCache.Default.Get(cacheKey) as TValue; if (item == null) { item = getItemCallback(); MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes)); } return item; } public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class { string cacheKey = string.Format(cacheKeyFormat, id); TValue item = MemoryCache.Default.Get(cacheKey) as TValue; if (item == null) { item = getItemCallback(id); MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes)); } return item; } } interface ICacheService { TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class; TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class; }

Ejemplos

Almacenamiento en caché de un solo elemento (cuando cada artículo se almacena en caché según su ID porque el almacenamiento en caché de todo el catálogo para el tipo de artículo sería demasiado intensivo).

Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);

Cachear todo de algo

IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);

Por qué TId

El segundo ayudante es especialmente bueno porque la mayoría de las claves de datos no son compuestas. Se podrían agregar métodos adicionales si usa claves compuestas a menudo. De esta manera, evita hacer todo tipo de concatenación de cadenas o cadenas. Formatos para obtener la clave para pasar al ayudante de caché. También facilita la transferencia del método de acceso a los datos porque no tiene que pasar la identificación al método de envoltura ... todo se vuelve muy escueto y consistente para la mayoría de los casos de uso.


Lo he usado de esta manera y me funciona. https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx información de parámetros para system.web.caching.cache.add.

public string GetInfo() { string name = string.Empty; if(System.Web.HttpContext.Current.Cache["KeyName"] == null) { name = GetNameMethod(); System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null); } else { name = System.Web.HttpContext.Current.Cache["KeyName"] as string; } return name; }


Me refiero a la publicación de TT y sugiero el siguiente enfoque:

Consulte la dll System.Web en su modelo y use System.Web.Caching.Cache

public string[] GetNames() { var noms = Cache["names"]; if(noms == null) { noms = DB.GetNames(); Cache["names"] = noms; } return ((string[])noms); }

No debe devolver un valor releído de la memoria caché, ya que nunca sabrá si en ese momento específico todavía está en la memoria caché. Incluso si lo insertó en la declaración anterior, es posible que ya no esté o que nunca se haya agregado a la memoria caché, simplemente no lo sabe.

Entonces, agrega los datos leídos de la base de datos y los devuelve directamente, sin volver a leerlos del caché.


Para .NET 4.5+ framework

añadir referencia: System.Runtime.Caching

agregar usando declaración: using System.Runtime.Caching;

public string[] GetNames() { var noms = System.Runtime.Caching.MemoryCache.Default["names"]; if(noms == null) { noms = DB.GetNames(); System.Runtime.Caching.MemoryCache.Default["names"] = noms; } return ((string[])noms); }

En .NET Framework 3.5 y versiones anteriores, ASP.NET proporcionó una implementación de caché en memoria en el espacio de nombres System.Web.Caching. En versiones anteriores de .NET Framework, el almacenamiento en caché solo estaba disponible en el espacio de nombres System.Web y, por lo tanto, requería una dependencia en las clases ASP.NET. En .NET Framework 4, el espacio de nombres System.Runtime.Caching contiene API diseñadas para aplicaciones web y no web.

Más información:


Se distribuye el almacenamiento en caché de AppFabric y una técnica de almacenamiento en memoria caché que almacena datos en pares clave-valor utilizando la memoria física en múltiples servidores. AppFabric proporciona mejoras de rendimiento y escalabilidad para aplicaciones de .NET Framework. Conceptos y arquitectura


Steve Smith hizo dos publicaciones de gran blog que demuestran cómo usar su patrón CachedRepository en ASP.NET MVC. Utiliza el patrón de repositorio de manera efectiva y le permite obtener almacenamiento en caché sin tener que cambiar su código existente.

http://ardalis.com/Introducing-the-CachedRepository-Pattern

http://ardalis.com/building-a-cachedrepository-via-strategy-pattern

En estas dos publicaciones, te muestra cómo configurar este patrón y también explica por qué es útil. Al utilizar este patrón, obtiene el almacenamiento en caché sin que su código existente vea ninguna de las lógicas de almacenamiento en caché. Esencialmente usa el repositorio en caché como si fuera cualquier otro repositorio.


También puedes probar y usar el almacenamiento en caché integrado en ASP MVC:

Agregue el siguiente atributo al método del controlador que desea almacenar en caché:

[OutputCache(Duration=10)]

En este caso, el resultado de esta acción se almacenará en caché durante 10 segundos.

Más sobre esto here


Yo uso dos clases. Primero uno el objeto central de caché:

public class Cacher<TValue> where TValue : class { #region Properties private Func<TValue> _init; public string Key { get; private set; } public TValue Value { get { var item = HttpRuntime.Cache.Get(Key) as TValue; if (item == null) { item = _init(); HttpContext.Current.Cache.Insert(Key, item); } return item; } } #endregion #region Constructor public Cacher(string key, Func<TValue> init) { Key = key; _init = init; } #endregion #region Methods public void Refresh() { HttpRuntime.Cache.Remove(Key); } #endregion }

La segunda es la lista de objetos de caché:

public static class Caches { static Caches() { Languages = new Cacher<IEnumerable<Language>>("Languages", () => { using (var context = new WordsContext()) { return context.Languages.ToList(); } }); } public static Cacher<IEnumerable<Language>> Languages { get; private set; } }


HttpContext.Current.Cache.Insert("subjectlist", subjectlist);


public sealed class CacheManager { private static volatile CacheManager instance; private static object syncRoot = new Object(); private ObjectCache cache = null; private CacheItemPolicy defaultCacheItemPolicy = null; private CacheEntryRemovedCallback callback = null; private bool allowCache = true; private CacheManager() { cache = MemoryCache.Default; callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback); defaultCacheItemPolicy = new CacheItemPolicy(); defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0); defaultCacheItemPolicy.RemovedCallback = callback; allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ; } public static CacheManager Instance { get { if (instance == null) { lock (syncRoot) { if (instance == null) { instance = new CacheManager(); } } } return instance; } } public IEnumerable GetCache(String Key) { if (Key == null || !allowCache) { return null; } try { String Key_ = Key; if (cache.Contains(Key_)) { return (IEnumerable)cache.Get(Key_); } else { return null; } } catch (Exception) { return null; } } public void ClearCache(string key) { AddCache(key, null); } public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null) { if (!allowCache) return true; try { if (Key == null) { return false; } if (cacheItemPolicy == null) { cacheItemPolicy = defaultCacheItemPolicy; } String Key_ = Key; lock (Key_) { return cache.Add(Key_, data, cacheItemPolicy); } } catch (Exception) { return false; } } private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments) { String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString()); LogManager.Instance.Info(strLog); } }