Magazine

find my dynamic ou l'innovation de C# 4

Publié le 29 août 2009 par Olivier Duval

Toute application Web (ou non) demande d'accéder à des données stockées en base. Nous avons souvent 1 ou 2 couches qui le permettent : 1 couche service + 1 couche DAL/repository, ou bien 1 couche qui représente les deux, selon ses besoins et la granularité du métier, plus ou moins compliqué.

On se retrouve souvent avec des FindByXXX qui ramènent les entités à afficher selon des critères : par libellé, date, catégorie, etc.

Plusieurs possibilités nous sont offertes

1- des find multiples

List<T> FindByName(string name), List<T> FindBySurname(string sn), List<T> FindByCategory(string category), List<T> FindByNameAndCategory(string name, string category), comme on peut s'en douter, cela peut très vite donner des dizaines de méthodes, déclinées en autant de critères croisés : cela en devient assez fastidieux et pénible d'en ajouter, voire de les maintenir.

2- API

On peut également utiliser le langage de l'ORM choisi - par exemple pour NHibernate, les Criteria/DetachedCriteria ou le HQL, qui serviront à sélectionner les entités

List<T> FindBy(DetachedCriteria mycrit) : cela a le mérite de réduire le nombre de méthodes mais crée une adhésion forte au framework de mapping - nous restons prisionniers de ce dernier, mais pourquoi pas, on ne change pas d'ORM tous les jours.

Pour réduire cette dépendance, on pourrait alors se créer sa propre API de plus niveau afin d'abstraire ce langage de requête spécifique à l'ORM

List<T> FindBy(CriteriumMaison mycrit) : chaque CriteriumMaison sera traduit en DetachedCriteria dans la méthode, cela a l'avantage d'abstraire l'ORM sur la couche supérieure (présentation ou service), mais demande un double travail : reproduire à l'identique les fonctionnalités de recherche de l'ORM sous-jacent, sous peine de réduire ses capacités de recherche.

3-Linq

Avec l'arrivée de C#3, on a maintenant accès à Linq, autrement dit aux Expressions. Etant inclut au langage, nul besoin d'abstraire cette possibilité. La plupart des ORM supportent maintenant Linq : Linq to sql, Linq to Entity, Linq to NHibernate, ...cette possibilité va nous permettre de passer directement l'expression de sélection, toujours avec un objectif de réduire le nombre de méthodes, et donc de code - même si on s'efforcera de factoriser le plus possible le code.

List<T> FindBy(Expression<Func<T, bool>> mycrit) ou List<T> FindBy(Func<T, bool> mycrit) il suffit alors de passer l'expression à l'ORM pour choisir ses éléments.

Dès lors, on passe à la méthode un expression autant de critères que l'on souhaite sur l'entité recherchée :

var mylist = repository.Find(x => x.CatId ==125 & x.Libelle == "c# 4");

il suffit alors de repasser tout simplement l'expression à Linq to NHibernate :

public List<T> Find(Func<T, bool> expression)
{
    return _laSession.Linq<T>().Where(expression).ToList();
}

