Usar dependencias de caché de SQL (C#)

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 SqlCacheDependency clase 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 SqlCacheDependency clase 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 tableName columna especifica el nombre de la tabla mientras notificationCreated indica la fecha y hora en que se agregó la fila a la tabla. La changeId columna 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 AspNet_SqlCacheTablesForChangeNotification tabla, 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 de la tabla en changeIdAspNet_SqlCacheTablesForChangeNotification.

El entorno de ejecución de ASP.NET realiza un seguimiento del actual changeId de una tabla al almacenar en caché los datos mediante un SqlCacheDependency objeto. La base de datos se comprueba periódicamente y los SqlCacheDependency objetos cuyos changeId valores difieren del valor de la base de datos se expulsan, ya que un valor diferente changeId indica que se ha producido un cambio en la tabla desde que se almacenaron en caché los datos.

Paso 1: Explorar el aspnet_regsql.exeprograma de línea de comandos

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 AspNet_SqlCacheTablesForChangeNotification tabla 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 db_securityadmin roles 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 aspnet_regsql.exe programa de línea de comandos, pero especifique el nombre de la tabla mediante el -t modificador y, en lugar de usar el -ed modificador, 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 Productstablas, 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 aspnet_regsql.exe programa de línea de comandos 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 App_Data carpeta? 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 localhost\SQLExpress instancia de base de datos 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 Microsoft SQL Server Management Studio Express Edition gratuita.

Comience cerrando Visual Studio. A continuación, abra SQL Server Management Studio y elija conectarse al servidor mediante la localhost\SQLExpress autenticación de Windows.

Attach to the localhost\SQLExpress Server

Figura 1: Adjuntar al localhost\SQLExpress servidor

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 NORTHWND.MDF carpeta de la base de datos en la carpeta de la App_Data aplicación web.

Attach the NORTHWND.MDF Database from the App_Data Folder

Figura 2: Adjuntar la NORTHWND.MDF base de datos desde la carpeta App_Data ( haga clic para ver la imagen de 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 Northwind

Ahora que hemos adjuntado la NORTHWND.MDF base de datos 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: Configuración del 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 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 name valor del <add> elemento ( 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 SqlCacheDependency clase 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 <databases> elementos cada pollTime milisegundos y ejecutará el procedimiento almacenadoAspNet_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 pollTime configuración presenta un equilibrio entre el rendimiento y la obsolescencia de los datos. Un valor pequeño pollTime 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 mayor pollTime 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 diferentes pollTime 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 más pequeño pollTime 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 EnableCaching propiedad en true y la CacheDuration propiedad 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 SqlCacheDependencies.aspx página en la Caching carpeta y arrastre un control GridView desde el cuadro de herramientas al Diseñador. Establezca GridView s de ID 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 de tamaño completo)

Configure ObjectDataSource para usar la ProductsBLL clase y establezca la lista desplegable de la pestaña SELECT en GetProducts(). En la pestaña UPDATE, elija la UpdateProduct sobrecarga 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 de 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 de 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 ObjectDataSource s OldValuesParameterFormatString 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 ID propiedad ODSEvents en y su EnableViewState propiedad 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 void ProductsDataSourceDeclarative_Selecting
    (object sender, ObjectDataSourceSelectingEventArgs e)
{
    ODSEvents.Text = "-- Selecting event fired";
}

Recuerde que el evento ObjectDataSource solo Selecting 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 ObjectDataSource se Selecting desencadena cada vez que GridView está paginado, editado o ordenado (haga clic para ver la imagen de tamaño completo)

Como vimos en el tutorial Datos de almacenamiento en caché con ObjectDataSource, establecer la EnableCaching propiedad en true hace que ObjectDataSource almacene en caché sus datos durante la duración especificada por su propiedad CacheDuration. ObjectDataSource también tiene una SqlCacheDependency propiedad, 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 name atributo del <add> elemento en Web.configy 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 tabla Northwind s Products, establezca la propiedad EnableCaching ObjectDataSource en true y su SqlCacheDependency propiedad 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 en JOINCategories). 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 de 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 products expulsa los datos de producto almacenados en caché (haga clic para ver la imagen de tamaño completo)

Paso 6: Trabajar mediante programación con laSqlCacheDependencyclase

En el tutorial Almacenamiento en caché de datos en el tutorial 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 SqlCacheDependency objeto debe estar asociado a un par de bases de datos y tablas concretos. El código siguiente, por ejemplo, crea un SqlCacheDependency objeto basado en la tabla de Products la base de datos Northwind:

Caching.SqlCacheDependency productsTableDependency = 
    new Caching.SqlCacheDependency("NorthwindDB", "Products");

Los dos parámetros de entrada para el SqlCacheDependency constructor s son los nombres de base de datos y tabla, respectivamente. Al igual que con la propiedad ObjectDataSource, SqlCacheDependency el nombre de la base de datos usado es el mismo que el valor especificado en el name atributo del <add> elemento en Web.config. El nombre de la tabla es el nombre real de la tabla de base de datos.

Para asociar un SqlCacheDependency elemento a un elemento agregado a la memoria caché de datos, use una de las sobrecargas del Insert método 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 tablaProducts. 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 Products tabla ha cambiado desde que se ha almacenado en caché.

Caching.SqlCacheDependency productsTableDependency = 
    new Caching.SqlCacheDependency("NorthwindDB", "Products");
Cache.Insert(key, 
             value, 
             productsTableDependency, 
             System.Web.Caching.Cache.NoAbsoluteExpiration, 
             System.Web.Caching.Cache.NoSlidingExpiration);

La clase capa de ProductsCL almacenamiento en caché almacena actualmente datos en caché de la Products tabla 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 ProductsCL método de la clase s AddCacheItem, que es responsable de agregar los datos a la memoria caché, contiene actualmente el código siguiente:

private void AddCacheItem(string rawKey, object value)
{
    System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
    // Make sure MasterCacheKeyArray[0] is in the cache
    DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
    // Add a CacheDependency
    Caching.CacheDependency dependency =
        new Caching.CacheDependency(null, MasterCacheKeyArray);
    DataCache.Insert(GetCacheKey(rawKey), value, dependency, 
        DateTime.Now.AddSeconds(CacheDuration), 
        System.Web.Caching.Cache.NoSlidingExpiration);
}

Actualice este código para usar un SqlCacheDependency objeto en lugar de la MasterCacheKeyArray dependencia de caché:

private void AddCacheItem(string rawKey, object value)
{
    System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
    // Add the SqlCacheDependency objects for Products
    Caching.SqlCacheDependency productsTableDependency = 
        new Caching.SqlCacheDependency("NorthwindDB", "Products");
    // Add the item to the data cache using productsTableDependency
    DataCache.Insert(GetCacheKey(rawKey), value, productsTableDependency, 
        Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration);
}

Para probar esta funcionalidad, agregue una clase GridView a la página situada debajo de GridView existente ProductsDeclarative. Establezca esta nueva clase GridView en IDProductsProgrammatic y, a través de su etiqueta inteligente, vincule a un nuevo ObjectDataSource denominado ProductsDataSourceProgrammatic. Configure ObjectDataSource para usar la ProductsCL clase, estableciendo las listas desplegables en las pestañas GetProducts SELECT y UPDATE en 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 de 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 de 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 de 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, pero ProductName, CategoryName y UnitPricedé 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 ProductsDataSourceDeclarative ObjectDataSource, Visual Studio establecerá la ProductsDataSourceProgrammatic propiedad OldValuesParameterFormatString de ObjectDataSource en original_{0}. Para que la característica de edición de GridView funcione correctamente, vuelva a {0} establecer esta propiedad en (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 de la ProductCL clase s AddCacheItem 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 Products tabla 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 el tutorial arquitectura, agregamos la MasterCacheKeyArray dependencia de caché para asegurarnos de que los datos que se editan a través del método de la ProductsCL clase se UpdateProduct expulsaron de la memoria caché. Sin embargo, reemplazamos esta dependencia de caché al modificar el método anterior AddCacheItem 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 MasterCacheKeyArray dependencia de caché en el paso 7.

Paso 7: Asociar varias dependencias con un elemento almacenado en caché

Recuerde que la MasterCacheKeyArray dependencia de caché 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 MasterCacheKeyArray dependencia de caché 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 de Insert 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 AddCacheItem método 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 Products tabla, sino también de las Categories tablas y Suppliers.

La AggregateCacheDependency clase proporciona un medio para asociar varias dependencias con un elemento de caché. Empiece por crear una AggregateCacheDependency instancia. A continuación, agregue el conjunto de dependencias mediante el método AggregateCacheDependency s Add. Al insertar el elemento en la memoria caché de datos después, pase la AggregateCacheDependency instancia. Cuando cambie cualquiera de las dependencias de la AggregateCacheDependency instancia, se expulsará el elemento almacenado en caché.

A continuación se muestra el código actualizado para el método ProductsCL de la clase s AddCacheItem. El método crea la MasterCacheKeyArray dependencia de caché junto con SqlCacheDependency objetos para las Productstablas, Categories y Suppliers. Todos se combinan en un AggregateCacheDependency objeto denominado aggregateDependencies, que luego se pasa al Insert método.

private void AddCacheItem(string rawKey, object value)
{
    System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
    // Make sure MasterCacheKeyArray[0] is in the cache and create a depedency
    DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
    Caching.CacheDependency masterCacheKeyDependency = 
        new Caching.CacheDependency(null, MasterCacheKeyArray);
    // Add the SqlCacheDependency objects for Products, Categories, and Suppliers
    Caching.SqlCacheDependency productsTableDependency = 
        new Caching.SqlCacheDependency("NorthwindDB", "Products");
    Caching.SqlCacheDependency categoriesTableDependency = 
        new Caching.SqlCacheDependency("NorthwindDB", "Categories");
    Caching.SqlCacheDependency suppliersTableDependency = 
        new Caching.SqlCacheDependency("NorthwindDB", "Suppliers");
    // Create an AggregateCacheDependency
    Caching.AggregateCacheDependency aggregateDependencies = 
        new Caching.AggregateCacheDependency();
    aggregateDependencies.Add(masterCacheKeyDependency, productsTableDependency, 
        categoriesTableDependency, suppliersTableDependency);
    DataCache.Insert(GetCacheKey(rawKey), value, aggregateDependencies, 
        Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration);
}

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 de la ProductsCL clase s UpdateProduct, al que se llama al editar un producto a través de GridView, expulsa la MasterCacheKeyArray dependencia de caché, 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, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él via mitchell@4GuysFromRolla.com. o a través de su blog, que se puede encontrar en http://ScottOnWriting.NET.

Agradecimientos especiales a

Esta serie de tutoriales fue revisada por muchos revisores de gran ayuda. 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.