Armazenar dados em cache na inicialização do aplicativo (C#)

por Scott Mitchell

Baixar PDF

Em qualquer aplicativo Web, alguns dados serão usados com frequência e alguns dados serão usados com pouca frequência. Podemos melhorar o desempenho de nosso aplicativo ASP.NET carregando com antecedência os dados usados com frequência, uma técnica conhecida como Cache. Este tutorial demonstra uma abordagem para o carregamento proativo, que é carregar dados no cache na inicialização do aplicativo.

Introdução

Os dois tutoriais anteriores analisaram o cache de dados nas Camadas de Apresentação e Cache. Em Armazenar dados em cache com o ObjectDataSource, examinamos o uso dos recursos de cache do ObjectDataSource para armazenar dados em cache na Camada de Apresentação. O cache de dados na arquitetura examinou o cache em uma nova camada de cache separada. Ambos os tutoriais usaram o carregamento reativo no trabalho com o cache de dados. Com o carregamento reativo, sempre que os dados são solicitados, o sistema primeiro verifica se eles estão no cache. Caso contrário, ele captura os dados da fonte de origem, como o banco de dados, e os armazena no cache. A vantagem main para o carregamento reativo é a facilidade de implementação. Uma de suas desvantagens é seu desempenho desigual entre solicitações. Imagine uma página que usa a Camada de Cache do tutorial anterior para exibir informações do produto. Quando essa página é visitada pela primeira vez ou visitada pela primeira vez depois que os dados armazenados em cache são removidos devido a restrições de memória ou à expiração especificada que foi atingida, os dados devem ser recuperados do banco de dados. Portanto, essas solicitações de usuários levarão mais tempo do que as solicitações dos usuários que podem ser atendidas pelo cache.

O carregamento proativo fornece uma estratégia alternativa de gerenciamento de cache que suaviza o desempenho entre solicitações carregando os dados armazenados em cache antes que eles sejam necessários. Normalmente, o carregamento proativo usa algum processo que verifica periodicamente ou é notificado quando há uma atualização para os dados subjacentes. Esse processo atualiza o cache para mantê-lo atualizado. O carregamento proativo é especialmente útil se os dados subjacentes vierem de uma conexão de banco de dados lenta, um serviço Web ou alguma outra fonte de dados particularmente lenta. Mas essa abordagem para carregamento proativo é mais difícil de implementar, pois requer a criação, o gerenciamento e a implantação de um processo para marcar para alterações e atualização do cache.

Outro tipo de carregamento proativo e o tipo que exploraremos neste tutorial é carregar dados no cache na inicialização do aplicativo. Essa abordagem é especialmente útil para armazenar dados estáticos em cache, como os registros em tabelas de pesquisa de banco de dados.

Observação

Para obter uma visão mais detalhada das diferenças entre o carregamento proativo e reativo, bem como as listas de prós, contras e recomendações de implementação, consulte a seção Gerenciando o conteúdo de um cache do Guia de Arquitetura de Cache para aplicativos .NET Framework.

Etapa 1: Determinando quais dados armazenar em cache na inicialização do aplicativo

Os exemplos de cache usando o carregamento reativo que examinamos nos dois tutoriais anteriores funcionam bem com dados que podem mudar periodicamente e não demoram muito para serem gerados. Mas se os dados armazenados em cache nunca forem alterados, a expiração usada pelo carregamento reativo será supérflua. Da mesma forma, se os dados que estão sendo armazenados em cache levarem um tempo extremamente longo para serem gerados, os usuários cujas solicitações localizarem o cache vazio terão que suportar uma longa espera enquanto os dados subjacentes são recuperados. Considere armazenar em cache dados estáticos e dados que levam um tempo excepcionalmente longo para serem gerados na inicialização do aplicativo.

Embora os bancos de dados tenham muitos valores dinâmicos e com alteração frequente, a maioria também tem uma quantidade justa de dados estáticos. Por exemplo, praticamente todos os modelos de dados têm uma ou mais colunas que contêm um valor específico de um conjunto fixo de opções. Uma Patients tabela de banco de dados pode ter uma PrimaryLanguage coluna cujo conjunto de valores pode ser inglês, espanhol, francês, russo, japonês e assim por diante. Geralmente, esses tipos de colunas são implementados usando tabelas de pesquisa. Em vez de armazenar a cadeia de caracteres em inglês ou francês na Patients tabela, uma segunda tabela é criada com, normalmente, duas colunas - um identificador exclusivo e uma descrição de cadeia de caracteres - com um registro para cada valor possível. A PrimaryLanguage coluna na Patients tabela armazena o identificador exclusivo correspondente na tabela de pesquisa. Na Figura 1, o idioma principal do paciente John Doe é o inglês, enquanto o de Ed Johnson é russo.

A tabela Languages é uma tabela de pesquisa usada pela tabela Patients

Figura 1: a Languages tabela é uma tabela de pesquisa usada pela Patients tabela

A interface do usuário para editar ou criar um novo paciente incluiria uma lista suspensa de idiomas permitidos preenchidos pelos registros na Languages tabela. Sem o cache, sempre que essa interface for visitada, o sistema deverá consultar a Languages tabela. Isso é um desperdício e desnecessário, pois os valores da tabela de pesquisa mudam com muita pouca frequência, se nunca.

Poderíamos armazenar em cache os Languages dados usando as mesmas técnicas de carregamento reativo examinadas nos tutoriais anteriores. O carregamento reativo, no entanto, usa uma expiração baseada em tempo, que não é necessária para dados de tabela de pesquisa estática. Embora o cache usando o carregamento reativo seja melhor do que nenhum cache, a melhor abordagem seria carregar proativamente os dados da tabela de pesquisa no cache na inicialização do aplicativo.

Neste tutorial, examinaremos como armazenar em cache dados da tabela de pesquisa e outras informações estáticas.

Etapa 2: Examinando as diferentes maneiras de armazenar dados em cache

As informações podem ser armazenadas em cache programaticamente em um aplicativo ASP.NET usando uma variedade de abordagens. Já vimos como usar o cache de dados em tutoriais anteriores. Como alternativa, os objetos podem ser armazenados em cache programaticamente usando membros estáticos ou o estado do aplicativo.

Ao trabalhar com uma classe, normalmente a classe deve primeiro ser instanciada antes que seus membros possam ser acessados. Por exemplo, para invocar um método de uma das classes em nossa Camada de Lógica de Negócios, primeiro devemos criar uma instância da classe :

ProductsBLL productsAPI = new ProductsBLL();
productsAPI.SomeMethod();
productsAPI.SomeProperty = "Hello, World!";

Antes de podermos invocar SomeMethod ou trabalhar com SomeProperty, primeiro devemos criar uma instância da classe usando o new palavra-chave. SomeMethod e SomeProperty estão associados a uma instância específica. O tempo de vida desses membros está vinculado ao tempo de vida de seu objeto associado. Os membros estáticos, por outro lado, são variáveis, propriedades e métodos que são compartilhados entre todas as instâncias da classe e, consequentemente, têm um tempo de vida, desde que a classe. Os membros estáticos são indicados pelo palavra-chave static.

Além dos membros estáticos, os dados podem ser armazenados em cache usando o estado do aplicativo. Cada aplicativo ASP.NET mantém uma coleção de nome/valor que é compartilhada entre todos os usuários e páginas do aplicativo. Essa coleção pode ser acessada usando a HttpContextpropriedade da Applicationclasse e usada de uma classe code-behind de uma página ASP.NET da seguinte forma:

Application["key"] = value;
object value = Application["key"];

O cache de dados fornece uma API muito mais rica para armazenar dados em cache, fornecendo mecanismos para expirações baseadas em tempo e dependência, prioridades de item de cache e assim por diante. Com membros estáticos e o estado do aplicativo, esses recursos devem ser adicionados manualmente pelo desenvolvedor da página. No entanto, ao armazenar dados em cache na inicialização do aplicativo durante o tempo de vida do aplicativo, as vantagens do cache de dados são discutíveis. Neste tutorial, examinaremos o código que usa todas as três técnicas para armazenar dados estáticos em cache.

Etapa 3: Armazenando em cache os dados daSupplierstabela

As tabelas de banco de dados Northwind que implementamos até o momento não incluem tabelas de pesquisa tradicionais. Os quatro DataTables implementados em nosso DAL todas as tabelas de modelo cujos valores são não estáticos. Em vez de gastar tempo para adicionar um novo DataTable ao DAL e, em seguida, uma nova classe e métodos à BLL, para este tutorial, vamos apenas fingir que os Suppliers dados da tabela são estáticos. Portanto, poderíamos armazenar esses dados em cache na inicialização do aplicativo.

Para começar, crie uma nova classe chamada StaticCache.cs na CL pasta .

Criar a classe StaticCache.cs na pasta CL

Figura 2: Criar a StaticCache.cs classe na CL pasta

Precisamos adicionar um método que carrega os dados na inicialização no repositório de cache apropriado, bem como métodos que retornam dados desse cache.

[System.ComponentModel.DataObject]
public class StaticCache
{
    private static Northwind.SuppliersDataTable suppliers = null;
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using a static member variable
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        suppliers = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return suppliers;
    }
}

