ASP.NET MVC Model Binding (parte 4 – custom binders)

Olá, galera

Este é o último post da série sobre ASP.NET MVC Model Binding, iniciada <aqui>.

Até o post anterior, estávamos falando do processo de binding padrão, tratado pelo default model binder. Neste post, veremos uma das  formas de customizar o processo de binding, implementando um custom model binder.

Vamos lá!

IMPLEMENTANDO UM CUSTOM BINDER

Como exemplo, vamos implementar um custom binder que saiba obter um objeto de determinado tipo a partir de um valor contido na sessão (Session).

Consideremos um objeto Carrinho responsável por representar um carrinho de compras de nosso domínio, com operações para adicionar/remover produtos, limpar o carrinho, etc.

————————————————————————-
Nota: 
Este é um exemplo meramente didático, não tendo a pretensão de representar uma solução técnica “ideal” de um carrinho de compras. Além disso, deve-se tomar cuidado quanto ao uso de sessão nas aplicações, pois podem acarretar em uma série de desvantagens, entre elas, o alto consumo de memória do servidor.
————————————————————————-

Queremos armazenar o carrinho na sessão e evitar código desse tipo:


public RedirectToRouteResult AdicionarAoCarrinho(int idProduto, int quantidade)
{
    // obter carrinho da sessão (ou criar um novo se não existir)
    Carrinho carrinho = Session["Carrinho"] as Carrinho;
    if (carrinho == null)
    {
        carrinho = new Carrinho();
        Session["Carrinho"] = carrinho;
    }

    // obtem produto do repositorio e adiciona item ao carrinho
    var produto = _repositorioDeProdutos.ObterPor(idProduto);
    carrinho.AdicionarItem(produto, quantidade);

    return RedirectToAction("Index");
}

Com a implementação do binder, poderemos deixar a action acima da forma que costumamos trabalhar, ou seja, recebendo os dados via parâmetro. Veja como fica:

public RedirectToRouteResult AdicionarAoCarrinho(Carrinho carrinho, int idProduto, int quantidade)
{
    // carrinho agora vem como um parametro da action

    // obtem produto do repositorio e adiciona item ao carrinho
    var produto = _repositorioDeProdutos.ObterPor(idProduto);
    carrinho.AdicionarItem(produto, quantidade);

    return RedirectToAction("Index");
}

Observem que da forma mostrada no último código, não precisamos nos preocupar com a Session, o que deixa a action mais limpa, ficamos menos sujeitos a erro – uma vez que poderíamos digitar incorretamente o nome da varíavel de sessão, e ainda deixamos nossa action mais fácil de ser testada.

Vamos então à implementação do binder:


public class CarrinhoModelBinder : IModelBinder
{
    private const string SessionKey = "Carrinho";

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // obtém carrinho da sessão (se não existir cria novo carrinho e o guarda na sessão)
        Carrinho carrinho = controllerContext.HttpContext.Session[SessionKey] as Carrinho;
        if (carrinho == null)
        {
            carrinho = new Carrinho();
            controllerContext.HttpContext.Session[SessionKey] = carrinho;
        }
        return carrinho;
    }
}

Notem acima que tudo que tivemos que fazer foi implementar a interface IModelBinder, que consiste em implementar um único método: BindModel (a lógica é semelhante à utilizada no primeiro código mostrado da action).

Para finalizar, devemos informar ao MVC Framework da existência deste binder. Fazemos isso registrando o binder no global.asax:


protected void Application_Start()
{
   // outras configuracoes existentes

   // registra novo binder
   ModelBinders.Binders.Add(typeof(Carrinho), new CarrinhoModelBinder());
}

Agora sempre que uma action receber um tipo “Carrinho”, o action invoker saberá utilizar o CarrinhoModelBinder para criar um objeto Carrinho a partir dos dados da sessão. Simples!

DECIMAL MODEL BINDER

Um custom model binder bastante útil é o binder para tipos “decimal”, publicado há pouco mais de 1 ano pelo Phil Haack. O default model binder não funciona corretamente quando postamos decimais onde o ponto (“.”) é o separador de milhares – que é o padrão brasileiro.

Não sei informar se este problema está corrigido no ASP.NET MVC 4 (ou se estará em sua versão final), mas esse binder é essencial para quem ainda está no MVC 3.

CONCLUSÃO

Vimos neste último post da série sobre Model Binding, como criar um custom model binder. Ainda há outras formas de customizar o mecanismo de model binding, como por exemplo, criando nossos próprios Value Providers.

Fechamos a série mostrando o quanto o ASP.NET MVC é flexível, disponibilizando uma série de pontos de extensão.

[]s e até a próxima!
_________________________________________________________________

Este artigo faz parte da série:
1) ASP.NET MVC Model Binding (parte 1 – DefaultModelBinder)
2) ASP.NET MVC Model Binding (parte 2 – outras formas de binding)
3) ASP.NET MVC Model Binding (parte 3 – binding manual)
4) ASP.NET MVC Model Binding (parte 4 – custom binders)

9 comentários em “ASP.NET MVC Model Binding (parte 4 – custom binders)

    1. Difícil responder sem um contexto mas não é uma boa ideia ficar guardando dados na Session, usando-a como se fosse um grande saco de variáveis globais. Além do uso de memória, fica difícil de entender o código e muito fácil de se introduzir bugs.

      Em geral, você deve enviar as informações que precisa junto com sua requisição e obtê-las do outro lado. Em algumas situações, pode ser uma boa ideia persistir e recuperar direto de um banco de dados, usando quem sabe caching, se for o caso.

      Espero ter ajudado.
      []s

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s