Pré compilação de views Razor no ASP.NET MVC 6

Imagine o cenário de uma aplicação ASP.NET MVC básica, com uma classe de modelo servindo dados para uma view, conforme código abaixo:

Agora, suponha que precisamos fazer alguma alteração simples, como mudar o namespace da classe de modelo, para maior organização do projeto. Passamos a classe HomeViewModel para dentro da pasta Home, debaixo do diretório Models, conforme figura abaixo:

Novo namespace
Novo namespace

Se tentarmos rodar a aplicação nesse momento, vamos obter erro de compilação e o build vai falhar, já que o namespace referenciado na controller não mais possui a classe HomeViewModel:

Erro de compilação
Erro de compilação

Esse é o resultado esperado. O compilador nos ajuda a detectar esse tipo de erro básico e aponta onde está o problema.

Depois de corrigido o namespace no controller, ainda obteremos erro ao rodar a aplicação, dessa vez em tempo de execução:

Erro em tempo de execução
Erro em tempo de execução

 

Você vem me dizer que o código não compila em tempo de execução?
Você vem me dizer que o código não compila em tempo de execução?

Isso ocorre, pois não atualizamos a referência do namespace do modelo no código da view.

Seria muito mais eficiente se o compilador nos informasse desse erro em tempo de compilação, da mesma forma que ocorreu na controller. No ASP.NET MVC 5, é possível alterar a configuração do arquivo .csproj, de forma a forçar a pré renderização da view, de modo que o compilador avise de qualquer erro em tempo de compilação. Basta adicionar a tag abaixo no código do arquivo .csproj:

<MvcBuildViews>true</MvcBuildViews>

Mas em uma aplicação ASP.NET MVC 6, o arquivo de projeto mudou para o formato JSON e já não temos mais o recurso de pré compilação da forma que existia nas versões anteriores do framework, conforme mostrado acima. Como fazer agora?

Utilizando o RazorPreCompileModule

O ASP.NET 5, antes conhecido como vNext, traz consigo o novo compilador do C#, chamado Roslyn. Com ele, a Microsoft disponibilizou formas do desenvolvedor alterar o comportamento de compilação de uma aplicação, através de interfaces e classe abstratas que podem sem implementadas ou estendidas para adicionarmos comportamento customizado.

No nosso caso, vamos criar uma classe chamada PreCompilacaoRazor, que herda da classe abstrata RazorPreCompilaModule, que por sua vez implementa a interface ICompileModule. Essa classe será responsável por realizar a pré renderização das views da nossa aplicação:

Observe que não é necessário declarar o módulo de compilação no nosso código. Basta adicionar o código acima em qualquer ponto do projeto, que o Roslyn se encarrega de encontrar qualquer classe que implemente a interface ICompileModule para aplicar os seus módulos.

Ao adicionarmos essa classe na aplicação, obtemos o comportamento desejado. Agora, o Roslyn informa erro em tempo de compilação ao tentarmos rodar a aplicação com erro no código da view e o build falha:

Erro de compilação na view
Erro de compilação na view

Observação: Esse recurso não funciona com a versão beta 1 das dlls do ASP.NET 5. Já foram lançadas as versões beta 3 com a versão mais recente do Visual Studio 2015 CTP 14.0.22609.0 D14REL. Inclusive, o template padrão de uma aplicação ASP.NET MVC 6 já está incluindo a classe para efetuar a pré compilação das views Razor.

O novo modelo de compilação como serviço introduzido com o ASP.NET 5 permite diversos recursos interessantes para as aplicações baseadas no .Net Framework. Vale muito a pena ficar atento às novidades e entender melhor o funcionamento do Roslyn.

SHOW ME THE CODE!!

https://github.com/marcellalves/pre-compilacao-view-razor-asp-net-mvc-6

Para saber mais sobre o assunto, leia também:

http://www.strathweb.com/2014/12/razor-views-pre-compilation-asp-net-5-mvc-6/