O código acima usa uma variável de membro estático, suppliers, para manter os resultados do SuppliersBLL método da GetSuppliers() classe, que é chamado do LoadStaticCache() método . O LoadStaticCache() método deve ser chamado durante o início do aplicativo. Depois que esses dados forem carregados na inicialização do aplicativo, qualquer página que precise trabalhar com dados do fornecedor poderá chamar o StaticCache método da GetSuppliers() classe. Portanto, a chamada para o banco de dados para obter os fornecedores só acontece uma vez, no início do aplicativo.

Em vez de usar uma variável de membro estático como o repositório de cache, poderíamos ter usado, como alternativa, o estado do aplicativo ou o cache de dados. O código a seguir mostra a classe retoolada para usar o estado do aplicativo:

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using application state
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpContext.Current.Application["key"] = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpContext.Current.Application["key"] as Northwind.SuppliersDataTable;
    }
}

No LoadStaticCache(), as informações do fornecedor são armazenadas na chave de variável de aplicativo. Ele é retornado como o tipo apropriado (Northwind.SuppliersDataTable) de GetSuppliers(). Embora o estado do aplicativo possa ser acessado nas classes code-behind de ASP.NET páginas usando Application["key"], na arquitetura, devemos usar HttpContext.Current.Application["key"] para obter o atual HttpContext.

