Usar dependencias de caché de SQL (VB)

por Scott Mitchell

Descargar PDF

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.

Attach to the localhost\SQLExpress Server

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.

Attach the NORTHWND.MDF Database from the App_Data Folder

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.

Rename the Attached Database to a More Human-Friendly Name

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.

The Database Now Includes the Necessary Polling Infrastructure

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.

Create a New ObjectDataSource Named 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.

Use the UpdateProduct Overload with Three Input Parameters

Figura 6: usar la sobrecarga UpdateProduct con tres parámetros de entrada (haga clic para ver la imagen a tamaño completo)

Set the Drop-Down List to (None) for the INSERT and DELETE Tabs

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.

The ObjectDataSource s Selecting Event Fires Each Time the GridView is Paged, Edited, or Sorted

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.

Configure the ObjectDataSource to Support Caching Using SQL Cache Dependencies on Products and 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é.

Modifying the Products Table Evicts the Cached Product Data

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.

Configure the ObjectDataSource to Use the ProductsCL Class

Figura 11: configurar ObjectDataSource para usar la clase ProductsCL (haga clic para ver la imagen a tamaño completo)

Select the GetProducts Method from the SELECT Tab s Drop-Down List

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)

Choose the UpdateProduct Method from the UPDATE Tab s Drop-Down List

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:

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.