Efetuando o cache de Views no ASP.NET MVC

Veja neste artigo como aplicar técnicas de caching em Views no ASP.NET MVC. A partir do armazenamento temporário de informações, este tipo de prática pode contribuir de forma significativa para uma melhor performance no acesso a sites.

O tratamento de requisições enviadas a uma aplicação Web envolve, basicamente, o processamento de cada solicitação recebida e a produção dos resultados correspondentes. Sem sombra de dúvidas, ações deste tipo podem exigir, em determinados momentos, muito da infraestrutura associada a um website, sobretudo se considerada a performance do sistema em questão.

Em aplicações voltadas à Internet um aumento incomum no número de acessos pode, em muitos casos, comprometer seriamente a continuidade das operações. Dentre os prováveis desdobramentos disto estão falhas inesperadas durante o processamento de requisições ou, até mesmo, usuários que abandonam tais sistemas em virtude de problemas de lentidão.

Procurando uma melhor performance na consulta a informações acessadas com frequência, muitos servidores Web dispõe de mecanismos que possibilitam o armazenamento e o reuso de dados processados anteriormente, sendo esta técnica conhecida pelo nome de "caching". Na plataforma ASP.NET isto não é diferente, com as tecncologias Web Forms e MVC dispondo de recursos que permitem efetuar o cache de páginas ou, até mesmo, objetos para posterior reutilização.

O objetivo deste artigo é descrever como o cache de dados pode ser efetuado em aplicações ASP.NET MVC. Para isto, será implementado um exemplo que consome informações de um feed de notícias, de maneira que a atualização disto aconteça apenas em intervalos regulares de tempo.

Criando a solução de exemplo

A aplicação apresentada neste artigo foi criada a partir do Visual Studio 2012 Professional, fazendo uso para isto do .NET Framework 4.5 e da versão 4.0 do ASP.NET MVC. O exemplo aqui discutido procura abordar a construção de uma View em que será exibido um resumo das manchetes mais recentes disponibilizadas por um site de notícias. A atualização de tais dados ocorrerá periodicamente, de forma a evitar um novo processamento a cada vez que um usuário acessar a página inicial do Web site que estaremos implementando.

Para gerar o projeto de testes será necessário, dentro do Visual Studio, acessar o menu File, opção New e, por fim, opção Project. Dentro da tela New Project (Figura 1) selecionar o template ASP.NET MVC 4 Web Application, preenchendo o campo Name com o nome da aplicação a ser gerada (“TesteCacheMVC”, neste caso); no campo Location é possível ainda definir o diretório no qual serão criados os arquivos para este projeto.


Figura 1: Criando um projeto ASP.NET MVC 4 para testes

Aparecerá então uma janela como a que consta na Figura 2. Deverão ser escolhidos o template para a criação do projeto (selecionar “Internet Application”), assim como o Engine utilizado para a geração das Views (marcar a opção “Razor”).


Figura 2: Criando um projeto ASP.NET MVC 4 para testes

Criação dos tipos utilizados na manipulação do feed de notícias

Com o projeto TestePartialViews já criado, chega o momento de prosseguir com a construção das classes utilizadas na manipulação das notícias que serão apresentadas aos usuários do site. As estruturas aqui criadas pertencerão à camada Model (namespace TesteCacheMVC.Models).

Muitos portais têm como prática comum disponibilizar serviços contendo um resumo de suas principais manchetes num determinado momento, utilizando para tanto um formato de dados conhecido como RSS. A partir disto, aplicações dos mais variados tipos podem consumir tais informações, oferecendo a seus respectivos usuários uma síntese do que pode estar acontecendo em um determinado canal de comunicação.

O formato RSS (sigla do inglês “Really Simple Syndication”) pode ser definido, em termos gerais, como uma implementação específica do padrão XML. Serviços baseados em RSS (e conhecidos popularmente como “feeds”) costumam ser empregados para a geração de notificações de atualizações em blogs, sites de notícias, dentre outros órgãos de informação.

Para o exemplo abordado por este artigo, estará sendo usado o feed de notícias econômicas do portal Yahoo: http://br.noticias.yahoo.com/rss/economia.

Primeiramente será implementada a classe Noticia (Listagem 1). Conforme pode ser observado, este tipo conta com uma estrutura bastante simples, servindo de base para a exibição de dados como o título, o link para acesso, a data de publicação, além de um resumo sobre as notícias mais recentes para o site considerado.

Listagem 1: Classe Noticia

using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace TesteCacheMVC.Models { public class Noticia { public string Titulo { get; set; } public string Link { get; set; } public DateTime DataPublicacao { get; set; } public string Resumo { get; set; } } }

Como próximo passo, adicionar ao projeto uma referência ao assembly System.ServiceModel (Figura 3). Este procedimento tornará o possível a utilização de classes voltadas à manipulação de feeds RSS e que se encontram definidas nesta biblioteca.


Figura 3: Adicionando uma referência à biblioteca System.ServiceModel

Já na Listagem 2 está o código que define a classe estática NoticiasReader. Este tipo será empregado na leitura de notícias de um feed, devolvendo através da operação ObterNoticias uma coleção de instâncias da classe Noticia.

Quanto ao método estático ObterNoticias, o mesmo foi estruturado para funcionar da seguinte maneira:

Listagem 2: Classe NoticiasReader

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ServiceModel.Syndication; using System.Xml; namespace TesteCacheMVC.Models { public static class NoticiasReader { public static List<Noticia> ObterNoticias( string enderecoFeed) { List<Noticia> noticias = new List<Noticia>(); SyndicationFeed feed = SyndicationFeed .Load(new XmlTextReader(enderecoFeed)); foreach (SyndicationItem item in feed.Items) { noticias.Add(new Noticia() { Titulo = item.Title.Text, Link = item.Links.Count > 0 ? item.Links[0].Uri.ToString() : null, DataPublicacao = item.PublishDate.LocalDateTime, Resumo = item.Summary.Text }); } return noticias; } } }

Ajustando o Controller que irá processar as Views da aplicação

Com os tipos da camada Model já definidos, deve-se proceder agora com o acerto do Controller responsável pelo processamento das diferentes Views deste projeto de exemplo (classe HomeController).

O primeiro passo será incluir uma configuração chamada “EnderecoFeedNoticias” na seção appSettings do arquivo Web.config (Listagem 3). Definir como valor deste item o endereço do feed de notícias a ser utilizado pela aplicação.

Listagem 3: Ajustes no arquivo Web.config

<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> ... <add key="EnderecoFeedNoticias" value="http://br.noticias.yahoo.com/rss/economia"/> </appSettings> ... </configuration>

Na Listagem 4 encontra-se o código que implementa o tipo HomeController.

Listagem 4: Classe HomeController

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Configuration; using TesteCacheMVC.Models; namespace TesteCacheMVC.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } [OutputCache(Duration = 120)] public PartialViewResult Noticias() { string enderecoFeed = ConfigurationManager .AppSettings["EnderecoFeedNoticias"]; ViewBag.EnderecoFeedNoticias = enderecoFeed; return PartialView(NoticiasReader.ObterNoticias( enderecoFeed)); } public ActionResult About() { return View(); } public ActionResult Contact() { return View(); } } }

Estão definidas neste Controller as Actions:

Diferente das demais operações implementadas em HomeController, a Action Noticias foi construída levando em conta os seguintes aspectos:

Na Tabela 1 estão relacionadas outras propriedades que podem ser configuradas ao se fazer uso do tipo OutputCacheAttribute:

Propriedade Tipo Descrição
VaryByParam String Quando utilizado, determina que serão armazenadas diferentes entradas em cache para cada combinação de valores constantes nos objetos Request.QueryString e Request.Form. O valor default “none” indica que o cache não irá variar com base em entradas de query strings ou formulários; já “*” especifica que o cache irá variar com base em quaisquer valores definidos em Request.QueryString e Request.Form.
VaryByHeader String Entradas no cache serão geradas com base em diferentes combinações de headers HTTP.
VaryByCustomString Permite o uso de caching de uma forma customizada, dependendo para isto do método GetVaryByCustomString definido dentro do arquivo Global.asax.
Location OutputCacheLocation (enumeration)

Define em que local os dados serão armazenados em cache. São valores possíveis para o enum OutputCacheLocation:

  • Server (apenas na memória do servidor);
  • Client (somente no browser do usuário que está acessando o recurso);
  • Downstream (no browser do visitante ou, ainda, em um dispositivo intermediário como um servidor proxy);
  • ServerAndClient (combinação de servidor e browser do cliente);
  • Any (combinação dos padrões Server e Downstream);
  • None (sem cache).

Caso não seja especificado um valor para a propriedade Location, o ASP.NET MVC assumirá como default o valor Any.

Tabela 1: Algumas propriedades da classe OutputCacheAttribute

Implementação das Views da aplicação

Finalmente, serão criadas/codificadas as Views utilizadas na exibição das notícias.

No caso da Partial View Noticias, a criação da estrutura que conterá o resultado da execução da Action de mesmo nome pode ser feita clicando-se com o botão direito do mouse sobre o método correspondente; no menu de atalho apresentado selecionar a opção Add View.

Com a janela “Add View” ativa, certificar-se de que o campo “View Engine” está preenchido com o valor “Razor (CSHTML)”. Marcar então a opção “Create a strongly typed-view”, selecionando em “Model class” o tipo Noticia; já em “Scaffold template”, escolher o valor “List”, a fim de que seja gerada uma listagem com as diferentes notícias. Concluindo este processo, selecionar ainda a opção “Create as a partial view”. Na Figura 4 estão ilustrados todos esses ajustes.


Figura 4: Criação de uma Partial View

Ao se acionar o botão Add da janela Add View será criado o arquivo Noticias.cshtml. Trata-se de uma View “fortemente tipada”, ou seja, através da propriedade Model desta estrutura é possível acessar uma coleção de objetos de um tipo específico (no caso, a classe Noticia).

Na Listagem 5 está o código corresponde à Partial View Noticias. Nota-se nesse arquivo:

Listagem 5: Partial View Noticias.cshtml

@model IEnumerable<TesteCacheMVC.Models.Noticia> <h3>Último acesso ao serviço de notícias: @DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss")</h3> <br /> Fonte: <a href="@ViewBag.EnderecoFeedNoticias">Yahoo! Economia</a> @foreach (var item in Model) { <p> <b><a href="@item.Link"> @item.DataPublicacao.ToString("dd/MM/yyyy HH:mm:ss") - @item.Titulo </a></b> <br /> @Html.Raw(item.Resumo) </p> }

Ajustes precisarão ser realizados agora na View Index (Listagem 6).

Ao ser visualizada, a ideia é que a View Index produza como resultado um documento HTML em que constem as diferentes notícias do feed RSS já mencionado anteriormente. Para que isto seja possível, o método Action do objeto Html foi acionado, tendo sido fornecido como parâmetro ao mesmo o nome da Partial View (“Noticias”) cujo conteúdo será incorporado dentro da View principal.

Além disso, a data atual também constará no código HTML a ser gerado para a View Index. A inclusão desta informação teve como objetivo demonstrar que enquanto a View principal é processada novamente (durante um novo acesso, ou mesmo, através de um refresh a partir de um browser), o resultado da Partial View Noticias sempre estará atrelado ao cache da aplicação (o que forçará a uma atualização sempre que existir algo armazenado e que expirou o limite de dois minutos).

Listagem 6: View Index.cshtml

@section featured { <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Principais notícias - @DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss")</h1> </hgroup> </div> </section> } @Html.Action("Noticias")

Executando a aplicação de testes

Iniciando a execução da aplicação criada neste artigo, será exibida uma tela como a que consta na Figura 5.


Figura 5: Aplicação TesteCacheMVC em execução

Acionando a função “Atualizar” do Internet Explorer tão logo aconteça este primeiro acesso, aparecerá um resultado similar ao da Figura 6. É possível observar que o horário no cabeçalho da página variou, ao passo que a mesma informação constante na Partial View Noticias permaneceu inalterada (já que o resultado disto foi recuperado do cache da aplicação).


Figura 6: Mecanismo de cache do ASP.NET MVC em ação

Sem que o tempo inicial de dois minutos tenha se encerrado, proceder com um teste a partir do navegador Google Chrome. Conforme indicado na Figura 7, o resultado da Partial View continua armazenado em cache.


Figura 7: Acessando a aplicação de testes a partir do Google Chrome

Esgotado este primeiro período de dois minutos, uma nova atualização da página dentro do Internet Explorer forçará a atualização do conteúdo associado à Partial View (e, consequentemente, a substituição do que estava no cache pelo resultado deste novo processamento). A Figura 8 exemplifica todo este fluxo.


Figura 8: Teste efetuado após a expiração do conteúdo em cache

Conclusão

A técnica conhecida como caching é empregada em soluções Web para armazenar dados e páginas em memória, visando com isso tornar mais rápido o acesso a tais recursos. Trata-se de uma prática importante, uma vez que ao evitar repetidos acessos a bancos de dados, arquivos, Web Services ou outros dispositivos pode contribuir para uma melhor performance durante a utilização de um site.

A fim de possibilitar o armazenamento temporário dos resultados processados por Actions de um Controller, o ASP.NET MVC conta com o atributo OutputCacheAttribute. Essa estrutura conta com diversas propriedades, permitindo que o processo de caching possa ser executado levando em conta variados fatores (tempo, parâmetros existentes em uma query string etc.).

Espero que o conteúdo aqui apresentado possa ser útil no seu dia a dia. Até uma próxima oportunidade!

Artigos relacionados