Uso de dependências de cache de SQL (C#)

por Scott Mitchell

Baixar PDF

A estratégia de cache mais simples é permitir que os dados armazenados em cache expirem após um período de tempo especificado. Mas essa abordagem simples significa que os dados armazenados em cache não mantêm nenhuma associação com sua fonte de dados subjacente, resultando em dados obsoletos que são mantidos dados muito longos ou atuais que expiraram muito cedo. Uma abordagem melhor é usar a classe SqlCacheDependency para que os dados permaneçam armazenados em cache até que seus dados subjacentes sejam modificados no banco de dados SQL. Este tutorial mostra como.

Introdução

As técnicas de cache examinadas nos Dados de Cache com os tutoriais ObjectDataSource e Caching Data na Arquitetura usaram uma expiração baseada em tempo para remover os dados do cache após um período especificado. Essa abordagem é a maneira mais simples de equilibrar os ganhos de desempenho do cache em relação à desatualização de dados. Ao selecionar uma expiração de tempo de x segundos, um desenvolvedor de página admite aproveitar os benefícios de desempenho do cache por apenas x segundos, mas pode ficar tranquilo de que seus dados nunca ficarão obsoletos por mais tempo do que um máximo de x segundos. É claro que, para dados estáticos, x pode ser estendido para o tempo de vida do aplicativo Web, como foi examinado no tutorial Armazenar dados em cache na inicialização do aplicativo .

Ao armazenar em cache dados de banco de dados, uma expiração baseada em tempo geralmente é escolhida para sua facilidade de uso, mas é frequentemente uma solução inadequada. O ideal é que os dados do banco de dados permaneçam armazenados em cache até que os dados subjacentes sejam modificados no banco de dados; somente então o cache seria removido. Essa abordagem maximiza os benefícios de desempenho do cache e minimiza a duração dos dados obsoletos. No entanto, para aproveitar esses benefícios, deve haver algum sistema em vigor que saiba quando os dados de banco de dados subjacentes foram modificados e remove os itens correspondentes do cache. Antes do ASP.NET 2.0, os desenvolvedores de página eram responsáveis por implementar esse sistema.

ASP.NET 2.0 fornece uma SqlCacheDependency classe e a infraestrutura necessária para determinar quando ocorreu uma alteração no banco de dados para que os itens armazenados em cache correspondentes possam ser removidos. Há duas técnicas para determinar quando os dados subjacentes foram alterados: notificação e sondagem. Depois de discutir as diferenças entre a notificação e a sondagem, criaremos a infraestrutura necessária para dar suporte à sondagem e, em seguida, exploraremos como usar a SqlCacheDependency classe em cenários declarativos e programaticamente.

Noções básicas sobre notificação e sondagem

Há duas técnicas que podem ser usadas para determinar quando os dados em um banco de dados foram modificados: notificação e sondagem. Com a notificação, o banco de dados alerta automaticamente o runtime ASP.NET quando os resultados de uma consulta específica foram alterados desde a última execução da consulta, momento em que os itens armazenados em cache associados à consulta são removidos. Com a sondagem, o servidor de banco de dados mantém informações sobre quando determinadas tabelas foram atualizadas pela última vez. O runtime ASP.NET sonda periodicamente o banco de dados para marcar quais tabelas foram alteradas desde que foram inseridas no cache. Essas tabelas cujos dados foram modificados têm seus itens de cache associados removidos.

A opção de notificação requer menos configuração do que sondagem e é mais granular, pois acompanha as alterações no nível da consulta e não no nível da tabela. Infelizmente, as notificações só estão disponíveis nas edições completas do Microsoft SQL Server 2005 (ou seja, as edições não Expressas). No entanto, a opção de sondagem pode ser usada para todas as versões do Microsoft SQL Server de 7.0 a 2005. Como esses tutoriais usam a edição Express do SQL Server 2005, nos concentraremos em configurar e usar a opção de sondagem. Consulte a seção Leitura Adicional no final deste tutorial para obter mais recursos sobre SQL Server recursos de notificação do 2005.

Com a sondagem, o banco de dados deve ser configurado para incluir uma tabela chamada AspNet_SqlCacheTablesForChangeNotification que tenha três colunas - tableName, notificationCreatede changeId. Esta tabela contém uma linha para cada tabela que tem dados que podem precisar ser usados em uma dependência de cache SQL no aplicativo Web. A tableName coluna especifica o nome da tabela enquanto notificationCreated indica a data e a hora em que a linha foi adicionada à tabela. A changeId coluna é do tipo int e tem um valor inicial de 0. Seu valor é incrementado com cada modificação na tabela.

Além da AspNet_SqlCacheTablesForChangeNotification tabela, o banco de dados também precisa incluir gatilhos em cada uma das tabelas que podem aparecer em uma dependência de cache SQL. Esses gatilhos são executados sempre que uma linha é inserida, atualizada ou excluída e incrementa o valor da changeId tabela em AspNet_SqlCacheTablesForChangeNotification.

O runtime ASP.NET rastreia o atual changeId de uma tabela ao armazenar dados em cache usando um SqlCacheDependency objeto . O banco de dados é verificado periodicamente e todos SqlCacheDependency os objetos que changeId diferem do valor no banco de dados são removidos, pois um valor diferente changeId indica que houve uma alteração na tabela desde que os dados foram armazenados em cache.

Etapa 1: Explorando oaspnet_regsql.exeprograma de linha de comando

Com a abordagem de sondagem, o banco de dados deve ser configurado para conter a infraestrutura descrita acima: uma tabela predefinida (AspNet_SqlCacheTablesForChangeNotification), um punhado de procedimentos armazenados e gatilhos em cada uma das tabelas que podem ser usadas em dependências de cache SQL no aplicativo Web. Essas tabelas, procedimentos armazenados e gatilhos podem ser criados por meio do programa aspnet_regsql.exede linha de comando , que é encontrado na $WINDOWS$\Microsoft.NET\Framework\version pasta . Para criar a AspNet_SqlCacheTablesForChangeNotification tabela e os procedimentos armazenados associados, execute o seguinte na linha de comando:

/* 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

Observação

Para executar esses comandos, o logon do banco de dados especificado deve estar nas db_securityadmin funções e db_ddladmin .

Por exemplo, para adicionar a infraestrutura para sondagem a um banco de dados do Microsoft SQL Server chamado pubs em um servidor de banco de dados chamado ScottsServer usando a Autenticação do Windows, navegue até o diretório apropriado e, na linha de comando, insira:

aspnet_regsql.exe -S ScottsServer -E -d pubs -ed

Depois que a infraestrutura no nível do banco de dados tiver sido adicionada, precisamos adicionar os gatilhos às tabelas que serão usadas nas dependências do cache SQL. Use o aspnet_regsql.exe programa de linha de comando novamente, mas especifique o nome da tabela usando a opção -t e, em vez de usar a opção -ed , use -et, da seguinte maneira:

/* 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 adicionar os gatilhos às authors tabelas e titles no pubs banco de dados no 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, adicione os gatilhos às Productstabelas , Categoriese Suppliers . Examinaremos a sintaxe de linha de comando específica na Etapa 3.

Etapa 2: Referenciando um banco de dados do Microsoft SQL Server 2005 Express Edition emApp_Data

O aspnet_regsql.exe programa de linha de comando requer o nome do banco de dados e do servidor para adicionar a infraestrutura de sondagem necessária. Mas qual é o nome do banco de dados e do servidor para um banco de dados do Microsoft SQL Server 2005 Express que reside na App_Data pasta? Em vez de ter que descobrir quais são os nomes do banco de dados e do servidor, descobri que a abordagem mais simples é anexar o banco de dados à instância do localhost\SQLExpress banco de dados e renomear os dados usando SQL Server Management Studio. Se você tiver uma das versões completas do SQL Server 2005 instalada no computador, provavelmente já terá SQL Server Management Studio instalados no computador. Se você tiver apenas a edição Express, poderá baixar o Microsoft SQL Server Management Studio Express Edition gratuito.

Comece fechando o Visual Studio. Em seguida, abra SQL Server Management Studio e escolha se conectar ao servidor usando a Autenticação do localhost\SQLExpress Windows.

Anexar ao localhost\SQLExpress Server

Figura 1: Anexar ao localhost\SQLExpress servidor

Depois de se conectar ao servidor, o Management Studio mostrará o servidor e terá subpastas para os bancos de dados, segurança e assim por diante. Clique com o botão direito do mouse na pasta Bancos de Dados e escolha a opção Anexar. Isso abrirá a caixa de diálogo Anexar Bancos de Dados (consulte a Figura 2). Clique no botão Adicionar e selecione a pasta de NORTHWND.MDF banco de dados na pasta do App_Data aplicativo Web.

Anexe o NORTHWND. Banco de dados MDF da pasta App_Data

Figura 2: Anexar o NORTHWND.MDF banco de dados da App_Data pasta (clique para exibir a imagem em tamanho real)

Isso adicionará o banco de dados à pasta Bancos de Dados. O nome do banco de dados pode ser o caminho completo para o arquivo de banco de dados ou o caminho completo anexado a um GUID. Para evitar a necessidade de digitar esse nome de banco de dados longo ao usar a ferramenta de linha de comando aspnet_regsql.exe, renomeie o banco de dados para um nome mais amigável ao clicar com o botão direito do mouse no banco de dados, basta anexar e escolher Renomear. Renomeei meu banco de dados para DataTutorials.

Renomear o banco de dados anexado para um nome mais Human-Friendly

Figura 3: renomeie o banco de dados anexado para um nome mais Human-Friendly

Etapa 3: Adicionar a infraestrutura de sondagem ao banco de dados Northwind

Agora que anexamos o NORTHWND.MDF banco de dados da App_Data pasta, estamos prontos para adicionar a infraestrutura de sondagem. Supondo que você renomeou o banco de dados para DataTutorials, execute os quatro comandos a seguir:

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

Depois de executar esses quatro comandos, clique com o botão direito do mouse no nome do banco de dados no Management Studio, vá para o submenu Tarefas e escolha Desanexar. Em seguida, feche o Management Studio e reabra o Visual Studio.

Depois que o Visual Studio for reaberto, faça uma busca detalhada no banco de dados por meio da Explorer do Servidor. Observe a nova tabela (AspNet_SqlCacheTablesForChangeNotification), os novos procedimentos armazenados e os gatilhos nas Productstabelas , Categoriese Suppliers .

O banco de dados agora inclui a infraestrutura de sondagem necessária

Figura 4: o banco de dados agora inclui a infraestrutura de sondagem necessária

Etapa 4: Configurando o serviço de sondagem

Depois de criar as tabelas, gatilhos e procedimentos armazenados necessários no banco de dados, a etapa final é configurar o serviço de sondagem, que é feito por meio Web.config da especificação dos bancos de dados a serem usados e a frequência de sondagem em milissegundos. A marcação a seguir sonda o banco de dados Northwind uma vez a cada 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>

O name valor no <add> elemento ( NorthwindDB ) associa um nome legível por humanos a um banco de dados específico. Ao trabalhar com dependências de cache DO SQL, precisaremos nos referir ao nome do banco de dados definido aqui, bem como à tabela na qual os dados armazenados em cache se baseiam. Veremos como usar a SqlCacheDependency classe para associar programaticamente dependências de cache SQL a dados armazenados em cache na Etapa 6.

Depois que uma dependência de cache SQL for estabelecida, o sistema de sondagem se conectará aos bancos de dados definidos nos elementos a <databases> cada pollTime milissegundos e executará o AspNet_SqlCachePollingStoredProcedure procedimento armazenado. Esse procedimento armazenado - que foi adicionado novamente na Etapa 3 usando a aspnet_regsql.exe ferramenta de linha de comando - retorna os tableName valores e changeId para cada registro em AspNet_SqlCacheTablesForChangeNotification. Dependências de cache SQL desatualizadas são removidas do cache.

A pollTime configuração introduz uma compensação entre desempenho e desatualização de dados. Um valor pequeno pollTime aumenta o número de solicitações para o banco de dados, mas remove dados obsoletos mais rapidamente do cache. Um valor maior pollTime reduz o número de solicitações de banco de dados, mas aumenta o atraso entre quando os dados de back-end são alterados e quando os itens de cache relacionados são removidos. Felizmente, a solicitação de banco de dados está executando um procedimento armazenado simples que está retornando apenas algumas linhas de uma tabela simples e leve. Mas faça experimentos com valores diferentes pollTime para encontrar um equilíbrio ideal entre o acesso ao banco de dados e a desatualização de dados para seu aplicativo. O menor pollTime valor permitido é 500.

Observação

O exemplo acima fornece um único pollTime valor no <sqlCacheDependency> elemento , mas você pode opcionalmente especificar o pollTime valor no <add> elemento . Isso será útil se você tiver vários bancos de dados especificados e quiser personalizar a frequência de sondagem por banco de dados.

Etapa 5: Trabalhando declarativamente com dependências de cache do SQL

Nas Etapas 1 a 4, examinamos como configurar a infraestrutura de banco de dados necessária e configurar o sistema de sondagem. Com essa infraestrutura em vigor, agora podemos adicionar itens ao cache de dados com uma dependência de cache SQL associada usando técnicas programáticas ou declarativas. Nesta etapa, examinaremos como trabalhar declarativamente com dependências de cache do SQL. Na Etapa 6, examinaremos a abordagem programática.

O tutorial Armazenar dados em cache com o ObjectDataSource explorou os recursos declarativos de cache do ObjectDataSource. Ao simplesmente definir a EnableCaching propriedade como true e a CacheDuration propriedade para algum intervalo de tempo, o ObjectDataSource armazenará automaticamente em cache os dados retornados de seu objeto subjacente para o intervalo especificado. O ObjectDataSource também pode usar uma ou mais dependências de cache SQL.

Para demonstrar o uso declarativamente das dependências de cache do SQL, abra a SqlCacheDependencies.aspx página na Caching pasta e arraste um GridView da Caixa de Ferramentas para o Designer. Defina o GridView s ID como ProductsDeclarative e, de sua marca inteligente, escolha associá-lo a um novo ObjectDataSource chamado ProductsDataSourceDeclarative.

Criar um novo objectDataSource chamado ProductsDataSourceDeclarative

Figura 5: Criar um novo objetoDataSource nomeado ProductsDataSourceDeclarative (clique para exibir a imagem em tamanho real)

Configure o ObjectDataSource para usar a ProductsBLL classe e defina a lista suspensa na guia SELECT como GetProducts(). Na guia UPDATE, escolha a UpdateProduct sobrecarga com três parâmetros de entrada – productName, unitPricee productID. Defina as listas suspensas como (Nenhum) nas guias INSERT e DELETE.

Usar a sobrecarga updateproduct com três parâmetros de entrada

Figura 6: Usar a sobrecarga UpdateProduct com três parâmetros de entrada (clique para exibir a imagem em tamanho real)

Defina a lista de Drop-Down como (Nenhum) para as guias INSERT e DELETE

Figura 7: Definir a lista de Drop-Down como (Nenhum) para as guias INSERT e DELETE (clique para exibir a imagem em tamanho real)

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio criará BoundFields e CheckBoxFields no GridView para cada um dos campos de dados. Remova todos os campos, exceto ProductName, CategoryNamee UnitPrice, e formate esses campos como desejar. Na marca inteligente GridView, marcar as caixas de seleção Habilitar Paginação, Habilitar Classificação e Habilitar Edição. O Visual Studio definirá a propriedade original_{0}objectDataSource como OldValuesParameterFormatString . Para que o recurso de edição do GridView funcione corretamente, remova essa propriedade inteiramente da sintaxe declarativa ou defina-a de volta para seu valor padrão, {0}.

Por fim, adicione um controle Web Label acima do GridView e defina sua ID propriedade como ODSEvents e sua EnableViewState propriedade como false. Depois de fazer essas alterações, a marcação declarativa da página deve ser semelhante à seguinte. Observe que fiz várias personalizações estéticas nos campos GridView que não são necessárias para demonstrar a funcionalidade de dependência do cache 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>

Em seguida, crie um manipulador de eventos para o evento ObjectDataSource e Selecting , nele, adicione o seguinte código:

protected void ProductsDataSourceDeclarative_Selecting
    (object sender, ObjectDataSourceSelectingEventArgs e)
{
    ODSEvents.Text = "-- Selecting event fired";
}

Lembre-se de que o evento ObjectDataSource é Selecting acionado somente ao recuperar dados de seu objeto subjacente. Se o ObjectDataSource acessar os dados de seu próprio cache, esse evento não será acionado.

Agora, visite esta página por meio de um navegador. Como ainda não implementamos nenhum cache, cada vez que você paginar, classificar ou editar a grade, a página deverá exibir o texto 'Selecionar evento disparado, como mostra a Figura 8.

O evento Selecting ObjectDataSource é acionado sempre que o GridView é paginado, editado ou classificado

Figura 8: o evento ObjectDataSource é Selecting acionado sempre que o GridView é paginado, editado ou classificado (clique para exibir a imagem em tamanho real)

Como vimos no tutorial Armazenar dados em cache com o ObjectDataSource , definir a EnableCaching propriedade true como faz com que ObjectDataSource armazene seus dados em cache durante a duração especificada por sua CacheDuration propriedade. O ObjectDataSource também tem uma SqlCacheDependency propriedade , que adiciona uma ou mais dependências de cache SQL aos dados armazenados em cache usando o padrão :

databaseName1:tableName1;databaseName2:tableName2;...

Em que databaseName é o nome do banco de dados, conforme especificado no name atributo do <add> elemento em Web.config, e tableName é o nome da tabela de banco de dados. Por exemplo, para criar um ObjectDataSource que armazena dados em cache indefinidamente com base em uma dependência de cache SQL na tabela NorthwindProducts, defina a propriedade true ObjectDataSource como EnableCaching e sua SqlCacheDependency propriedade como NorthwindDB:Products.

Observação

Você pode usar uma dependência de cache SQL e uma expiração baseada em tempo definindo EnableCachingtruecomo , CacheDuration para o intervalo de tempo e SqlCacheDependency para o banco de dados e os nomes da tabela. O ObjectDataSource removerá seus dados quando a expiração baseada em tempo for atingida ou quando o sistema de sondagem observar que os dados subjacentes do banco de dados foram alterados, o que acontecer primeiro.

O GridView em SqlCacheDependencies.aspx exibe dados de duas tabelas – Products e Categories (o campo do CategoryName produto é recuperado por meio de um JOIN em Categories). Portanto, queremos especificar duas dependências de cache SQL: NorthwindDB:Products; NorthwindDB:Categories .

Configurar o ObjectDataSource para dar suporte ao cache usando dependências de cache do SQL em produtos e categorias

Figura 9: Configurar o ObjectDataSource para dar suporte ao cache usando dependências do Cache SQL em Products e Categories (clique para exibir a imagem em tamanho real)

Depois de configurar o ObjectDataSource para dar suporte ao cache, reveja a página por meio de um navegador. Novamente, o texto "Selecionar evento acionado deve aparecer na visita da primeira página, mas deve desaparecer ao paginar, classificar ou clicar nos botões Editar ou Cancelar. Isso ocorre porque, depois que os dados são carregados no cache do ObjectDataSource, eles permanecem lá até que as Products tabelas ou Categories sejam modificadas ou que os dados sejam atualizados por meio do GridView.

Depois de paginar a grade e notar a falta do texto 'Selecionar evento disparado, abra uma nova janela do navegador e navegue até o tutorial Básico na seção Edição, Inserção e Exclusão (~/EditInsertDelete/Basics.aspx). Atualize o nome ou o preço de um produto. Em seguida, de até a primeira janela do navegador, exiba uma página de dados diferente, classifique a grade ou clique no botão Editar de uma linha. Desta vez, o 'Evento de seleção acionado deve reaparecer, pois os dados de banco de dados subjacentes foram modificados (consulte a Figura 10). Se o texto não aparecer, aguarde alguns instantes e tente novamente. Lembre-se de que o serviço de sondagem está verificando se há alterações na tabela a Products cada pollTime milissegundos, portanto, há um atraso entre quando os dados subjacentes são atualizados e quando os dados armazenados em cache são removidos.

Modificar a tabela products remove os dados do produto armazenados em cache

Figura 10: Modificar a tabela Produtos remove os dados do produto armazenados em cache (clique para exibir a imagem em tamanho real)

Etapa 6: Trabalhar programaticamente com aSqlCacheDependencyclasse

O tutorial Armazenando dados em cache no tutorial Arquitetura analisou os benefícios de usar uma camada de cache separada na arquitetura em vez de acoplar firmemente o cache com o ObjectDataSource. Nesse tutorial, criamos uma ProductsCL classe para demonstrar programaticamente o trabalho com o cache de dados. Para utilizar dependências de cache SQL na Camada de Cache, use a SqlCacheDependency classe .

Com o sistema de sondagem, um SqlCacheDependency objeto deve ser associado a um determinado banco de dados e par de tabelas. O código a seguir, por exemplo, cria um SqlCacheDependency objeto com base na tabela do banco de Products dados Northwind:

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

Os dois parâmetros de entrada para o SqlCacheDependency construtor s são os nomes de banco de dados e tabela, respectivamente. Assim como acontece com a propriedade ObjectDataSource s SqlCacheDependency , o nome do banco de dados usado é o mesmo que o valor especificado no name atributo do <add> elemento em Web.config. O nome da tabela é o nome real da tabela de banco de dados.

Para associar um SqlCacheDependency a um item adicionado ao cache de dados, use uma das Insert sobrecargas de método que aceita uma dependência. O código a seguir adiciona valor ao cache de dados por uma duração indefinida, mas o associa a Products um SqlCacheDependency na tabela. Em suma, o valor permanecerá no cache até que seja removido devido a restrições de memória ou porque o sistema de sondagem detectou que a Products tabela foi alterada desde que foi armazenada em cache.

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

Atualmente, a classe da Camada de Cache armazena ProductsCL em cache dados da Products tabela usando uma expiração baseada em tempo de 60 segundos. Vamos atualizar essa classe para que ela use dependências de cache do SQL. O ProductsCL método de classe s AddCacheItem , que é responsável por adicionar os dados ao cache, atualmente contém o seguinte código:

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);
}

Atualize este código para usar um SqlCacheDependency objeto em vez da MasterCacheKeyArray dependência de cache:

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 testar essa funcionalidade, adicione um GridView à página abaixo do GridView existente ProductsDeclarative . Defina esse novo GridView s ID como ProductsProgrammatic e, por meio de sua marca inteligente, associe-o a um novo ObjectDataSource chamado ProductsDataSourceProgrammatic. Configure o ObjectDataSource para usar a ProductsCL classe , definindo as listas suspensas nas guias SELECT e UPDATE como GetProducts e UpdateProduct, respectivamente.

Configurar o ObjectDataSource para usar a classe ProductsCL

Figura 11: Configurar o ObjectDataSource para usar a ProductsCL classe (clique para exibir a imagem em tamanho real)

Selecione o método GetProducts na lista de Drop-Down da guia SELECT

Figura 12: Selecione o GetProducts Método na Lista de Drop-Down da Guia SELECT (Clique para exibir a imagem em tamanho real)

Escolha o método UpdateProduct na lista de Drop-Down da guia UPDATE

Figura 13: Escolher o método UpdateProduct na lista de Drop-Down da guia UPDATE (clique para exibir a imagem em tamanho real)

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio criará BoundFields e CheckBoxFields no GridView para cada um dos campos de dados. Assim como ocorre com o primeiro GridView adicionado a esta página, remova todos os campos, exceto ProductName, CategoryNamee UnitPrice, e formate esses campos como desejar. Na marca inteligente GridView, marcar as caixas de seleção Habilitar Paginação, Habilitar Classificação e Habilitar Edição. Assim como acontece com o ProductsDataSourceDeclarative ObjectDataSource, o Visual Studio definirá a ProductsDataSourceProgrammatic propriedade original_{0}objectDataSource como OldValuesParameterFormatString . Para que o recurso de edição do GridView funcione corretamente, defina essa propriedade {0} como (ou remova a atribuição de propriedade da sintaxe declarativa completamente).

Depois de concluir essas tarefas, a marcação declarativa GridView e ObjectDataSource resultante deve ser semelhante à seguinte:

<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 testar a dependência de cache do SQL na Camada de Cache, defina um ponto de interrupção no método da classe s AddCacheItem e, em ProductCL seguida, inicie a depuração. Quando você visita SqlCacheDependencies.aspxpela primeira vez , o ponto de interrupção deve ser atingido à medida que os dados são solicitados pela primeira vez e colocados no cache. Em seguida, vá para outra página no GridView ou classifique uma das colunas. Isso faz com que o GridView exiba seus dados, mas os dados devem ser encontrados no cache, pois a tabela de Products banco de dados não foi modificada. Se os dados não forem encontrados repetidamente no cache, verifique se há memória suficiente disponível no computador e tente novamente.

Depois de paginar algumas páginas do GridView, abra uma segunda janela do navegador e navegue até o tutorial Básico na seção Edição, Inserção e Exclusão (~/EditInsertDelete/Basics.aspx). Atualize um registro da tabela Produtos e, na primeira janela do navegador, exiba uma nova página ou clique em um dos cabeçalhos de classificação.

Nesse cenário, você verá uma das duas coisas: o ponto de interrupção será atingido, indicando que os dados armazenados em cache foram removidos devido à alteração no banco de dados; ou, o ponto de interrupção não será atingido, o que significa que SqlCacheDependencies.aspx agora está mostrando dados obsoletos. Se o ponto de interrupção não for atingido, é provável que o serviço de sondagem ainda não tenha sido acionado desde que os dados foram alterados. Lembre-se de que o serviço de sondagem está verificando se há alterações na tabela a Products cada pollTime milissegundos, portanto, há um atraso entre quando os dados subjacentes são atualizados e quando os dados armazenados em cache são removidos.

Observação

Esse atraso é mais provável de aparecer ao editar um dos produtos por meio do GridView em SqlCacheDependencies.aspx. No tutorial Dados de Cache no tutorial Arquitetura , adicionamos a MasterCacheKeyArray dependência de cache para garantir que os dados que estão sendo editados por meio do ProductsCL método da classe s UpdateProduct sejam removidos do cache. No entanto, substituímos essa dependência de cache ao modificar o AddCacheItem método anteriormente nesta etapa e, portanto, a ProductsCL classe continuará a mostrar os dados armazenados em cache até que o sistema de sondagem observe a alteração na Products tabela. Veremos como reintroduzir a dependência de MasterCacheKeyArray cache na Etapa 7.

Etapa 7: Associar várias dependências a um item armazenado em cache

Lembre-se de que a dependência de MasterCacheKeyArray cache é usada para garantir que todos os dados relacionados ao produto sejam removidos do cache quando qualquer item associado a ele for atualizado. Por exemplo, o GetProductsByCategoryID(categoryID) método armazena em cache instâncias ProductsDataTables para cada valor categoryID exclusivo. Se um desses objetos for removido, a dependência de MasterCacheKeyArray cache garantirá que os outros também sejam removidos. Sem essa dependência de cache, quando os dados armazenados em cache são modificados, existe a possibilidade de que outros dados do produto armazenados em cache possam estar desatualizados. Consequentemente, é importante mantermos a dependência de MasterCacheKeyArray cache ao usar dependências de cache SQL. No entanto, o método s do cache de Insert dados permite apenas um único objeto de dependência.

Além disso, ao trabalhar com dependências de cache SQL, talvez seja necessário associar várias tabelas de banco de dados como dependências. Por exemplo, o ProductsDataTable armazenado em cache na ProductsCL classe contém os nomes de categoria e fornecedor para cada produto, mas o AddCacheItem método usa apenas uma dependência em Products. Nessa situação, se o usuário atualizar o nome de uma categoria ou fornecedor, os dados do produto armazenados em cache permanecerão no cache e estarão desatualizados. Portanto, queremos tornar os dados do produto armazenados em cache dependentes não apenas da Products tabela, mas também das Categories tabelas e Suppliers .

A AggregateCacheDependency classe fornece um meio para associar várias dependências a um item de cache. Comece criando uma AggregateCacheDependency instância. Em seguida, adicione o conjunto de dependências usando o AggregateCacheDependency método s Add . Ao inserir o item no cache de dados posteriormente, passe a AggregateCacheDependency instância. Quando qualquer uma das AggregateCacheDependency dependências da instância for alterada, o item armazenado em cache será removido.

A seguir, mostra o código atualizado para o ProductsCL método da classe s AddCacheItem . O método cria a dependência de MasterCacheKeyArray cache junto com SqlCacheDependency objetos para as Productstabelas , Categoriese Suppliers . Todos eles são combinados em um AggregateCacheDependency objeto chamado aggregateDependencies, que é então passado para o 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);
}

Teste esse novo código. Agora, as alterações nas Productstabelas , Categoriesou fazem Suppliers com que os dados armazenados em cache sejam removidos. Além disso, o ProductsCL método da classe s UpdateProduct , que é chamado ao editar um produto por meio do GridView, remove a dependência de MasterCacheKeyArray cache, o que faz com que o cache ProductsDataTable seja removido e os dados sejam recuperados novamente na próxima solicitação.

Observação

As dependências do cache SQL também podem ser usadas com o cache de saída. Para obter uma demonstração dessa funcionalidade, consulte: Usando ASP.NET cache de saída com SQL Server.

Resumo

Ao armazenar em cache os dados do banco de dados, o ideal é que os dados permaneçam no cache até que sejam modificados no banco de dados. Com ASP.NET 2.0, as dependências do cache SQL podem ser criadas e usadas em cenários declarativos e programáticos. Um dos desafios dessa abordagem é descobrir quando os dados foram modificados. As versões completas do Microsoft SQL Server 2005 fornecem recursos de notificação que podem alertar um aplicativo quando um resultado da consulta for alterado. Para o Express Edition de SQL Server 2005 e versões mais antigas do SQL Server, um sistema de sondagem deve ser usado. Felizmente, a configuração da infraestrutura de sondagem necessária é bastante simples.

Programação feliz!

Leitura Adicional

Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:

Sobre o autor

Scott Mitchell, autor de sete livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.

Agradecimentos Especiais

Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Marko Rangel, Teresa Murphy e Hilton Giesenow. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, solte-me uma linha em mitchell@4GuysFromRolla.com.