Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En cualquier aplicación web, algunos datos se usarán con frecuencia y algunos datos se usarán con poca frecuencia. Podemos mejorar el rendimiento de nuestra aplicación de ASP.NET cargando con antelación los datos usados con frecuencia, una técnica conocida como Almacenamiento en caché. En este tutorial se muestra un enfoque para la carga proactiva, que consiste en cargar datos en la memoria caché durante el inicio de la aplicación.
Introducción
Los dos tutoriales anteriores examinaron los datos de almacenamiento en caché en las capas de presentación y almacenamiento en caché. En Almacenamiento en caché de datos con ObjectDataSource, analizamos el uso de las características de almacenamiento en caché de ObjectDataSource para almacenar en caché los datos en la capa de presentación. Examen del almacenamiento en caché de datos en la arquitectura examinó el uso del almacenamiento en caché en una nueva capa independiente. Ambos tutoriales usaron la carga reactiva en el trabajo con la caché de datos. Con la carga reactiva, cada vez que se solicitan los datos, el sistema comprueba primero si está en la memoria caché. Si no es así, toma los datos del origen de origen, como la base de datos y, a continuación, lo almacena en la memoria caché. La principal ventaja de la carga reactiva es su facilidad de implementación. Una de sus desventajas es su rendimiento desigual en todas las solicitudes. Imagine una página que usa la capa de almacenamiento en caché del tutorial anterior para mostrar información del producto. Cuando se visita esta página por primera vez o se visita por primera vez después de que se hayan expulsado los datos almacenados en caché debido a restricciones de memoria o a la expiración especificada, los datos deben recuperarse de la base de datos. Por lo tanto, estas solicitudes de usuarios tardarán más tiempo que las solicitudes de los usuarios que la memoria caché puede atender.
La carga proactiva proporciona una estrategia alternativa de administración de caché que suaviza el rendimiento entre las solicitudes cargando los datos almacenados en caché antes de que sea necesario. Normalmente, la carga proactiva usa algún proceso que comprueba periódicamente o se notifica cuando se ha producido una actualización de los datos subyacentes. A continuación, este proceso actualiza la memoria caché para mantenerla actualizada. La carga proactiva es especialmente útil si los datos subyacentes proceden de una conexión de base de datos lenta, un servicio web o algún otro origen de datos especialmente lento. Pero este enfoque para la carga proactiva es más difícil de implementar, ya que requiere crear, administrar e implementar un proceso para comprobar los cambios y actualizar la memoria caché.
Otro tipo de carga proactiva y el tipo que exploraremos en este tutorial es cargar datos en la memoria caché en el inicio de la aplicación. Este enfoque es especialmente útil para almacenar en caché datos estáticos, como los registros de las tablas de búsqueda de base de datos.
Nota:
Para obtener una visión más detallada de las diferencias entre la carga proactiva y reactiva, así como listas de ventajas, desventajas e recomendaciones de implementación, consulte la sección Administración del contenido de una memoria caché de la Guía de arquitectura de almacenamiento en caché para aplicaciones de .NET Framework.
Paso 1: Determinar qué datos almacenar en caché en el inicio de la aplicación
Los ejemplos de almacenamiento en caché que usan la carga reactiva que hemos examinado en los dos tutoriales anteriores funcionan bien con datos que pueden cambiar periódicamente y que no tardan exorbitantemente en generarse. Pero si los datos almacenados en caché nunca cambian, la expiración utilizada por la carga reactiva es superflua. Del mismo modo, si los datos que se almacenan en caché tardan mucho tiempo en generarse, los usuarios cuyas solicitudes encuentran la memoria caché vacía tendrán que soportar una espera larga mientras se recuperan los datos subyacentes. Considere la posibilidad de almacenar en caché datos estáticos y datos que tardan mucho tiempo en generarse al iniciar la aplicación.
Aunque las bases de datos tienen muchos valores dinámicos y que cambian con frecuencia, la mayoría también tienen una cantidad razonable de datos estáticos. Por ejemplo, prácticamente todos los modelos de datos tienen una o varias columnas que contienen un valor determinado de un conjunto fijo de opciones. Una Patients
tabla de base de datos podría tener una PrimaryLanguage
columna, cuyo conjunto de valores podría ser inglés, español, francés, ruso, japonés, etc. A menudo, estos tipos de columnas se implementan mediante tablas de búsqueda. En lugar de almacenar la cadena inglés o francés en la Patients
tabla, se crea una segunda tabla que tiene, normalmente, dos columnas : un identificador único y una descripción de cadena, con un registro para cada valor posible. La PrimaryLanguage
columna de la Patients
tabla almacena el identificador único correspondiente en la tabla de búsqueda. En la figura 1, el idioma principal del paciente John Doe es el inglés, mientras que el de Ed Johnson es el ruso.
Figura 1: La Languages
tabla es una tabla de búsqueda usada por la Patients
tabla
La interfaz de usuario para editar o crear un nuevo paciente incluiría una lista desplegable de idiomas permitidos rellenados por los registros de la Languages
tabla. Sin almacenamiento en caché, cada vez que se visita esta interfaz, el sistema debe consultar la Languages
tabla. Esto es inútil e innecesario, ya que los valores de la tabla de búsqueda cambian con poca frecuencia, si es que alguna vez.
Podríamos almacenar en caché los Languages
datos mediante las mismas técnicas de carga reactiva que se examinaron en los tutoriales anteriores. Sin embargo, la carga reactiva usa una expiración basada en el tiempo, que no es necesaria para los datos de la tabla de búsqueda estática. Aunque el almacenamiento en caché mediante la carga reactiva sería mejor que no almacenar en caché en absoluto, el mejor enfoque sería cargar proactivamente los datos de la tabla de búsqueda en la memoria caché durante el inicio de la aplicación.
En este tutorial veremos cómo almacenar en caché los datos de la tabla de búsqueda y otra información estática.
Paso 2: Examinar las distintas formas de almacenar en caché los datos
La información se puede almacenar en caché mediante programación en una aplicación de ASP.NET mediante diversos enfoques. Ya hemos visto cómo usar la caché de datos en tutoriales anteriores. Como alternativa, los objetos se pueden almacenar en caché mediante programación mediante miembros estáticos o estado de aplicación.
Al trabajar con una clase, normalmente se debe crear una instancia de la clase antes de que se pueda acceder a sus miembros. Por ejemplo, para invocar un método desde una de las clases de nuestra capa lógica de negocios, primero debemos crear una instancia de la clase :
ProductsBLL productsAPI = new ProductsBLL();
productsAPI.SomeMethod();
productsAPI.SomeProperty = "Hello, World!";
Para poder invocar SomeMethod o trabajar con SomeProperty, primero debemos crear una instancia de la clase mediante la new
palabra clave .
SomeMethod y SomeProperty están asociados a una instancia determinada. La duración de estos miembros está vinculada a la duración de su objeto asociado.
Los miembros estáticos, por otro lado, son variables, propiedades y métodos que se comparten entre todas las instancias de la clase y, por consiguiente, tienen una vida útil tan larga como la de la clase. La palabra clave static
indica los miembros estáticos .
Además de los miembros estáticos, los datos se pueden almacenar en caché mediante el estado de la aplicación. Cada aplicación de ASP.NET mantiene una colección nombre/valor que se comparte entre todos los usuarios y las páginas de la aplicación. Para acceder a esta colección, use la propiedad de la clase HttpContext
, y se puede usar desde la clase de código subyacente de una página ASP.NET de la siguiente manera:
Application["key"] = value;
object value = Application["key"];
La caché de datos proporciona una API mucho más completa para almacenar datos en caché, lo que proporciona mecanismos para expiraciones basadas en tiempo y dependencias, prioridades de elementos de caché, etc. Con los miembros estáticos y el estado de la aplicación, el desarrollador de páginas debe agregar manualmente estas características. Sin embargo, al almacenar en caché los datos en el inicio de la aplicación durante toda la vida de la aplicación, las ventajas de la memoria caché resultan irrelevantes. En este tutorial veremos el código que usa las tres técnicas para almacenar datos estáticos en caché.
Paso 3: Almacenamiento en caché de los datos de tablaSuppliers
Las tablas de base de datos Northwind implementadas hasta la fecha no incluyen ninguna tabla de búsqueda tradicional. Las cuatro DataTables implementadas en nuestra DAL modelan tablas cuyos valores no son estáticos. En lugar de dedicar tiempo a agregar una nueva DataTable a la DAL y, a continuación, a una nueva clase y métodos al BLL, para este tutorial vamos a pretender simplemente que los datos de la Suppliers
tabla son estáticos. Por lo tanto, podríamos almacenar en caché estos datos en el inicio de la aplicación.
Para empezar, cree una nueva clase denominada StaticCache.cs
en la CL
carpeta .
Figura 2: Crear la StaticCache.cs
clase en la CL
carpeta
Es necesario agregar un método que cargue los datos al iniciarse en el almacén de caché adecuado, así como métodos que devuelvan datos de esta memoria caché.
[System.ComponentModel.DataObject]
public class StaticCache
{
private static Northwind.SuppliersDataTable suppliers = null;
public static void LoadStaticCache()
{
// Get suppliers - cache using a static member variable
SuppliersBLL suppliersBLL = new SuppliersBLL();
suppliers = suppliersBLL.GetSuppliers();
}
[DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
public static Northwind.SuppliersDataTable GetSuppliers()
{
return suppliers;
}
}
El código anterior usa una variable de miembro estática, suppliers
, para contener los resultados del método SuppliersBLL
de la clase GetSuppliers()
, al que se llama desde el método LoadStaticCache()
. El LoadStaticCache()
método está pensado para llamarse durante el inicio de la aplicación. Una vez que estos datos se hayan cargado en el inicio de la aplicación, cualquier página que necesite trabajar con los datos del proveedor puede llamar al método StaticCache
de la clase GetSuppliers()
. Por lo tanto, la llamada a la base de datos para obtener los proveedores solo se produce una vez, al iniciar la aplicación.
En lugar de usar una variable de miembro estática como almacén de caché, podríamos haber usado alternativamente el estado de la aplicación o la caché de datos. En el código siguiente se muestra la clase readaptada para usar el estado de la aplicación.
[System.ComponentModel.DataObject]
public class StaticCache
{
public static void LoadStaticCache()
{
// Get suppliers - cache using application state
SuppliersBLL suppliersBLL = new SuppliersBLL();
HttpContext.Current.Application["key"] = suppliersBLL.GetSuppliers();
}
[DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
public static Northwind.SuppliersDataTable GetSuppliers()
{
return HttpContext.Current.Application["key"] as Northwind.SuppliersDataTable;
}
}
En LoadStaticCache()
, la información del proveedor se almacena en la clave de variable de aplicación. Se devuelve como el tipo adecuado (Northwind.SuppliersDataTable
) de GetSuppliers()
. Aunque se puede acceder al estado de la aplicación en las clases de código subyacente de las páginas de ASP.NET mediante Application["key"]
, en la arquitectura se debe usar HttpContext.Current.Application["key"]
para obtener el HttpContext
actual.
Del mismo modo, la caché de datos se puede usar como almacén de caché, como se muestra en el código siguiente:
[System.ComponentModel.DataObject]
public class StaticCache
{
public static void LoadStaticCache()
{
// Get suppliers - cache using the data cache
SuppliersBLL suppliersBLL = new SuppliersBLL();
HttpRuntime.Cache.Insert(
/* key */ "key",
/* value */ suppliers,
/* dependencies */ null,
/* absoluteExpiration */ Cache.NoAbsoluteExpiration,
/* slidingExpiration */ Cache.NoSlidingExpiration,
/* priority */ CacheItemPriority.NotRemovable,
/* onRemoveCallback */ null);
}
[DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
public static Northwind.SuppliersDataTable GetSuppliers()
{
return HttpRuntime.Cache["key"] as Northwind.SuppliersDataTable;
}
}
Para agregar un elemento a la caché de datos sin expiración basada en el tiempo, use los System.Web.Caching.Cache.NoAbsoluteExpiration
valores y System.Web.Caching.Cache.NoSlidingExpiration
como parámetros de entrada. Esta sobrecarga concreta del método Insert
del caché de datos se seleccionó para que pudiéramos especificar la prioridad del elemento de caché. La prioridad se utiliza para determinar qué elementos se deben rescatar de la memoria caché cuando la memoria disponible se agota. Aquí usamos la prioridad NotRemovable
, que garantiza que este elemento de caché no será eliminado.
Nota:
La descarga de este tutorial implementa la clase StaticCache
usando el enfoque de variable de miembro estático. El código de las técnicas de estado de aplicación y caché de datos está disponible en los comentarios del archivo de clase.
Paso 4: Ejecutar código en el inicio de la aplicación
Para ejecutar código cuando se inicia una aplicación web por primera vez, es necesario crear un archivo especial denominado Global.asax
. Este archivo puede contener controladores de eventos para eventos de aplicación, sesión y nivel de solicitud, y aquí se puede agregar código que se ejecutará cada vez que se inicie la aplicación.
Agregue el Global.asax
archivo al directorio raíz de la aplicación web haciendo clic con el botón derecho en el nombre del proyecto de sitio web en el Explorador de soluciones de Visual Studio y seleccionando Agregar nuevo elemento. En el cuadro de diálogo Agregar nuevo elemento, seleccione el tipo de elemento Clase de aplicación global y, a continuación, haga clic en el botón Agregar.
Nota:
Si ya tiene un Global.asax
archivo en el proyecto, el tipo de elemento Clase de aplicación global no aparecerá en el cuadro de diálogo Agregar nuevo elemento.
Figura 3: Agregar el archivo al directorio raíz de la aplicación web (Global.asax
imagen de tamaño completo)
La plantilla de archivo predeterminada de Global.asax
incluye cinco métodos dentro de una etiqueta del lado del servidor <script>
.
-
Application_Start
se ejecuta cuando se inicia la aplicación web por primera vez. -
Application_End
se ejecuta cuando la aplicación se cierra -
Application_Error
se ejecuta cada vez que una excepción no controlada alcanza la aplicación. -
Session_Start
se ejecuta cuando se crea una nueva sesión -
Session_End
se ejecuta cuando una sesión ha expirado o abandonado
El controlador de eventos Application_Start
se llama solo una vez durante el ciclo de vida de una aplicación. La aplicación inicia la primera vez que se solicita un recurso de ASP.NET desde la aplicación y continúa ejecutándose hasta que se reinicie la aplicación, lo que puede ocurrir modificando el contenido de la /Bin
carpeta, modificando Global.asax
, modificando el contenido de la App_Code
carpeta o modificando el Web.config
archivo, entre otras causas. Consulte el documento Descripción general del ciclo de vida de la aplicación en ASP.NET para obtener una discusión más detallada sobre el ciclo de vida de la aplicación.
En estos tutoriales solo es necesario agregar código al Application_Start
método, así que no dude en quitar los demás. En Application_Start
, simplemente llame al método de la clase StaticCache
, que cargará y almacenará en caché la información del proveedor LoadStaticCache()
.
<%@ Application Language="C#" %>
<script runat="server">
void Application_Start(object sender, EventArgs e)
{
StaticCache.LoadStaticCache();
}
</script>
¡Eso es todo! Al iniciar la aplicación, el LoadStaticCache()
método capturará la información del proveedor de BLL y la almacenará en una variable miembro estática (o cualquier almacén de caché que haya terminado usando en la StaticCache
clase ). Para comprobar este comportamiento, establezca un punto de interrupción en el Application_Start
método y ejecute la aplicación. Tenga en cuenta que el punto de interrupción se alcanza al iniciar la aplicación. Sin embargo, las solicitudes posteriores no hacen que el Application_Start
método se ejecute.
Figura 4: Usar un punto de interrupción para comprobar que el Application_Start
controlador de eventos se está ejecutando (haga clic para ver la imagen de tamaño completo)
Nota:
Si no alcanza el punto de interrupción Application_Start
al iniciar la depuración por primera vez, se debe a que la aplicación ya se ha iniciado. Fuerza el reinicio de la aplicación modificando los archivos Global.asax
o Web.config
e inténtalo de nuevo. Simplemente puede agregar (o quitar) una línea en blanco al final de uno de estos archivos para reiniciar rápidamente la aplicación.
Paso 5: Mostrar los datos almacenados en caché
En este momento, la StaticCache
clase tiene una versión de los datos del proveedor almacenados en caché durante el inicio de la aplicación a los que se puede acceder a través de su GetSuppliers()
método. Para trabajar con estos datos desde la capa de presentación, podemos usar un ObjectDataSource o invocar programáticamente el método de la clase StaticCache
desde una clase de código subyacente de la página ASP.NET. Echemos un vistazo al uso de los controles ObjectDataSource y GridView para mostrar la información de proveedor almacenada en caché.
Para empezar, abra la página AtApplicationStartup.aspx
en la carpeta Caching
. Arrastre un GridView desde el cuadro de herramientas al diseñador y establezca su propiedad en ID
. A continuación, en la etiqueta inteligente de GridView, elija crear un objeto ObjectDataSource denominado SuppliersCachedDataSource
. Configure el ObjectDataSource para usar el método StaticCache
de la clase GetSuppliers()
.
Figura 5: Configurar objectDataSource para usar la clase (StaticCache
imagen de tamaño completo)
Figura 6: Usar el GetSuppliers()
método para recuperar los datos de proveedores almacenados en caché (haga clic para ver la imagen de tamaño completo)
Después de completar el asistente, Visual Studio agregará automáticamente BoundFields para cada uno de los campos de datos de SuppliersDataTable
. El marcado declarativo de GridView y ObjectDataSource debe ser similar al siguiente:
<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False"
DataKeyNames="SupplierID" DataSourceID="SuppliersCachedDataSource"
EnableViewState="False">
<Columns>
<asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
InsertVisible="False" ReadOnly="True"
SortExpression="SupplierID" />
<asp:BoundField DataField="CompanyName" HeaderText="CompanyName"
SortExpression="CompanyName" />
<asp:BoundField DataField="Address" HeaderText="Address"
SortExpression="Address" />
<asp:BoundField DataField="City" HeaderText="City"
SortExpression="City" />
<asp:BoundField DataField="Country" HeaderText="Country"
SortExpression="Country" />
<asp:BoundField DataField="Phone" HeaderText="Phone"
SortExpression="Phone" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersCachedDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="StaticCache" />
En la figura 7 se muestra la página cuando se ve a través de un explorador. La salida sería la misma si sacáramos los datos de la clase SuppliersBLL
de la BLL, pero usar la clase StaticCache
devuelve los datos del proveedor tal como estaban en caché al inicio de la aplicación. Puede establecer puntos de interrupción en el método StaticCache
de la clase GetSuppliers()
para comprobar este comportamiento.
Figura 7: Los datos de proveedores almacenados en caché se muestran en una vista GridView (haga clic para ver la imagen de tamaño completo)
Resumen
La mayoría de los modelos de datos contienen una cantidad razonable de datos estáticos, normalmente implementados en forma de tablas de búsqueda. Dado que esta información es estática, no hay ninguna razón para acceder continuamente a la base de datos cada vez que es necesario mostrar esta información. Además, debido a su naturaleza estática, al almacenar en caché los datos no es necesario que expire. En este tutorial hemos visto cómo tomar estos datos y almacenarlos en caché en la caché de datos, el estado de la aplicación y a través de una variable de miembro estática. Esta información se almacena en caché durante el inicio de la aplicación y permanece en la memoria caché durante toda la vigencia de la aplicación.
En este tutorial y en los dos últimos, hemos examinado los datos de almacenamiento en caché durante la duración de la aplicación, así como el uso de expiraciones basadas en el tiempo. Sin embargo, al almacenar en caché los datos de la base de datos, una expiración basada en el tiempo puede ser menor que la ideal. En lugar de vaciar periódicamente la memoria caché, sería óptimo expulsar solo el elemento almacenado en caché cuando se modifican los datos de la base de datos subyacentes. Este ideal es posible mediante el uso de dependencias de caché de SQL, que examinaremos en el siguiente tutorial.
¡Feliz programación!
Acerca del autor
Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 en 24 horas. Se puede contactar con él en mitchell@4GuysFromRolla.com.
Agradecimientos especiales a
Esta serie de tutoriales contó con la revisión de muchos revisores que fueron de gran ayuda. Los revisores principales de este tutorial fueron Teresa Murphy y Zack Jones. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.