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.