Développement .NET, Programmation Model Binder en .NET MVC – Default ou Custom ?
Cover600x600

Développement .NET, Programmation

Model Binder en .NET MVC – Default ou Custom ?

Le Model Binder d’une application .NET MVC est le composant qui contribue, par les méthodes d’action Controller, à la création des objets .NET nécessaires comme paramètres des Actions.

Le Model Binder associe des valeurs pour tous les types de paramètres qui sont déclarés par les méthodes d’action Controller lorsqu’elles sont appelées.

Allons voir le fonctionnement de base de Model Binder, la puissance du concept, comment on peut l’étendre, comment le rendre plus flexible par des personnalisations.

Le DefaultModelBinder implémente l’interface IModelBinder par la méthode unique BindModel. Il est utilisé à chaque fois qu’on ne trouve pas un Model Binder customisé. Pour la plupart des cas, il s’est montré performant.
Dès qu’une méthode d’action d’un Controller déclare des paramètres d’entrée, elle se retrouve dans le périmètre d’action d’un Model Binder. En partant du nom de chaque paramètre, plusieurs collections .NET sont parcourues par le DefaultModelBinder durant l’appel, afin d’essayer d’identifier les valeurs : les paramètres POST, les valeurs des routes, les paramètres GET, les fichiers téléchargés.

Fig1

Figure 1. Mécanisme d’analyse de données reçues en vue d’initialisation des paramètres

Le mécanisme d’identification des valeurs par le Model Binder fonctionne pour tous les types de paramètres:

1. les paramètres de type simple (int, string, etc), par conversions automatiques du format string d’origine.
Recevoir des erreurs dans le cas des conversions impossibles est envisageable, ces cas sont à prévoir et à traiter dans le code (exemple : séquence de lettres reçue pour un paramètre de type numérique)

2. les paramètres de type complexe, correspondant aux classes utilisateurs définies dans le projet.
Nous identifions tous les membres publics de type simple qui seront traités selon le mécanisme des paramètres de type simple.
Les membres publics de type complexe seront décomposés jusqu’à l’obtention des membres publics de type simple.

3. les paramètres de type array ou collection de types simples
Trouver tous les éléments contenant le nom du paramètre afin de composer des listes.

4. les paramètres de type array ou collection de types complexes
Dans l’attribut Nom de données, chercher le préfixe « [indexe]. » ([0]., [1]. etc.) devant le nom des membres de type simple, afin d’obtenir les valeurs des propriétés des objets d’une certaine position dans la collection :

Fig2

Figure 2. Flux de données avec Model Binder implicite

Il y a des scénarios quand nous souhaitons transmettre des données qui ne sont pas contenues dans le formulaire POST d’origine, dans les routes, dans la chaine GET ou dans la collection des fichiers, vers les méthodes d’actions. Pour ces cas, plusieurs solutions sont envisageables :

1. Ajouter dans les collections standard et analyser à l’aide du Model Binder implicite

Dans le cas des types simples, il suffit d’ajouter les données qu’on souhaite transmettre dans une des collections indiquées. Elles seront prises en compte par le mécanisme d’association du Model Binder.
Elles seront ajoutées en tant que champs Hidden dans les formulaires POST, comme paramètres supplémentaires GET, ou comme des niveaux supplémentaires dans les requêtes HTTP.

Dans le cas des types complexes, il n’est pas évident d’ajouter des structures entières de données aux formulaires des pages ou encore aux requêtes HTTP. Ceci implique des flux supplémentaires pour les données transmises par le réseau.

2. Utilisation directe de l’objet Session ASP.NET

Nous avons recours à l’objet familier Session de l’ASP .NET qui réalise la persistance des données à travers les requêtes, dans la limite de la persistance de la session web connectée. Par défaut, la mémoire du serveur est utilisée. Nous pouvons placer nos objets dans la session et retrouver ensuite leurs références afin de lire leur contenu ou de les modifier.

Nous prenons l’exemple d’un panier d’achats des produits individuels, des quantités et autres éléments auxiliaires et méthodes. Il est représenté dans une application par une classe « Cart ». Le souhait serait qu’il soit conservé lors d’une consultation des pages produits du magasin virtuel jusqu’à la finalisation de l’achat.

Nous plaçons dans la session une référence vers l’objet avec la phrase :

Session[« cart »] = shoppingCart;

et il peut être retrouvé avec :

(Cart)Session[« cart »];

Nous ajoutons ainsi une voie parallèle pour conserver notre objet panier : le Controller sera chargé des tâches de maintenance de l’objet dans la session :

Fig3

Figure 3. Flux de données avec Model Binder implicite et Session