Da mesma forma, o cache de dados pode ser usado como um repositório de cache, como mostra o seguinte código:

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using the data cache
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpRuntime.Cache.Insert(
          /* key */                "key", 
          /* value */              suppliers, 
          /* dependencies */       null, 
          /* absoluteExpiration */ Cache.NoAbsoluteExpiration, 
          /* slidingExpiration */  Cache.NoSlidingExpiration, 
          /* priority */           CacheItemPriority.NotRemovable, 
          /* onRemoveCallback */   null);
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpRuntime.Cache["key"] as Northwind.SuppliersDataTable;
    }
}

Para adicionar um item ao cache de dados sem expiração baseada em tempo, use os System.Web.Caching.Cache.NoAbsoluteExpiration valores e System.Web.Caching.Cache.NoSlidingExpiration como parâmetros de entrada. Essa sobrecarga específica do método do Insert cache de dados foi selecionada para que pudéssemos especificar a prioridade do item de cache. A prioridade é usada para determinar quais itens serão retirados do cache quando a memória disponível for baixa. Aqui, usamos a prioridade NotRemovable, que garante que esse item de cache não seja armazenado em scaveng.

Observação

O download deste tutorial implementa a StaticCache classe usando a abordagem de variável de membro estático. O código para o estado do aplicativo e as técnicas de cache de dados está disponível nos comentários no arquivo de classe.

