Breno Ferreira

Opniões e códigos de um desenvolvedor de software .NET.

Async no C#5

with one comment

Olá pessoal,

Hoje irei falar sobre um assunto bastante comentado nos últimos meses: assincronia. Assincronia está longe de ser um conceito novo, estando presente em várias linguagens a bastante tempo. No caso de linguagens C#, é possível escrever código assíncrono desde a primeira versão da linguagem.

Porque escrever código assíncrono

Operações assíncronas no nosso código são importantes quando desejamos executar alguma operação em background. Isso é de extrema importância quando precisamos ter uma interface gráfica responsiva, ou seja, que não fica bloqueada esperando alguma operação terminar. Quem nunca viu uma aplicação travar conforme a imagem abaixo?

image

Isso acontece por que a Thread responsável por tratar os comandos do usuário (geralmente chamada de UI Thread), está ocupada esperando alguma operação completar (no caso do Notepad acima, ela está bloqueada esperando um arquivo terminar de ser lido). Por isso, ela não pode executar nenhum comando do usuário. Para que isso não ocorra, devemos executar essas operações que levam um certo tempo para completar de maneira assíncrona, ou seja, em background, assim sendo possível deixar outras operações sejam executadas ao mesmo tempo.

Código Assíncrono no C#

Antes da versão 5 da linguagem, escrever código assíncrono era um tanto tedioso, e propício a erros. O desenvolvedor tinha que lidar com a interface IAsyncResult e então ter que lidar com callbacks. Abaixo está um exemplos de como isso era feito:

private void DownloadAsync(String uri)
{
	try
	{
		var webRequest = WebRequest.Create(uri);
		webRequest.BeginGetResponse(new AsyncCallback(HandleResponse), null);
	}
	catch(Exception)
	{
		Console.WriteLine("Error while downloading async");
	}
}

private void HandleResponse(IAsyncResult result)
{
	try
	{
		var response = webRequest.EndGetResponse(responseResult);
		StreamReader reader = new StreamReader(response.GetResponseStream());
		Console.WriteLine(reader.ReadToEnd());
	}
	catch(Exception)
	{
		Console.WriteLine("Error while handling download response");
	}
}

Repare que o código baseado em callbacks pode ficar meio confuso. O exemplo acima é bem simples, mas em casos mais complexos, com muitas chamadas assíncronas, o código tende a ficar bem complicado de ler, pois há “desvios” no código, e o programador é obrigado a desviar a leitura para outro trecho do código para ver a continuação da operação assíncrona. Se começarmos a adicionar IFs e Loops no código assíncrono, e ainda ter que fazer o tratamento de Exceptions, o código irá ficar bem complicado.

Async e Await

Com base nessas ocasionais dificuldades de se escrever código assíncrono, o time de desenvolvimento do C# criou uma nova técnica de se escrever código assíncrono no C#. Essa nova maneira de escrever código assíncrono no C# faz uso de duas novas keywords: async e await.

private async Task<String> DownloadAsync(String uri)
{
	try
	{
		var webRequest = WebRequest.Create(uri);
		var response = await webRequest.GetResponseAsync();

		StreamReader reader = new StreamReader(response.GetResponseStream());
		return reader.ReadToEnd();
	}
	catch(Exception)
	{
		Console.WriteLine("Error while downloading async");
	}
}

Bem mais simples, certo? O código ficou bem mais simples de entender, e ao mesmo tempo, está praticamente idêntico ao código síncrono, ou seja, sem callbacks e IAsyncResults espalhados pelo código.

Para dizermos que um método possui algum tipo de operação assíncrona, precisamos marcar o método como assíncrono, utilizando a keyword async antes do tipo de retorno do método. E quando desejamos receber o resultado de uma operação assíncrona, utilizamos a keyword await.

Importante: repare que a keyword await não faz com que a Thread bloqueie e literalmente espere o resultado da operação terminar. O que de fato acontece é que ao chegar no ponto onde há um await, o método retorna, e o que estiver abaixo (no exemplo acima, a instanciação da StreamReader e o retorno do resultado), é executado quando a operação assíncrona terminar, como se houvesse um callback mascarado.

Repare que ao invés de retornarmos uma String, retornamos uma Task<String>. Isso se deve ao fato de que o async do C# faz uso de Tasks para coordenar o fluxo de execução do código. Tasks nada mais são do que uma implementação de um conceito conhecido como Futures. Pense como se fosse uma representação de uma operação que ainda não completou, e que seu resultado ainda é desconhecido. Quando esta operação completar, quem tiver uma referência a esta Future (Task) irá ser notificado de que a operação terminou e poderá obter o resultado da operação. Então, no método acima, quando a execução chega em uma instrução que contém a keyword await, o método retorna uma Task representando a operação assíncrona que está sendo executada, neste caso, o download. Quando a operação terminar, quem estava em estado de await irá ser notificado e irá poder continuar com a execução do código.

Mais alguns exemplos

Operação assíncrona em um Loop:

public async Task<int> CountToTen()
{
	int count = 1;
	for (int i = 0; i < 10; i++)
	{
		await Task.Delay(500); //simulating async work
		count += i;
	}

	return count;
}

Neste exemplo estamos em um loop e executamos uma operação assíncrona no escopo deste loop. Como estamos usando a keyword await, a próxima iteração do loop só irá executar quando a operação assíncrona terminar.

Outra maneira de se fazer o await:

private static void PrintFromOneToThenThenFromElevenToTwentyUsingTasks()
{
	var task = 
		new Task(() =>
				Enumerable.Range(1, 10).ToList().ForEach(i =>
				{
					Thread.Sleep(10);
					Console.WriteLine(i);
				})
			 );

	task
		.GetAwaiter()
		.OnCompleted(() =>
					Enumerable.Range(11, 10).ToList().ForEach(i =>
					{
						Thread.Sleep(10);
						Console.WriteLine(i);
					})
				  );
	task.Start();

	task.Wait();
}

Neste exemplo, fazemos o await manualmente, ou seja, fazemos (grosseiramente) o que o compilador faz quando encontra a keyword await. O que acontece é que aqui pegamos um Awaiter e definimos um callback (action passada como parâmetro para o método OnCompleted) que será chamado quando a operação assíncrona terminar e pegamos o resultado (GetResult).

Conclusão

Vimos o quão fácil ficou escrever código assíncrono no C#5, ficando bastante parecido com código síncrono. Assim, conseguimos uma maior facilidade na hora de lidar com as complexidades que ocorrem quando a complexidade do nosso código aumenta.

Para saber mais, acesse a página do Async CTP na MSDN. Lá voce poderá baixar o Async CTP para o Visual Studio 2010, ou então baixar a versão CTP do Visual Studio 11 (recomendado!), baixar um Whitepaper, e assistir alguns vídeos do time do C# explicando em maiores detalhes. Publiquei também dois Gists (aqui e aqui) com alguns exemplos descritos aqui no post e mais alguns. Para mais exemplos, também recomendo acessar o 101 Async Examples

Abraços

Breno

PS: Gostaria de agradecer ao meu amigo Rodrigo Vidal por ter me ajudado e ter dado umas dicas para este post!

Advertisements

Written by Breno Ferreira

06/11/2011 at 20:46

Posted in C#

Tagged with , , ,

Overview do Windows Runtime (WinRT)

with one comment

Olá pessoal,

Hoje irei falar um pouco sobre a nova API de desenvolvimento de aplicações para o Windows 8, conhecida como Windows Runtime, ou simplesmente WinRT.

Por que uma nova API?

Tradicionalmente, quando desenvolvemos uma aplicação para o Windows (seja ele Windows 7, Vista, XP, ou anterior), quando precisamos acessar algum recurso do sistema operacional, como por exemplo, o sistema de arquivos, ou a recursos de rede, acessamos, de alguma maneira, uma API disponibilizada pelo sistema operacional. Essa API é conhecida como WinAPI, e é composta de milhares de funções e estruturas de dados. As funcionalidades da WinAPI são expostas em um modelo ao estilo de programação em C, ou seja, não é orientada a objetos. Isso a torna um tanto complexa, difícil de usar e aprender. Por essa razão, foram criadas algumas camadas de abstração em cima dessa API, sendo uma das mais famosa delas o .NET Framework. Muitas das APIs do .NET são wrappers, ou seja, encapsulam uma chamada a uma função da WinAPI para que o desenvolvedor não precise se preocupar em detalhes complexos da API nativa do Windows.

Windows Runtime (WinRT)

Para a construção de aplicações Metro do Windows 8, a Microsoft resolveu criar uma nova API chamada Windows Runtime, ou simplesmente WinRT. Essa API é completamente diferente da WinAPI tradicional, baseada no COM (Component Object Model), e toda a API possui metadados descrevendo cada componente, seja ele uma classe, interface, método, propriedade, etc., e tem também alguns recursos que oferecem maior segurança ao usuário.

image

Como você pode ver na figura acima, o Windows Runtime é utilizado para construção de aplicações Metro, que é o novo tipo de aplicação no Windows 8. Essas aplicações são imersivas e dão ao usuário uma experiência diferente daquela oferecida por aplicações desktop tradicionais.

Com o WinRT, é possível acessar vários recursos do sistema operacional, como arquivos, recursos de media e gráficos, recursos de rede, dados de geolocation, sensores, entre outros recursos. E para criar aplicações que utilizam os componentes do WinRT, o desenvolvedor poderá escolher sua linguagem de preferência, seja ela C++, C# ou VB.NET, ou Javascript. E para a construção da interface de usuário (UI), caso o desenvolvedor escreva a aplicação em C#, VB ou C++, ele irá utilizar XAML para definição dos elementos da UI. No caso do Javascript, será usado HTML5+CSS3.

Arquitetura do Windows Runtime

image

O WinRT é, obviamente, uma API implementada em código nativo, e é utilizada amplamente pelo sistema operacional para executar suas aplicações, e pela aplicação em si para interagir com o usuário e o sistema operacional.

Cada tipo de dados no WinRT possui metadados sobre ele mesmo, e esses metadados são criados seguindo o mesmo padrão dos metadados do .NET Framework. Esses arquivos de metadados estão localizados no seguinte diretório: C:\Windows\System32\WinMetadata. Nesse diretório, há vários arquivos .winmd, que são os arquivos contendo os metadados sobre os tipos de dados do WinRT. A imagem abaixo mostra o arquivo “Windows.Storage.winmd” aberto no IL Disassembler.

image

Podemos ver que ele mostra informações sobre os métodos, propriedades, etc.. sobre os tipos contidos na biblioteca de Storage do WinRT. Repare que não há Intermediate Language gerada. Isso acontece por que a implementação dos métodos é nativa, e está contida em algumas dlls do sistema operacional. Esses arquivos .winmd contém somente metadados.

Como o WinRT é baseado em COM, todos os tipos de dados expostos por ele são baseados em Interfaces. E quando ativamos um objeto do WinRT, estamos, na verdade, recebendo uma referência de um objeto que implementa os métodos desta interface.

As linguagens que utilizamos para acessar o WinRT são por sua vez, baseadas em classes, e cada uma tem suas particularidades de utilização de objetos. É exatamente para prover esse mapeamento, e acesso às interfaces do WinRT pelas linguagens utilizadas para desenvolver os aplicativos (C++, C#, VB e JS), e prover uma experiência de desenvolvimento familiar ao desenvolvedor, que a foi criado um componente chamado Language Projection. Assim, o desenvolvedor fica livre de ter que lidar com particularidades de APIs baseadas em COM e trabalha com os objetos da mesma maneira que ele já está acostumado na sua linguagem de preferência.

Outro componente muito importante do WinRT, é o Runtime Broker. Certas APIs do WinRT expoem dados privados do usuário, como arquivos nas pastas pessoais, dados da webcam, dados de geolocation, entre outros. Uma aplicação construída sobre o WInRT não tem acesso direto a esses dados. Quando uma aplicação chama um método para ter acesso a esses dados, a execução da aplicação é desviada para um processo chamado RuntimeBroker.exe que irá verificar se sua aplicação tem privilégios para acessar esses dados, e irá também mostrar um prompt para o usuário perguntando se ele permite que a aplicação acesse estes dados. Se o usuário permitir, então sua aplicação irá poder acessar os dados requisitados. Além disso, para acessar esses recursos, a aplicação terá que definir em um arquivo de manifesto, quais recursos ela irá acessar.

Por último, se uma aplicação é criada em C#/VB ou Javascript, essa aplicação rodará sobre um ambiente de execução, como tradicionalmente aplicações construídas com essas linguagens são executadas. No caso do C#/VB, as aplicações irão rodar sobre a CLR (Common Language Runtime), e aplicações feitas com HTML e Javascript irão rodar sobre a engine do Internet Explorer 10.

Assincronia

Como o Windows 8 é um sistema operacional projetado para rodar em dispositivos móveis como tablets e slate PCs, a Microsoft se preocupou muito em ter certeza que a User Experience fosse a mais responsiva e rápida possível. Por isso, uma das decisões feitas no design da WinRT foi que qualquer chamada a método que pudesse demorar mais que 50 milisegundos, iria ser uma chamada assíncrona. Isso significa que todas as operações de IO, ações que dependem de alguma ação do usuário, e algumas outras, serão assíncronas.

Por hoje é só. Em breve, irei postar mais algumas coisas aqui mais detalhadamente sobre outros assuntos relacionados ao Windows Runtime. Até lá.

Breno Ferreira

Written by Breno Ferreira

23/10/2011 at 14:41

Posted in Windows 8

Tagged with ,

Dual-Boot do Windows 8 via VHD

with 16 comments

Olá pessoal,

A grande novidade da semana é o lançamento do Windows 8 Developer Preview. E como todo grande lançamento, muitas pessoas estão interessadas em instalar, testar, e ver como funciona a nova versão do sistema operacional. Por causa disso, vejo que muitas pessoas estão tendo dificuldades em instalar, ou tentando virtualizar (seja com VMWare ou Virtual Box) e obtendo desempenho abaixo do esperado. Uma maneira de evitar esses problemas de performance é fazer dual-boot através de um VHD (Virtual Hard Disk) com o Windows 8 instalado. É um processo bem simples, porém, para aqueles que não conhecem as ferramentas e um pouco de scripts Powershell, pode ficar meio propício a erros e problemas.

Aviso importante: essa versão do Windows 8 é apenas uma prévia bem rudimentar, e por causa disso, pode não ser muito estável. Não assumo qualquer responsabilidade por problemas ou danos no seu computador. Faça isso por sua conta e risco. Eu, até agora, não tive nenhum problema. Então, preste bastante atenção nos passos desmonstrados aqui e divirta-se!

Caso voce ainda não tenha baixado os arquivos de instalação do Windows 8, clique aqui.

Criando um VHD no Windows 7

Para fazer o dual-boot com um VHD, voce irá precisar ter o Windows 7 instalado na sua máquina. O primeiro passo para criar um VHD no Windows 7 é abrir o Prompt de Comando como Administrador.

image

Depois disso, execute os seguintes comandos:

  1. DISKPART
  2. CREATE VDISK FILE=”c:\Windows8Vhd\Windows8.vhd” MAXIMUM=50000 TYPE=FIXED
  3. SELECT VDISK FILE=”c:\Windows8Vhd\Windows8.vhd”
  4. ATTACH VDISK
  5. CREATE PARTITION PRIMARY
  6. ASSIGN LETTER=V
  7. FORMAT QUICK LABEL=Windows8
  8. EXIT

Repare duas coisas no segundo comando: Estamos definindo um tamanho de 50000 MBs para o VHD, ou seja, 50GB. Caso voce queira um tamanho menor ou maior, altere esse valor (mantendo o valor sempre em Megabytes). Estamos dizendo também que o VHD será do tipo fixo, ou seja, ele irá alocar os 50GB totais de uma vez só no HD. Isso irá ajudar um pouco na performance de acesso a disco. Caso voce queira um disco expansível, troque para “TYPE=expandable”. Mas lembre-se: mesmo que o disco seja expansível, todas as vezes que o boot ocorrer, esse VHD irá ser expandido para o tamanho total. Então, reserve um espaço em disco na sua máquina. Outra coisa importante: esse VHD deve, obrigatoriamente, estar em um disco interno. Ele não poderá estar em HDs externos, pen-drives, etc..

Instalando o Windows 8 no VHD

Agora que o VHD está criado, precisamos instalar o Windows 8 neste VHD. Para não precisarmos reiniciar o Windows, vamos instalar o Windows 8 via Powershell. Para fazer isso, baixe o arquivo Install-WindowsImage.ps1 aqui.

Agora, abra a console do PowerShell como Administrador e execute o seguinte comando:

.\Install-WindowsImage.ps1 -WIM E:\sources\install.wim -Apply -Index 1 -Destination V:\

Neste comando, passamos dois parâmetros importantes:

O caminho para o arquivo .wim de instalação do Windows 8. Para descobrir o caminho deste arquivo no seu computador, monte a ISO de instalação em um drive virtual, ou descompacte a ISO, e pegue o caminho raiz, e adicione o caminho \sources\install.wim. Por exemplo, se voce montou um drive virtual, e esse drive ficou com a letra E:, então o caminho vai ser E:\sources\install.wim. Caso voce tenha descompactado para a pasta C:\Windows8Instalacao, então o caminho vai ser C:\Windows8Instalacao\sources\install.wim.

Outra coisa é o parâmetro Destination. No caso, coloquei V:\, pois quando criamos o VHD (veja comandos acima), definimos a letra V (ASSIGN LETTER=V) para termos um drive virtual apontando para o VHD. Utilize a letra que voce tenha escolhido no comando acima.

Esse processo irá demorar em torno de 10 a 15 minutos, então vá tomar um café, água, coca-cola ou fazer alguma outra coisa.

<edição>

Você poderá obter uma mensagem de erro (Install-WindowsImange.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see “get-help about_signing” for more details.”)  ao executar esse comando alertando sobre assinatura de scripts. Caso isso aconteça, basta executar o comando:

Set-ExecutionPolicy RemoteSigned

Obrigado por me lembrar Vinicius Amaral por me lembrar!

</edição>

Depois de pronto, precisamos configurar o Dual-Boot.

Configurando Dual-Boot

Para configurar o dual-boot, basta abrir um prompt de comando como administrador novamente e executar o comando abaixo:

bcdboot V:\Windows

Novamente, utilize a letra apropriada para o seu drive virtual que aponta para o VHD.

Reinicie o computador e divirta-se!

Revertendo o processo.

Caso queira remover tudo, basta desconfigurar o dual-boot e depois deletar o VHD do seu computador.

Para desconfigurar o dual-boot, execute o comando abaixo em um prompt de comando aberto como administrador:

bcdedit /delete {identifier} /cleanup

Para descobrir o {identifier} basta executar o comando “bcdedit /v” e copiar o “identifer” da entrada cujo “device” aponta para o VHD no disco. Após isso, delete o VHD do seu computador e pronto.

Abraços

Breno

Written by Breno Ferreira

15/09/2011 at 17:27

Posted in Windows

Tagged with , ,

Novidades do Windows 8

leave a comment »

Olá pessoal,

Hoje, depois de muita espera e ansiedade (da minha parte pelo menos), teve início o BUILD. E o grande anúncio do evento foi o Windows 8. Steven Sinofsky revelou ao mundo os detalhes de como funcionará o Windows 8.

Quick Overview do Windows 8

Realmente, a nova versão do sistema operacional cumpre o que foi prometido. É a maior revolução no SO desde o Windows 95. Se em em 1995 o Windows 95 deslanchou a plataforma PC, o Windows 8, poderá causar uma revolução no mercado de computação móvel (Mobile Computing).

O Windows 8 suportará duas arquiteturas diferentes, x86/x64 e também ARM. Isso significa que o Windows 8 será uma nova maneira de pensar em PCs e Tablets, pois o novo sistema operacional irá suportar os dois tipos de dispositivos. Ele irá funcionar bem tanto em dispositivos Touch-Screen (Tablets), quanto com os bons e velhos mouse e teclado (PCs).

Sobre a interface gráfica, o Windows 8 contará com a nova experiência baseada no estilo Metro (ver figura abaixo), e também possuirá a interface clássica, praticamente idêntica a do Windows 7, onde o usuário poderá usar o Windows da maneira normal, com acesso ao Windows Explorer, Prompts de comando, etc.. Uma coisa que não ficou realmente clara (pelo menos não para mim) é se o Windows 8 rodando em arquitetura ARM (Tablets) irá suportar a interface clássica, e poder rodar os aplicativos "legados” como o Office. Quem sabe daqui a uns dias eu consiga essa resposta.

untitled

Desenvolvimento de Apps

A não ser que você tenha hibernado em uma caverna nos últimos meses, você deve estar sabendo das grandes discussões em torno de qual vai ser a linguagem/framework/API utilizada para criar aplicativos baseados na nova interface do Windows 8. Muitos chegaram a achar que o .NET Framework, e consequentemente linguagens como C# estavam mortas. Isso por que foi dito que as aplicações poderiam ser feitas com HTML5+Javascript. Mas, no evento hoje, Steven Sinofsky sanou, em parte, algumas dessas dúvidas.

windows-8-platform-tools

Como você pode ver na imagem acima mostra que, o Windows 8 irá suportar aplicações feitas com as tecnologias atuais, como .NET e Silverlight, C++ com a Win32 API e HTML+Javascript sobre o Internet Explorer.

Mas há um conceito novo (o qual preciso estudar, pois ainda não foi divulgado muita coisa), chamado Windows Runtime (WinRT). O WinRT é um conjunto de APIs novas que será utilizado para criar os aplicativos “imersivos” baseados no estilo Metro. O grande diferencial do WinRT é que ele irá possibilitar que as aplicações troquem informações entre si, e não sejam “silos” isolados sem nenhum tipo de interação com os vários outros Apps instalados. Na apresentação, foi demonstrado algo bem legal que foi uma aplicação de edição de fotos que conseguia enviar a foto sendo editada com um aplicativo do Facebook, e esse aplicativo, por vez, enviava a foto para um álbum no Facebook. Outro exemplo bacana é que com essas novas APIs do WinRT, o aplicativo irá poder expor dados internos para busca no Sistema Operacional. Por exemplo, um cliente do Twitter poderá expor os twits para serem buscados pelo SO.

O melhor dessas novas APIs do WinRT é que será possível desenvolver aplicativos com a sua linguagem de preferência. Seja você um desenvolvedor de código nativo (C++), .NET (C#/VB) ou Javascript, haverá a possibilidade de utilizar essas APIs novas com todas estas linguagens. A escolha é sua.

Conclusão

O Windows 8 já é uma grande revolução no mundo da tecnologia. Esta nova versão é uma reimaginação do sistema operacional, e é diferente de tudo já visto até hoje.

Caso você queira saber mais detalhes, recomendo assistir ao Keynote do Build que rolou hoje. Basta acessar http://www.buildwindows.com.

E também é muito importante lembrar que o primeiro preview do Windows 8 já está disponível para download. Basta acessar http://dev.windows.com. Lá existem três links:

Em breve, tentarei postar algumas coisas sobre o desenvolvimento de aplicações na nova plataforma e mais alguns detalhes técnicos sobre o Windows 8.

Até lá,

Breno Ferreira

Written by Breno Ferreira

13/09/2011 at 23:41

Posted in Evento

Tagged with , ,

Testando Rotas no ASP.NET MVC

leave a comment »

Olá pessoal, tudo bem?

Rotas e MVC

Uma conceito muito importante do ASP.NET MVC é o de rotas. Elas servem para mapear a URI dos requests HTTP para algum Controller e uma Action, e, opcionalmente, algum parâmetro.

A rota mais conhecida é aquela que já vem por padrão quando criamos um projeto novo:

  1. routes.MapRoute(
  2.     "Default", // Route name
  3.     "{controller}/{action}/{id}", // URL with parameters
  4.     new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
  5. );

Conforme adicionamos outras rotas ao nosso projeto, fica clara a necessidade de começarmos a testa-las, para termos certeza de que os Controllers e Actions e parâmetros estão sendo mapeados corretamente.

Definindo Rotas

Como exemplo, criei um Controller chamado ProductsController e adicionei duas rotas, uma para a Action Details, e outra para a Action ProductsInCategory, conforme os dois trechos de código abaixo:

ProductsController
  1. public class ProductsController : Controller
  2. {
  3.     // GET: /Produtos/
  4.     public ActionResult Index()
  5.  
  6.     // GET: /Produtos/123
  7.     public ActionResult Details(int id)
  8.  
  9.     //Get: /Produtos/Bebidas
  10.     public ActionResult ProductsInCategory(String category)
  11. }

 

Routes
  1. routes.MapRoute(
  2.     "ProductDetails",
  3.     "Products/{id}",
  4.     new { controller = "Products", action = "Details" },
  5.     new { id = "\\d+" }); //id must be a number
  6.  
  7. routes.MapRoute(
  8.     "ProductsInCategory",
  9.     "Products/{category}",
  10.     new { controller = "Products", action = "ProductsInCategory" },
  11.     new { category = "[a-zA-Z]+" }); //category must be chars only

 

Testando as Rotas

Para testarmos essas rotas, iremos adicionar algums UnitTests ao nosso projeto de testes. Repare que irei usar o Moq para criar um objeto Fake de um HttpContext. Irei também utilizar o FluentAssertions para fazer as Assertions nos tests.

Teste 1
  1. [TestMethod()]
  2. public void QuandoARotaEBarraProductsOControllerMapeadoDeveSerProductsEActionDeveSerIndex()
  3. {
  4.     //arrange
  5.     RouteCollection routes = new RouteCollection();
  6.  
  7.     var httpContextMock = new Mock<HttpContextBase>();
  8.     httpContextMock.Setup(httpContext => httpContext.Request.AppRelativeCurrentExecutionFilePath)
  9.         .Returns("~/Products");
  10.  
  11.     //act
  12.     MvcApplication.RegisterRoutes(routes);
  13.  
  14.     var routeData = routes.GetRouteData(httpContextMock.Object);
  15.  
  16.     //assert
  17.     routeData.Values["Controller"].Should().Be("Products");
  18.     routeData.Values["Action"].Should().Be("Index");
  19. }

 

Teste 2
  1. [TestMethod()]
  2. public void QuandoARotaEBarraProductsBarraIdOControllerMapeadoDeveSerProductsEActionDeveSerDetailsEIdDeveSer1()
  3. {
  4.     //arrange
  5.     RouteCollection routes = new RouteCollection();
  6.  
  7.     var httpContextMock = new Mock<HttpContextBase>();
  8.     httpContextMock.Setup(httpContext => httpContext.Request.AppRelativeCurrentExecutionFilePath)
  9.         .Returns("~/Products/1");
  10.  
  11.     //act
  12.     MvcApplication.RegisterRoutes(routes);
  13.  
  14.     var routeData = routes.GetRouteData(httpContextMock.Object);
  15.  
  16.     //assert
  17.     routeData.Values["Controller"].Should().Be("Products");
  18.     routeData.Values["Action"].Should().Be("Details");
  19.     routeData.Values["id"].Should().Be("1");
  20. }

 

Teste 3
  1. [TestMethod()]
  2. public void QuandoARotaEBarraProductsBarraCategoriaOControllerMapeadoDeveSerProductsEActionDeveSerProductsInCategoryECategoriaDeveSerBebidas()
  3. {
  4.     //arrange
  5.     RouteCollection routes = new RouteCollection();
  6.  
  7.     var httpContextMock = new Mock<HttpContextBase>();
  8.     httpContextMock.Setup(httpContext => httpContext.Request.AppRelativeCurrentExecutionFilePath)
  9.         .Returns("~/Products/Bebidas");
  10.  
  11.     //act
  12.     MvcApplication.RegisterRoutes(routes);
  13.  
  14.     var routeData = routes.GetRouteData(httpContextMock.Object);
  15.  
  16.     //assert
  17.     routeData.Values["Controller"].Should().Be("Products");
  18.     routeData.Values["Action"].Should().Be("ProductsInCategory");
  19.     routeData.Values["category"].Should().Be("Bebidas");
  20. }

Para testarmos as rotas, precisamos seguir 3 passos:

Arrange: Criamos um objeto novo do tipo RouteCollection. Esse objeto é passado como parâmetro para o método RegisterRoutes. Também devemos criar um objeto Fake do tipo HttpContextBase. Esse Fake irá simular um Request feito para uma URI pré-definida para o teste.

Act: Chamamos o método RegisterRoutes, passando o objeto do tipo RouteCollection, e em seguida, chamamos o método GetRouteData, passando o nosso Fake de HttpContext. Isso irá mapear as informações na URI definida no objeto Fake para Controllers, Actions e parâmetros.

Assert: Realizamos as nossas Assertions necessárias para validar que a rota foi mapeada corretamente. Os dados mapeados estão na propriedade Values do nosso objeto RouteData.

Conclusão

Não é muito difícil testar as rotas, basta conhecer um pouco de como o as rotas são definidas por debaixo dos panos, e saber um pouco sobre como criar objetos Fake. Repare que o conceito de Rotas não é exclusivo do ASP.NET MVC, podendo ser usado em outras tecnologias também, como o WCF WebAPI. Porém, a maneira de testar as rotas aqui são específicas do ASP.NET MVC.

Abraços

Breno Ferreira

Written by Breno Ferreira

11/09/2011 at 18:49

Posted in Dev

Tagged with , ,

Criando Scaffolder customizado para o WCF WebAPI

leave a comment »

Olá pessoal,

No último post falei um pouco sobre como customizar um template do MvcScaffolding. Hoje irei explicar como podemos criar um Scaffolder totalmente novo, com funcionalidades nada relacionadas ao ASP.NET MVC. Irei demonstrar rapidamente como voce pode criar um Scaffolder para gerar código para serviços REST utilizando o WCF WebAPI.

Para quem não conhece o WCF WebAPI, recomendo ler um post do Israel Aece que explica muito bem alguns conceitos básicos.

Criando um Custom Scaffolder

Para criarmos um Custom Scaffolder, basta executarmos o seguinte comando no Package Manager Console (depois de ter adicionado o pacote MvcScaffolding é claro):

Gerar Custom Scaffolder
  1. Scaffold CustomScaffolder RestfulService

Esse comando basicamente é um Scaffolder de Scaffolders, ou seja, ele gera um Scaffolder customizado.

No Solution Explorer, agora teremos a seguinte estrutura de arquivos:

image_thumb1

Os arquivos marcados em amarelo foram gerados pelo MvcScaffolding. Um arquivo é o template T4 que possui o template do arquivo de saída, e outro é um arquivo Powershell (.ps1) que irá ser chamado quando executarmos o comando para utilizarmos esse Scaffolder.

O código PowerShell ficou assim:

Default Custom Scaffolder
  1. [T4Scaffolding.Scaffolder(Description = "Enter a description of RestfulService here")][CmdletBinding()]
  2. param(        
  3.     [string]$Project,
  4.     [string]$CodeLanguage,
  5.     [string[]]$TemplateFolders,
  6.     [switch]$Force = $false
  7. )
  8.  
  9. $outputPath = "ExampleOutput"
  10. $namespace = (Get-Project $Project).Properties.Item("DefaultNamespace").Value
  11.  
  12. Add-ProjectItemViaTemplate $outputPath -Template RestfulServiceTemplate `
  13.     -Model @{ Namespace = $namespace; ExampleValue = "Hello, world!" } `
  14.     -SuccessMessage "Added RestfulService output at {0}" `
  15.     -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force

O Comando mais importante aqui é o Add-ProjectItemViaTemplate, que irá gerar o código baseado no Template T4. Esse comando recebe alguns parâmetros importantes:
$outputPath: caminho onde o arquivo será gerado
-Template: Nome do template T4
-Model: dados que são passados para o template T4 como parâmetros para gerar o código
 
Para gerar o serviço com o WebAPI, precisamos de alguns parâmetros para o comando PowerShell:
Service Scaffolder Parameters
  1. [T4Scaffolding.Scaffolder(Description = "Creates a Restful service using WCF WebApi")][CmdletBinding()]
  2. param(
  3.     [parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ModelType,
  4.     [string]$DbContextType,        
  5.     [string]$Project,
  6.     [string]$CodeLanguage,
  7.     [switch]$NoChildItems = $false,
  8.     [string[]]$TemplateFolders,
  9.     [switch]$Force = $false
  10. )

O primeiro parâmetro é o tipo do nosso Model, o segundo é o tipo de algum DbContext do EntityFramework já existente. Note que o primeiro parâmetro é obrigatório.

Não vou entrar em todos os detalhes do script Powershell, senão o post ficaria muito longo, mas o comando para gerarmos o código do serviço é o seguinte:

Gerando o c&amp;amp;#243;digo do servi&amp;amp;#231;o
  1. Add-ProjectItemViaTemplate $outputPath -Template RestfulServiceTemplate `
  2.     -Model @{
  3.         ServiceName = $serviceName;
  4.         RepositoryName = $repositoryName;
  5.         ModelType = [MarshalByRefObject]$foundModelType;
  6.         PrimaryKey = [string]$primaryKey;
  7.         DefaultNamespace = $defaultNamespace;
  8.         ModelTypeNamespace = $modelTypeNamespace;
  9.         ServiceNamespace = $serviceNamespace;
  10.          } `
  11.     -SuccessMessage "Added RestfulService output at {0}" `
  12.     -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force

Passamos como informações para o template T4 os seguintes dados:

  • ServiceName: Nome do tipo do nosso serviço (ex: PersonService)
  • RepositoryName: Nome do tipo do repositório (ex: PersonRepository)
  • ModelType: Tipo do modelo (ex: Person)
  • Primary Key: Propriedade que representa a chave primária no banco (ex: ID)
  • Default Namespace: Namespace padrão do projeto
  • ModelTypeNamespace: Namespace onde o tipo do nosso Model está localizado
  • ServiceNamespace: Namespace onde o serviço estará localizado (DefaultNamespace + “Services”)

Esse comando irá ler o template T4 e gerar um arquivo de saída com o serviço WCF WebAPI.

Uma parte desse template pode ser vista no código abaixo:

Code Snippet
  1.  [ServiceContract]
  2. public class <#= Model.ServiceName #> : I<#= Model.ServiceName #>
  3. {
  4.      private readonly I<#= repositoryName #> <#= repositoryVariableName #>;
  5.      
  6.      //if you are using Dependency Injection, you can delete the following constructor.
  7.      public <#= Model.ServiceName #> ()
  8.          : this(new <#= repositoryName #>())
  9.      {
  10.      }
  11.      
  12.      public <#= Model.ServiceName #> (I<#= repositoryName #> <#= repositoryVariableName #>)
  13.      {
  14.          this.<#= repositoryVariableName #> = <#= repositoryVariableName #>;
  15.      }
  16.      
  17.      [WebGet(UriTemplate="")]
  18.      public IQueryable< <#= modelName #> > GetAll()
  19.      {
  20.          return this.<#= repositoryVariableName #>.All;
  21.      }

Para quem quiser ver maiores detalhes do Scaffolder, criei um projeto no Codeplex e o código fonte complate está disponível lá. Basta entrar no site: http://servicescaffolder.codeplex.com/

Quem quiser utilizar esse Scaffolder, pode fazer uso do Nuget e referenciar o pacote:

Install-Package ServiceScaffolder

Scaffold RestfulService <Model>

Abraços

Breno

Written by Breno Ferreira

14/08/2011 at 15:16

Posted in Dev

Tagged with , , ,

Customizando os templates do MvcScaffolding

leave a comment »

Olá pessoal,

Muitos já devem ter usado, ou pelo menos ouvido falar do MvcScaffolding, uma ferramenta muito útil para nos ajudar a agilizar algumas tarefas digamos, tediosas, como criação de CRUDs. Se você não conhece, o Vinicius Quaiato tem dois posts no blog dele que explicam muito bem como você pode começar a utilizar a ferramenta.

Mas, as vezes, a funcionalidade padrão do MvcScaffolding não nos atende, e precisamos customizar o código gerado. Para fazermos isso, precisamos primeiramente, entender como que o código é gerado.

HowStuffWorks? – MvcScaffolding

O MvcScaffolding funciona utilizando duas tecnologias: T4 (Text Template Transformation Toolkit) Templates e Powershell. T4 Template, basicamente, é um código que gera código, e o alguns scripts Powershell utilizam esses templates para automatizar a geração do código que precisamos.

Por exemplo: o comando que executamos no Package Manager Console do Nuget, nada mais é do que um comando Powershell. Então, quando executamos o comando abaixo, estamos chamando um script Powershell que irá gerar o nosso código baseado nos arquivos T4 Templates apropriados.

Scaffold Controller Person –Repository
 

Se você for na pasta “%solution_folder%\packages\MvcScaffolding.1.0.0\tools”, você irá encontrar algumas pastas, e lá, voce irá encontrar alguns arquivos .ps1 e outros .t4. Esses arquivos que são utilizados quando executamos o Scaffolder que precisamos. No caso do comando acima, ele irá gerar o código baseado nos templates nas pastas: “Controller”, “RazorView” caso você esteja utilizando a Razor View Engine, ou “AspxView”, caso você esteja utilizando a WebForms View Engine.

Para podermos customizar o código gerado, há um comando do MvcScaffolding que automaticamente coloca o arquivo .t4 em uma pasta no nosso projeto. Assim, quando o Scaffolder for utilizar o template, ele irá utilizar este arquivo, e não o arquivo padrão localizado na pasta acima.

Customizando Templates T4

Para podermos customizar o arquivo .t4, basta executar o comando abaixo:

Scaffold CustomTemplate ScaffolderName TemplateName
 

Basta substituir o “ScaffolderName” e o “TemplateName” pelo template do Scaffolder que voce deseja customizar. Para isso, você pode ir na pasta onde estão localizados os templates (veja acima), ou então seguir a tabela abaixo:

ScaffolderName TemplateName
DbContext DbContext
DbContext DbContextEntityMember
Repository Repository
Action Action
Action ActionPost
Action ViewModel
ActionUnitTest TestClass
ActionUnitTest TestMethod
View Index, Create, _CreateOrEdit, Details, Edit, Delete, Empty,
Controller ControllerWithContext
Controller ControllerWithRepository

Após executar o comando, ele irá gerar um arquivo .t4 na seguinte pasta do nosso projeto:

“Code Templates\Scaffolders\<ScaffolderName>\<TemplateName>.t4”

Agora, basta editar este arquivo. A sintaxe é bem simples: os trechos que estiverem entre <#= #> irão ser executados para gerar um código de saída. Pense como uma ViewEngine do MVC, os blocos que estão com um @ (no caso do Razor) ou um <% %> (no caso do WebForms) são executados para gerar algum HTML. No template T4 é a mesma coisa, só a sintaxe que muda, e ao invés de gerar HTML, ele gera código C# (ou VB se dependendo da sua linguagem de preferência). Caso queira saber mais sobre a sintaxe dos T4 Templates, veja aqui na documentação da MSDN.

Como exemplo prático, criei um template customizado para a View Index, e substitui a <table> padrão por uma gerada utilizando o Helper WebGrid.

O código T4 ficou assim:

Antes:

   1: <table>

   2:     <tr>

   3:         <th></th>

   4: <#

   5: List<ModelProperty> properties = GetModelProperties(Model.ViewDataType, true);

   6: foreach (ModelProperty property in properties) {

   7:     if (!property.IsPrimaryKey && !property.IsForeignKey) {

   8: #>

   9:         <th>

  10:             <#= property.Name #>

  11:         </th>

  12: <#

  13:     }

  14: }

  15: #>

  16:     </tr>

  17:  

  18: @foreach (var item in Model) {

  19:     <tr>

  20: <# if (!String.IsNullOrEmpty(Model.PrimaryKeyName)) { #>

  21:         <td>

  22:             @Html.ActionLink("Edit", "Edit", new { id=item.<#= Model.PrimaryKeyName #> }) |

  23:             @Html.ActionLink("Details", "Details", new { id=item.<#= Model.PrimaryKeyName #> }) |

  24:             @Html.ActionLink("Delete", "Delete", new { id=item.<#= Model.PrimaryKeyName #> })

  25:         </td>

  26: <# } else { #>

  27:         <td>

  28:             @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |

  29:             @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |

  30:             @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })

  31:         </td>

  32: <# } #>

  33: <#  

  34: foreach (ModelProperty property in properties) {

  35:     if (!property.IsPrimaryKey && !property.IsForeignKey) {

  36: #>

  37:         <td>

  38:             @<#= property.ValueExpression.Replace("Model.", "item.") #>

  39:         </td>

  40: <#

  41:     }

  42: }

  43: #>

  44:     </tr>

  45: }

  46:  

  47: </table>

Depois

   1: @{

   2:     var grid = new WebGrid(Model);

   3: }

   4:  

   5: <# List<ModelProperty> properties = GetModelProperties(Model.ViewDataType, true); #>

   6:  

   7: @grid.GetHtml(

   8:     tableStyle: "webgrid",

   9:     rowStyle: "webgrid-row",

  10:     alternatingRowStyle: "webgrid-alternaterow",

  11:     headerStyle: "webgrid-header",

  12:     columns: grid.Columns(

  13:         <# foreach(var property in properties) { #>

  14:             grid.Column("<#= property.Name #>", "<#= property.Name #>", format:@<span>@item.<#= property.Name #></span>),

  15:         <# } #>

  16:     )

  17: )

Você pode ver que estamos alternando entre código C# utilizando a ViewEngine Razor (como na linha 1 até 3), e logo em seguida, trocamos para um bloco <# #> (linha 5). Aqui, estamos executando um código que irá me retornar todas as proprieades do tipo que estamos “Scaffoldando”. Logo em seguida, voltamos para código comum, e na linha 13, executamos um foreach para gerarmos as colunas do WebGrid, e definimos o nome da coluna e seu Header baseado no nome da propriedade, e o valor no item da tabela (<#= property.Name #>). Esse bloco irá ser substituído no arquivo gerado pelo nome da propriedade (por exemplo, Email).

Quando executamos o Scaffolder agora, o código gerado agora utiliza o WebGrid para exibir os dados (neste caso, há uma classe com as propriedades ID, Name e Email:

   1: @{

   2:     var grid = new WebGrid(Model);

   3: }

   4:  

   5:  

   6: @grid.GetHtml(

   7:     tableStyle: "webgrid",

   8:     rowStyle: "webgrid-row",

   9:     alternatingRowStyle: "webgrid-alternaterow",

  10:     headerStyle: "webgrid-header",

  11:     columns: grid.Columns(

  12:                     grid.Column("ID", "ID", format:@<span>@item.ID</span>),

  13:                     grid.Column("Name", "Name", format:@<span>@item.Name</span>),

  14:                     grid.Column("Email", "Email", format:@<span>@item.Email</span>)

  15:             )

  16: )

Repare que os lugares onde haviam os blocos <#= #> agora há o nome das propriedades do meu Model, que foi gerado pelo T4 template.

Voce pode baixar o código fonte da aplicação de exemplo no link abaixo:

https://skydrive.live.com/embedicon.aspx/Blog/CustomScaffoldingTemplates.zip?cid=1498c467c14dc20b&sc=documents

Agora voce poderá customizar o código gerado pelo Scaffolder, poderá criar Scaffolders para View Engines diferentes, como NHaml, Repositórios que não utilizam Entity Framework, etc..

Abraços

Breno

Written by Breno Ferreira

20/07/2011 at 20:11

Posted in Dev

Tagged with , , ,

%d bloggers like this: