Tag Archives: asp.net mvc

Controlando a crição dos Controllers no ASP.NET MVC

Uma das características que eu mais gosto do ASP.NET MVC é sua extensibilidade, existem diversos pontos que são possíveis de customizar e configurar para que o framework se comporte da maneira que você quer.

A criação dos controllers é um destes pontos e possivelmente um dos menos usados pelos desenvolvedores por ai. Talvez por desconhecimento ou não saber como tirar proveito disso, não sei. Foi isso que me levou a criar este post onde vou apresentar o que podemos fazer ao tomar controle sobre a construção dos controllers.

Antes de prosseguirmos, sabiam que os Controllers utilizam o princípio de Hollywood? Este princípio se resume na seguinte frase “não nos chame, nós chamaremos você” (don’t call us, we’ll call you). Isso significa que, ao criar um novo Controller, você não precisa dizer ao ASP.NET MVC quando chamar ou então como criá-lo, você apenas o define e deixa que o próprio framework cuide do ciclo de vida  dele.

Voltando ao assunto principal, quando realizamos um HTTP Request o framework utiliza as definições de rotas para descobrir qual o controller que deve ser criado e então repassa a responsabilidade de criação para a classe DefaultControllerFactory.

Os controllers são objetos como qualquer outro e é da natureza da Programação Orientada a Objetos que haja a “troca de mensagens” entre os objetos e consequentemente uma dependência de um objeto para outro. É comum um controller depender de uma classe de serviço ou de acesso à dados, a maneira mais comum de gerenciarmos isso é através dos contrutores. Um controller com dependência ficaria assim:

private IMailService mailService;

public HomeController(IMailService mailService)
{
    this.mailService = mailService;
}

Se executarmos assim como está, recebemos uma exception com a mensagem “No parameterless constructor defined for this object.“. Se olharmos um pouco o código do DefaultControllerFactory é possível perceber que é utilizado o Activator.CreateInstance sem a passagem de parâmetros, isso significa que ele irá procurar um construtor sem parâmetros. No nosso caso onde não podemos aceitar a construção do HomeController sem o serviço de e-mail, a solução é tomar controle sobre a criação dos controllers e customizá-los como bem quisermos. Uma implementação bem simples ficaria assim.

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == typeof(HomeController))
            return new HomeController(new LocalMailService());
        return base.GetControllerInstance(requestContext, controllerType);
    }
}

Apesar de eu ter herdado de DefaultControllerFactory, seria perfeitamente aceitável implementar diretamente a IControllerFactory, a diferença é que assim eu economizo diversas linhas de código já que não preciso escrever todos os métodos exigidos pela interface. Para registrar esta customização, é necessário adicionar a seguinte instrução no Application_Start do Global.asax.

ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());

Done! Agora nosso controller está sendo criado com uma instância de LocalMailService. Isso é chamado de injeção de dependência via construtor. Como eu disse acima, essa é uma implementação simples, se olharmos com mais cuidado a classe CustomControllerFactory é possível verificar que esta solução não escala. Se para cada controller for necessário adicionar uma nova condição, nossa classe vai crescer em tamanho e complexidade.

A solução mais utilizada e até onde eu sei a mais recomendada, é utilizar um framework de injeção de dependência. Neste exemplo vou utilizar o StructureMap, o download está disponível no site oficial.

Ao utilizar o StructureMap, a solução final fica assim.

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterDependencies();

    ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

private void RegisterDependencies()
{
    ObjectFactory.Configure(x =>
        x.For<IMailService>().Use<LocalMailService>()
    );
}

E nossa ControllerFactory ficou bem mais limpa.

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    return ObjectFactory.GetInstance(controllerType) as IController;
}

Quando tivermos novos controllers e novas dependências basta registar no StructureMap e o resto já estará sendo feito sozinho.

UPDATE: Para quem está usando o MVC3 e está buscando injeção de dependência nos controllers, uma outra opção ainda mais elegante é usar o DependencyResolver,  o Vínicius Quaiato fez um post sobre isso.

Espero que seja útil :)

Compilando suas views no ASP.NET MVC

Uma das vantagens em se utilizar uma linguagem compilada é que alguns erros de digitação são pegos no processo de compilação e não somente quando você executa a aplicação. No ASP.NET MVC as views não são compiladas ao fazer o build no visual studio e, portanto, estão sujeitas a ter tais erros. O problema mais comum é quando você faz um Refactor -> Rename em uma de suas classes, o visual studio NÃO vai renomear essas classes em suas views, e você só vai notar o erro quando estiver nagendo na página. Para que o visual studio compile suas views, e consequentemente, resolva o problema citado, siga os passos:

  1. Com o notepad, abra o arquivo *.csproj ou *.vbproj da sua aplicação MVC.
  2. Procure a tag <PropertyGroup> e antes de fechá-la (</PropertyGroup>), adicione o seguinte conteúdo: <MvcBuildViews>true</MvcBuildViews>.

Existem várias tags PropertyGroup e cada uma deles corresponde a uma configuração de build dentro do visual studio (Debug/Release), utilize aquela que você achar melhor ou então insira dentro de todas elas. Feito isso, os problemas encontrados serão listados na aba Error List.

So far, so good

Compiling!

Só tem um porém, você vai sentir um enorme delay no build, o tempo de compilação quase triplica.

Quando se está trabalhando em um projeto pequeno não fará tanta diferença, agora quando seu projeto for muito grande, você pode optar por esperar enquanto toma um café, brincar de “lutinha” com seu amigo de trabalho ou pedir para seu chefe um servidor de integração contínua.

No caso de optar pela última opção (a melhor delas, mesmo gostando da segunda), você pode deixar para compilar suas views uma vez por dia caso demore muito.

É importante lembrar que uma aplicação compilada não significa que está funcionando, a melhor maneira de saber é rodando testes automatizados, logo TATFT.

Sharing Buttons by Linksku