Ceci a un prix : le Controller a reçu la logique de gestion des objets de la session qui ne rentre pas dans ses attributions habituelles. Le Contrôleur devient ainsi sensible au moyen de stockage de ces objets.
Si ultérieurement nous décidons de changer ce moyen de stockage, le Controller devra être modifié, tandis que sa fonctionnalité restera inchangée.
Cet ensemble ne peut pas être testé facilement par des méthodes Unit tests ; à cet instant, la reproduction de l’état de la session se fait dans les flux des tests mises en place.

3. Utilisation d’un Model Binder customisé contenant la gestion des objets en Session

Cette méthode consiste à faire passer directement les objets de la session dans le mécanisme du Model Binder, comme si ils étaient transmis du formulaire d’une page à une autre, ensemble avec le reste des données POST. Le Model Binder implicite ne sait tout de même pas lire dans la session, cela ne fait pas parti des collections qu’il analyse. Ceci serait pourtant possible, .NET MVC offre la possibilité de créer et ajouter des Model Binders customisés comportant des nouvelles fonctionnalités pour différents modèles de données.

Les bénéfices sont visibles immédiatement: concernant la décision de stocker un certain type d’objet dans la session, le Controller ne connait plus les détails d’implémentation car il recevra tout simplement une référence vers l’objet.

Les méthodes avec tests automatiques utilisent les objets sans faire référence à la session. Dans ce cas, la gestion des objets impliquant la session est déplacée dans le Model Binder customisé. Cette solution semble être plus agréable que la précédente.

Fig4

Figure 4. Flux de données avec Model Binder customisé, intégrant la gestion Session

Etapes pour l’implémentation du Model Binder customisé :

A. Création de la nouvelle classe Model Binder

Elle sera ajoutée dans un nouveau fichier du projet, étant associée au type d’objet modèle qui nous intéresse. Cette classe implémente l’interface IModelBinder (System.Web.Mvc), ayant comme unique méthode à implémenter BindModel avec la signature :

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

– ControllerContext fournit l’accès aux objets visibles depuis la classe Controller, y compris la session par ControllerContext.HttpContext.Session
– ModelBindingContext fournit l’accès au modèle

On ajoute ici les opérations de lecture et d’initialisation des objets de la session, que l’on traitait auparavant dans le Controller.

Exemple de panier d’achats : dans la méthode BindModel, nous aurons des expressions d’initialisation lorsque l’objet n’existe pas :

cart = new Cart();
ControllerContext.HttpContext.Session[« cart »] = cart;

La récupération de l’objet existant :

cart = (Cart)ControllerContext.HttpContext.Session[« cart »];

La méthode retournera l’objet :

return cart;

B. Enregistrement de la nouvelle classe Model Binder dans l’application

L’application saura sur quel type d’objets appliquer le Model Binder customisé :
– à placer dans le fichier Global.asax, méthode Application_Start()
– concrétisé par un ajout simple à la collection ModelBinders.Binders d’un élément composé du type de l’objet modèle et d’une instance du Model Binder :

ModelBinders.Binders.Add(typeOf(Cart), new CartModelBinder());

Nous pouvons également réaliser le même enregistrement sans toucher au Global.asax, en ajoutant à la classe de l’objet modèle qui nous intéresse une sentence :

[ModelBinder(typeof(classe Model Binder customisé)]

C. Nettoyage des méthodes Controller

Suite aux opérations précédentes, l’objet modèle pris en compte par notre version customisée devient compatible avec le mécanisme Model Binder. Il nous reste plus qu’à placer des paramètres du type défini dans les signatures des actions.
Ceux-ci seront lus en utilisant la méthode BindModel surchargée, et dans notre cas depuis la session.
L’utilisation des objets obtenus explicitement depuis la session est remplacée avec les objets reçus comme paramètres.

Les méthodes de notre Controller utilisant le panier d’achats auront des signatures similaires à :

ViewResult Summary(Cart cart)
RedirectToRouteResult AddToCart(Cart cart, int productID)

En conclusion, nous pouvons laisser le Model Binder implicite prendre en charge la gestion habituelle des paramètres, la plupart des besoins seront remplis.
Dans les cas particuliers, où les collections implicites ne suffisent pas ou l’on veut un traitement particulier d’un modèle, nous pouvons ajouter un ou plusieurs Model Binders customisés. L’effort d’implémentation n’est pas conséquent, les Binders tourneront ensemble pour une fonctionnalité plus large tout en gardant la souplesse du code.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Facebook
Google+
http://blog.beleringenierie.com/2017/06/27/model-binder-en-net-mvc-default-ou-custom/">
Twitter