Blog Arolla

Linq provider : un essai… partie 2

Vous attendez depuis longtemps la suite de l'article précédent Linq provider : un essai… partie 1, alors la voici !

Tout d'abord, préparons le terrain:

Pour construire mon exemple de provider, je vais interroger une Web service. Tout d’abord, voyons de manière générale ce que je cherche à faire :

  • DummyWs : mon exemple de provider va être construit sur la base d’un web service. Ce service va renvoyer des objets de son modèle, correspondant à des critères de recherche (des objets Person dans un premier temps, et peut-être d’autres types par la suite).
  • DummyWsModel : les classes du modèle et les entités vont être regroupées dans un assembly indépendant du service, et qui sera partagé par tous les projets de la solution.
  • DummyWsLinqer : le provider sera dépendant à la fois du modèle et du service.
  • Tests: ce projet va utiliser le provider Linq pour consommer des données du service, mais ne contiendra aucune référence directe au service. L’idée ici est de vérifier :
    • que les données récupérées sont correctes,
    • que les parties de la requête qui peuvent être gérées par le service ont bien effectivement été gérées par le service et non par le client.

Voici un schéma représentant les dépendances entre les projets :

Je vais commencer avec un modèle très simple, formé d’une seule entité nommée Person. J’enrichirai probablement le modèle lors des étapes suivantes, lorsque j’aurai déjà quelque chose d’opérationnel. Pour le moment, nous avons donc une personne, avec un ID, qui est un GUID, un prénom, un nom de famille et un âge.

Mon premier objectif va être de faire en sorte que le provider appelle effectivement le service web, et qu’il retourne les résultats. Pour cela, je vais avoir à capturer l’arbre d’expression représentant l’appel, et remplacer dans cet arbre l’expression qui représente les personnes par les résultats du web service.

J’ai basé mon travail sur les Exemples fournis avec Visual Studio (2008 à l’origine), et en particulier le tutoriel "Creating an IQueryable LINQ Provider". La majorité du code basique est issue de cet exemple. Mais bien que l’exemple en question ne soit pas particulièrement compliqué au final, il mélange plusieurs sujets et objectifs, et je l’ai trouvé assez difficile à comprendre. C’est pour cette raison que j’ai voulu réaliser ma propre implémentation, en avançant à tout petits pas.

Étant donné que je sais que mon service va renvoyer des données (donc au moins une personne), le test le plus simple auquel j’ai pu penser pour mon provider est d’obtenir une instance d’un énumérateur et de vérifier que l’on puisse itérer au moins une fois.

public void GivenAProviderWhenIGetAnEnumeratorThenICanMoveNext()
{
    QueryableDummyData<Person> queryablePersons =
        new QueryableDummyData<Person>();

    bool exists = false;
    using (IEnumerator<Person> enumerator =
        queryablePersons.GetEnumerator())
    {
        exists = enumerator.MoveNext();
    }

    Assert.IsTrue(exists);
}

Le code précédent fait partie du projet “Tests”, et à partir du premier schéma, nous savons que seuls les projets DummyWsModel et DummyWsLinqer sont référencés. Person est une classe du modèle, et QueryableDummyData<T> est le point d’entrée pour écrire des requêtes basées sur le modèle. Il est à noter qu’il n’y a ici aucune référence directe au web service.

De l’exemple MSDN, je n’ai extrait que les composants essentiels :

  • une classe à utiliser comme point d’entrée : QueryableDummyData<T>, une implémentation de IOrderedQueryable<T>
  • le provider : DummyQueryProvider, une implémentation de IQueryProvider,
  • DummyQueryContext, la classe qui gère réellement les requêtes, et les transforme de manière à ce qu’elles puissent être exécutées.
  • TypeSystem, un classe utilitaire, interne à l’assembly.

Les classes QueryableDummyData, DummyQueryProvider et TypeSystem sont grosso-modo des copier-coller (Honte sur moi !) depuis l’exemple MSDN, et très peu modifiées excepté le fait qu'elles ont été renommées. DummyQueryContext est l’endroit où le travail est réellement effectué.

QueryableDummyData implémente IOrderedQueryable<T>, ce qui signifie également IQueryable<T>, IEnumerable<T>, IOrderedQueryable, IQueryable et IEnumerable. Parmi toutes ces interfaces, les implémentations réellement nécessaires sont 3 propriétés de IQueryable, les surcharges génériques et non-génériques de GetEnumerator, provenant respectivement des interfaces IEnumerable<T> et IEnumerable. Cette classe n’effectue aucun travail vraiment significatif, et les implémentations de GetEnumerator délèguent tout le travail à la méthode Execute du provider.

DummyQueryProvider implémente IQueryProvider, mais pour le moment les seules méthodes qui vont nous intéresser sont les deux surcharges de Execute (générique and non-générique). A nouveau, ces méthodes ne font rien de plus que déléguer le travail à la classe DummyQueryContext.

Comme je l’ai déjà dit précédemment, DummyQueryContext est l’endroit où il va enfin se passer quelque chose. Pour savoir comment, continuez tout simplement votre lecture avec le post suivant !

Site Web | Plus de publications

Contributeur enthousiaste

3 comments for “Linq provider : un essai… partie 2

  1. Pingback: Interview de Pierre Irrmann | Arolla