Neste artigo vamos falar sobre o conceito ModelBinder do MVC. Um dos pontos de extensão interessante da estrutura do ASP.NET MVC são os “Model Binder’. Observe o gráfico da Figura 1.

 Model Binding

Figura 1. Model Binding

Um POST normal tem apenas valores-chave, o que não facilita tanto o trabalho do programador web para resgatar informações enviadas.

O Model Binder tem a tarefa de transformar a requisição/formulário HTTP e as informações em QueryString em tipos coerentes do .NET Framework, os quais foram previamente programados, como por exemplo, ViewModels.

Esta solução foi eficaz contra a prática de Request.Form, adotada nas outras linguagens Web, inclusive o ASP, irmão mais velho do ASP.NET, aumentando a complexidade ao tratar as informações de tipos complexos, classes aninhadas e assim por diante.

O ASP.NET MVC supri essa necessidade com a classe DefaultModelBinder que faz essa tradução literal do HTTP e os transforma em tipos complexos, como lista de classe, arrays, matrizes. Para quem trabalha com MVC sabe que existe uma convenção na criação da view que facilita este trabalho de tradução. Para isso, declaramos o modelo que irá representar a view e geramos os objetos HTML usando a notação Razor. Veja um exemplo no código apresentado na Listagem 1.

Listagem 1. Notação Razor para gerar input do tipo texto


  @Html.TextboxFor(m => m.Produto.Descricao)

Desta forma, ao postar o formulário via submit, ou utilizando um post via Ajax serializando o formulário, a action correspondente já recebe um tipo complexo pronto e totalmente populado, bastando apenas fazer a operação de negócio.

Entretanto, existe no dia a dia a necessidade de alterar a forma que nosso Binder trabalha ou necessitamos de algum tratamento específico, como por exemplo, em relação a cultura imposta na data do sistema quando se tem clientes em todo o globo. Para tal podemos estender o serviço do Model Binder facilmente.

Vamos começar estendendo a nossa classe para o binder: vamos chamá-la de CustomBinder e ela vai herdar a interface IModelBinder. Observe a Listagem 2.

Listagem 2. Interface IModelBinder


  public class CustomBinder  : IModelBinder {}

Pronto, já temos uma classe estendida e pronta para trabalhar durante o processo de binding do MVC.

Agora devemos programar a classe com as rotinas que desejamos. O ideal neste caso e criarmos uma necessidade para desenvolver.

O nosso cenário necessita que o Custom Binder receba as informações do tipo DateTime. Nesse momento, a classe deve identificar esse tipo e transformar o tipo de data para o formato brasileiro de datas. Imaginando um cenário onde não foi feito uma pré-validação do formato de data em nossa view ou até mesmo por convenção, vamos fazer todas as validações apenas no Server.

Primeiramente vamos criar a classe DateTimeBinder e herdar da interface como fizemos no momento anterior. Observe o código da Listagem 3.

Listagem 3. Criando classe Binder


  public class DateTimeBinder  : IModelBinder {}

Feito isso ,vamos programar a classe criando um método que terá parâmetros de entrada que receberá o contexto do controller e do binder. Para isso, devemos apenas programar poucas linhas de programação, de forma bem simples, como mostra a Listagem 4.

Listagem 4. Implementação da classe DateTimeBinder


  public class DateTimeBinder : IModelBinder
      {
          public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
          {
              var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
              var date = value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
              return date;
          }
      } 

O método BindModel receberá os contextos já citados.

Utilizando a variável bindingContext, conseguimos resgatar o valor da view utilizando o nome do objeto. Este processo captura apenas o valor do campo que desejamos e armazena na variável.

O próximo passo da aplicação é a conversão desejada. No nosso caso eu converto o valor de data para o formato que irá respeitar o CultureInfo do framework. Claro, para que este CultureInfo esteja correto, tudo dependerá do que foi definido no web.config.

Para isso, vamos definir no web.config principal da aplicação os cultures brasileiros, garantindo a conversão da data satisfatória. Observe o resultado final no código da Listagem 5.

Listagem 5. System.Web Globalization


  <system.web>
  <globalization culture="pt-BR" uiCulture="pt-BR" />
  </system.web>

