Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 19 de febrero de 2019
ASP.NET Core MVCUna gestión apropiada de la caché es muchas veces el secreto para los sistemas de alto rendimiento o que tienen que atender a una gran carga de peticiones por segundo, y el framework ASP.NET Core MVC incluye numerosas herramientas para ello.

Entre otras, el tag helper <cache> es especialmente útil para almacenar en la memoria del servidor el resultado de procesar una porción de página o vista, de forma que pueda ser reutilizada en peticiones siguientes. Un ejemplo bastante básico, pero que deja bastante clara su utilidad y forma de uso, podría ser el siguiente:
<h1>Atomic clock</h1>
<cache expires-after="@TimeSpan.FromSeconds(30)">
    @{
        await Task.Delay(3000);
    }
    Current time: @DateTime.UtcNow.ToLongTimeString()
</cache>
Al acceder a una vista con el código anterior por primera vez, se producirá un retardo forzado de tres segundos, se renderizará el interior del tag <cache> mostrando la hora actual, y el resultado será almacenado en memoria durante 30 segundos.

Si volvemos a acceder a la misma vista durante esos 30 segundos, el resultado será mostrado inmediatamente (sin esperar los tres segundos) y el cliente recibirá el contenido que se cacheó durante la primera visita. Al transcurrir este plazo, en la siguiente petición se volverá a procesar el contenido del tag helper, ejecutándose la espera y volviendo a generar y cachear el resultado enviado al cliente.

Pero podemos tener ejemplos algo más complejos, como el siguiente. En el interior del tag helper hemos insertado un view component que podría mostrar las últimas noticias obtenidas de una base de datos, en lo que podría ser la página de inicio de un servicio de información on-line:
<section>
    <h1>Latest news</h1>
    <cache expires-after="@TimeSpan.FromSeconds(30)">
        <vc:latest-news></vc:latest-news>
        <p>
            Updated at: @DateTime.UtcNow.ToLongTimeString()
        </p>
    </cache>
</section>
Fijaos que en este caso el tiempo de invalidación del contenido almacenado en caché no tendría sentido que fuera periódico: ¿para qué invalidar la caché y volver a obtener las noticias de la base de datos cada X segundos si quizás no se ha introducido ninguna nueva desde la última vez que se renderizó la página? ¿No sería mejor recargar la caché sólo cuando se hayan modificado las noticias?

Invalidar el contenido de la caché

Cuando nos encontramos con escenarios como ese, donde queremos invalidar el contenido de la caché ante determinados eventos, quizás nos venga bien conocer el atributo vary-by del tag helper <cache>.

Su forma de uso es muy sencilla: basta con indicar con ese atributo una expresión de cadena cuya modificación implicará la invalidación automática del contenido de la caché. Por ejemplo, en el siguiente código forzaremos a que esto ocurra cuando cambie el valor de una propiedad, en este caso estática, a la que accedemos mediante la expresión LatestNewsCache.Timestamp:
<cache vary-by="@LatestNewsCache.Timestamp">
    <vc:latest-news></vc:latest-news>
    <p>
        Updated at: @DateTime.UtcNow.ToLongTimeString()
    </p>
</cache>
La clase LatestNewCache podría ser algo similar a la mostrada a continuación, donde exponemos un método Invalidate() que forzaría la modificación del timestamp y, por tanto, la invalidación de la caché de noticias:
public class LatestNewsCache
{
    public static string Timestamp { get; private set;  }
    public void Invalidate()
    {
        Timestamp = DateTime.UtcNow.Ticks.ToString();
    }
}
Esta clase podríamos registrarla en los servicios de ASP.NET Core y usarla desde los métodos de gestión de noticias para forzar la recarga del caché en las siguientes peticiones cuando la colección haya sido modificada:
public class NewsServices: INewsServices
{
    [...]
    private readonly LatestNewsCache _latestNewsCache;

    public NewsController(LatestNewsCache latestNewsCache, ...)
    {
        [...]
        _latestNewsCache = latestNewsCache;
    }

    public async Task Create(NewsDto newsDto)
    {
        [...]
        // Everything was ok, so let's invalidate the cache
        _latestNewsCache.Invalidate();
    }
}
¡Y esto es todo! Espero que os haya resultado interesante y aplicable para en vuestros proyectos :)

Publicado en: www.variablenotfound.com.

2 Comentarios:

Jose Carlos Carballo dijo...

Me parece una característica muy útil y sencilla de implementar.
Muchas gracias por la explicación.

José María Aguilar dijo...

Hola, Carlos!

Pues sí, útil y sorprendentemente sencilla :)

Muchas gracias por comentar!