Deep Dive into ASP.Net Model Binding

Pour ce deep dive, nous allons étudier le Model Binding qui est à l’oeuvre dans le framework ASP.Net MVC. Pour cela, je me base sur le code décrit dans ce tutoriel du site www.asp.net.

aspnet

Voici pour info le modèle de film tel qu’il est défini dans le tutoriel d’ASP.Net:

DeepDiveModelBinding_MovieModel

Notez que sont présents des attributs de contraintes, qui définissent les règles que doivent suivre les propriétés associées.

Avant le Model Binding…

Si le Model Binding d’ASP.Net MVC n’existait pas, nous ferions notre mapping de la requête HTTP vers nos objets en utilisant l’objet Request de HttpContext.

DeepDiveModelBinding_RequestMappingCela consiste à aller chercher les données POSTées. C’est plutôt fastidieux puisqu’il faut écrire le mapping de chaque propriété ainsi que le parsing nécessaire. Ci-dessous, les données postées dans le formulaire html:

DeepDiveModelBinding_MoviePOST

A noter que je n’ai pas pris en compte dans ce code la validation du modèle récupéré. C’est également fastidieux 🙂

Avec le Model Binder d’ASP.Net MVC

Dans le screenshot ci-dessous, on voit que le Movie est maintenant passé en paramètre de la méthode. Qui plus est, un attribut BindAttribute est utilisé pour éviter qu’un utilisateur malicieux poste un champ qu’il ne devrait pas modifier.DeepDiveModelBinding_ModelBinding

Le modèle est maintenant également validé. Et plutôt que de faire une validation propriété par propriété, on utilise juste la propriété IsValid de ModelState, qui vérifie l’ensemble. Le code est considérablement simplifié et donc bien plus rapide à écrire.

Voyons maintenant ce qu’il se passe à l’intérieur de la boîte.

Tout commence dans le Controller

Notre premier problème est de comprendre comment l’on arrive dans cette méthode Edit qui se voit attribuer le bon paramètre, c’est à dire un Movie bound bien comme il faut.

Bon… un rapide coup d’œil d’une application asp.net mvc nous incite fortement à chercher du côté des modules HTTP :).

Je ne vous décris pas ici la formule magique merlin2 pour parvenir à ce que nous cherchons mais voici tout de même le chemin complet pour les plus curieux d’entre vous:

  • Global.asax -> RegisterRoutes() -> RouteTable
  • UrlRoutingModule -> RequestContext
  • MvcRouteHandler -> MvcHandler
  • MvcHandler -> IControllerFactory -> DefaultControllerFactory
  • MvcHandler -> ControllerBase.Execute()
  • ControllerBase -> ControllerActionInvoker

Dans la méthode TryUpdateModel() de l’abstract Controller, on voit immédiatement ci-dessous l’application des paramètres du BindAttribute qui a été positionné sur l’argument de notre action.

DeepDiveModelBinding_TryUpdateModel

La deuxième partie qui nous intéresse est le Model Binding.  La préparation est réalisée avec la création du ModelBindingContext, qui prend d’ailleurs en paramètre le prédicat permettant de définir si une propriété doit être bound ou non.

Ce ModelBindingContext est utilisé en paramètre pour le binding du model, avec le ControllerContext: binder.BindModel()

Cela va dans la majeure partie des cas nous emmener dans l’implémentation framework DefaultModelBinder qui implémente IModelBinder et qui gère correctement la plupart des cas.

Nous allons regarder en détail deux sous méthodes appelées par cette méthode BindModel()

  • BindSimpleModel()
  • BindComplexModel()

BindSimpleModel()

DeepDiveModelBinding_BindSimpleModel

En réalité, juste avant d’appeler la méthode BindSimpleModel(), la méthode BindModel() génère dans ce cas un ValueProviderResult.

Ensuite, la méthode BindSimpleModel() effectue une série de tests:

  • Est-ce que mon ValueProviderResult comporte une RawValue du type de mon objet à bind? si oui, return copy
  • est ce que mon objet à bind est une collection? si oui, tente une conversion
  • est ce que mon objet à bind est un enumerable? si oui, tente une conversion vers le un tableau du type des éléments contenus
  • Finalement, tente un TypeConverter.ConvertFrom(typeof(rawType))

BindComplexModel()

DeepDiveModelBinding_BindComplexModelDans le cas d’un binding vers un modèle complexe, la méthode BindComplexModel() est appelée. Soit le model a été instancié à la création du BindingContext, soit c’est une collection et la méthode gère alors les différents cas pour l’instancier (Array, IDictionary, ICollection)

Ensuite, le binding commence avec l’appel de la méthode BindComplexElemental(). Celle -ci appelle à son tour la méthode BindProperties(), qui pour chaque propriété à « binder », va appeler BindProperty(). Enfin, la méthode SetProperty() se charge de setter les différents PropertyDescriptor et le binding est terminé 😮

Ci-dessous, une vue de la méthode BindProperty() qui va récupérer les valeurs et les assigner via la méthode SetProperty().

DeepDiveModelBinding_BindProperty

Advertisements

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s