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.
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.
_________________________________________________________________
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)
Blz, vlw Robson uma série muito boa.
CurtirCurtir
Obrigado, cara
[]s
CurtirCurtir
Mais uma vez muito bom.
CurtirCurtir
Excelente artigo, bem didático!
CurtirCurtir
Obrigado, cara.
[]s
CurtirCurtir
Muito bom, e qual abordagem você utilizaria ao invés da Session?
CurtirCurtir
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
CurtirCurtir
Excelente, Favoritado, muito obrigado!
CurtirCurtir
De nada! 🙂 Vlw
CurtirCurtir