Usar dependencias de caché de SQL (VB)
por Scott Mitchell
La estrategia de almacenamiento en caché más sencilla es permitir que los datos almacenados en caché expiren después de un período de tiempo especificado. Pero este enfoque sencillo significa que los datos almacenados en caché no mantienen ninguna asociación con su origen de datos subyacente, lo que da lugar a datos obsoletos que se mantienen demasiado largos o actuales que han expirado demasiado pronto. Un mejor enfoque consiste en usar la clase SqlCacheDependency para que los datos permanezcan almacenados en caché hasta que sus datos subyacentes se hayan modificado en la base de datos SQL. En este tutorial se muestra cómo realizar las siguientes acciones.
Introducción
Las técnicas de almacenamiento en caché examinadas en los tutoriales Almacenamiento en caché de datos con el ObjectDataSource y Almacenamiento en caché de datos en la arquitectura utilizaban una caducidad basada en el tiempo para desalojar los datos de la caché tras un periodo especificado. Este enfoque es la manera más sencilla de equilibrar las ganancias de rendimiento del almacenamiento en caché frente a la obsolescencia de los datos. Al seleccionar una expiración de tiempo de x segundos, un desarrollador de páginas concede disfrutar de las ventajas de rendimiento del almacenamiento en caché durante solo x segundos, pero puede descansar fácilmente que sus datos nunca estarán obsoletos durante un máximo de x segundos. Por supuesto, para los datos estáticos, x se puede extender a la duración de la aplicación web, como se examinó en el tutorial Datos de almacenamiento en caché en inicio de la aplicación.
Al almacenar en caché los datos de la base de datos, a menudo se elige una expiración basada en el tiempo para su facilidad de uso, pero suele ser una solución inadecuada. Idealmente, los datos de la base de datos permanecerán almacenados en caché hasta que los datos subyacentes se hayan modificado en la base de datos; solo entonces se expulsaría la memoria caché. Este enfoque maximiza las ventajas de rendimiento del almacenamiento en caché y minimiza la duración de los datos obsoletos. Sin embargo, para disfrutar de estas ventajas debe haber algún sistema que sepa cuándo se han modificado los datos de la base de datos subyacentes y expulsa los elementos correspondientes de la memoria caché. Antes de ASP.NET 2.0, los desarrolladores de páginas eran responsables de implementar este sistema.
ASP.NET 2.0 proporciona una clase SqlCacheDependency
y la infraestructura necesaria para determinar cuándo se ha producido un cambio en la base de datos para que se puedan expulsar los elementos almacenados en caché correspondientes. Existen dos técnicas para determinar cuándo han cambiado los datos subyacentes: notificación y sondeo. Después de analizar las diferencias entre la notificación y el sondeo, crearemos la infraestructura necesaria para admitir el sondeo y, a continuación, exploraremos cómo usar la clase SqlCacheDependency
en escenarios declarativos y mediante programación.
Descripción de las notificaciones y sondeos
Hay dos técnicas que se pueden usar para determinar cuándo se han modificado los datos de una base de datos: notificación y sondeo. Con la notificación, la base de datos alerta automáticamente a la ASP.NET tiempo de ejecución cuando se han cambiado los resultados de una consulta determinada desde que se ejecutó por última vez la consulta, en cuyo punto se expulsan los elementos almacenados en caché asociados a la consulta. Con el sondeo, el servidor de bases de datos mantiene información sobre cuándo se han actualizado por última vez tablas concretas. El ASP.NET tiempo de ejecución sondea periódicamente la base de datos para comprobar qué tablas han cambiado desde que se especificaron en la memoria caché. Esas tablas cuyos datos se han modificado tienen sus elementos de caché asociados expulsados.
La opción de notificación requiere menos configuración que el sondeo y es más granular, ya que realiza un seguimiento de los cambios en el nivel de consulta en lugar de en el nivel de tabla. Desafortunadamente, las notificaciones solo están disponibles en las ediciones completas de Microsoft SQL Server 2005 (es decir, las ediciones que no son Express). Sin embargo, la opción de sondeo se puede usar para todas las versiones de Microsoft SQL Server de 7.0 a 2005. Dado que estos tutoriales usan la edición Express de SQL Server 2005, nos centraremos en la configuración y el uso de la opción de sondeo. Consulte la sección Lectura adicional al final de este tutorial para obtener más recursos sobre las funcionalidades de notificación de SQL Server 2005.
Con el sondeo, la base de datos debe configurarse para incluir una tabla denominada AspNet_SqlCacheTablesForChangeNotification
que tiene tres columnas: tableName
, notificationCreated
y changeId
. Esta tabla contiene una fila para cada tabla que tiene datos que podrían necesitar usarse en una dependencia de caché de SQL en la aplicación web. La columna tableName
especifica el nombre de la tabla mientras que notificationCreated
indica la fecha y hora en que se agregó la fila a la tabla. La columna changeId
es de tipo int
y tiene un valor inicial de 0. Su valor se incrementa con cada modificación en la tabla.
Además de la tabla AspNet_SqlCacheTablesForChangeNotification
, la base de datos también debe incluir desencadenadores en cada una de las tablas que pueden aparecer en una dependencia de caché de SQL. Estos desencadenadores se ejecutan cada vez que se inserta, actualiza o elimina una fila e incrementan el valor changeId
de la tabla en AspNet_SqlCacheTablesForChangeNotification
.
El entorno de ejecución de ASP.NET realiza un seguimiento del changeId
actual de una tabla al almacenar en caché los datos mediante un objeto SqlCacheDependency
. La base de datos se comprueba periódicamente y los objetos SqlCacheDependency
cuyos valores changeId
difieren del valor de la base de datos se expulsan, ya que un valor changeId
diferente indica que se ha producido un cambio en la tabla desde que se almacenaron en caché los datos.
Paso 1: explorar el programa de línea de comandos aspnet_regsql.exe
Con el enfoque de sondeo, la base de datos debe configurarse para contener la infraestructura descrita anteriormente: una tabla predefinida (AspNet_SqlCacheTablesForChangeNotification
), un puñado de procedimientos almacenados y desencadenadores en cada una de las tablas que se pueden usar en dependencias de caché de SQL en la aplicación web. Estas tablas, procedimientos almacenados y desencadenadores se pueden crear a través del programa de línea de comandos aspnet_regsql.exe
, que se encuentra en la carpeta $WINDOWS$\Microsoft.NET\Framework\version
. Para crear la tabla AspNet_SqlCacheTablesForChangeNotification
y los procedimientos almacenados asociados, ejecute lo siguiente desde la línea de comandos:
/* For SQL Server authentication... */
aspnet_regsql.exe -S server -U user -P password -d database -ed
/* For Windows Authentication... */
aspnet_regsql.exe -S server -E -d database -ed
Nota:
Para ejecutar estos comandos, el inicio de sesión de base de datos especificado debe estar en los roles db_securityadmin
y db_ddladmin
.
Por ejemplo, para agregar la infraestructura para sondear a una base de datos de Microsoft SQL Server denominada pubs
en un servidor de bases de datos denominado ScottsServer
mediante la autenticación de Windows, vaya al directorio adecuado y, desde la línea de comandos, escriba:
aspnet_regsql.exe -S ScottsServer -E -d pubs -ed
Una vez agregada la infraestructura de nivel de base de datos, es necesario agregar los desencadenadores a esas tablas que se usarán en las dependencias de la caché de SQL. Vuelva a usar el programa de línea de comandos aspnet_regsql.exe
, pero especifique el nombre de la tabla mediante el modificador -t
y, en lugar de usar el modificador -ed
, use -et
, de este modo:
/* For SQL Server authentication... */
aspnet_regsql.exe -S <i>server</i>
-U <i>user</i> -P <i>password</i> -d <i>database</i> -t <i>tableName</i> -et
/* For Windows Authentication... */
aspnet_regsql.exe -S <i>server</i>
-E -d <i>database</i> -t <i>tableName</i> -et
Para agregar los desencadenadores a las tablas authors
y titles
de la base de datos pubs
en ScottsServer
, use:
aspnet_regsql.exe -S ScottsServer -E -d pubs -t authors -et
aspnet_regsql.exe -S ScottsServer -E -d pubs -t titles -et
Para este tutorial, agregue los desencadenadores a las tablas Products
, Categories
y Suppliers
. Veremos la sintaxis de línea de comandos determinada en el paso 3.
Paso 2: hacer referencia a una base de datos de Microsoft SQL Server 2005 Express Edition enApp_Data
El programa de línea de comandos aspnet_regsql.exe
requiere el nombre de la base de datos y del servidor para agregar la infraestructura de sondeo necesaria. Pero ¿cuál es el nombre de la base de datos y el servidor de una base de datos de Microsoft SQL Server 2005 Express que reside en la carpeta App_Data
? En lugar de tener que detectar cuáles son los nombres de base de datos y servidor, he encontrado que el enfoque más sencillo es adjuntar la base de datos a la instancia de base de datos localhost\SQLExpress
y cambiar el nombre de los datos mediante SQL Server Management Studio. Si tiene una de las versiones completas de SQL Server 2005 instaladas en el equipo, es probable que ya tenga SQL Server Management Studio instalado en el equipo. Si solo tiene la edición Express, puede descargar de froma gratuita Microsoft SQL Server Management Studio.
Comience cerrando Visual Studio. A continuación, abra SQL Server Management Studio y elija conectarse al servidor localhost\SQLExpress
mediante la autenticación de Windows.
Figura 1: adjuntar al servidor localhost\SQLExpress
Después de conectarse al servidor, Management Studio mostrará el servidor y tendrá subcarpetas para las bases de datos, la seguridad, etc. Haga clic con el botón derecho en la carpeta Bases de datos y elija la opción Adjuntar. Se abrirá el cuadro de diálogo Adjuntar bases de datos (vea la figura 2). Haga clic en el botón Agregar y seleccione la carpeta de la base de datos NORTHWND.MDF
en la carpeta App_Data
de la aplicación web.
Figura 2: adjuntar la base de datos NORTHWND.MDF
desde la carpeta App_Data
(haga clic para ver la imagen a tamaño completo)
Esto agregará la base de datos a la carpeta Bases de datos. El nombre de la base de datos puede ser la ruta de acceso completa al archivo de base de datos o la ruta de acceso completa antepuesto a un GUID. Para evitar tener que escribir este nombre largo de base de datos al usar la herramienta de línea de comandos de aspnet_regsql.exe, cambie el nombre de la base de datos a un nombre más descriptivo haciendo clic con el botón derecho en la base de datos simplemente adjuntada y seleccionando Cambiar nombre. He cambiado el nombre de mi base de datos a DataTutorials.
Figura 3: cambiar el nombre de la base de datos adjunta a un nombre más descriptivo para humanos
Paso 3: agregar la infraestructura de sondeo a la base de datos de Northwind
Ahora que hemos adjuntado la base de datos NORTHWND.MDF
desde la carpeta App_Data
, estamos listos para agregar la infraestructura de sondeo. Suponiendo que ha cambiado el nombre de la base de datos a DataTutorials, ejecute los cuatro comandos siguientes:
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -ed
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Products -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Categories -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Suppliers -et
Después de ejecutar estos cuatro comandos, haga clic con el botón derecho en el nombre de la base de datos en Management Studio, vaya al submenú Tareas y elija Separar. A continuación, cierre Management Studio y vuelva a abrir Visual Studio.
Una vez que Visual Studio se haya vuelto a abrir, explore en profundidad la base de datos a través del Explorador de servidores. Anote la nueva tabla (AspNet_SqlCacheTablesForChangeNotification
), los nuevos procedimientos almacenados y los desencadenadores en las tablas Products
, Categories
y Suppliers
.
Figura 4: la base de datos ahora incluye la infraestructura de sondeo necesaria
Paso 4: configurar el servicio de sondeo
Después de crear las tablas, desencadenadores y procedimientos almacenados necesarios en la base de datos, el paso final es configurar el servicio de sondeo, que se realiza mediante Web.config
la especificación de las bases de datos que se van a usar y la frecuencia de sondeo en milisegundos. El marcado siguiente sondea la base de datos de Northwind una vez por segundo.
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="NORTHWNDConnectionString" connectionString=
"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\NORTHWND.MDF;
Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
...
<!-- Configure the polling service used for SQL cache dependencies -->
<caching>
<sqlCacheDependency enabled="true" pollTime="1000" >
<databases>
<add name="NorthwindDB"
connectionStringName="NORTHWNDConnectionString" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
El valor name
del elemento <add>
(NorthwindDB) asocia un nombre legible a una base de datos determinada. Al trabajar con dependencias de caché de SQL, es necesario hacer referencia al nombre de la base de datos definido aquí, así como a la tabla en la que se basan los datos almacenados en caché. Veremos cómo usar la clase SqlCacheDependency
para asociar mediante programación las dependencias de caché de SQL con datos almacenados en caché en el paso 6.
Una vez establecida una dependencia de caché de SQL, el sistema de sondeo se conectará a las bases de datos definidas en los elementos <databases>
cada pollTime
milisegundos y ejecutará el procedimiento almacenado AspNet_SqlCachePollingStoredProcedure
. Este procedimiento almacenado, que se agregó de nuevo en el paso 3 mediante la herramienta de línea de comandos aspnet_regsql.exe
, devuelve los valores tableName
y changeId
de cada registro de AspNet_SqlCacheTablesForChangeNotification
. Las dependencias obsoletas de la caché de SQL se expulsan de la memoria caché.
La configuración pollTime
presenta un equilibrio entre el rendimiento y la obsolescencia de los datos. Un valor pollTime
pequeño aumenta el número de solicitudes a la base de datos, pero más rápidamente expulsa los datos obsoletos de la memoria caché. Un valor pollTime
mayor reduce el número de solicitudes de base de datos, pero aumenta el retraso entre cuando cambian los datos de back-end y cuando se expulsan los elementos de caché relacionados. Afortunadamente, la solicitud de base de datos ejecuta un procedimiento almacenado sencillo que devuelve solo unas pocas filas de una tabla simple y ligera. Pero experimente con valores pollTime
diferentes para encontrar un equilibrio ideal entre el acceso a la base de datos y la obsolescencia de los datos para la aplicación. El valor pollTime
más pequeño permitido es 500.
Nota:
En el ejemplo anterior se proporciona un único valor pollTime
en el elemento <sqlCacheDependency>
, pero opcionalmente puede especificar el valor pollTime
en el elemento <add>
. Esto resulta útil si tiene varias bases de datos especificadas y desea personalizar la frecuencia de sondeo por base de datos.
Paso 5: trabajar mediante declaración con dependencias de caché de SQL
En los pasos 1 a 4 hemos visto cómo configurar la infraestructura de base de datos necesaria y configurar el sistema de sondeo. Con esta infraestructura en su lugar, ahora podemos agregar elementos a la caché de datos con una dependencia de caché de SQL asociada mediante técnicas mediante programación o declarativa. En este paso, examinaremos cómo trabajar mediante declaración con dependencias de caché de SQL. En el paso 6 veremos el enfoque mediante programación.
El tutorial Almacenamiento en caché de datos con ObjectDataSource ha explorado las funcionalidades declarativas de almacenamiento en caché de ObjectDataSource. Simplemente estableciendo la propiedad EnableCaching
en True
y la propiedad CacheDuration
en algún intervalo de tiempo, ObjectDataSource almacenará automáticamente en caché los datos devueltos desde su objeto subyacente para el intervalo especificado. ObjectDataSource también puede usar una o varias dependencias de caché de SQL.
Para demostrar el uso de las dependencias de caché de SQL mediante declaración, abra la página SqlCacheDependencies.aspx
en la carpeta Caching
y arrastre un control GridView desde el cuadro de herramientas al Diseñador. Establezca el ID
de GridView hasta ProductsDeclarative
y, desde su etiqueta inteligente, elija enlazarlo a un nuevo ObjectDataSource denominado ProductsDataSourceDeclarative
.
Figura 5: crear un nuevo objetoDataSource denominado ProductsDataSourceDeclarative
(haga clic para ver la imagen a tamaño completo)
Configure ObjectDataSource para usar la clase ProductsBLL
y establezca la lista desplegable de la pestaña SELECT en GetProducts()
. En la pestaña UPDATE, elija la sobrecarga UpdateProduct
con tres parámetros de entrada: productName
, unitPrice
y productID
. Establezca las listas desplegables en (Ninguno) en las pestañas INSERT y DELETE.
Figura 6: usar la sobrecarga UpdateProduct con tres parámetros de entrada (haga clic para ver la imagen a tamaño completo)
Figura 7: establecer la lista desplegable en (Ninguno) para las pestañas INSERT y DELETE (haga clic para ver la imagen a tamaño completo)
Después de completar el Asistente para configurar orígenes de datos, Visual Studio creará BoundFields y CheckBoxFields en GridView para cada uno de los campos de datos. Quite todos los campos, pero ProductName
, CategoryName
y UnitPrice
, y dé formato a estos campos según se ajuste. En la etiqueta inteligente GridView, active las casillas Habilitar paginación, Habilitar ordenación y Habilitar edición. Visual Studio establecerá la propiedad OldValuesParameterFormatString
de ObjectDataSource en original_{0}
. Para que la característica de edición de GridView funcione correctamente, quite esta propiedad completamente de la sintaxis declarativa o establézcala de nuevo en su valor predeterminado, {0}
.
Por último, agregue un control Web Label encima de GridView y establezca su propiedad ID
en ODSEvents
y su propiedad EnableViewState
en False
. Después de realizar estos cambios, el marcado declarativo de LoginView debe ser similar al siguiente. Tenga en cuenta que he realizado una serie de personalizaciones estéticas en los campos GridView que no son necesarios para demostrar la funcionalidad de dependencia de caché de SQL.
<asp:Label ID="ODSEvents" runat="server" EnableViewState="False" />
<asp:GridView ID="ProductsDeclarative" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceDeclarative"
AllowPaging="True" AllowSorting="True">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>' />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName" Display="Dynamic"
ErrorMessage="You must provide a name for the product."
SetFocusOnError="True"
runat="server">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<EditItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice"
ErrorMessage="You must enter a valid currency value with
no currency symbols. Also, the value must be greater than
or equal to zero."
Operator="GreaterThanEqual" SetFocusOnError="True"
Type="Currency" Display="Dynamic"
ValueToCompare="0">*</asp:CompareValidator>
</EditItemTemplate>
<ItemStyle HorizontalAlign="Right" />
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSourceDeclarative" runat="server"
SelectMethod="GetProducts" TypeName="ProductsBLL"
UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
A continuación, cree un controlador de eventos para el evento ObjectDataSource y Selecting
, en él, agregue el código siguiente:
Protected Sub ProductsDataSourceDeclarative_Selecting _
(sender As Object, e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSourceDeclarative.Selecting
ODSEvents.Text = "-- Selecting event fired"
End Sub
Recuerde que el evento Selecting
de ObjectDataSource solo se activa al recuperar datos de su objeto subyacente. Si ObjectDataSource tiene acceso a los datos desde su propia memoria caché, este evento no se desencadena.
Ahora pruebe esta página a través de un explorador. Puesto que todavía hemos implementado cualquier almacenamiento en caché, cada vez que se pagina, ordena o edita la cuadrícula, la página debe mostrar el texto " Seleccionar evento desencadenado, como se muestra en la figura 8.
Figura 8: el evento Selecting
de ObjectDataSource se desencadena cada vez que GridView está paginado, editado o ordenado (haga clic para ver la imagen a tamaño completo)
Como vimos en el tutorial Datos de almacenamiento en caché con ObjectDataSource, establecer la propiedad EnableCaching
en True
hace que ObjectDataSource almacene en caché sus datos durante la duración especificada por su propiedad CacheDuration
. ObjectDataSource también tiene una propiedad SqlCacheDependency
, que agrega una o varias dependencias de caché de SQL a los datos almacenados en caché mediante el patrón:
databaseName1:tableName1;databaseName2:tableName2;...
Donde databaseName es el nombre de la base de datos tal como se especifica en el atributo name
del elemento <add>
en Web.config
y tableName es el nombre de la tabla de base de datos. Por ejemplo, para crear un ObjectDataSource que almacena en caché los datos indefinidamente en función de una dependencia de caché de SQL en la tablaProducts
de Northwind, establezca la propiedad EnableCaching
de ObjectDataSource en True
y su propiedad SqlCacheDependency
en NorthwindDB:Products.
Nota:
Puede usar una dependencia de caché de SQL y una expiración basada en el tiempo estableciendo EnableCaching
en True
, CacheDuration
en el intervalo de tiempo y SqlCacheDependency
en los nombres de la base de datos y de la tabla. ObjectDataSource expulsará sus datos cuando se alcance la expiración basada en el tiempo o cuando el sistema de sondeo tenga en cuenta que los datos de la base de datos subyacentes han cambiado, lo que ocurra primero.
GridView en SqlCacheDependencies.aspx
muestra los datos de dos tablas Products
y Categories
(el campo del producto CategoryName
se recupera a través de JOIN
en Categories
). Por lo tanto, queremos especificar dos dependencias de caché de SQL: NorthwindDB:Products; NorthwindDB:Categories.
Figura 9: configurar ObjectDataSource para admitir el almacenamiento en caché mediante dependencias de caché de SQL en Products
y Categories
(haga clic para ver la imagen a tamaño completo)
Después de configurar ObjectDataSource para admitir el almacenamiento en caché, vuelva a visitar la página a través de un explorador. De nuevo, el texto "Seleccionar evento desencadenado debe aparecer en la primera visita de la página, pero debe desaparecer al paginar, ordenar o hacer clic en los botones Editar o Cancelar. Esto se debe a que después de cargar los datos en la memoria caché de ObjectDataSource, permanece allí hasta que las tablas Products
o Categories
se modifican o los datos se actualizan a través de GridView.
Después de paginar a través de la cuadrícula y observar la falta del texto desencadenado por el evento de selección, abra una nueva ventana del explorador y vaya al tutorial Aspectos básicos en la sección Edición, Inserción y Eliminación (~/EditInsertDelete/Basics.aspx
). Actualice el nombre o el precio de un producto. A continuación, de a la primera ventana del explorador, vea otra página de datos, ordene la cuadrícula o haga clic en un botón Editar de fila. Esta vez, el evento de selección desencadenado debe volver a aparecer, ya que se han modificado los datos de la base de datos subyacentes (vea la figura 10). Si el texto no aparece, espere unos instantes e inténtelo de nuevo. Recuerde que el servicio de sondeo comprueba si hay cambios en la tabla Products
cada pollTime
milisegundos, por lo que hay un retraso entre cuándo se actualizan los datos subyacentes y cuándo se expulsan los datos almacenados en caché.
Figura 10: modificar la tabla de productos expulsa los datos de producto almacenados en caché (haga clic para ver la imagen a tamaño completo)
Paso 6: trabajar mediante programación con la clase SqlCacheDependency
En el tutorial Almacenamiento en caché de datos la arquitectura se examinan las ventajas de usar una capa de almacenamiento en caché independiente en la arquitectura, en lugar de acoplar estrechamente el almacenamiento en caché con ObjectDataSource. En ese tutorial hemos creado una clase ProductsCL
para mostrar cómo trabajar mediante programación con la caché de datos. Para usar dependencias de caché de SQL en la capa de almacenamiento en caché, use la clase SqlCacheDependency
.
Con el sistema de sondeo, un objeto SqlCacheDependency
debe estar asociado a un par de bases de datos y tablas concretos. El código siguiente, por ejemplo, crea un objeto SqlCacheDependency
basado en la tabla Products
de la base de datos Northwind:
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Los dos parámetros de entrada para el SqlCacheDependency
del constructor son los nombres de base de datos y tabla, respectivamente. Al igual que con la propiedad SqlCacheDependency
de ObjectDataSource, el nombre de la base de datos usado es el mismo que el valor especificado en el atributo name
del elemento <add>
en Web.config
. El nombre de la tabla es el nombre real de la tabla de base de datos.
Para asociar un elemento SqlCacheDependency
a un elemento agregado a la memoria caché de datos, use una de las sobrecargas del método Insert
que acepta una dependencia. El código siguiente agrega valor a la memoria caché de datos durante una duración indefinida, pero la asocia a SqlCacheDependency
en la tabla Products
. En resumen, el valor permanecerá en la memoria caché hasta que se expulse debido a restricciones de memoria o porque el sistema de sondeo ha detectado que la tabla Products
ha cambiado desde que se ha almacenado en caché.
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Cache.Insert(key, _
value, _
productsTableDependency, _
System.Web.Caching.Cache.NoAbsoluteExpiration, _
System.Web.Caching.Cache.NoSlidingExpiration)
La clase ProductsCL
de capa de almacenamiento en caché almacena actualmente datos en caché de la tabla Products
mediante una expiración basada en el tiempo de 60 segundos. Vamos a actualizar esta clase para que use dependencias de caché de SQL en su lugar. El método AddCacheItem
de la clase ProductsCL
, que es responsable de agregar los datos a la memoria caché, contiene actualmente el código siguiente:
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Make sure MasterCacheKeyArray(0) is in the cache - if not, add it
If DataCache(MasterCacheKeyArray(0)) Is Nothing Then
DataCache(MasterCacheKeyArray(0)) = DateTime.Now
End If
' Add a CacheDependency
Dim dependency As _
New Caching.CacheDependency(Nothing, MasterCacheKeyArray)
DataCache.Insert(GetCacheKey(rawKey), value, dependency, _
DateTime.Now.AddSeconds(CacheDuration), _
Caching.Cache.NoSlidingExpiration)
End Sub
Actualice este código para usar un objeto SqlCacheDependency
en lugar de la dependencia de caché MasterCacheKeyArray
:
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Add the SqlCacheDependency objects for Products
Dim productsTableDependency As New _
Caching.SqlCacheDependency("NorthwindDB", "Products")
DataCache.Insert(GetCacheKey(rawKey), value, productsTableDependency, _
Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration)
End Sub
Para probar esta funcionalidad, agregue una clase GridView a la página situada debajo de GridView ProductsDeclarative
existente. Establezca esta nuevo ID
de GridView en ProductsProgrammatic
y, a través de su etiqueta inteligente, vincule a un nuevo ObjectDataSource denominado ProductsDataSourceProgrammatic
. Configure ObjectDataSource para usar la clase ProductsCL
, estableciendo las listas desplegables en las pestañas SELECT y UPDATE en GetProducts
y UpdateProduct
, respectivamente.
Figura 11: configurar ObjectDataSource para usar la clase ProductsCL
(haga clic para ver la imagen a tamaño completo)
Figura 12: seleccionar el método GetProducts
en la lista desplegable de pestañas SELECT (haga clic para ver la imagen a tamaño completo)
Figura 13: elegir el método UpdateProduct en la lista desplegable de la pestaña UPDATE (haga clic para ver la imagen a tamaño completo)
Después de completar el Asistente para configurar orígenes de datos, Visual Studio creará BoundFields y CheckBoxFields en GridView para cada uno de los campos de datos. Al igual que con la primera clase GridView agregada a esta página, quite todos los campos, a excepción de ProductName
, CategoryName
y UnitPrice
, y dé formato a estos campos según se ajuste. En la etiqueta inteligente GridView, active las casillas Habilitar paginación, Habilitar ordenación y Habilitar edición. Al igual que con ObjectDataSource ProductsDataSourceDeclarative
, Visual Studio establecerá la propiedad OldValuesParameterFormatString
de ObjectDataSource ProductsDataSourceProgrammatic
en original_{0}
. Para que la característica de edición de GridView funcione correctamente, vuelva a establecer esta propiedad en {0}
(o quite la asignación de propiedades de la sintaxis declarativa por completo).
Después de completar estas tareas, el marcado declarativo GridView y ObjectDataSource resultante debe ser similar al siguiente:
<asp:GridView ID="ProductsProgrammatic" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceProgrammatic" AllowPaging="True"
AllowSorting="True">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>' />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName" Display="Dynamic"
ErrorMessage="You must provide a name for the product."
SetFocusOnError="True"
runat="server">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<EditItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice" Display="Dynamic"
ErrorMessage="You must enter a valid currency value with no
currency symbols. Also, the value must be greater than
or equal to zero."
Operator="GreaterThanEqual" SetFocusOnError="True"
Type="Currency" ValueToCompare="0">*</asp:CompareValidator>
</EditItemTemplate>
<ItemStyle HorizontalAlign="Right" />
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSourceProgrammatic" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetProducts"
TypeName="ProductsCL" UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
Para probar la dependencia de caché de SQL en la capa de almacenamiento en caché, establezca un punto de interrupción en el método AddCacheItem
de la clase ProductCL
y, a continuación, inicie la depuración. Cuando visite SqlCacheDependencies.aspx
por primera vez, el punto de interrupción debe alcanzarse cuando se solicitan los datos por primera vez y se colocan en la memoria caché. A continuación, vaya a otra página de GridView o ordene una de las columnas. Esto hace que GridView vuelva a consultar sus datos, pero los datos se deben encontrar en la memoria caché, ya que la tabla Products
de base de datos no se ha modificado. Si los datos no se encuentran repetidamente en la memoria caché, asegúrese de que hay suficiente memoria disponible en el equipo e inténtelo de nuevo.
Después de paginar a través de algunas páginas de GridView, abra una segunda ventana del explorador y vaya al tutorial Aspectos básicos en la sección Edición, Inserción y Eliminación (~/EditInsertDelete/Basics.aspx
). Actualice un registro de la tabla Products y, a continuación, desde la primera ventana del explorador, vea una nueva página o haga clic en uno de los encabezados de ordenación.
En este escenario verá una de las dos cosas: se alcanzará el punto de interrupción, lo que indica que los datos almacenados en caché se expulsaron debido al cambio en la base de datos; o bien, el punto de interrupción no se alcanzará, lo que significa que SqlCacheDependencies.aspx
ahora muestra datos obsoletos. Si no se alcanza el punto de interrupción, es probable que el servicio de sondeo aún no se haya desencadenado desde que se cambiaron los datos. Recuerde que el servicio de sondeo comprueba si hay cambios en la tabla Products
cada pollTime
milisegundos, por lo que hay un retraso entre cuándo se actualizan los datos subyacentes y cuándo se expulsan los datos almacenados en caché.
Nota:
Este retraso es más probable que aparezca al editar uno de los productos a través de GridView en SqlCacheDependencies.aspx
. En el tutorial de Almacenamiento en caché de datos en la arquitectura, agregamos la dependencia de caché MasterCacheKeyArray
para asegurarnos de que los datos que se editan a través del métodoUpdateProduct
de la clase ProductsCL
se expulsaron de la memoria caché. Sin embargo, reemplazamos esta dependencia de caché al modificar el método AddCacheItem
anterior en este paso y, por lo tanto, la clase ProductsCL
seguirá mostrando los datos almacenados en caché hasta que el sistema de sondeo adiga el cambio en la tabla Products
. Veremos cómo volver a introducir la dependencia de caché MasterCacheKeyArray
en el paso 7.
Paso 7: asociar varias dependencias con un elemento almacenado en caché
Recuerde que la dependencia de caché MasterCacheKeyArray
se usa para asegurarse de que todos los datos relacionados con el producto se expulsan de la memoria caché cuando se actualiza cualquier elemento único asociado dentro de él. Por ejemplo, el método GetProductsByCategoryID(categoryID)
almacena en caché las instancias ProductsDataTables
de cada valor categoryID único. Si se expulsa uno de estos objetos, la dependencia de caché MasterCacheKeyArray
garantiza que las demás también se quiten. Sin esta dependencia de caché, cuando se modifican los datos almacenados en caché, existe la posibilidad de que otros datos del producto almacenados en caché no estén actualizados. Por lo tanto, es importante mantener la dependencia de caché MasterCacheKeyArray
al usar dependencias de caché de SQL. Sin embargo, el método Insert
de la caché de datos solo permite un único objeto de dependencia.
Además, al trabajar con dependencias de caché de SQL, es posible que tengamos que asociar varias tablas de base de datos como dependencias. Por ejemplo, la memoria caché ProductsDataTable
de la clase ProductsCL
contiene los nombres de categoría y proveedor de cada producto, pero el método AddCacheItem
solo usa una dependencia en Products
. En esta situación, si el usuario actualiza el nombre de una categoría o proveedor, los datos del producto almacenados en caché permanecerán en la memoria caché y estarán obsoletos. Por lo tanto, queremos hacer que los datos del producto almacenados en caché dependan no solo de la tabla Products
, sino también de las tablas Categories
y Suppliers
.
La clase AggregateCacheDependency
proporciona un medio para asociar varias dependencias con un elemento de caché. Empiece por crear una instancia AggregateCacheDependency
. A continuación, agregue el conjunto de dependencias mediante el método Add
de AggregateCacheDependency
. Al insertar el elemento en la memoria caché de datos después, pase la instancia AggregateCacheDependency
. Cuando cambie cualquiera de las dependencias de la instancia AggregateCacheDependency
, se expulsará el elemento almacenado en caché.
A continuación se muestra el código actualizado para el método AddCacheItem
de la clase ProductsCL
. El método crea la dependencia de caché MasterCacheKeyArray
junto con objetos SqlCacheDependency
para las tablas Products
, Categories
y Suppliers
. Todos se combinan en un objeto AggregateCacheDependency
denominado aggregateDependencies
, que luego se pasa al método Insert
.
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Make sure MasterCacheKeyArray(0) is in the cache - if not, add it.
If DataCache(MasterCacheKeyArray(0)) Is Nothing Then
DataCache(MasterCacheKeyArray(0)) = DateTime.Now
End If
'Create the CacheDependency
Dim masterCacheKeyDependency As _
New Caching.CacheDependency(Nothing, MasterCacheKeyArray)
' Add the SqlCacheDependency objects for Products, Categories, and Suppliers
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Dim categoriesTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Categories")
Dim suppliersTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Suppliers")
' Create an AggregateCacheDependency
Dim aggregateDependencies As New Caching.AggregateCacheDependency()
aggregateDependencies.Add(masterCacheKeyDependency, productsTableDependency, _
categoriesTableDependency, suppliersTableDependency)
DataCache.Insert(GetCacheKey(rawKey), value, aggregateDependencies, _
Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration)
End Sub
Pruebe este nuevo código. Ahora, los cambios realizados en las tablas Products
, Categories
o Suppliers
hacen que los datos almacenados en caché se expulsen. Además, el método UpdateProduct
de la clase ProductsCL
, al que se llama al editar un producto a través de GridView, expulsa la dependencia de caché MasterCacheKeyArray
, lo que hace que se expulse la memoria caché ProductsDataTable
y se vuelvan a recuperar los datos en la siguiente solicitud.
Nota:
Las dependencias de caché de SQL también se pueden usar con el almacenamiento en caché de salida. Para obtener una demostración de esta funcionalidad, consulte: Uso del almacenamiento en caché de salida ASP.NET con SQL Server.
Resumen
Al almacenar en caché los datos de la base de datos, los datos permanecerán idealmente en la memoria caché hasta que se modifiquen en la base de datos. Con ASP.NET 2.0, se pueden crear y usar dependencias de caché de SQL en escenarios declarativos y mediante programación. Uno de los desafíos de este enfoque es detectar cuándo se han modificado los datos. Las versiones completas de Microsoft SQL Server 2005 proporcionan funcionalidades de notificación que pueden alertar a una aplicación cuando ha cambiado un resultado de consulta. Para la edición Express de SQL Server 2005 y versiones anteriores de SQL Server, se debe usar en su lugar un sistema de sondeo. Afortunadamente, configurar la infraestructura de sondeo necesaria es bastante sencilla.
¡Feliz programación!
Lecturas adicionales
Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:
- Uso de notificaciones de consulta en Microsoft SQL Server 2005
- Creación de una notificación de consulta
- Almacenamiento en caché en ASP.NET con la clase
SqlCacheDependency
- Herramienta de registro de SQL Server () de ASP.NET (
aspnet_regsql.exe
) - Información general de
SqlCacheDependency
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, instructor y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él en mitchell@4GuysFromRolla.com. o a través de su blog, http://ScottOnWriting.NET.
Agradecimientos especiales a
Esta serie de tutoriales fue revisada por un gran número de revisores. Los clientes principales de este tutorial fueron Marko Rangel, Teresa Murphy y Hilton Giesenow. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.