Arquivo | NuGet RSS feed for this section

Configurando DI em aplicações ASP.NET MVC 3 com Ninject usando NuGet – Parte 2

20 dez

Pessoal,

Na primeira parte desse post, procurei demonstrar de forma mais simples possível uma situação onde seria interessantes aplicarmos o pattern Dependency Injection.

O foco deste post é demonstrar como é fácil fazer a configuração do Ninject em uma aplicação ASP.NET MVC 3, graças à Interface IDependencyResolver e à classe DependencyResolver, mas principalmente pelo NuGet, que são novidades lançadas junto ao ASP.NET MVC 3. Caso você também não conheça o NuGet, existem muitas informações a respeito por aí. Mais uma vez, não vou entrar em detalhes sobre o NuGet aqui, vou somente indicar algumas referências:

http://nuget.codeplex.com
http://haacked.com
http://www.hanselman.com

Somente relembrando do exemplo utilizado na primeira parte:

Interface:

public Interface ICategoryRepository
{
   IList GetAll();
}

Repositório “verdadeiro”:

public class CategoryRepository : ICategoryRepository
{
   public IList GetAll()
   {
      DataContext ctx = new DataContext();
      return ctx.Categories;
   }
}

Repositório “fake”:

public class CategoryRepositoryFake : ICategoryRepository
{
   public IList GetAll()
   {
      var category1 = new Category() { Id = 1, Name = "Cat 1" };
      var category2 = new Category() { Id = 2, Name = "Cat 2" };
      var category3 = new Category() { Id = 3, Name = "Cat 3" };

      return new List() { category1, category2, category3 };
   }
}

Controller:

 public class StoreController : Controller
{
   ICategoryRepository categoryRepository;

   public StoreController() : this(new CategoryRepository())
   {
   }

   public StoreController(ICategoryRepository repository)
   {
      this.categoryRepository = repository;
   }

   public ActionResult Index()
   {
      var categories = this.categoryRepository.GetAll();

      return View(categories);
   }
}

Como dito no outro post, isso funciona, mas temos alguns problemas. Imagine a situação de utilizarmos vários repositórios no nosso Controller… Teríamos que ter vários parâmetros no nosso construtor. Além disso, ainda temos um dependência “incômoda” do construtor sem parâmetros com a implementação do repositório “verdadeiro”.

O que fazer então? É aí que entram os containeres de IoC (Inversion of Control). Eles ficam “esperando” que “alguém peça” algum objeto de determinado tipo, e então se encarregam de instanciar e disponibilizar o objeto para nós, provendo o objeto com todas as suas dependências. Confuso? Vamos ver como fazer isso na prática, usando o container Ninject.

Primeiramente, vamos incluir o Ninject no nosso projeto, utilizando o NuGet. Simples, muito simples:

Abra o NuGet (View > Other Windows > Package Manager Console):

Tela inicial do NuGet

Liste os pacotes disponíveis no repositório oficial do NuGet:

Comando para listar os packages disponíveis

Lista de packages disponíveis, destaque para o Ninject e o Ninject.MVC3

Instale o pacote Ninject.MVC3, que é um wrapper da interface IDependencyResolver para o Ninject. O próprio Ninject será instalado junto, pois trata-se de uma dependência do Ninject.MVC3:

Comando para instalar o Ninject.MVC3, e a mensagem de sucesso, listando todas as dependências instaladas

As referências aos assemblies Ninject e Ninject.MVC3 adicionadas ao projeto

O próximo passo é “informar” ao Ninject quais classes ele deverá instanciar quando lhe for “solicitado” uma instância de determinada Interface. No nosso caso, queremos que o Ninject instancie o ICategoryRepository pra gente. Assim, primeiro vamos remover os construtores do Controller e decorar a propriedade “Repository” com o atributo Inject (linha 3):

public class StoreController : Controller
{
   [Inject]
   public ICategoryRepository Repository { get; set; }

   public ActionResult Index()
   {
      var categories = this.Repository.GetAll();

      return View(categories);
   }
}

O atributo Inject é o responsável por informar ao Ninject que o elemento decorado deve ser instanciado por ele (pode ser um campo, uma propriedade, o parâmetro de um método, entre outros. Veja mais sobre esse atributo aqui).

Em seguida configuramos o Ninject. Para isso, o Ninject.MVC3 cria no diretório raiz da aplicação uma classe chamada AppStart_NinjectMVC3

Classe adicionada ao projeto para configuração do Ninject

… que possui um método chamado RegisterServices. É exatamente esse método que utilizamos para fazer a tal “configuração” do Ninject:

public static void RegisterServices(IKernel kernel) {
   kernel.Bind(ICategoryRepository).To(CategoryRepository);
}

No exemplo acima, estamos informando ao Ninject para, sempre que ele “encontrar” algum elemento cujo tipo seja a Interface ICategoryRepository decorado com o atributo Inject, o container deverá instanciar um CategoryRepository e atribuí-lo a esse elemento.

Uma vez feito isso, no nosso exemplo quando o framework ASP.NET MVC instanciar o seu Controller, “magicamente” a propriedade “Repository” do mesmo estará devidamente instanciada, removendo totalmente a dependência do Controller das implementações de ICategoryRepository.

Forte abraço.

