Mejorar el rendimiento con el almacenamiento en caché de resultados (C#)

por Microsoft

En este tutorial, aprenderá a mejorar drásticamente el rendimiento de las aplicaciones web de ASP.NET MVC aprovechando el almacenamiento en caché de salida. Aprenderá a almacenar en caché el resultado devuelto desde una acción del controlador para que no sea necesario crear el mismo contenido cada vez que un nuevo usuario invoque la acción.

El objetivo de este tutorial es explicar cómo puede mejorar drásticamente el rendimiento de una aplicación de ASP.NET MVC aprovechando la memoria caché de salida. La memoria caché de salida permite almacenar en caché el contenido devuelto por una acción del controlador. De este modo, no es necesario generar el mismo contenido cada vez que se invoca la misma acción del controlador.

Imagine, por ejemplo, que la aplicación de ASP.NET MVC muestra una lista de registros de base de datos en una vista llamada Index. Normalmente, cada vez que un usuario invoca la acción del controlador que devuelve la vista Index, se debe recuperar el conjunto de registros de base de datos desde la base de datos ejecutando una consulta de base de datos.

Por otro lado, si aprovecha la memoria caché de salida, puede evitar ejecutar una consulta de base de datos cada vez que cualquier usuario invoca la misma acción del controlador. La vista se puede recuperar de la memoria caché en lugar de volver a generarse desde la acción del controlador. El almacenamiento en caché permite evitar trabajo redundante en el servidor.

Habilitación del almacenamiento en caché de salida

Para habilitar el almacenamiento en caché de salida, agregue un atributo [OutputCache] a una acción individual del controlador o a una clase completa del controlador. Por ejemplo, el controlador de la lista 1 expone una acción llamada Index(). La salida de la acción Index() se almacena en caché durante 10 segundos.

Lista 1: Controllers\HomeController.cs

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [OutputCache(Duration=10, VaryByParam="none")]
        public ActionResult Index()
        {
            return View();
        }
    }
}

En las versiones beta de ASP.NET MVC, el almacenamiento en caché de salida no funciona para una dirección URL como http://www.MySite.com/. En su lugar, debe escribir una URL como http://www.MySite.com/Home/Index.

En la lista 1, la salida de la acción Index() se almacena en caché durante 10 segundos. Si lo prefiere, puede especificar una duración en caché mucho más larga. Por ejemplo, si desea almacenar en caché la salida de una acción del controlador durante un día, puede especificar una duración en caché de 86 400 segundos (60 segundos * 60 minutos * 24 horas).

No hay ninguna garantía de que el contenido vaya a estar almacenado en caché durante el período de tiempo que especifique. Cuando los recursos de memoria quedan en un nivel bajo, la memoria caché comienza a expulsar el contenido automáticamente.

El controlador Home de la lista 1 devuelve la vista Index de la lista 2. No hay nada especial sobre esta vista. La vista Index simplemente muestra la hora actual (ver figura 1).

Lista 2: Views\Home\Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Index</title>
</head>
<body>
    <div>
    
    The current time is: <%= DateTime.Now.ToString("T") %>
    
    
    </div>
</body>
</html>

Figura 1: vista Index almacenada en caché

clip_image002

Si invoca la acción Index() varias veces escribiendo la dirección URL /Home/Index en la barra de direcciones del explorador y pulsando el botón Actualizar/Volver a cargar en el explorador repetidamente, la hora mostrada por la vista Index no cambiará durante 10 segundos. Se muestra la misma hora porque la vista está almacenada en caché.

Es importante comprender que se almacena en caché la misma vista para todos los usuarios que visitan la aplicación. Cualquier persona que invoque la acción Index() obtendrá la misma versión almacenada en caché de la vista Index. Esto significa que la cantidad de trabajo que debe realizar el servidor web para atender la vista Index se reduce drásticamente.

La vista de la lista 2 hace algo realmente sencillo. La vista solo muestra la hora actual. Sin embargo, podría almacenar en caché fácilmente una vista que muestre un conjunto de registros de base de datos. En ese caso, el conjunto de registros de base de datos no se tendría que recuperar de la base de datos cada vez que se invoque la acción del controlador que devuelve la vista. El almacenamiento en caché puede reducir la cantidad de trabajo que deben realizar el servidor web y el servidor de base de datos.

