Blog Arolla

Linq provider : un essai… partie 3

Faisons enfin quelque chose !

Dans le post précédent de cette série , j’ai introduit les premières classes impliquées dans la mise en place d’un provider Linq : QueryableDummyData et DummyQueryProvider. Mais ces deux classes ne prenaient pas vraiment en charge le traitement de la requête Linq. Nous en arrivions justement à la classe DummyQueryContext… qui, je l’annonçais, va vraiment faire… quelque chose.

DummyQueryContext est une classe interne, qui n’expose qu’une unique méthode :

internal static object Execute(Expression expression,
                               bool isEnumerable)

Le but de cette méthode est de prendre un arbre d’expression en entrée, et de retourner les résultats attendus par la requête que constitue cet arbre. Jusqu’à présent, la chaîne des appels ressemble à ceci :

La première implémentation de la méthode Execute est donc la suivante :

internal static object Execute(Expression expression,
                                bool isEnumerable)
{
    // D’abord, on appelle le web service et récupère
    // un tableau de personnes
    IEnumerable<Person> people;

    using (DummyService.PeopleFinderClient service =
        new DummyService.PeopleFinderClient())
    {
        people = service.FindPeople(new DummyService.SearchCriteria());
    }

    // On utilise Ling to Objects pour récupérer une instance IQueryable
    // de personnes
    var queryablePeople = people.AsQueryable();

    // On transforme l'arbre d'expression
    Expression finalExpressionTree =
        ExpressionTreeConstantReplacer
            .CopyAndReplace(
                expression,
                typeof(QueryableDummyData<Person>),
                queryablePeople);

    // Finalement, en se basant sur le nouvel arbre, soit on crée
    // une requête soit on exécute avec le provider Linq to Objects
    IQueryProvider provider = queryablePeople.Provider;
    if (isEnumerable)
        return provider.CreateQuery(finalExpressionTree);
    else
        return provider.Execute(finalExpressionTree);
}

Rappelez-vous : mon cas de test consiste simplement à obtenir un énumérateur, et à vérifier que l’on peut itérer une fois. Pour faire en sorte que ce test passe, nous n’avons pas à nous soucier de quelque façon de filtrer ou trier les données renvoyées, donc l’implémentation précédente appelle le web service sans lui fournir d’argument particulier, et le service renvoie toutes ses données.

A présent, la partie la plus délicate est la transformation de l’arbre d’expression. Son but est de construire un nouvel arbre d’expression dans lequel l’instance de QueryableDummyData<Person> est remplacée par le tableau Person[] retourné par l’appel du web service. Une fois ce remplacement effectué, la méthode Execute va laisser le provider Linq to Objects jouer la requête par rapport au tableau. Le paramètre isEnumerable est utilisé pour savoir si la méthode renvoie une valeur scalaire (via la méthode queryablePeople.Provider.Execute) ou un IEnumerable (via la méthode queryablePeople.Provider.CreateQuery). Pour notre premier test, nous voulons obtenir un IEnumerable<Person>.

L’arbre d’expression manipulé est construit à partir de l’extrait de code suivant :

var queryablePeople =
    new QueryableDummyData<Person>();

Voici une représentation de l’arbre d’expression généré :

Et une représentation de l’arbre d’expression final, une fois que le QueryableDummyData<Person> a été remplacé par le tableau Person[] récupéré depuis le service :

Dans l'article suivant, nous parlerons du pattern Visitor, et nous verrons comment la classe ExpressionTreeModifier effectue ce remplacement..

Site Web | Plus de publications

Contributeur enthousiaste