Configurando DI em aplicações ASP.NET MVC 3 com Ninject usando NuGet – Parte 1

8 dez

Pessoal,

Esse é a primeira parte (de dois) desse post. Nessa parte, tentarei falar de forma mais simples e direta possível sobre o pattern injeção de dependência (DI – Dependency Injection). Na segunda parte, mostrarei como configurar e utilizar um container de IoC (Inversion of Control) em uma aplicação ASP.NET MVC 3, utilizando o NuGet.

Para quem não conhece DI, não vou entrar em detalhes conceituais aqui, pois existe muita referência na internet, vou somente recomendar algumas:

Inversion of Control Containers and the Dependency Injection pattern (Martin Fowler)
Wikipedia
Design Patterns: Dependency Injection (MSDN)
Dependency Injection for Loose Coupling

Sendo assim, vamos ver exemplos práticos. Supondo que temos o seguinte código:

Repositório:

public class CategoryRepository
{
   public IList<Category> GetAll()
   {
      DataContext ctx = new DataContext();
      return ctx.Categories;
   }
}

Controller:

public class StoreController : Controller
{
   public ActionResult Index()
   {
      CategoryRepository categoryRepository = new CategoryRepository();
      IList<Category> categories = categoryRepository.GetAll();

      return View(categories);
   }
}

Veja que na linha 5 nós estamos instanciando um CategoryRepository, que acaba nos deixando com um forte acoplamento entre o Controller e o CategoryRepository.

Por isso que, se formos implementar os testes de unidade deste Controller, precisamos criar um Stub desse repositório (uma espécie de repositório “fake”), para que os nossos testes de unidade não precisem se conectar ao banco de dados efetivamente. Assim, quando estivermos executando o teste de unidade desse Controller, vamos precisar “trocar” o repositório CategoryRepository por um repositório que nos retorne um conjunto de dados “fake”, como por exemplo um CategoryRepositoryMock.

Para fazer essa “troca” de repositórios, em primeiro lugar criamos uma interface para o nosso repositório, de modo que tanto o repositório “verdadeiro” quanto o repositório “fake” implementem essa interface, e o nosso Controller terá uma referência para essa interface, e não para os repositórios em si:

Interface:

public Interface ICategoryRepository
{
   IList<Category> GetAll();
}

Repositório “verdadeiro”:

public class CategoryRepository : ICategoryRepository
{
   public IList<Category> GetAll()
   {
      DataContext ctx = new DataContext();
      return ctx.Categories;
   }
}

Repositório “fake”:

public class CategoryRepositoryFake : ICategoryRepository
{
   public IList GetAll()
   {
      Category category1 = new Category() { Id = 1, Name = "Cat 1" };
      Category category2 = new Category() { Id = 2, Name = "Cat 2" };
      Category category3 = new Category() { Id = 3, Name = "Cat 3" };

      return new List<Category>() { category1, category2, category3 };
   }
}

Controller:

public class StoreController : Controller
{
   ICategoryRepository categoryRepository;

   public StoreController() : this(new CategoryRepository())
   {
   }

   public StoreController(ICategoryRepository repository)
   {
      this.categoryRepository = repository;
   }

   public ActionResult Index()
   {
      IList<Category> categories = this.categoryRepository.GetAll();

      return View(categories);
   }
}

Veja que criamos no Controller dois construtores. O primeiro deles não recebe nenhum parâmetro, enquanto o outro recebe por parâmetro um objeto que implemente a interface ICategoryRepository, que executa o outro construtor passando o repositório “verdadeiro”. É esse construtor sem parâmetros que é chamado pelo framework ASP.NET MVC ao instanciar o Controller. Nos nossos testes de unidade, usaremos o segundo construtor para que possamos passar o repositório “fake” para o Controller:

Teste de unidade:

public class StoreControllerTests
{
   [TestMethod]   
   public void Index_Test()
   {
      // Arrange
      ICategoryRepository categoryRepository
                                            = new CategoryRepositoryFake();
      StoreController controller = new StoreController(categoryRepository);

      // Act
      ViewResult result = controller.Index() as ViewResult;

      // Assert
      Assert.IsNotNull(result);
      Assert.AreEqual("Index", result.ViewName);
   }
}

Veja bem: Nós já implementamos uma forma do pattern injeção de dependência (Constructor Injection). O nosso repositório não é mais instanciado dentro do Controller, mas é “injetado” através do construtor (linha 8). Daí vem a “tal” Inversion of Control, pois a responsabilidade de instanciar o repositório não é mais do Controller, mas de alguma outra classe. Em outras palavras, quem controla qual “versão” do repositório será utilizada pelo Controller não é mais o próprio Controller.

Isso funciona, mas temos alguns problemas. Imagine a situação de utilizarmos vários repositórios no nosso Controller… Teríamos que ter vários parâmetros no nosso construtor, além de termos uma dependência no mínimo “incômoda” do construtor sem parâmetros com a implementação do repositório “verdadeiro”.

O que fazer então? É aí que entram em cena os container‘s de IoC. Mas esse é exatamente o assunto principal desse post, e vai ser tratado na segunda parte, onde iremos configurar o container de IoC Ninject utilizando o NuGet usando esse mesmo exemplo.

Forte abraço.