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.

Deixe um comentário