Etapa 4: Executando código na inicialização do aplicativo

Para executar o código quando um aplicativo Web é iniciado pela primeira vez, precisamos criar um arquivo especial chamado Global.asax. Esse arquivo pode conter manipuladores de eventos para eventos de nível de aplicativo, sessão e solicitação, e é aqui que podemos adicionar código que será executado sempre que o aplicativo for iniciado.

Adicione o Global.asax arquivo ao diretório raiz do aplicativo Web clicando com o botão direito do mouse no nome do projeto do site no Gerenciador de Soluções do Visual Studio e escolhendo Adicionar Novo Item. Na caixa de diálogo Adicionar Novo Item, selecione o tipo de item Classe de Aplicativo Global e clique no botão Adicionar.

Observação

Se você já tiver um Global.asax arquivo em seu projeto, o tipo de item Classe de Aplicativo Global não será listado na caixa de diálogo Adicionar Novo Item.

Adicionar o arquivo Global.asax ao diretório raiz do aplicativo Web

Figura 3: Adicionar o Global.asax arquivo ao diretório raiz do aplicativo Web (clique para exibir a imagem em tamanho real)

O modelo de arquivo padrão Global.asax inclui cinco métodos dentro de uma marca do lado <script> do servidor:

  • Application_Start é executado quando o aplicativo Web é iniciado pela primeira vez
  • Application_End é executado quando o aplicativo está sendo desligado
  • Application_Error executa sempre que uma exceção sem tratamento atinge o aplicativo
  • Session_Start é executado quando uma nova sessão é criada
  • Session_End é executado quando uma sessão está expirada ou abandonada

O Application_Start manipulador de eventos é chamado apenas uma vez durante o ciclo de vida de um aplicativo. O aplicativo inicia a primeira vez que um recurso de ASP.NET é solicitado do aplicativo e continua a ser executado até que o aplicativo seja reiniciado, o que pode acontecer modificando o conteúdo da /Bin pasta, modificando Global.asax, modificando o conteúdo na App_Code pasta ou modificando o Web.config arquivo, entre outras causas. Consulte ASP.NET Visão geral do Ciclo de Vida do Aplicativo para obter uma discussão mais detalhada sobre o ciclo de vida do aplicativo.

Para esses tutoriais, só precisamos adicionar código ao Application_Start método , portanto, fique à vontade para remover os outros. No Application_Start, basta chamar o StaticCache método da LoadStaticCache() classe, que carregará e armazenará em cache as informações do fornecedor:

<%@ Application Language="C#" %>
<script runat="server">
    void Application_Start(object sender, EventArgs e) 
    {
        StaticCache.LoadStaticCache();
    }
</script>

Isso é tudo! Na inicialização do aplicativo, o LoadStaticCache() método obterá as informações do fornecedor da BLL e as armazenará em uma variável de membro estático (ou em qualquer repositório de cache que você acabou usando na StaticCache classe). Para verificar esse comportamento, defina um ponto de interrupção no Application_Start método e execute seu aplicativo. Observe que o ponto de interrupção é atingido no início do aplicativo. No entanto, as solicitações subsequentes não fazem com que o Application_Start método seja executado.

Usar um ponto de interrupção para verificar se o manipulador de eventos Application_Start está sendo executado

Figura 4: usar um ponto de interrupção para verificar se o Application_Start manipulador de eventos está sendo executado (clique para exibir a imagem em tamanho real)

Observação

Se você não atingir o Application_Start ponto de interrupção ao iniciar a depuração pela primeira vez, isso ocorre porque seu aplicativo já foi iniciado. Force o aplicativo a reiniciar modificando seus Global.asax arquivos ou Web.config e tente novamente. Você pode simplesmente adicionar (ou remover) uma linha em branco no final de um desses arquivos para reiniciar rapidamente o aplicativo.