au choix, on pourrait ramener un IQueryable afin de peaufiner par la suite la sélection, mais attention, cela exécutera à chaque fois un appel à la base par une requête SQL (ce n'est pas du linq to objects) :

public IQueryable<T> FindBy(Expression<Func<T, bool>> expression)
{
    return _laSession.Linq<T>().Where(expression);
}
 
var mylist = repository.FindBy(x => x.CatId == 125 & x.Libelle.StartsWith("c#") );

l'ORM traduira cette requête en SQL à base de like 'c#%' et un critère sur CatId



on peut alors prolonger le filtrage après l'appel à notre méthode FindBy, cela lancera une nouvelle requête SQL avec les critères ajoutés, ici avec from l in mylist where l.Libelle.EndsWith("4") select l qui produira en plus un like '%4'

Assert.AreEqual(2, mylist.Count());
Assert.AreEqual("c# 4", mylist.ToList()[0].Libelle);
Assert.AreEqual(1, (from l in mylist where l.Libelle.EndsWith("4") select l).Count());

cela nécessite que le framework implémente Linq, mais s'avère une solution assez élégante car : nul besoin d'abstraction car se base sur les capacités du langage C#.

4- dynamic

A chaque version de C# / .NET, arrive son lot d'innovations et de nouveautés : Generics, Linq, Lambda Expressions, méthodes d'extension, Expressions Tree, ... avec C#4, qui devrait arriver en 2010 (disponible dès maintenant en version Beta), apporte une grande avancée (ou une gageure pour d'autres) : le mot clé dynamic, qui le fait entrer dans les styles de langages dits dynamiques, du type Ruby, Python, Perl & consorts (PHP). Dans un langage dit "statique", il y a une phase supplémentaire à l'exécution : la compilation. Toute erreur de syntaxe, de type incorrect ou d'utilisation d'objets inexistants provoquent des erreurs à la compilation.

C'est l'un des avantages de la compilation : de détecter les erreurs avant leur exécution, tant qu'elles ne sont pas corrigées, pas d'exécution possible. Un autre avantage avancé est aussi un gain en termes de performances : le compilateur C# générera un pseudo-code, l'IL, qui sera exécuté par la CLR (Common Language Runtime).

Dans les langages interprétés, ou dynamiques, du fait des types qui sont indéterminées (à typage dynamique), il n'y a pas de phase de génération d'un exécutable, préalable à l'exécution, ce qui peut leur conférer une meilleure réactivité : je code et j'exécute immédiatement. La contre partie est qu'il n'y a pas de détection syntaxique ou de vérification de type, elle est effectuée pendant l'exécution, ce qui peut engendrer des soucis lors de l'exécution.

Intrigué par ce dynamic, qui sera interprété par la DLR (Dynamic Language Runtime, déjà utilisé pour IronRuby ou IronPython), je me suis demandé comment ils avaient réalisé cet exploit : mélanger à la fois CLR (compilé) et DLR (dynamic). Les membres d'Alt.NET bien compatissants envers moi m'ont apporté quelques réponses, un extrait :

... Quand on utilise le mot clef dynamic en C#4, le compilateur va ajouter des appels à la librairie Microsoft.CSharp.dll, qui est une librairie qui utilise la DLR pour faire communiquer le monde statique de C# avec celui, et bien, dynamique

[Jb Evain, 7 août 2009, oui, je sais, ça fait un peu gourou, voir le thread du groupe].

Cela se traduit par cet exemple :

compile en C#4 - FindByName() n'existe pas

dynamic cl = new MaClass();
cl.FindByName();

en C# 3.5 :

var cl = new MaClass();
cl.FindByName();

le code précédent ne compilera pas en C#3.5, la méthode appelée sera introuvable pour le compilateur. Voir cette page pour d'autres exemples.

Oui et alors ? le rapport avec mes find ?

Par curiosité, je m'intéresse à d'autres langages, notamment Ruby et Rails. Dans Rails, l'ORM qui accompagne le framework MVC, est ActiveRecord, un framework qui permet de mapper la base de données sur un modèle objet. ActiveRecord permet d'effectuer des recherches du type find_by_name, find_by_category, ou encore find_by_name_and_category, sauf que ces méthodes n'existent pas. Ruby a une méthode magique : method_missing (on pourra lire ce billet sur les atouts de Ruby), appelée lors d'un appel vers une méthode inexistante. ActiveRecord met en oeuvre une convention sur les find afin de découvrir dynamiquement le champ à interroger. Cela fait longtemps que j'aimerais appliquer le même modèle en C#. Et voilà, au hasard de mes recherches, que je tombe sur ce billet : comment reproduire ces conventions, en se basant sur NHibernate.

A partir de là, on pourrait avoir FindName, FindNameAndSurname, le framework prendra les postfix, ici Name et Surname pour en faire des critères de sélection. C'est là que le mot dynamic va être intéressant, afin de reproduire ce type de convention. La classe DynamicObject permet d'intercepter toute méthode inexistante et d'effectuer tout traitement approprié le cas échéant.

Le dynamic est-il dangereux ? à mon sens non, pas vraiment, tant que c'est utilisé à bon escient et pour quelque chose de précis, afin de nous faciliter la vie. Avant cette arrivée, qui n'a jamais utilisé d'Arraylist, la Reflexion, ou d'autres astuces de programmation pour introspecter dynamiquement des types, les charger à l'exécution, tout ceci étant bien souvent non détectable à la compilation. Les tests sont également là pour réduire le risque, ainsi que les vérifications d'usage sur l'utilisation de telle ou telle technique.

Le dynamic est l'une des nouveauté de C# 4.0, rendez-vous en 2010 pour profiter de toutes ces avancées, ou essayez Visual Studio 2010 Bêta dès maintenant.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Olivier Duval 4 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte