Deep Dive into AutoMapper – CreateMap

La première méthode diabolique à regarder pour comprendre comment fonctionne notre exemple précédent, c’est la méthode CreateMap<TSource, TDestination>().

So deep dive into CreateMap…

DeepDiveIntoCreateMap1

Seulement, comme elle est diabolique, et qu’on pourrait écrire un livre pour expliquer ce qui se passe à l’intérieur, on va plutôt s’attarder sur quelques méthodes qui créent le mapping en lui même. On  ne verra par exemple pas la création des TypeInfo, qui sont plus simples à appréhender et consistent majoritairement en de l’introspection de classes pour lister les différents membres (Voici le lien vers la source si malgré tout, cela vous intéresse: il peut être utile de regarder comment les différents membres sont organisés pour l’optimisation des mappers). C’est évidemment un pré-requis à ce que nous allons regarder, mais c’est un procédé connu et qui n’est pas le coeur de la User Story de base d’Automapper, qui est de mapper 🙂

TypeMapFactory.CreateTypeMap()

Ce qui va par contre nous intéresser, c’est de voir fonctionner la TypeMapFactory lorsqu’elle crée les mappings de Properties, au cours de l’exécution de la méthode CreateTypeMap().

Pour vous aider à vous repérer, voici la call stack. On part de notre programme qui demande la création d’une map, et on se retrouve 7 niveaux plus bas, dans la fameuse TypeMapFactory.DeepDiveIntoCreateMap2_CallStack

 

Et voici l’implémentation de la méthode CreateTypeMap().

DeepDiveIntoCreateMap2_TypeMapFactoryCreateTypeMapJe vous ai laissé le debugger pour mettre en valeur la partie qui nous intéresse: la boucle sur les properties writables du type de destination. Ce sont ces propriétés que le mapper va pouvoir exploiter à partir de l’instance de type TSource.

Les deux lignes qui précèdent, consistent à la création des TypeInfo dont on parlait en intro.

on voit d’ores et déjà apparaître la méthode MapDestinationPropertyToSource() qui va particulièrement retenir notre attention.

TypeMapFactory.MapDestinationPropertyToSource()

Nous voici donc un niveau plus bas encore, dans la méthode qui va permettre d’obtenir le flattening dans le mapping.

DeepDiveIntoCreateMap2_TypeMapFactoryMapDestinationPropertyToSource

Cette fois ci également, le debugger a été laissé sur la partie qui nous intéresse le plus. L’exécution de FindTypeMember (voir quelques lignes plus haut dans l’image ci-dessus) n’a pas été fructueuse dans la mesure où l’on cherchait une propriété nommée CustomerName dans le type source Order, comme celle que l’on trouve dans le type de destination OrderDto.

La ligne en surbrillance exécute alors un split sur la propriété qui n’a pas trouvé de source pour se faire mapper la tronche :

La regex qui permet de split la propriété qui n’a pas été mapped 😎 :

(\p{Lu}+(?=$|\p{Lu}[\p{Ll}0-9])|\p{Lu}?[\p{Ll}0-9]+)

Le résultat, c’est les 2 matches que l’on voit dans la capture d’écran précédente: « Customer » et « Name ».

La méthode MapDestinationPropertyToSource est une méthode récursive qui va donc juste après le split, trouver une property Customer, et ensuite, définir un TypeInfo pour le type Customer, puis chercher à l’intérieur un membre correspondant à Name.

Ce membre, elle va le trouver trivialement à l’intérieur de Customer (pas besoin de faire du split 🙂 ). On a ainsi notre premier mapping de Customer.Name vers CustomerName.

Le mapping GetTotal -> Total

Là aussi, le mapper trouve de manière triviale la correspondance entre la propriété Total de OrderDto et la méthode GetTotal() contenue dans Order.

DeepDiveIntoCreateMap3_TrivialMatchingVoici le code de la méthode FindTypeMember(). Etant donné qu’on va mapper la méthode GetTotal() dans Total, le Graal va se trouver dans la ligne qui travaille sur l’IEnumerable d’entrée getMethods() perceval1

DeepDiveIntoCreateMap4_FindTypeMembersLe seul petit tricky trick va se trouver dans le fait de faire correspondre la string « Total » avec la string « GetTotal ».

DeepDiveIntoCreateMap4_NameMatches

Cela se fait dans la méthode en surbrillance ci-dessus: PossibleNames() va extraire les noms possible pour un mapping de GetTotal() et va « deviner » « Total » et « GetTotal » comme le montrent les espions également ci-dessus.

In conclusion

Nous avons vu comment se créaient les mappings dans des cas de flattening d’un modèle complexe vers un ViewModel simple. L’utilisation de ces maps par la méthode Map() fera l’objet d’un prochain article.

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