Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
por Scott Mitchell
Em qualquer aplicativo Web, alguns dados serão usados com freqüência e alguns dados serão usados com pouca frequência. Podemos melhorar o desempenho do nosso aplicativo ASP.NET carregando com antecedência os dados usados com frequência, uma técnica conhecida como Caching. 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 armazenamento em cache de dados nas camadas de apresentação e cache. Em Caching Data with the ObjectDataSource, analisamos o uso dos recursos de cache do ObjectDataSource para armazenar dados em cache na Camada de Apresentação. O Caching Data in the Architecture examinou o cache em uma nova camada de cache separada. Ambos os tutoriais usaram carregamento reativo ao trabalhar com o cache de dados. Com o carregamento reativo, cada vez 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 principal vantagem do carregamento reativo é a sua facilidade de implementação. Uma das suas desvantagens é o seu desempenho desigual entre pedidos. Imagine uma página que usa a camada de cache do tutorial anterior para exibir informações do produto. Quando esta página é visitada pela primeira vez, ou visitada pela primeira vez depois que os dados armazenados em cache foram removidos devido a restrições de memória ou a expiração especificada ter sido 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 de 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 sejam necessários. Normalmente, o carregamento proativo usa algum processo que verifica periodicamente ou é notificado quando há uma atualização nos dados subjacentes. Em seguida, 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 de carregamento proativo é mais difícil de implementar, pois requer a criação, o gerenciamento e a implantação de um processo para verificar se há alterações e atualizar o cache.
Outro tipo de carregamento proativo, e o tipo que exploraremos neste tutorial, é o carregamento de dados no cache na inicialização do aplicativo. Essa abordagem é especialmente útil para armazenar em cache dados estáticos, como os registros em tabelas de pesquisa de banco de dados.
Observação
Para obter uma visão mais aprofundada das diferenças entre carregamento proativo e reativo, bem como 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 carregamento reativo que examinamos nos dois tutoriais anteriores funcionam bem com dados que podem mudar periodicamente e não levam muito tempo 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 excessivamente longo para serem gerados, os usuários cujas solicitações encontrarem 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 gerar na inicialização do aplicativo.
Embora os bancos de dados tenham muitos valores dinâmicos e que mudam com frequência, a maioria também tem uma quantidade razoável 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. Muitas vezes, esses tipos de colunas são implementados usando tabelas de pesquisa. Em vez de armazenar a cadeia de caracteres inglês ou francês na Patients tabela, é criada uma segunda tabela que tem, geralmente, 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 tabela armazena Patients o identificador exclusivo correspondente na tabela de pesquisa. Na Figura 1, a língua principal do paciente John Doe é o inglês, enquanto a de Ed Johnson é o russo.
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 preenchida pelos registros na Languages tabela. Sem cache, cada vez que esta interface é visitada o sistema deve consultar a Languages tabela. Isso é um desperdício e desnecessário, uma vez que os valores da tabela de pesquisa mudam muito raramente, ou nunca.
Poderíamos armazenar os Languages dados em cache 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 estáticos da tabela de pesquisa. Embora o cache usando 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, veremos 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 programaticamente armazenadas em cache 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 programaticamente armazenados em cache 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, devemos primeiro 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, devemos primeiro criar uma instância da classe usando a new palavra-chave.
SomeMethod e SomeProperty estão associados a uma instância específica. O tempo de vida desses membros está ligado 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 uma vida útil tão longa quanto a classe. Membros estáticos são indicados pela 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 a partir da 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 itens de cache e assim por diante. Com membros estáticos e estado do aplicativo, esses recursos devem ser adicionados manualmente pelo desenvolvedor da página. Ao armazenar dados em cache na inicialização do aplicativo durante o tempo de vida do aplicativo, no entanto, as vantagens do cache de dados são discutíveis. Neste tutorial, veremos o código que usa 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 nenhuma tabela de pesquisa tradicional. As quatro DataTables implementadas em nossa DAL todas as tabelas de modelo cujos valores são não-estáticos. Em vez de gastar o tempo para adicionar uma nova DataTable à 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 nomeada StaticCache.cs na CL pasta.
Figura 2: Criar a StaticCache.csCL classe na pasta
Precisamos adicionar um método que carregue os dados na inicialização no armazenamento de cache apropriado, bem como métodos que retornem 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 armazenar os resultados do método da SuppliersBLLGetSuppliers() classe, que é chamado a partir 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 da aplicação.
Em vez de usar uma variável de membro estático como armazenamento de cache, poderíamos ter usado alternativamente o estado do aplicativo ou o cache de dados. O código a seguir mostra a classe retooled 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 variável do aplicativo. Ele é retornado como o tipo apropriado (Northwind.SuppliersDataTable) de GetSuppliers(). Enquanto o estado do aplicativo pode ser acessado nas classes code-behind de páginas ASP.NET usando Application["key"], na arquitetura que devemos usar HttpContext.Current.Application["key"] para obter o atual HttpContext.
Da mesma forma, o cache de dados pode ser usado como um armazenamento de cache, como mostra o código a seguir:
[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 cache Insert de dados foi selecionada para que pudéssemos especificar a prioridade do item de cache. A prioridade é usada para determinar quais itens devem ser removidos do cache quando a memória disponível estiver baixa. Aqui usamos a prioridade NotRemovable, que garante que esse item de cache não será removido.
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 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 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 arquivo ao diretório raiz do aplicativo Web clicando com o Global.asax 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.
Figura 3: Adicionar o arquivo ao diretório raiz do aplicativo Web (Global.asax 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_StartExecuta quando o aplicativo Web é iniciado pela primeira vez -
Application_Endé executado quando o aplicativo está sendo desligado -
Application_Erroré executado sempre que uma exceção não tratada chega ao aplicativo -
Session_StartExecuta quando uma nova sessão é criada -
Session_Endé executado quando uma sessão expira ou é abandonada
O Application_Start manipulador de eventos é chamado apenas uma vez durante o ciclo de vida de um aplicativo. O aplicativo é iniciado na 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 /Bin conteúdo da pasta, modificando Global.asax, modificando o App_Code conteúdo na 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, então sinta-se à 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>
É só isso! Na inicialização do aplicativo, o LoadStaticCache() método pegará as informações do fornecedor da BLL e as armazenará em uma variável de membro estática (ou qualquer armazenamento de cache que você acabou usando na StaticCache classe). Para verificar esse comportamento, defina um ponto de interrupção no método e execute seu Application_Start aplicativo. Observe que o ponto de interrupção é atingido ao iniciar o aplicativo. Solicitações subsequentes, no entanto, não fazem com que o Application_Start método seja executado.
Figura 4: Usar um ponto de interrupção para verificar se o manipulador de eventos está sendo executado (Application_Start imagem em tamanho real)
Observação
Se você não atingir o ponto de interrupção quando iniciar a Application_Start depuração pela primeira vez, é porque seu aplicativo já foi iniciado. Força a reinicialização do aplicativo 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 pode ser acessada 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 a partir da 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 armazenadas em cache.
Comece por abrir a página AtApplicationStartup.aspx na pasta Caching. 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.
Figura 5: Configurar o ObjectDataSource para usar a classe (StaticCache imagem em tamanho real)
Figura 6: Use o método para recuperar os dados do fornecedor armazenados em cache (GetSuppliers() imagem em tamanho real)
Depois de concluir o assistente, o Visual Studio adicionará automaticamente BoundFields para cada um dos campos de dados no 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 visualizada através de um navegador. A saída é a mesma se tivéssemos extraído os dados da classe BLL SuppliersBLL , mas usar a StaticCache classe retorna os dados do fornecedor como armazenados em cache na inicialização do aplicativo. Você pode definir pontos de interrupção no StaticCache método da GetSuppliers() classe para verificar esse comportamento.
Figura 7: Os dados do fornecedor em cache são exibidos em um GridView (Clique para visualizar a imagem em tamanho real)
Resumo
A maioria dos modelos de dados contém uma quantidade razoável 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 cada vez que essas informações precisam ser exibidas. Além disso, devido à sua natureza estática, ao armazenar os dados em cache, não há necessidade de um prazo de validade. Neste tutorial, vimos como pegar esses dados e armazená-los em cache no cache de dados, no estado do aplicativo e por meio de uma variável membro estática. 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 anteriores, analisamos o armazenamento em cache de dados durante a vida útil do aplicativo, bem como o uso de expirações baseadas no tempo. Ao armazenar dados de banco de dados em cache, no entanto, uma expiração baseada em tempo pode ser menos do que ideal. Em vez de liberar periodicamente o cache, seria ideal remover apenas o item armazenado em cache quando os dados do banco de dados subjacente forem modificados. Esse ideal é possível através do uso de dependências de cache SQL, que examinaremos em nosso próximo tutorial.
Feliz Programação!
Sobre o Autor
Scott Mitchell, autor de sete livros sobre ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias Web da Microsoft desde 1998. Scott trabalha como consultor, formador e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Ele pode ser contatado em mitchell@4GuysFromRolla.com.
Um agradecimento especial a
Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Teresa Murphy e Zack Jones. Interessado em rever meus próximos artigos do MSDN? Se for o caso, envie-me uma mensagem para mitchell@4GuysFromRolla.com.