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.

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

  1. Edmilson Lani 06/01/2011 às 11:54 #

    André, muito bem escrito os posts. Parabéns!

    Fiquei com uma dúvida, no primeiro post tinhamos o RepositorioFake e passavamos ele pro construtor quando fossemos testar a aplicação. Com o Ninject, como seria isso já que toda referencia a interface vai ser instanciado o verdadeiro?

    []s

    • andreborges 06/01/2011 às 12:04 #

      Caro Edmilson, obrigado.

      Nos testes unitários, nós informaremos ao container que, quando for solicitado um ICategoryRepository, que seja instanciado o “Fake”. Ao invés de termos:

      kernel.Bind(ICategoryRepository).To(CategoryRepository);

      Nos testes teremos:

      kernel.Bind(ICategoryRepository).To(CategoryRepositoryFake);

      Ou seja, nós temos que configurar o container (no caso o Ninject), de forma diferente em cada ambiente (Aplicação x Testes unitários).

      • Edmilson Lani 06/01/2011 às 12:11 #

        Certo, teremos o mesmo AppStart_NinjectMVC3 nas duas aplicações: na web e nos testes. Perfeito!

        Obrigado!

Deixe um comentário