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.

Criando um projeto ASP.NET MVC 4 para testes

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”).

Criando um projeto ASP.NET MVC 4 para testes

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.

Adicionando uma referência à biblioteca System.ServiceModel

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:

  • Inicialmente é gerada uma coleção de objetos do tipo Noticia;
  • Uma nova referência para a classe SyndicationFeed (namespace System.ServiceModel.Syndication) é gerada, invocando para isto o método estático Load. Esta última operação recebe como parâmetro um objeto do tipo XmlTextReader (namespace System.Xml), com a geração deste último tomando como base o endereço do feed RSS a ser consumido;
  • Com a instância do tipo SyndicationFeed, o conteúdo da propriedade Items deste objeto será lido dentro de um loop foreach. Esta propriedade nada mais é do que uma coleção de objetos baseados na classe SyndicationItem (namespace System.ServiceModel.Syndication); por meio destas referências serão geradas as instâncias do tipo Noticia, as quais correspondem ao conjunto de resultados esperado ao se acionar o método ObterNoticias;
  • Quanto aos objetos da classe SyndicationItem manipulados dentro da operação ObterNoticias, é possível observar o uso das propriedades Title (título de uma notícia), Links (endereço/link da notícia em questão), PublishDate (data em que a notícia foi publicada) e Summary (texto resumido descrevendo o conteúdo abordado por tal notícia).

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:

  • Index:
  • corresponde à página inicial do site, a partir da qual também será possível a visualização da Partial View contendo as notícias mais recentes;
  • Noticias: Partial View que será processada e armazenada em cache durante dois minutos, evitando assim novos reprocessamentos a cada vez em que a View Index for acessada. Logo, um benefício direto da adoção de tal prática está em melhorar a performance na utilização da aplicação;
  • About: permite a visualização da página “Sobre”, a qual contém uma descrição resumida dos objetivos deste projeto de exemplo; Contact: responsável pelo processamento da View em que serão exibidas as informações para contato.

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

  • O resultado da execução da Action Noticias é um objeto do tipo PartialViewResult (namespace System.Web.Mvc), o qual é obtido ao se invocar o método PartialView. Uma Partial View é um tipo de estrutura cujo resultado de seu processamento (trechos de código HTML) pode ser utilizado dentro de uma outra View convencional;
  • O endereço do feed de notícias definido no arquivo Web.config é repassado ao método ObterNoticias da classe NoticiasReader, bem como associado a uma nova propriedade (“EnderecoFeedNoticias”) pertencente ao objeto dinâmico ViewBag;
  • É possível observar ainda que a coleção de instâncias do tipo Noticia é fornecida como parâmetro à operação PartialView, a fim de que tais objetos possam ser utilizados posteriormente pela Partial View de nome “Noticias” (com o intuito de gerar uma visualização das manchetes mais recentes que constam no feed RSS);
  • Por fim, o uso do atributo OutputCacheAttribute (namespace System.Web.Mvc) permite que o resultado da Partial View seja armazenado em cache por um período de dois minutos, especificando-se para isto o valor 120 (segundos) à propriedade Duration. Requisições subsequentes, ao longo do período especificado em Duration, retornarão a saída produzida ainda no processamento inicial. Expirado o tempo de dois minutos, o resultado em questão é descartado do cache, a fim de que uma nova solicitação force o reprocessamento da Partial View Noticias.

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.

Criação de uma Partial View

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:

    A presença de uma instrução imprimindo a data em que o resultado dessa estrutura foi processado; este artifício foi empregado aqui com o objetivo de se facilitar o teste do mecanismo de caching provido pelo ASP.NET MVC;

    O uso do objeto ViewBag e da propriedade Model, consumindo assim os dados gerados durante o processamento da Action Noticias;

    Uma chamada ao método Raw do objeto Html; esta ação tem por finalidade gerar o conteúdo em HTML correspondente ao resumo de uma notícia, considerando para isto meios que garantam a correta visualização de prováveis tags no texto (imagens e/ou referências a outros links, por exemplo).

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.

Aplicação TesteCacheMVC em execução

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).

Mecanismo de cache do ASP.NET MVC em açã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.

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

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.

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

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!