Geek quizz II: comment stopper l'imbrication des expressions Linq to Sql ?
Soit la méthode de ma couche business suivante:
IQueryable<Customer> GetCustomers()
{
return
from c in dc.Customers
select c;
}
J'aimerai fournir à ma couche présentation l'accès à cette méthode afin d'offrir la possibilité de lister le clients venant de la base.
Juste là, un besoin tout à fait légitime :p.
le problème est que ma couche de présentation va pouvoir écrire ceci:
var q =
from c in GetCustomers()
where (c.City == "London")
select c;
Et je ne veux pas de cette possibilité car cette écriture va générer une nouvelle requête sql et je ne veux pas que ma couche présentation en soit capable. Je veux bien de la notion de filtre (where) mais en mémoire uniquement sans que cela modifie la requête sql que seule la couche business doit être capable de définir.
Comment faire ?
Mitsu
[Update] : Délégation d'implémentation d'interface: réponse au Quizz
[Update] Quizz suivant: Geek Quizz III: subtilités dans les conversions
Comments
Anonymous
July 25, 2007
J'ai proposé: Créér une liste de Customers intermédiaire pour faire une copie en mémoire de la requête issue de l'objet métier. List<Customer> GetCustomer() { return q.ToList(); } Pour pouvoir ensuite la manipuler et la filtrer librement avec un .FindAll et un jolie predicate. Mais tu m'as dit que c'était pas la meilleure solution :)Anonymous
July 25, 2007
En effet, peupler une collection une fois pour toute en parcourant l'énumération nous fait quitter le monde de Linq to Sql pour se retrouver avec une simple collection mémoire et toute l'artillerie de Linq to Objects. C'est donc UNE solution. Cependant, retarder l'exécution de la requête à travers toutes les couches de l'architecture reste très tentant et bien plus efficace au niveau des objects alloués. Donc en effet, on peut faire mieux.Anonymous
July 25, 2007
private IQueryable<Customer> GetCustomersQueryable() { return from c in dc.Customers select c; } public IEnumerable<Customer> GetCustomers() { return GetCustomersQueryable().AsEnumerable(); }Anonymous
July 25, 2007
Florent, je repensais à ton histoire de liste. Tu auras un autre problème avec ta liste. Si les données de ta BD sont modifiées, ta liste ne sera plus à jour alors qu'avec une Query si.Anonymous
July 25, 2007
Attention, y'a un piège Matthieu ^^ C'est presque ca.Anonymous
July 26, 2007
En effet Matthieu, Ton code fonctionne bien car désormais les extensions sur GetCustomers() seront bien en mémoire mais cette couche est mal isolée car rien ne m'empêche de recaster en IQueryable. var q = GetCustomer() as IQueryable<Customer>; q.Where(...); //sql à nouveau. La solution de Florent, isole bien Linq to Sql car List<Customer> n'est plus un IQueryable. La solution est à mi-cheminAnonymous
July 26, 2007
ok et comme ça ? private IQueryable<Customer> GetCustomersQueryable() { return from c in dc.Customers select c; } public IEnumerable<Customer> GetCustomers() { foreach (Customer c in GetCustomersQueryable()) yield return c; }Anonymous
July 26, 2007
The comment has been removedAnonymous
July 26, 2007
Next please!Anonymous
July 26, 2007
Ouais, des quizz, je veux des quizz bien tordus ! ^^Anonymous
July 26, 2007
Pas mal: j'était partit sur la même première idée que Matthieu. Comme quoi, faut faire attention :) Allez Mitsu, des quizz, des quizz !! :)Anonymous
July 26, 2007
Les quizz sont super intéressants car ils permettent de confronter les différentes idées personnes qui y participent et, de plus, ils permettent de progresser avec des questions dont on ignore la réponse. Donc oui 100% pour que tu continues dans ce sens. :)Anonymous
July 27, 2007
A mon tour de lancer un quizz : http://blog.developpez.com/index.php?blog=121&title=a_mon_tour_de_lancer_un_quizz&more=1&c=1&tb=1&pb=1Anonymous
August 16, 2007
Petite question amusante sur C# 3: Le framework 3.5 apporte la méthode AsEnumerable. Quel est donc l'intérêtAnonymous
October 30, 2009
Salut Mitsu, Des heures de discussion avec Daniel G. sur ce sujet... Faut-il ou ne faut-il pas isoler les deux mondes ? Si la couche de présentation fait du Linq, soit elle se retrouve à construire des requêtes SQL sans le savoir, soit elle se retrouve à filtrer des énumérations, c'est à dire à refaire côté présentation du travail que devrait faire la BDD, avec tout ce que ça veut dire en terme de perfs... Ton astuce de reconstruire un itérateur au lieu de tout monter en mémoire est déjà un grand progrès. Mais dans ton exemple, mettons que le premier client habitant à Londres soit le 1 000 001 ème de la table ; en isolant on perd l'intelligence de la BDD, la possibilité d'utiliser un index de la BDD, on se retrouve à parcourir un million d'enregistrement avec tout ce que ça veut dire comme transfert entre la BDD et le serveur de présentation. Pour moi c'est bien plus nuisible qu'une brèche dans la sacro-sainte isolation entre le M et le V du MVC. Alors évidemment, la solution c'est de n'exposer à la couche de présentation que des énumérateurs "petits", c'est à dire prémacher le travail dans la couche métier de manière à ce qu'en aucun cas la couche de présentation ne se retrouve à devoir itérer sur de gros jeux de résultats. Mais au final je suis super admiratif de ce que Microsoft a fait avec Linq (à des années lumière de ce qu'on trouve côté Java, oui c'est moi qui dit ça) et je trouve dommage de se priver de ses capacités. Sur le projet sur lequel nous travaillons actuellement avec Daniel, le client impose l'utilisation de procédures stockées pour accéder à la BDD, ce qui tranche le débat (interdit de générer des requêtes à la volée), mais dans un monde plus libre, à tout prendre, je préfèrerais ne pas isoler et manipuler directement l'ObjectQuery, avec toute la puissance que cela induit. Ou alors il faut interdire carrément l'utilisation de Linq dans la couche de présentation pour éviter le genre de problème indiqué ci-dessus :) @+ Nicolas