Flush no ASP.NET MVC 6

Fazer o flush parcial do HTML gerado no servidor já é uma técnica bem conhecida, divulgada e recomendada no desenvolvimento de aplicações web de alta performance. Desde o ASP clássico até as versões mais recentes do .Net Framework, o recurso já estava presente, através do método Flush da classe Response.

Para facilitar o uso de flush em uma aplicação ASP.NET MVC de versão anterior à 6, podemos utilizar um pacote NuGet chamado Courtesy Flush. Para saber mais, recomendo a leitura do post do Scott Hanselman:

http://www.hanselman.com/blog/NuGetPackageOfTheWeekCourtesyFlushToFlushBuffersEarlierAndOptimizeTimeToFirstByte.aspx

Com a nova versão do Razor, que acompanha o ASP.NET MVC 6, fazer o flush parcial do conteúdo de uma view ficou ainda mais fácil.

Para exemplificar esse recurso na prática, vou utilizar a aplicação construída no post anterior, que é uma página estilo dashboard, com elementos que possuem um delay total de 3 segundos para processar.

Onde colocar o flush?

Para fazer uso efetivo do flush, é muito importante definir o local correto da chamada do método na aplicação. Segundo Steve Souders, autor dos clássicos livros sobre performance de aplicações web: High Performance Web Sites e Even Faster Web Sites, o flush deve ser colocado logo antes da execução de algum método de servidor que demore mais para responder, mas também deve trazer estrutura e recursos mínimos para que, pelo menos uma parte da página, possa ser renderizada e exibida para o usuário.

Seguindo essa diretiva, alterar a aplicação de exemplo utilizada no post passado, adicionando o novo recurso de flush parcial do Razor.

O primeiro passo é criar uma section chamada “corpoDaPagina”  no layout geral da aplicação:

Na página index, a chamada ao método FlushAsync fica logo antes da renderização das views parciais que possuem delay, dessa forma, todo o cabeçalho e os recursos referenciados no header da página são enviados para o navegador do usuário antes do processamento ser completado:

Notem a diferença no gráfico waterfall de renderização de cada uma das páginas:

Sem flush
Sem flush
Com flush
Com flush

Quando não utilizamos flush, a aplicação espera todo o processamento no servidor para carregar o css da página e do bootstrap. Quando usamos, esses recursos são carregados durante o processamento do lado do servidor, dando ao usuário uma sensação de que a página respondeu mais rápido à sua requisição, já que o cabeçalho é exibido rapidamente, antes do processamento pesado do lado do servidor ocorrer.

Esse é mais um recurso interessante para a melhoria do desempenho de uma aplicação ASP.NET MVC 6.

Aplicação de exemplo: http://razorasyncmvc6.azurewebsites.net

SHOW ME THE CODE!!

https://github.com/marcellalves/asp-net-mvc-6-razor-async

Para ampliar o conhecimento sobre esse assunto, leia também:

http://www.stevesouders.com/blog/2013/01/31/http-archive-adding-flush/

http://nikcodes.com/2014/03/04/flushing-in-asp-net-mvc/

http://nikcodes.com/2014/03/17/more-http-flushing-in-asp-net-mvc/

 

Views com métodos assíncronos em uma aplicação ASP.NET MVC 6

Continuando a série que explora a utilização de programação assíncrona para melhorar o desempenho de uma aplicação ASP.NET MVC, nesse post mostrarei como utilizar uma nova funcionalidade presente no ASP.NET MVC 6, que é a possibilidade de utilizar chamadas assíncronas dentro de uma view utilizando o Razor, que é a engine de renderização de HTML utilizada pelo framework.

Para contextualizar esse exemplo, é recomendado ler os posts anteriores da série:

http://www.dotnetflash.com.br/2015/01/melhorando-a-performance-percebida-com-views-parciais-assincronas/

http://www.dotnetflash.com.br/2015/01/utilizando-chamadas-assincronas-no-servidor-em-uma-aplicacao-asp-net-mvc/

No segundo post da série, implementei chamadas assíncronas para os métodos mais lentos, que causavam a queda do desempenho da aplicação. Vou utilizar essa mesma aplicação como base para exemplificar o uso de chamadas assíncronas dentro de uma view Razor.

Relembrando os métodos assíncronos utilizados para retornar as informações pesadas da view:

Para simular os métodos lentos, introduzi um atraso de 1 segundo na execução do RecuperarContasAPagarRecentesAsync() e 2 segundos no RecuperarLogsRecentesAsync(), utilizando o método Delay(Int32) da classe Task. Os métodos assíncronos em uma aplicação .Net retornam um objeto do tipo Task<TResult> e é justamente isso que vocês podem ver no exemplo de código acima. Implementamos os métodos lentos do nosso repositório de modo que eles retornem tasks a serem executadas quando a informação for necessária. Observe que não estamos utilizando a palavra reservada await para recuperar o resultado dos métodos assíncronos, e sim repassando as tarefas para as propriedades dos modelos das views:

Com essa nova implementação, só vamos utilizar o await para executar o resultado dos métodos dentro do código da view, conforme código abaixo:

A nova sintaxe do Razor nos permite fazer chamadas a métodos assíncronos dentro do código da view.

O ganho de desempenho utilizando essa técnica é de 30%, conforme mostra a tabela abaixo.

ViewTempo total de renderização da página
Com chamadas síncronas4,52 segundos
Com chamadas assíncronas3,17 segundos
Ganho de desempenho com chamadas assíncronas30%

Esse ganho de desempenho ocorre, pois a chamada de um método assíncrono utilizando a palavra reservada await não bloqueia a thread principal da aplicação, liberando-a para continuar a execução. No nosso exemplo, a view chama primeiro o método RecuperarContasAPagarRecentesAsync() e continua executando, até chamar o método RecuperarLogsRecentesAsync(). Com isso, o tempo de resposta melhora, pois uma função não fica esperando a outra terminar de executar para iniciar o seu processamento.

O modelo de programação assíncrona é uma tendência forte no mundo .Net e vemos que a Microsoft está cada vez mais incorporando esse paradigma dentro do framework, com o objetivo de facilitar e incentivar o uso. Vale a pena investir o seu tempo para dominar esse tipo de técnica, pois desempenho é um fator vital para qualquer aplicação.

Para ver uma demonstração ao vivo da aplicação, clique no link: http://razorasyncmvc6.azurewebsites.net

SHOW ME THE CODE!!

https://github.com/marcellalves/asp-net-mvc-6-razor-async

Para ampliar o conhecimento sobre esse assunto, leia também:

http://www.rodolfofadino.com.br/2014/11/asp-net-mvc-6-razor-com-suporte-ao-async-e-flush/

http://blog.stephencleary.com/2012/02/async-and-await.html (em inglês)

Melhorando a performance percebida com views parciais assíncronas

A velocidade é um dos principais fatores de sucesso de uma aplicação web. Os usuários buscam uma experiência semelhante à que eles encontram em aplicativos nativos para smartphone ou serviços que utilizam tecnologia de ponta, como Facebook, Google e Twitter. Atender às expectativas de um público cada vez mais exigente é um desafio constante para os desenvolvedores web.

O desempenho de uma aplicação é um fator tão relevante que o Google estima que cada 0,5 segundo extra na renderização de uma página de resultado de pesquisa causa uma queda de 20% na audiência. A Amazon calcula que cada 100 milissegundos de latência custam 1% de prejuízo nas vendas.

Fonte: http://highscalability.com/blog/2009/7/25/latency-is-everywhere-and-it-costs-you-sales-how-to-crush-it.html (em inglês)

