Injeção de dependências no ASP.NET MVC

Para melhorar a manutenibilidade de um sistema, devemos programar voltados para interface. Assim, nos preocupamos apenas com o contrato definido pela interface, não com sua implementação, diminuindo o acoplamento do código. Uma das formas de facilitar essa prática é utilizar a injeção de dependências. Dessa forma, nossas classes não precisam conhecer a lógica de instanciação, ciclo devida e nem implementação dos componente do sistema, podendo mudar facilmente a implementação de suas dependências.

Quando escrevemos um controller no ASP.NET MVC, é interessante que a classe não precise se preocupar com a instanciação de suas dependências. Queremos, por exemplo, receber todas essas dependências no construtor. Vamos usar como exemplo um UserController que precisa de um IUserDao:

class UserController {
  private IUserDao userDao;
  public UserController(IUserDao userDao) {
      this.userDao = userDao;
  }
}

Porém, o ASP.NET MVC não consegue instanciar um controller escrito dessa forma. Ele não sabe como instanciar um IUserDao! Por esse motivo, apenas controllers que tenham um construtor sem argumentos podem ser instanciados. Para mudar esse comportamento, precisamos ensinar o ASP.NET MVC a instanciar controladores que recebem parâmetros no construtor.

Unity Container

Para que possamos criar instâncias de classes que recebam as dependências pelo construtor, precisamos primeiro guardar as instâncias dessas dependências em algum lugar. Precisamos, por exemplo, ter um IUserDao em mãos para passar para o construtor do UserController.

Existem diversos frameworks que nos ajudam nessa tarefa. Eles são conhecidos como frameworks de injeção de dependência. Nesse post utilizaremos o framework de injeção de dependências da Microsoft, o Unity.

O Unity nos oferece uma classe chamada UnityContainer, classe na qual podemos registrar tipos que serão injetados. Para registrar uma dependência, utilizamos o método RegisterType do container, passando a interface, a implementação que deve ser injetada e um nome para ela.

No exemplo, queremos registrar o UserController como uma implementação de IController e o UserDao como uma implementação de IUserDao. Além disso, para seguir a convenção de nomes do ASP.NET MVC, registraremos o UserController com o nome User.

Container.RegisterType(typeof(IUserDao), typeof(UserDao));
Container.RegisterType(typeof(IController), typeof(UserController), “User”);

Agora que temos registrado no Unity tanto o Dao quanto o Controller, podemos pedir instâncias dessas classes para ele! Ao pedirmos uma instância de UserController, o Unity se encarregará de enviar o UserDao para o construtor dele:

IController controller = Container.Resolve<IController>("User");

ControllerFactory com Injeção de Dependências

Agora precisamos ensinar o ASP.NET MVC a utilizar o Unity para instanciar os controllers. Para fazer isso, precisamos criar uma classe que implementa IControllerFactory. Implementações dessa interface devem fornecer três métodos: CreateController, ReleaseController e GetControllerSessionBehavior.

CreateController recebe uma String com o nome do controller que precisa ser criado e deve devolver uma instância de IController. ReleaseController recebe um IController e deve liberar os recursos utilizados. A String com o nome do controller segue a convenção do ASP.NET MVC, ou seja, quando recebemos a StringUser“, devemos criar uma instância de UserController.

class UnityFactory : IControllerFactory {
  public UnityFactory() {
    this.Container = new UnityContainer();
    this.Container.RegisterType(typeof(IUserDao), typeof(UserDao));
    this.Container.RegisterType(typeof(IController), typeof(UserController), “User”);
  }

  public IController CreateController (RequestContext context, String controllerName) {
    IController controller = this.Container.Resolve<IController>(controllerName);
    return controller;
  }
  // ...
}

Precisamos também ensinar o ASP.NET MVC a destruir nosso controller e liberar quaisquer recursos que alocamos. Para isso, vamos implementar o método ReleaseController:

public void ReleaseController(IController controller) {
  this.Container.Teardown(controller);
}

O GetControllerSessionBehavior deve retornar o valor de um enum que define o comportamento da session do ASP.NET MVC. Como queremos que a Session tenha o mesmo comportamento da Session padrão, esse método vai retornar System.Web.SessionState.SessionStateBehavior.Default.

