A cultura de desenvolvimento com ASP.NET MVC
É notável a rápida aceitação e popularização do ASP.NET MVC entre a comunidade .NET, não só pelas suas óbvias vantagens comparadas com o Web Forms (modelo mais simples, controle total sobre a marcação e separação clara de responsabilidades), como também pela grande sensação passada pelo framework de que estamos “desenvolvendo da forma correta”.
Desenvolver de forma correta é algo muito relativo, por exemplo, considerando que um desenvolvedor irá utilizar o ASP.NET MVC como plataforma para criar uma aplicação, poderíamos dizer que ele está fazendo tudo certo caso siga as regras abaixo:
- Inserir lógica de negócio/domínio e camada de acesso a dados no Modelo;
- Criar a interface do usuário, não vinculando regras de negócio, através das Visões;
- Efetuar o tratamento de requisições buscando objetos no modelo e selecionando Visões através do Controle.
As regras acima são suficientes para assegurar que o padrão MVC está sendo seguido, mas será que elas respondem a pergunta: “Estou desenvolvendo da forma correta?”.
Para responder a pergunta acima, é necessário entender se existem algumas necessidades para a aplicação, entre elas podemos citar:
- Facilidade de manutenção;
- Extensível de forma simples (sem traumas).
Os dois requisitos citados acima podem mudar drasticamente o modo como a aplicação deverá ser desenvolvida, o padrão MVC não garante que o objetivo será cumprido. Para entender melhor vejamos o exemplo abaixo:
Listagem 1: Exmplo de modelo e controlador
namespace Models
{
public class Pessoa
{
public int PessoaID { get; set; }
public string Nome { get; set; }
public DateTime DataNascimento { get; set; }
}
}
namespace Controllers
{
public class PessoaController : Controller
{
readonly Pessoa _pessoa;
public PessoaController(Pessoa pessoa)
{
_pessoa = pessoa;
}
public ActionResult Index()
{
return View();
}
}
}
Existe um grande problema no código mostrado, note que o construtor do controle está instanciando um objeto do tipo Pessoa. Isso cria um forte acoplamento entre as duas classes, dificultando o processo de manutenção, isso porque caso a classe Pessoa precise ser substituída, teremos que alterar todos os controles que fazem referência a essa classe.
Para resolver esse problema temos um padrão de desenvolvimento chamado de Inversão de Controle, que basicamente permite retirarmos a responsabilidade dos controles de instanciar objetos do modelo, centralizando essa ação em um “container” que irá injetar as dependências quando necessário.
Resumindo, a responsabilidade de instanciar objetos é retirada do desenvolvedor e delegada para um componente.
É aí que entra o Ninject, um container para injeção de dependência que pode ser configurado facilmente no ASP.NET MVC.
Ninject
Vamos começar a fazer algumas alterações no código para poder utilizar o Ninject. A primeira coisa que vamos fazer é criar uma Interface IPessoa que a classe Pessoa irá implementar como mostrado abaixo:
Listagem 2: Interface IPessoa implementada pela classe Pessoa
public interface IPessoa
{
int PessoaID { get; set; }
string Nome { get; set; }
DateTime DataNascimento { get; set; }
}
public class Pessoa : IPessoa
{
public int PessoaID { get; set; }
public string Nome { get; set; }
public DateTime DataNascimento { get; set; }
}
Agora podemos fazer o desacoplamento da classe Pessoa no nosso controle fazendo o mesmo trabalhar com a interface:
Listagem 3: Desacomplando o controlador da classe Pessoa
public class PessoaController : Controller
{
readonly IPessoa _pessoa;
public PessoaController(IPessoa pessoa)
{
_pessoa = pessoa;
}
public ActionResult Index()
{
return View();
}
}
O próximo passo é configurar o Ninject para injetar as dependências em nosso controle.
O Ninject contém um pacote NuGet que já adiciona todas as referências necessárias em nosso projeto, para utilizá-lo execute o comando abaixo no Package Manager Console:
Listagem 4: Baixando o Ninject para o projeto
install-package Ninject
O ASPNET MVC 4 trabalha com uma forma muito simples para adicionarmos injeção de dependência na aplicação, basta criarmos uma classe que implementa a interface System.Web.Mvc.IdependencyResolver e registra-la, nenhuma configuração adicional é necessária.
Crie um arquivo chamado IocConfig na pasta App_Start e adicione as referências abaixo:
Listagem 5: Referências necessárias para uso do Ninject
using Ninject;
using Ninject.Syntax;
Crie a classe abaixo no mesmo arquivo sem apagar a classe IocConfig:
Listagem 6: Classe NinjectDependencyResolver
public class NinjectDependencyResolver : IDependencyResolver
{
private readonly IResolutionRoot _resolutionRoot;
public NinjectDependencyResolver(IResolutionRoot kernel)
{
_resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
return _resolutionRoot.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _resolutionRoot.GetAll(serviceType);
}
}
Agora temos que configurar os tipos que o Ninject irá resolver e registrá-lo no ASP.NET, para isso vamos criar o seguinte método na classe IocConfig:
Listagem 7: Método ConfigurarDependencias
public static void ConfigurarDependencias()
{
//Cria o Container
IKernel kernel = new StandardKernel();
//Instrução para mapear a interface IPessoa para a classe concreta Pessoa
kernel.Bind<IPessoa>().To<Pessoa>();
//Registra o container no ASP.NET
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
}
O método acima cria e registra o Ninject, é importante notar a linha onde é configurado o mapeamento da interface para a classe que será utilizada para injetar a dependência, nesse local pode ser configurado todos os tipos necessários que o Ninject terá que manipular no projeto.
Para terminar basta chamar o método no arquivo Global.asax da seguinte forma:
Listagem 8: Método Aplication_Start
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
IocConfig.ConfigurarDependencias();
}
Agora basta executar a aplicação, coloque um breakpoint no construtor da classe PessoaController e você verá que o Ninject inseriu um objeto do tipo Pessoa no parâmetro do construtor como mostrado abaixo:
Figura 1: Depurando a aplicação
Podemos observar que com o mínimo de esforço tornamos nossa aplicação facilmente alterável, centralizando a construção de instâncias, permitindo futuras modificações sem a necessidade de reestruturações complexas.