Welcome to MSDN Blogs Sign in | Join | Help

News


  • Locations of visitors to this page
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

Posted: Thursday, July 26, 2007 1:09 AM by mitsu
Filed under: , ,

Comments

Florent said:

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 :)

# July 25, 2007 7:35 PM

Mitsu said:

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.

# July 26, 2007 12:38 AM

Matthieu MEZIL said:

private IQueryable<Customer> GetCustomersQueryable()

{

   return

       from c in dc.Customers

       select c;

}

public IEnumerable<Customer> GetCustomers()

{

   return GetCustomersQueryable().AsEnumerable();

}

# July 26, 2007 4:01 AM

Matthieu MEZIL said:

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.

# July 26, 2007 5:29 AM

Simon said:

Attention, y'a un piège Matthieu ^^

C'est presque ca.

# July 26, 2007 7:32 AM

Mitsu said:

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-chemin

# July 26, 2007 11:23 AM

Matthieu MEZIL said:

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;

}

# July 26, 2007 12:25 PM

Mitsu said:

Good point !!

C'est en effet la solution.

IEnumerable<Customer> GetCustomers()

{

   var q =

       from c in dc.Customers

       select c;

   return

       from c in q.AsEnumerable()

       select c;

}

Le fait de réécrire l'itérateur génère une nouvelle classe qui implémente uniqueme IEnumerable<T> sans être IQueryable<T>. Le code est légèrement plus conséquent puis qu'il y a un itérateur supplémentaire mais nous conservons les avantages de l'exécution tardive de la requête sans créer de collection intermédiaire.

Qui veut encore des Quizz ? :p

# July 26, 2007 2:06 PM

Florent said:

Next please!

# July 26, 2007 2:24 PM

Simon said:

Ouais, des quizz, je veux des quizz bien tordus ! ^^

# July 26, 2007 2:31 PM

Thomas Lebrun said:

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 !! :)

# July 27, 2007 3:24 AM

Matthieu MEZIL said:

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. :)

# July 27, 2007 5:38 AM

Mitsuru FURUTA - Microsoft FRANCE said:

Petite question amusante sur C# 3: Le framework 3.5 apporte la méthode AsEnumerable. Quel est donc l'intérêt

# August 16, 2007 11:32 AM

Nicolas Lehuen said:

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

# October 31, 2009 2:22 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker