Cet article fait partie d’une série consacrée à l’injection de dépendance dans MVC 3 :

Le code source de cet article se trouve ici vs2010solution_43E39653.

ModelMetadata introduit dans le Framework MVC2 bénéficie de quelques améliorations dans le Framework MVC3. Nous allons les voir dans les détails.

Support natif de Metadata.

ASP.NET MVC permet de créer la métadata correspondante au modèle de vue. La métadata est utilisée pour réaliser un certain nombre de tâches au Framework MVC comme par exemple la validation de données ou un affichage particulier du modèle dans une vue. Je ne vais pas rentrer dans les détails expliquant les bases de métadata mais pour avoir plus d’informations. Un article complet à ce sujet se trouve ici : http://msdn.microsoft.com/fr-fr/magazine/ee336030.aspx.

ModelMetadata

Il ne peut y avoir qu’un seul ModelMetadata provider enregistré dans le Framework MVC. C’est un enregistrement d’un service unique qui a d’ailleurs été introduit dans le MVC 2. Le point d’enregistrement statique est ModelMetadataProviders.Current.

Le ModelMetadata provider analyse le modèle pour retourner les informations de métadata comme par exemple les display names, chaines de formatage, etc.

Une classe qui dérive de ModelMetadataProvider fourni des informations métadata propos des modèles.

Nouveauté MVC 3 : Les métadata providers sont localisables via le DependencyResolver. La logique a été mise à jour pour d’abord essayer de localiser une implémentation de ModelMetadataProvider en faisant appel vers DependencyResolver.GetService(typeof(ModelMetadataProvider)). Si le service n’existe pas dans DependencyResolver il y a le fallback vers l’enregistrement statique (exception si enregistré dans les deux car un seul ModelMetadataProvider est autorisé à être enregistré).

Ecrire son propre ModelMetadataProvider basé sur les conventions.

Comme vous allez le constater l’écriture d’un ModelMetadataProvider n’est pas très difficile.

Un mot sur le ModelMetadataProvider par défaut.

Dans le Framework MVC, la classe ModelMetadataProvider définie la classe DataAnnotationsModelMetadataProvider en tant que métadata provider par défaut. Nous n’avons pas besoin d’utiliser cette classe directement. Si nous voulons développer notre propre provider de métadata basé sur des attributs nous pouvons hériter de cette classe de base.

Comme dans la plupart des composants du Framework MVC, le système de métadata est complément personnalisable. Le Framework est fourni avec une implémentation par défaut mais vous avez la possibilité de l’étendre ou de le remplacer complément.

image

En bleu, les providers implémentés par défaut.

En vert, vos providers personnalisés.

Comme vous le pouvez constater d’après ce schéma, les attributs data annotations ainsi que le DataAnnotationsModelMetadataProvider peuvent être complètement remplacés s’il ne correspondent pas à vos besoins. Pour créer votre provider vous pouvez hériter des classes de base suivantes :

  • ModelMetadataProvider : La classe abstraite de base pour tous les métadata providers.
  • AssociatedMetadataProvider : Une autre classe de base qui hérite déjà de la classe ModelMetadataProvider  et qui apporte quelques fonctionnalités. Elle implémente quelques fonctions utiles permettant d’obtenir une liste d’attributs associées avec chaque propriété du modèle. Ils sont obtenus à partir des classes configurés via MetadataType. Tout ce que vous avez à faire est d’overrider une seule méthode CreateMetadata et retourner une instance de ModelMetadata basée sur une collection d’attributs fournies, le type du modèle, nom de la propriété, etc. etc.
  • DataAnnotationsMetadataProvider : C’est le provider métadata par défaut. Si vous héritez de cette classe vous obtenez le support pour les attributs standards du System.ComponentModel. Comme dans le point précédent vous n’aurez qu’à overrider la méthode CreateMetadata.

Passons maintenant à la pratique.

La convention.

Nous allons créer un simple formulaire qui permet de rentrer un post/article dans une vue prévue à cette effet. Voici à quoi ressemble la classe du superbe modèle de vue qui contiendra les données rentrées par l’utilisateur dans la vue en question (Attention c’est violant !!!).

PostInput

Il sera plus facile maintenant de parler de conventions que nous allons appliquer pour nos modèles :

  • Champs cachés : si nous voulons afficher un champ caché le nom de la propriété du modèle en question doit commencer par le mot “Hidden”.
  • Champs multiligne : si nous voulons afficher un champs multiligne (TextArea), le nom de la propriété du modèle en question doit commencer par le mot “TextArea”
  • Affichage du nom : le nom du champ affiché dans la vue ne doit bien évidemment pas comporter les prefixes “Hidden” et “TextArea” et de plus devrait transformer le nom de la propriété de Pascal-Case en mots déparés par des espaces blancs. Donc par conséquence la propriété TextAreaPostBody devrait s’afficher en “Post Body”.

Je pense que pour commencer cela sera suffisant.

Mais d’abord vérifions comment cela se comporte avec le modèle métadata provider par défaut (DataAnnotationsModelMetadataProvider). Si on affiche le modèle dans la vue nous obtenons ceci :

FormWithoutModelMetadata

Le code de la vue est très simple :

CodePostView

Nous appelons le helper EditorForModel pour afficher noter formulaire d’édition. Cela, comme vous pouvez le constater ne correspond pas du tout à ce qu’on voudrait comme affichage défini dans les conventions ci-dessus. Nous allons donc écrire notre propre provider de métadata basé sur nos conventions.

Ecriture du provider métadata personnalisé.

Puisque le provider par défaut DataAnnotationsModelMetadataProvider ne correspond pas à nos besoin, nous allons construire notre propre provider de métadata dont le code est montrée ci-dessous :

ConventionMetadataProvider

Comme indiqué plus haut de l’article, il est plus facile de créer un provider de métadata personnalisé en héritant directement de la classe AssociatedMetadataProvider. Nous avons besoin uniquement de surcharger la méthode CreateMetadata et de retourner une nouvelle instance de ModelMetadata.

Quelques explications rapides.

  • ligne 12 : création du modèle métadata de base par rapport au paramètres fournies à la méthode CreateMetadata par le Framework MVC. Ce modèle sera retourné si les critères des filtres concernant le nom de la propriété ne correspondent pas à notre convention établie plus tôt.
  • ligne 17 : vérification si le nom de la propriété commence par le mot “Hidden”. Si c’est le cas, nous indiquons quel template faut il générer pour cette propriété par le biais de la propriété TemplateHint. La chaine que vous renseignez (ici “HiddenInput”) correspond au nom du template qui est natif au Framework MVC où personnalisé par vous. La liste de noms de templates natifs se trouve ici : http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html. Ensuite à la ligne 21 nous enlevons le prefix “Hidden” du nom de notre propriété afin qu’il ne s’affiche pas dans l’interface.
  • ligne 24 : traitement similaire que pour le “if” à la ligne 17 mais cette fois-ci pour le text area.
  • ligne 30 : transformation de noms de nos propriétés du type “PostBody” en “Post Body”.
  • ligne 31 : on retourne notre modèle basé sur nos conventions.

Ceci n’est pas suffisant. Il faut maintenant enregistrer notre provider de métadata.

Enregistrement statique.

L’enregistrement statique est très simple. Voici la ligne à ajouter dans le Global.asax (ou autre bootstrapper)

StaticModelMetaDataProvider

Ce mode statique existe depuis le MVC 2 et est disponible si vous n’avez pas besoin de DI dans votre provider. Il existe également un mode dynamique.

Enregistrement dynamique.

L’enregistrement dynamique permet de bénéficier du DI au sein de vos providers ce qui est une bonne chose.

DynamicModelMetadataProvider

Les lignes 45 et 46 ne sont pas nécessaires, donc n’en tenez pas compte.

Le code de la vue reste le même.

Dans le prochain article je vais continuer sur le sujet. Ne vous inquiétez pas, bientôt ça sera la fin de cette série !

// Thomas