asp.net-mvc - enable - outputcache mvc 5
¿Cómo puedo almacenar en caché objetos en ASP.NET MVC? (8)
@ njappboy: Buena implementación. Solo pospondré la invocación de Generator( )
hasta el último momento responsable. por lo tanto, también puede cachear las invocaciones de métodos.
/// <summary>
/// Allows Caching of typed data
/// </summary>
/// <example><![CDATA[
/// var user = HttpRuntime
/// .Cache
/// .GetOrStore<User>(
/// string.Format("User{0}", _userId),
/// () => Repository.GetUser(_userId));
///
/// ]]></example>
/// <typeparam name="T"></typeparam>
/// <param name="Cache">calling object</param>
/// <param name="Key">Cache key</param>
/// <param name="Generator">Func that returns the object to store in cache</param>
/// <returns></returns>
/// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks>
public static T GetOrStore<T>( this Cache Cache, string Key, Func<T> Generator )
{
return Cache.GetOrStore( Key, Generator, DefaultCacheExpiration );
}
/// <summary>
/// Allows Caching of typed data
/// </summary>
/// <example><![CDATA[
/// var user = HttpRuntime
/// .Cache
/// .GetOrStore<User>(
/// string.Format("User{0}", _userId),
/// () => Repository.GetUser(_userId));
///
/// ]]></example>
/// <typeparam name="T"></typeparam>
/// <param name="Cache">calling object</param>
/// <param name="Key">Cache key</param>
/// <param name="Generator">Func that returns the object to store in cache</param>
/// <param name="ExpireInMinutes">Time to expire cache in minutes</param>
/// <returns></returns>
public static T GetOrStore<T>( this Cache Cache, string Key, Func<T> Generator, double ExpireInMinutes )
{
var Result = Cache [ Key ];
if( Result == null )
{
lock( Sync )
{
if( Result == null )
{
Result = Generator( );
Cache.Insert( Key, Result, null, DateTime.Now.AddMinutes( ExpireInMinutes ), Cache.NoSlidingExpiration );
}
}
}
return ( T ) Result;
}
Me gustaría almacenar objetos en ASP.NET MVC. Tengo un BaseController
que quiero que todos los controladores hereden. En el BaseController hay una propiedad del User
que simplemente tomará los datos del Usuario de la base de datos para que yo pueda usarlos dentro del controlador o pasarlos a las vistas.
Me gustaría guardar esta información en caché Estoy usando esta información en cada página, por lo que no es necesario ir a la base de datos de cada solicitud de página.
Me gustaría algo como:
if(_user is null)
GrabFromDatabase
StuffIntoCache
return CachedObject as User
¿Cómo implemento el almacenamiento en caché simple en ASP.NET MVC?
Algunas de las otras respuestas aquí no tratan con lo siguiente:
- estampida de caché
- doble control de bloqueo
Esto podría llevar al generador (que podría tomar mucho tiempo) ejecutarse más de una vez en diferentes hilos.
Aquí está mi versión que no debería sufrir este problema:
// using System.Web.Caching;
public static class CacheExtensions
{
private static object sync = new object();
private static TimeSpan defaultExpire = TimeSpan.FromMinutes(20);
public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator) =>
cache.GetOrStore(key, generator, defaultExpire);
public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator, TimeSpan expire)
{
var result = cache[key];
if (result == null)
{
lock (sync)
{
result = cache[key];
if (result == null)
{
result = generator();
cache.Insert(key, result, null, DateTime.Now.AddMinutes(expire.TotalMinutes), Cache.NoSlidingExpiration);
}
}
}
return (T)result;
}
}
Implementación con un mínimo bloqueo de caché. El valor almacenado en la memoria caché está envuelto en un contenedor. Si el valor no está en la memoria caché, entonces el contenedor de valores está bloqueado. La memoria caché se bloquea solo durante la creación del contenedor.
public static class CacheExtensions
{
private static object sync = new object();
private class Container<T>
{
public T Value;
}
public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<TValue> create, TimeSpan slidingExpiration)
{
return cache.GetOrStore(key, create, Cache.NoAbsoluteExpiration, slidingExpiration);
}
public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<TValue> create, DateTime absoluteExpiration)
{
return cache.GetOrStore(key, create, absoluteExpiration, Cache.NoSlidingExpiration);
}
public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<TValue> create, DateTime absoluteExpiration, TimeSpan slidingExpiration)
{
return cache.GetOrCreate(key, x => create());
}
public static TValue GetOrStore<TValue>(this Cache cache, string key, Func<string, TValue> create, DateTime absoluteExpiration, TimeSpan slidingExpiration)
{
var instance = cache.GetOrStoreContainer<TValue>(key, absoluteExpiration, slidingExpiration);
if (instance.Value == null)
lock (instance)
if (instance.Value == null)
instance.Value = create(key);
return instance.Value;
}
private static Container<TValue> GetOrStoreContainer<TValue>(this Cache cache, string key, DateTime absoluteExpiration, TimeSpan slidingExpiration)
{
var instance = cache[key];
if (instance == null)
lock (cache)
{
instance = cache[key];
if (instance == null)
{
instance = new Container<TValue>();
cache.Add(key, instance, null, absoluteExpiration, slidingExpiration, CacheItemPriority.Default, null);
}
}
return (Container<TValue>)instance;
}
}
Me gusta ocultar el hecho de que los datos están almacenados en caché en el repositorio. Puede acceder a la memoria caché a través de la propiedad HttpContext.Current.Cache y almacenar la información del usuario usando "User" + id.ToString () como la clave.
Esto significa que todo acceso a los datos de usuario del repositorio usará datos en caché si están disponibles y no requiere cambios de código en el modelo, controlador o vista.
Utilicé este método para corregir problemas graves de rendimiento en un sistema que consultaba la base de datos para cada propiedad del usuario y reducía los tiempos de carga de la página de minutos a segundos de un solo dígito.
Si lo quiere almacenar en caché durante la duración de la solicitud, coloque esto en la clase base de su controlador:
public User User {
get {
User _user = ControllerContext.HttpContext.Items["user"] as User;
if (_user == null) {
_user = _repository.Get<User>(id);
ControllerContext.HttpContext.Items["user"] = _user;
}
return _user;
}
}
Si desea almacenar en caché durante más tiempo, use la función Reemplazar la llamada ControllerContext con una en Caché []. Si opta por utilizar el objeto Caché para caché por más tiempo, deberá usar una clave de caché única, ya que se compartirá entre las solicitudes / usuarios.
Si no necesita funciones específicas de invalidación del almacenamiento en caché de ASP.NET, los campos estáticos son bastante buenos, livianos y fáciles de usar. Sin embargo, tan pronto como necesitó las características avanzadas, puede cambiar al objeto Cache
de ASP.NET para almacenamiento.
El enfoque que uso es crear una propiedad y un campo private
. Si el campo es null
, la propiedad lo rellenará y lo devolverá. También proporciono un método InvalidateCache
que establece manualmente el campo como null
. La ventaja de este enfoque es que el mecanismo de almacenamiento en caché está encapsulado en la propiedad y puede cambiar a un enfoque diferente si lo desea.
Todavía puede usar el caché (compartido entre todas las respuestas) y la sesión (única por usuario) para el almacenamiento.
Me gusta el siguiente patrón "try get from cache / create and store" (pseudocódigo tipo c #):
public static class CacheExtensions
{
public static T GetOrStore<T>(this Cache cache, string key, Func<T> generator)
{
var result = cache[key];
if(result == null)
{
result = generator();
cache[key] = result;
}
return (T)result;
}
}
lo usarías así:
var user = HttpRuntime
.Cache
.GetOrStore<User>(
$"User{_userId}",
() => Repository.GetUser(_userId));
Puede adaptar este patrón a la Session, ViewState (ugh) o cualquier otro mecanismo de caché. También puede extender ControllerContext.HttpContext (que creo que es uno de los contenedores en System.Web.Extensions), o crear una nueva clase para hacerlo con espacio para burlarse de la caché.
Tomé la respuesta de Will y la modifiqué para hacer que la clase CacheExtensions
estática y sugerir una ligera alteración para tratar con la posibilidad de que Func<T>
sea null
:
public static class CacheExtensions
{
private static object sync = new object();
public const int DefaultCacheExpiration = 20;
/// <summary>
/// Allows Caching of typed data
/// </summary>
/// <example><![CDATA[
/// var user = HttpRuntime
/// .Cache
/// .GetOrStore<User>(
/// string.Format("User{0}", _userId),
/// () => Repository.GetUser(_userId));
///
/// ]]></example>
/// <typeparam name="T"></typeparam>
/// <param name="cache">calling object</param>
/// <param name="key">Cache key</param>
/// <param name="generator">Func that returns the object to store in cache</param>
/// <returns></returns>
/// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks>
public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator ) {
return cache.GetOrStore( key, (cache[key] == null && generator != null) ? generator() : default( T ), DefaultCacheExpiration );
}
/// <summary>
/// Allows Caching of typed data
/// </summary>
/// <example><![CDATA[
/// var user = HttpRuntime
/// .Cache
/// .GetOrStore<User>(
/// string.Format("User{0}", _userId),
/// () => Repository.GetUser(_userId));
///
/// ]]></example>
/// <typeparam name="T"></typeparam>
/// <param name="cache">calling object</param>
/// <param name="key">Cache key</param>
/// <param name="generator">Func that returns the object to store in cache</param>
/// <param name="expireInMinutes">Time to expire cache in minutes</param>
/// <returns></returns>
public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator, double expireInMinutes ) {
return cache.GetOrStore( key, (cache[key] == null && generator != null) ? generator() : default( T ), expireInMinutes );
}
/// <summary>
/// Allows Caching of typed data
/// </summary>
/// <example><![CDATA[
/// var user = HttpRuntime
/// .Cache
/// .GetOrStore<User>(
/// string.Format("User{0}", _userId),_userId));
///
/// ]]></example>
/// <typeparam name="T"></typeparam>
/// <param name="cache">calling object</param>
/// <param name="key">Cache key</param>
/// <param name="obj">Object to store in cache</param>
/// <returns></returns>
/// <remarks>Uses a default cache expiration period as defined in <see cref="CacheExtensions.DefaultCacheExpiration"/></remarks>
public static T GetOrStore<T>( this Cache cache, string key, T obj ) {
return cache.GetOrStore( key, obj, DefaultCacheExpiration );
}
/// <summary>
/// Allows Caching of typed data
/// </summary>
/// <example><![CDATA[
/// var user = HttpRuntime
/// .Cache
/// .GetOrStore<User>(
/// string.Format("User{0}", _userId),
/// () => Repository.GetUser(_userId));
///
/// ]]></example>
/// <typeparam name="T"></typeparam>
/// <param name="cache">calling object</param>
/// <param name="key">Cache key</param>
/// <param name="obj">Object to store in cache</param>
/// <param name="expireInMinutes">Time to expire cache in minutes</param>
/// <returns></returns>
public static T GetOrStore<T>( this Cache cache, string key, T obj, double expireInMinutes ) {
var result = cache[key];
if ( result == null ) {
lock ( sync ) {
result = cache[key];
if ( result == null ) {
result = obj != null ? obj : default( T );
cache.Insert( key, result, null, DateTime.Now.AddMinutes( expireInMinutes ), Cache.NoSlidingExpiration );
}
}
}
return (T)result;
}
}
También consideraría llevar esto un paso más allá para implementar una solución de sesión comprobable que extienda la clase abstracta System.Web.HttpSessionStateBase.
public static class SessionExtension
{
/// <summary>
///
/// </summary>
/// <example><![CDATA[
/// var user = HttpContext
/// .Session
/// .GetOrStore<User>(
/// string.Format("User{0}", _userId),
/// () => Repository.GetUser(_userId));
///
/// ]]></example>
/// <typeparam name="T"></typeparam>
/// <param name="cache"></param>
/// <param name="key"></param>
/// <param name="generator"></param>
/// <returns></returns>
public static T GetOrStore<T>( this HttpSessionStateBase session, string name, Func<T> generator ) {
var result = session[name];
if ( result != null )
return (T)result;
result = generator != null ? generator() : default( T );
session.Add( name, result );
return (T)result;
}
}