Etapa 5: Exibindo os dados armazenados em cache

Neste ponto, a StaticCache classe tem uma versão dos dados do fornecedor armazenados em cache na inicialização do aplicativo que podem ser acessados por meio de seu GetSuppliers() método. Para trabalhar com esses dados da Camada de Apresentação, podemos usar um ObjectDataSource ou invocar programaticamente o StaticCache método da GetSuppliers() classe de uma classe code-behind de uma página ASP.NET. Vamos examinar o uso dos controles ObjectDataSource e GridView para exibir as informações do fornecedor armazenado em cache.

Comece abrindo a AtApplicationStartup.aspx página na Caching pasta . Arraste um GridView da Caixa de Ferramentas para o designer, definindo sua ID propriedade como Suppliers. Em seguida, na marca inteligente do GridView, escolha criar um novo ObjectDataSource chamado SuppliersCachedDataSource. Configure o ObjectDataSource para usar o StaticCache método da GetSuppliers() classe.

Configurar o ObjectDataSource para usar a classe StaticCache

Figura 5: configurar o ObjectDataSource para usar a StaticCache classe (Clique para exibir a imagem em tamanho real)

Usar o método GetSuppliers() para recuperar os dados do fornecedor armazenado em cache

Figura 6: usar o GetSuppliers() método para recuperar os dados do fornecedor armazenado em cache (clique para exibir a imagem em tamanho real)

Depois de concluir o assistente, o Visual Studio adicionará automaticamente BoundFields para cada um dos campos de dados em SuppliersDataTable. A marcação declarativa de GridView e ObjectDataSource deve ser semelhante à seguinte:

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="SupplierID" DataSourceID="SuppliersCachedDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="SupplierID" />
        <asp:BoundField DataField="CompanyName" HeaderText="CompanyName" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="Address" HeaderText="Address" 
            SortExpression="Address" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
        <asp:BoundField DataField="Phone" HeaderText="Phone" 
            SortExpression="Phone" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersCachedDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliers" TypeName="StaticCache" />

A Figura 7 mostra a página quando exibida por meio de um navegador. A saída é a mesma se tivéssemos extraído os dados da classe da BLL, mas o uso da StaticCache classe retorna os dados do fornecedor conforme armazenados em cache na inicialização do SuppliersBLL aplicativo. Você pode definir pontos de interrupção no StaticCache método da GetSuppliers() classe para verificar esse comportamento.

Os dados do fornecedor armazenados em cache são exibidos em um GridView

Figura 7: Os dados do fornecedor armazenados em cache são exibidos em um GridView (clique para exibir a imagem em tamanho real)

Resumo

A maioria de cada modelo de dados contém uma boa quantidade de dados estáticos, geralmente implementados na forma de tabelas de pesquisa. Como essas informações são estáticas, não há razão para acessar continuamente o banco de dados sempre que essas informações precisarem ser exibidas. Além disso, devido à sua natureza estática, ao armazenar em cache os dados, não há necessidade de expiração. Neste tutorial, vimos como pegar esses dados e armazená-los em cache de dados, no estado do aplicativo e por meio de uma variável de membro estático. Essas informações são armazenadas em cache na inicialização do aplicativo e permanecem no cache durante todo o tempo de vida do aplicativo.

Neste tutorial e nos dois últimos, examinamos os dados de cache durante o tempo de vida do aplicativo, bem como usando expirações baseadas em tempo. No entanto, ao armazenar em cache dados de banco de dados, uma expiração baseada em tempo pode ser menor do que o ideal. Em vez de liberar periodicamente o cache, seria ideal remover apenas o item armazenado em cache quando os dados do banco de dados subjacentes forem modificados. Esse ideal é possível por meio do uso de dependências de cache SQL, que examinaremos em nosso próximo tutorial.

Programação feliz!

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 Teresa Murphy e Zack Jones. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, solte-me uma linha em mitchell@4GuysFromRolla.com.