Vinicius Quaiato

{tecnologia, conceitos, negócios, idéias, práticas, .NET, ruby, osx, ios e algo mais}

ASP.NET MVC 3 e o DependencyResolver


Na última semana vi várias dúvidas surgindo sobre IoC e DI no ASP.NET MVC 3.O que poucas pessoas sabem é que no ASP.NET MVC 3 há uma forma bastante simples de resolvermos dependências.

IDependencyResolver

O MVC 3 faz uso de um Service Locator do tipo IDependencyResolver.aspx). Esta interface possui 2 métodos:

  • GetService
  • GetServices e existem ainda mais 2 extension methods:
  • GetService<TService>
  • GetServices<TService>

Como vocês podem ver esta interface permite ao ASP.NET MVC abstrair a resolução de dependências.

A classe DependencyResolver

A classe DependencyResolver atua como um Registry para os provedores de serviços, que como podemos imaginar são objetos do tipo IDependencyResolver.O principal método utilizado(o mais simples e trivial) é o SetResolver(IDependencyResolver).aspx).

Injetando dependências nos controllers

Chega de falatório e vamos ver como podemos resolver dependências nos construtores de nossos controllers com um objeto do tipo IDependencyResolver.Vamos criar um controller bem simples:

 1 public class TemDependenciasController : Controller{
 2     public IDependencia1 Dependencia1 { get; set; }
 3     public IDependencia2 Dependencia2 { get; set; }
 4 
 5     public TemDependenciasController(IDependencia1 dependencia1, IDependencia2 dependencia2) {
 6         Dependencia1 = dependencia1;
 7         Dependencia2 = dependencia2;
 8     }
 9 
10     public ActionResult Index()    {
11         var b = this.Dependencia1.Metodo("foo");
12         this.Dependencia2.AlgumMetodo();
13 
14         return View();
15     }
16 }

Este controller possui duas dependências em seu construtor. Este código compila porém não conseguimos executar esta aplicação pois no momento em que o ASP.NET MVC tentar instanciar o controller não conseguirá resolver as dependências do seu construtor. Vamos então criar uma classe que implemente IDependencyResolver que atuará como nosso SL:

 1 public class MeuDependencyResolver : IDependencyResolver{
 2     private static ILookup<Type, object> lookup;
 3     public MeuDependencyResolver()    {
 4         lookup = AsDependencias().ToLookup(l => l.GetType().GetInterfaces().FirstOrDefault());
 5     }
 6 
 7     public IEnumerable<object> AsDependencias() {
 8         yield return new DummyDependencia1();
 9         yield return new DummyDependencia2();
10         yield return new DummyDependencia2_2();
11     }
12 
13     public object GetService(Type serviceType)    {
14         var ctorInfo = serviceType.GetConstructors().FirstOrDefault();
15         if(ctorInfo == null)
16             return null;
17         var paramInfo = ctorInfo.GetParameters();
18         var resolvedParams = ResolveParamTypes(paramInfo);
19         var resolvedParamDependencies = ResolveDependencies(resolvedParams).ToList();
20         return Activator.CreateInstance(serviceType, resolvedParamDependencies.ToArray());
21     }
22 
23     private IEnumerable<object> ResolveDependencies(IEnumerable<type> resolvedParams) {
24         return resolvedParams.Select(paramType => lookup[paramType].FirstOrDefault());
25     }
26 
27     private IEnumerable<type> ResolveParamTypes(ParameterInfo[] paramInfo) {
28         foreach(var param in paramInfo) {
29             yield return param.ParameterType;
30         }
31     }
32 
33     public IEnumerable<object> GetServices(Type serviceType) {
34         return new List<object>();
35     }
36 }

Este código não possui nada muito diferente do normal. O que fazemos é configurar tipos de forma hard-coded que serão utilizados pelo ASP.NET MVC para resolver as dependências. E usamos um pouco de reflection para resolver estas dependências.

Percebam que este nosso SL é bem pobrinho. Ele só sabe resolver dois tipos de dependências(e para este exemplo é tudo o que precisamos, pois o foco aqui é a feature e não a implementação). Usamos reflection pois o type do service que é informado no método GetService não é o type da dependência e sim o type do controller que está sendo criado. Por isso descobrimos quais os parâmetros do construtor do controller, obtemos os mesmos do nosso dicionário e criamos então uma instância do controller. O código acima serve para mostrar que é sempre melhor usar um container de IoC/DI de mercado. Não tente criar o seu. Mas fiz isso pois mostrarei em outro post como é simples fazer isso com Ninject

Configurando o DependencyResolver

Para configurarmos nossa classe para ser utilizada precisamos chamar o método SetResolver da classe DependencyResolver. O melhor para fazer isso neste caso é o Application_Start no Global.asax:

1 protected void Application_Start(){
2     AreaRegistration.RegisterAllAreas();
3     RegisterGlobalFilters(GlobalFilters.Filters);
4     RegisterRoutes(RouteTable.Routes);
5     DependencyResolver.SetResolver(new MeuDependencyResolver());
6 }

Na linha 5 chamamos o método SetResolver para informar ao ASP.NET MVC que o IDependecyResolver a ser utilizado é o que criamos(MeuDependencyResolver).Desta forma podemos executar nossa aplicação e ver que "magicamente" nossas dependências serão resolvidas e passadas para o construtor do nosso controller.

Resolvendo dependencias controller MVC3 com  IDependencyResolver

Dependencias controller MVC3 resolvidas com IDependencyResolver

Fontes

Confira os fontes completos aqui no Github: https://github.com/vquaiato/MVC3DependencyResolver

Resumo

Todo o trabalho que tivemos aqui foi para criar uma classe que sabe resolver dependências, pois o resto a infra do ASP.NET MVC 3 cuidou de tudo.No próximo post faremos exatamente a mesma coisa porém utilizando o Ninject ao invés de criar nosso próprio container.O ASP.NET MVC 3 possui boas melhorias com relação a injeção de dependências e resolução de dependências. Muitas coisas ficaram simples. No exemplo acima não precisamos mais criar uma classe que implemente IControllerFactory.Se você ainda está por fora do ASP.NET MVC 3 confira estes posts e não perca o MVC Summit em março!

Abraços,

Vinicius Quaiato.

Voltar

Fork me on GitHub