This article has been localized into Spanish by the community.
Cache en memoria con IMemoryCache
Como vimos en el artículo previo, el concepto de "OuputCache" fue introducido en la versión anterior de .NET Framework y también es soportado por el Framework de .NET Core por medio de un paquete NuGet. Sin embargo, en .NET Core 2.0, Microsoft presentó un enfoque mas nativo para el almacenamiento en memoria Cache con el nombre de espacio Microsoft.Extensions.Caching.Memory
Esto quiere decir que ahora puedes usar Cache en el servidor de forma nativa, sin el uso de paquetes NuGet, en tus proyectos "ASP.NET Core". Esto es comúnmente hecho en tus controladores, usando "Dependency Injection". Así, por ejemplo, si quieres usar Cache en tu HomeController, simplemente necesitas incluir el nombre de espacio Microsoft.Extensions.Caching.Memory con la sentencia "using" y luego inyectar el servicio IMemoryCache en el constructor del controlador. Aquí un ejemplo:
....
using Microsoft.Extensions.Caching.Memory;
namespace MemoryCacheSample.Controllers
{
public class HomeController : Controller
{
private IMemoryCache _memoryCache;
public HomeController(IMemoryCache memoryCache)
{
this._memoryCache = memoryCache;
}
....
Ahora puede usar el objeto _memoryCache en las acciones de tu controlador. Viene con varios métodos útiles para almacenar y recuperar objetos. Esta es también una de las principales diferencias entre OutputCache y el enfoque IMemoryCache: Mientras que OutputCache era generalmente usado para almacenar el resultado completo de un método del controlador, IMemoryCache es usado para almacenar los objetos necesarios para generar el resultado.
Por ejemplo, si tienes un UserController con un método Details() para mostrar los detalles de una forma de usuario desde una base de datos, el enfoque OutputCache almacenará la salida completa de éste método - con IMemoryCache almacenaras, en su lugar, el objeto usuario y luego lo usarás para devolver la Vista apropiada. En ambas situaciones, te ahorras la potencialmente costosa llamada a la base de datos, pero adquieres un mayor control del proceso con IMemoryCache.
Solo asegúrate que recuerdas seguir las siguientes pautas cuando usas Cache en memoria:
- No asumir que los objetos están aún en el Cache - asegúrate de verificar siempre y de proporcionar código para (re)llenar el Cache
- Como el almacenamiento en memoria usa RAM en tu servidor para almacenar objetos, deberías tratarlo como un recurso escaso y limitar la cantidad y tamaño de las cosas que almacenas
- Además deberías estar seguro de poner un vencimiento a la información que almacenas, para que se le permita al Framework eliminar las cosas que ya no son necesarias
- El Framework no aplica ninguna limitación en el tamaño de memoria de tu almacenamiento - necesitas hacer esto manualmente, usando las propiedades/métodos SetSize, Size y SizeLimit, si el uso del mecanismo de almacenamiento va por sobre el uso básico
Hecho esto, trataremos de usar MemoryCache - Aquí un ejemplo simple:
public IActionResult Index()
{
string cacheKey = DateTime.Now.ToString("yyyyMMddHHmm");
string cachedMessage;
if(!this._memoryCache.TryGetValue(cacheKey, out cachedMessage))
{
// Create a fake delay of 3 seconds to simulate heavy processing...
System.Threading.Thread.Sleep(3000);
cachedMessage = "Cache was last refreshed @ " + DateTime.Now.ToLongTimeString();
this._memoryCache.Set(cacheKey, cachedMessage);
}
return Content(cachedMessage);
}
Lo primero que hacemos, es crear una clave de almacenamiento. ésta será usada para guardar nuestros valores en el Cache, además de recuperarlos nuevamente. En nuestro ejemplo, nuestra clave es generada con un string DateTime que consiste en el Día, Mes, Año, Hora y Minuto. Así, cada vez que usamos el método TryGetValue() o tratamos de obtener un objeto desde el Cache que fue almacenado en el minuto actual - si no existe, el método TryGetValue() devolverá falso y crearemos el objeto (en este caso un string), almacenándolo en el Cache usando el método Set().
Para simular el proceso de p.ej. buscar un montón de datos que necesiten ser almacenados, desde una base de datos o un recurso de red, agregamos un retardo de 3 segundos usando el método Sleep. Al final de nuestra acción, retornaremos cachedMessage (el que ha sido generado o extraído desde el cache) como un simple string usando el método Content().
Si pruebas esto, obtendrás una salida como esta:
Cache was last refreshed @ 09:58:36
Trata recargando un par de veces y veras que la marca de tiempo solo cambia cuando el minuto cambia, esto es por la forma en que generamos la clave de almacenamiento. También observarás una mayor diferencia en cuanto tiempo le toma a la solicitud, gracias a nuestro retraso falso cuando llenamos el Cache - tomara cerca de 3 segundos llenar el Cache, pero será casi instantáneo cuando podamos traer los valores del Cache.
Vencimiento del Cache
Como se menciona anteriormente, siempre es una buena idea poner fecha de vencimiento a las cosas que almacenas. Si no, no serán removidas (a menos que el pool de la aplicación sea reciclado) y mientras eso este bien para ciertos casos, la mayoría de los datos probablemente expiraran tarde o temprano, para que puedan ser renovados p.ej. desde una base de datos. Afortunadamente, ASP.NET nos permite especificar cuando los datos almacenados deberían expirar. Existen dos tipos de vencimiento que puedes configurar para tus entradas de almacenamiento: Sliding y Absolute.
Vencimiento "Sliding"
El vencimiento "Sliding" permitirá a una entrada de almacenamiento expirar si no ha sido accesada por un periodo de tiempo definido. Un ejemplo común para esto sería el cargar algo pesado y luego poner un vencimiento "Sliding" de p.ej. 15 minutos: Cada vez que el usuario acceda a los datos, el vencimiento será pospuesto, permitiendo al usuario seguir trabajando con los datos. Sin embargo, cuando hayan pasado los 15 minutos sin que el usuario haya accedido a los datos, estos expiraran y el recurso sera liberado.
En el próximo ejemplo, te mostraré como configurar un vencimiento "Sliding". Estará basado en nuestro primer ejemplo en éste artículo, pero te darás cuenta de que use otro enfoque para accesar/llenar el Cache. El método se llama GetOrCreate() y es muy conveniente - solo debes especificar la clave de almacenamiento y luego un método para llenar en caso de que nada sea encontrado con dicha clave. El método incluirá un parámetro que también puedes usar para especificar opciones para la entrada de Cache y retornará una entrada almacenada. Así es como se ve:
public IActionResult GetOrCreateWithSlidingExpiration()
{
string cacheKey = DateTime.Now.ToString("yyyyMMdd");
string cachedMessage = this._memoryCache.GetOrCreate(cacheKey, entry =>
{
// Create a fake delay of 3 seconds to simulate heavy processing...
System.Threading.Thread.Sleep(3000);
entry.SetSlidingExpiration(TimeSpan.FromSeconds(30));
return "Cache was last refreshed @ " + DateTime.Now.ToLongTimeString();
});
return Content(cachedMessage);
}
Primero que todo, te darás cuenta que he cambiado el string de la la variable cacheKey. ahora solo corresponde a la fecha (no la hora), básicamente significa que la entrada almacenada será valida mientras la fecha no cambie. Sin Embargo, cuando se llena el Cache, especifique un vencimiento "Sliding" de 30 segundos, usando el método SetSlidingExpiration(). Prueba esta nueva acción en tu navegador y recárgalo varias veces. Te darás cuenta que la entrada almacenada permanece igual mientras no hayan pasado los 30 segundos desde tu última solicitud - tan pronto como dejes que mas de 30 segundos pasen, la entrada almacenada es removida y será llenada con una nueva entrada sobre tu requerimiento.
Vencimiento "Absolute"
El otro tipo de vencimiento es llamado "Absolute". Puede ser usado como una alternativa o una adición al vencimiento "Sliding". con un vencimiento "Absolute" las entradas almacenadas expirarán en un tiempo predeterminado, no importando cuantas veces hayan sido accesadas. Como puedes ver en el próximo ejemplo, puedes usarlo de la misma forma como configuras un vencimiento "Sliding":
public IActionResult GetOrCreateWithAbsoluteExpiration()
{
string cacheKey = DateTime.Now.ToString("yyyyMMdd");
string cachedMessage = this._memoryCache.GetOrCreate(cacheKey, entry =>
{
// Create a fake delay of 3 seconds to simulate heavy processing...
System.Threading.Thread.Sleep(3000);
entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(120));
return "Cache was last refreshed @ " + DateTime.Now.ToLongTimeString();
});
return Content(cachedMessage);
}
En este ejemplo, las entradas almacenadas expirarán después de 120 segundos (2 minutos) sin importar si estas son accesadas dentro de esos 2 minutos o no.
Como se ha mencionado, puedes combinar vencimientos "Sliding" y "Absolute". Esto es generalmente una buena idea cuando usas vencimiento "Sliding", para asegurarte de que las entradas expiren en algún momento, incluso si los usuarios permanecen accediéndolas antes que el vencimiento "Sliding" sea alcanzado.
Sumario
Como puedes ver en los ejemplos anteriores, trabajar con IMemoryCache en "ASP.NET Core" es ambos, fácil y eficiente. Almacenar objetos puede ser una herramienta muy poderosa cuando necesitas hacer tu aplicación web mas rápida y puede ahorrarle a tu servidor de base de datos un montón de trabajo. Solo asegúrate que piensas bien sobre cuanto puedes almacenar y por cuanto tiempo, para que no perjudique la experiencia del usuario.