No use la directiva <%@ OutputCache %> de la página en una vista MVC. Esta directiva proviene del mundo de Web Forms y no se debe usar en una aplicación ASP.NET MVC.

Dónde se almacena en caché el contenido

De forma predeterminada, cuando se usa el atributo [OutputCache], el contenido se almacena en caché en tres ubicaciones: el servidor web, los servidores proxy y el explorador web. Puede controlar exactamente dónde se almacena en caché el contenido modificando la propiedad Location del atributo [OutputCache].

Puede establecer la propiedad Location en cualquiera de los siguientes valores:

· Any

· Client

· Downstream

· Server

· None

· ServerAndClient

De manera predeterminada, la propiedad Location tiene el valor Any. Sin embargo, hay situaciones en las que es posible que quiera almacenar en caché solo en el explorador o solo en el servidor. Por ejemplo, si va a almacenar en caché información personalizada para cada usuario, no debe almacenar en caché la información en el servidor. Si va a mostrar información diferente a distintos usuarios, debe almacenar en caché la información solo en el cliente.

Por ejemplo, el controlador de la lista 3 expone una acción llamada GetName() que devuelve el nombre de usuario actual. Si Jack inicia sesión en el sitio web e invoca la acción GetName(), la acción devuelve la cadena "Hi Jack". Si, posteriormente, Jill inicia sesión en el sitio web e invoca la acción GetName(), también obtendrá la cadena "Hi Jack". La cadena se almacena en caché en el servidor web para todos los usuarios después de que Jack invoque inicialmente la acción del controlador.

Lista 3: Controllers\BadUserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
    public class BadUserController : Controller
    {
        [OutputCache(Duration = 3600, VaryByParam = "none")]
        public string GetName()
        {
            return "Hi " + User.Identity.Name;
        }
    }
}

Lo más probable es que el controlador de la lista 3 no funcione de la manera que quiere. No querrá mostrar el mensaje "Hi Jack" a Jill.

Nunca debe almacenar en caché el contenido personalizado en la memoria caché del servidor. Sin embargo, es posible que quiera almacenar en caché el contenido personalizado en la memoria caché del explorador para mejorar el rendimiento. Si almacena en caché el contenido en el explorador y un usuario invoca la misma acción de controlador varias veces, el contenido se puede recuperar de la caché del explorador en lugar del servidor.

El controlador modificado de la lista 4 almacena en caché la salida de la acción GetName(). Sin embargo, el contenido solo se almacena en caché en el explorador y no en el servidor. De este modo, cuando varios usuarios invocan el método GetName(), cada persona obtiene su propio nombre de usuario y no el nombre de usuario de otra persona.

Lista 4: Controllers\UserController.cs

using System.Web.Mvc;
using System.Web.UI;