public System.Web.SessionState.SessionStateBehavior 
      GetControllerSessionBehavior(RequestContext ctx, string controllerName) {
  return System.Web.SessionState.SessionStateBehavior.Default;
}

Estamos quase lá. Resta apenas substituir o mecanismo padrão do ASP.NET MVC pelo que acabamos de criar. Essa substituição é feita no arquivo Global.asax do projeto, através do método SetControllerFactory. Para manter a organização do arquivo Global.asax, o registro do ControllerFactory ficará junto com as configurações globais dentro do método Application_Start():

protected void Application_Start() {
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
  UnityFactory factory = new UnityFactory();
  ControllerBuilder.Current.SetControllerFactory(factory);
}

Agora que a aplicação está utilizando a fábrica customizada, o ASP.NET MVC é capaz de instanciar o UserController, passando o UserDao para ele! Essa factory ainda pode ser melhorada. Poderíamos, por exemplo, registrar automaticamente no Unity todos os controllers e componentes que serão injetados. Você pode ver essa implementação, um pouco mais complicada, aqui. Além disso, você pode ver a implementação da factory utilizando o container Windsor no projeto agile-tickets-csharp, projeto utilizado no curso PM-87 da Caelum.

Pronto! Nossos controllers agora podem declarar suas dependências no construtor, deixando o código mais claro e mais fácil de ser testado. Aprenda a desenvolver com ASP.NET MVC em nosso curso online.

11 Comentários

  1. Flavio Sakamura 03/05/2012 at 12:15 #

    Legal ver a Caelum investindo em .NET também! Parabéns Victor.

  2. Murilo 03/05/2012 at 12:45 #

    O Ninject me parece muito mais fácil. Ou estou enganado?

  3. Deivid 03/05/2012 at 13:31 #

    Legal Vitor!

    Bem no ponto onde estávamos discutindo sobre injeção de dependências.
    Tomara que a Microsoft olhe para outras maneiras de fazer essas injeção alem dessa forma programática, usando anotações ou fazendo igual ao VRaptor que “advinha” as dependências do Controller desde que elas estejam anotadas com Resource.

    Abs

  4. Victor Kendy Harada 03/05/2012 at 14:29 #

    Valeu Deivid.

    Seria realmente interessante se Microsoft fizesse essa configuração automática da injeção de dependências.

  5. Victor Kendy Harada 03/05/2012 at 14:33 #

    Obrigado Flavio.

  6. Victor Kendy Harada 03/05/2012 at 14:38 #

    Olá Murilo. Eu conheço apenas um pouco sobre o Ninject, porém eu não acho que ele seja muito mais simples do que o Unity ou o Windsor.

  7. Lucas 04/05/2012 at 13:23 #

    Eu acho o Ninject mais simples, sem falar que existe um plugin chamado NInject.MVC3 que já faz integração com o ASP NET MVC 3

  8. Lucas Oleiro 22/05/2012 at 12:03 #

    Já trabalhei com Unity, Ninject e StructureMap e não vi grandes diferenças entre eles. Seja na declaração (bootstrap) dos objetos ou no uso. Acho que uma análise interessante é a de performance. http://weblogs.asp.net/gunnarpeipman/archive/2010/09/21/unity-castle-windsor-structuremap-ninject-who-has-best-performance.aspx

  9. Rolemberg 22/05/2012 at 18:34 #

    Prezado Victor,

    Nesta injeção de dependência que vc explicou no artigo é possível fazer o controle de transação do tipo SPRING.NET.

    aproveitando, poderia me explicar quais as diferenças entre o ninject e o Spring.NET?

    abs!

  10. Rolemberg 22/05/2012 at 18:36 #

    complementando o comentário:

    explicar as diferenças entre o ninject, o Spring.NET, o UNIT e o Windsor?

    abs,

    Rolemberg

  11. Victor Kendy Harada 23/05/2012 at 13:46 #

    Olá Rolemberg.

    Assim como o Lucas Oleiro comentou, os containers de injeção de dependências são muito parecidos. A principal diferença é na api de registro das classes e como as dependências são injetadas.

    O Ninject, por exemplo, consegue injetar tanto pelo construtor da classe quanto por suas propriedades.

    Quanto a sua primeira pergunta, ao registrarmos uma classe no Unity, podemos também passar um objeto que irá controlar o tempo de vida das instâncias do tipo registrados. Você pode encontrar a documentação aqui

Deixe uma resposta