Até aqui parte do trabalho foi feito. Existe um arquivo dentro da solução que é executado no início da aplicação no IIS, onde ele deixa registrado diversas tarefas que o framework irá realizar durante a sessão ativa do usuário, sendo elas, as definições de rotas, filtros globais como tratamento de erros personalizados, registros dos pacotes jQuery, JavaScript ou outra linguagem client-side.

O arquivo chama-se Global.asax. Dentro dele existem diversas rotinas para incluir no início do sistema todas as referências necessárias para que tudo funcione corretamente.

A importância de citar tudo isso vem justamente para não nos esquecermos de incluir nossa classe DateTimeBinder na lista de classes que serão necessárias ser referenciadas durante o vínculo

Para incluir a classe devemos declarar dentro do Global.asax as seguintes linhas de código, como mostra a Listagem 6.

Listagem 6. Global.asax adicionando binders


      public class MvcApplication : System.Web.HttpApplication
      {
          protected void Application_Start()
          {
              AreaRegistration.RegisterAllAreas();
              FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
              RouteConfig.RegisterRoutes(RouteTable.Routes);
              BundleConfig.RegisterBundles(BundleTable.Bundles);
   
              ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
              ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder());
              }
      } 

Passamos o tipo e uma instância do DateTimeBinder, assim, na hora do vínculo o framework identifica e executa a rotina do Binder, realizando todos os passos já descritos.

O retorno deste nosso caso é a data em formato correto dentro da culture escolhida.

Neste caso estamos adicionando aos delegates do binder mais uma classe que ele irá executar e esperar um retorno. Podemos também criar nosso próprio Custom Binder, sendo que este irá sobrescrever o binder default do framework por outro codificado por nós.

Para isso, devemos definir no global.asax que o Binder Default terá outro construtor sobrescrevendo as rotinas normais do sistema. Para isso, devemos saber exatamente o que estamos fazendo, pois a rotina de mapeamento dos dados para uma viewmodel ou modelo fica a cargo do binder. Observe o código da Listagem 7.

Listagem 7. Global.asax adicionando binder default


      public class MvcApplication : System.Web.HttpApplication
      {
          protected void Application_Start()
          {
              AreaRegistration.RegisterAllAreas();
              FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
              RouteConfig.RegisterRoutes(RouteTable.Routes);
              BundleConfig.RegisterBundles(BundleTable.Bundles);
              ModelBinders.Binders.DefaultBinder = new CustomDefaultBinder();
          }
      } 

Feito a declaração no global.asax, vamos definir corretamente a nossa classe CustomDefaultBinder. Esta irá herdar a classe do namespace MVC DefaultModelBinder. Lembre-se que esta classe já herda da interface IModelBinder e dela iremos sobrescrever o método BindModel. Neste ponto podemos fazer qualquer implementação que desejarmos no nosso binder, conversões, mapeamentos, gerar logs, enfim, as possibilidades são imensas.

Além de compreender como o binder trabalha, podemos estender o método e fazer com que ele se adéque a nossa necessidade, facilitando muito o trabalho.

Essas são algumas das vantagens e premissas do MVC, entregando o controle na mão do desenvolvedor para que ele programa em cima do que o framework o permite usar, porém, nada impedi de estendermos uma classe e fazer a nossa adaptação de certas rotinas. Por isso, é sempre importante conhecer bem o framework e aprenderemos a usufruir cada vez mais dele. Veja um exemplo dessa adaptação na Listagem 8.

Listagem 8. Global.asax adicionando CustomBinder


  public class CustomBinder : DefaultModelBinder
      {
          public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
          {
              //Some stuff Here
              return base.BindModel(controllerContext, bindingContext);
          }
      }

O Custom Binder abre um poder maior para nós desenvolvedores web, diferente do que era a estrutura de binding do GridView ASP.NET, onde não tínhamos como estender o método de bind se não fosse utilizando o row_databound. Agora podemos estruturar uma rotina própria customizada e até mais performática negando alguns caminhos do framework.

Este assunto é bem discutido nos blogs, então trouxe algumas referências mais avançadas sobre o assunto que trará mais perspectiva sobre o assunto e mais curiosidade.

Obrigado.

Referências

http://www.prideparrot.com/blog/archive/2012/6/customizing_property_binding_through_attributes

http://www.codeproject.com/Articles/605595/ASP-NET-MVC-Custom-Model-Binder

http://weblogs.asp.net/melvynharbour/mvc-modelbinder-and-localization