namespace MvcApplication1.Controllers
{
    public class UserController : Controller
    {
        [OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
        public string GetName()
        {
            return "Hi " + User.Identity.Name;
        }
    }
}

Observe que el atributo [OutputCache] de la lista 4 incluye una propiedad Location establecida en el valor OutputCacheLocation.Client. El atributo [OutputCache] también incluye una propiedad NoStore. La propiedad NoStore se usa para informar a los servidores proxy y los exploradores que no deben almacenar una copia permanente del contenido almacenado en caché.

Variación de la caché de salida

En algunas situaciones, es posible que quiera diferentes versiones almacenadas en caché del mismo contenido. Imagine, por ejemplo, que está creando una página master/detail. La página maestra muestra una lista de títulos de películas. Al hacer clic en un título, obtendrá los detalles de la película seleccionada.

Si almacena en caché la página de detalles, se mostrarán los detalles de la misma película independientemente de la película en la que se haga clic. Se mostrará la primera película seleccionada por el primer usuario a todos los usuarios futuros.

Puede solucionar este problema aprovechando la propiedad VaryByParam del atributo [OutputCache]. Esta propiedad permite crear diferentes versiones almacenadas en caché del mismo contenido cuando varía un parámetro del formulario o un parámetro de la cadena de consulta.

Por ejemplo, el controlador de la lista 5 expone dos acciones llamadas Master() y Details(). La acción Master() devuelve una lista de títulos de películas y la acción Details() devuelve los detalles de la película seleccionada.

Lista 5: Controllers\MoviesController.cs

using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{
    public class MoviesController : Controller
    {
        private MovieDataContext _dataContext;

        public MoviesController()
        {
            _dataContext = new MovieDataContext();
        }

        [OutputCache(Duration=int.MaxValue, VaryByParam="none")]
        public ActionResult Master()
        {
            ViewData.Model = (from m in _dataContext.Movies 
                              select m).ToList();
            return View();
        }

        [OutputCache(Duration = int.MaxValue, VaryByParam = "id")]
        public ActionResult Details(int id)
        {
            ViewData.Model = _dataContext.Movies.SingleOrDefault(m => m.Id == id);
            return View();
        }


    }
}

La acción Master() incluye la propiedad VaryByParam con el valor "none". Cuando se invoca la acción Master(), se devuelve la misma versión almacenada en caché de la vista maestra. Los parámetros del formulario o los parámetros de la cadena de consulta se omiten (ver figura 2).

Figura 2: Vista /Movies/Master

clip_image004

Figura 3: Vista /Movies/Details

clip_image006

La acción Details() incluye la propiedad VaryByParam con el valor "Id". Cuando se pasan valores diferentes del parámetro Id a la acción del controlador, se generan diferentes versiones almacenadas en caché de la vista de detalles.

Es importante comprender que el uso de la propiedad VaryByParam da como resultado más almacenamiento en caché y no menos. Se crea una versión almacenada en caché diferente de la vista de detalles para cada versión diferente del parámetro Id.

Puede establecer la propiedad VaryByParam en los valores siguientes:

* = Crear una versión almacenada en caché diferente cada vez que varía un parámetro de la cadena de consulta o del formulario.

none = No crear nunca diferentes versiones almacenadas en caché

Lista de parámetros separados por punto y coma = Crear diferentes versiones almacenadas en caché cada vez que varía cualquiera de los parámetros del formulario o la cadena de consulta de la lista

Creación de un perfil de caché

Como alternativa a configurar las propiedades de caché de salida modificando las propiedades del atributo [OutputCache], puede crear un perfil de caché en el archivo de configuración web (web.config). La creación de un perfil de caché en el archivo de configuración web ofrece un par de ventajas importantes.

En primer lugar, mediante la configuración del almacenamiento en caché de salida en el archivo de configuración web, puede controlar cómo las acciones del controlador almacenan en caché el contenido en una ubicación central. Puede crear un perfil de caché y aplicar el perfil a varios controladores o acciones del controlador.

En segundo lugar, puede modificar el archivo de configuración web sin volver a compilar la aplicación. Si necesita deshabilitar el almacenamiento en caché para una aplicación que ya se ha implementado en producción, simplemente puede modificar los perfiles de caché definidos en el archivo de configuración web. Los cambios realizados en el archivo de configuración web se detectarán y se aplicarán automáticamente.

Por ejemplo, la sección de configuración web de <almacenamiento en caché> de la lista 6 define un perfil de caché llamado Cache1Hour. La sección de <almacenamiento en caché> debe aparecer en la sección <system.web> de un archivo de configuración web.

Lista 6: sección de almacenamiento en caché de web.config

<caching>
<outputCacheSettings>
    <outputCacheProfiles>
        <add name="Cache1Hour" duration="3600" varyByParam="none"/>
    </outputCacheProfiles>
</outputCacheSettings>
</caching>

El controlador de la lista 7 muestra cómo puede aplicar el perfil Cache1Hour a una acción de controlador con el atributo [OutputCache].

Lista 7: Controllers\ProfileController.cs

using System;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    public class ProfileController : Controller
    {
        [OutputCache(CacheProfile="Cache1Hour")]
        public string Index()
        {
            return DateTime.Now.ToString("T");
        }
    }
}

Si invoca la acción Index() expuesta por el controlador en la lista 7, se devolverá la misma hora durante 1 hora.

Resumen

El almacenamiento en caché de salida proporciona un método muy sencillo para mejorar drásticamente el rendimiento de las aplicaciones ASP.NET MVC. En este tutorial, ha aprendido a usar el atributo [OutputCache] para almacenar en caché la salida de las acciones del controlador. También ha aprendido a modificar las propiedades del atributo [OutputCache], como las propiedades Duration y VaryByParam para modificar cómo se almacena en caché el contenido. Por último, ha aprendido a definir perfiles de caché en el archivo de configuración web.