Nesse post, apresentarei uma técnica interessante para melhorar a performance percebida pelo usuário em uma aplicação ASP.NET MVC. Com ela, fazemos uso de views parciais para renderizar um layout de maneira assíncrona. Importante notar que essa técnica melhora a performance percebida pelo usuário, mas não substitui otimizações do lado do servidor. No cenário apresentado, a página responde mais rápido para o usuário, mas o tempo total de carregamento da página é praticamente o mesmo.

O cenário que vou expor é de uma página no estilo painel, com diversas áreas que trazem informações diferentes, como a do iGoogle Portal. Essa aplicação de exemplo é a página inicial de um sistema financeiro, que mostra as últimas 5 transações de contas a pagar e a receber e uma lista com os últimos 10 logs de evento gerados pelo sistema:

Tela inicial
Clique para ampliar

Cada um dos painéis possui a sua própria origem de dados. Ocorre que por motivos de processamento interno, as fontes de dados de contas a pagar e logs demoram para serem recuperadas, fazendo com que o carregamento inicial da página fique lento. Dessa forma, a página inicial e o painel de contas a pagar, que são rápidos, demoram a aparecer devido à lentidão dos outros dois painéis.

Ao executarmos a aplicação com renderização da página inicial de forma síncrona, obtemos o seguinte resultado:

Desempenho síncrono
Clique para ampliar

O tempo total de carregamento da página é de 3,97 segundos, suficiente para tirar a paciência de qualquer usuário. Observe que o gargalo maior se encontra no tempo que o servidor demora para enviar os dados para a aplicação: 3,21 segundos, 98% do tempo total.

Agora, é hora de aplicar a técnica proposta. A estratégia é simples: vamos carregar primeiro a página inicial e o painel de contas a receber, que são rápidos, e delegar o carregamento dos painéis lentos de contas a pagar e log para chamadas AJAX, que são naturalmente assíncronas. Além disso, vamos obter um benefício adicional ao executar as duas requisições de forma paralela.

Para atingir esse objetivo, precisamos alterar a página inicial. Antes, ela renderizava as views parciais de forma síncrona:

Agora, vamos criar divs que funcionarão como marcadores para o carregamento das informações. Elas foram marcadas com a classe css “conteudoParcial”, para serem referenciadas pelo código jQuery. Além disso, fazemos uso do atributo customizado “data-url”, para armazenar o endereço da fonte de dados da view parcial. Esse é um recurso muito útil introduzido na especificação do HTML 5, para armazenamento de informações customizadas em elementos de marcação. Mais detalhes nesse link (em inglês). Por fim, adicionamos um gif de load para dar um retorno para o usuário quanto ao carregamento dos painéis:

jQuery necessário para executar o carregamento assíncrono das views parciais:

Também foi necessário alterar o controller da página, criando actions separadas para a página inicial / painel de contas a receber e para os outros dois painéis (contas a pagar e logs):

Depois de aplicadas as alterações, percebemos um ganho substancial no tempo de resposta da página:

Desempenho assíncrono
Clique para ampliar

Agora, o tempo total de carregamento inicial é de 1,29 segundo, um ganho de 68% em relação ao anterior. Esse é o novo desempenho percebido pelo usuário no tempo de resposta da aplicação, mesmo que as requisições dos outros painéis continuem demorando um pouco mais para aparecer na tela.

Concluindo: é possível aplicar técnicas no cliente para melhorar a performance percebida pelo usuário em uma aplicação ASP.NET MVC. Esse exemplo poderia ficar ainda melhor se trabalhássemos o lado do servidor, tornando a recuperação das informações necessárias para a página mais ágil. Isso poderia ser conseguido através de chamadas assíncronas dos métodos de recuperação dos dados e será o tema do próximo post. Fique ligado!

Aplicação de exemplo:

Síncrono: http://viewassincronasaspnetmvc.azurewebsites.net/

Assíncrono: http://viewassincronasaspnetmvc.azurewebsites.net/Assincrono

Código da aplicação: https://github.com/marcellalves/viewsparciaisassincronasASPNETMVC.