GQ08 IV: Linq to Sql bug ?


Un peu de Linq to Sql !

Le fonctionnel est simple, j’aimerais depuis toutes les catégories récupérer une liste d’éléments regroupant le nom de la catégorie ainsi qu’un dictionnaire des produits appartenants à cette catégorie. Ceci afin de pouvoir retrouver rapidement un produit appartenant à une catégorie.

Le code suivant est syntaxiquement correct, il compile même !

Pourtant son exécution est impossible. Quelle est l’erreur ?

var db = new NorthwindDataContext(); var q = from c in db.Categories select new { Name = c.CategoryName, Products = c.Products.ToDictionary(cat => cat.CategoryID) }; foreach (var c in q) { if (c.Products.ContainsKey(123)) Console.WriteLine(c.Name + " contient le produit d'id 123"); }


PS: c’est vendredi !! Prenez le temps de répondre et ne laissez pas tout à Matthieu ! :p

Comments (14)

  1. t’es sûr que tu ne t’es pas trompé ?

    Ton dico, sa clé ça va être le CategoryID. Donc le WriteLine ne me parait pas bon !

  2. Pour ce qui est de la réponse, je pense qu’il faut utiliser un AsEnumerable()

  3. Pour ma part, j’aurais fait ça :

    var db = new DataClasses1DataContext();

    var productsDico =

       db.Categories.AsEnumerable().ToDictionary(cat => cat.CategoryID, cat => cat.Products);

    var q = db.Categories;

    foreach (var c in q)

    {

       if (productsDico[c.CategoryID].Any(p=>p.ProductID == 123))

           Console.WriteLine(c.CategoryName + " contient le produit d’id 123");

    }

    mais je ne pense pas que ce soit ce que tu veux.

  4. ou plutôt ça (au cas où il y est des catégories sans products :

    var db = new DataClasses1DataContext();

    var productsDico =

       db.Categories.AsEnumerable().ToDictionary(cat => cat.CategoryID, cat => cat.Products);

    foreach (var c in db.Categories)

    {

       if (productsDico.ContainsKey(c.CategoryID) && productsDico[c.CategoryID].Any(p=>p.ProductID == 123))

           Console.WriteLine(c.CategoryName + " contient le produit d’id 123");

    }

  5. FredDenisot says:

    Mitsu, continu stp a poster tes quizz pendant que Matthieu déjeune, ce nous laisse un peu plus de temps pour réflechir avant la réponse 🙂

  6. Mitsu Furuta says:

    J’ai trouvé la solution ! J’ai activé la modération et je limiterai Matthieu à une seule réponse 🙂

  7. FredDenisot says:

    Mieux !

    tu me file les réponses discret et je te ré-invite à prendre une bierre 🙂

  8. Mais euh !

    Et puis si ton énoncé était plus compréhensible, je n’aurais pas eu besoin de plusieurs réponses :p

    A propos de réponse, j’ai pas encore vu la tienne (http://blogs.codes-sources.com/matthieu/archive/2008/08/08/comme-mitsu.aspx). Tu le dis si c’est trop compliqué :p

    Plus sérieusement, c’est ça que tu voulais ?

  9. Naboki says:

    J’ai quizz moi aussi…

    Je programme en C#2.0 et j’ai encore un peu de mal à appréhender le C# 3.0, linq et tout ça…

    Le problème est le suivant:

    Classer des chaines de caractères style: “A8, A1, B2, B10, A10, B11” d’abord par la lettre, ensuite par le nombre, de sorte que ça sorte dans cet ordre: “A1, A8, A10, B2, B10, B11”.

    Un simple “Sort()” ferai ça: “A1, A10, A8, B10, B11, B2” (classement “ASCII”), du coup j’avais écrit un algorithme compliqué qui séparait les chaines en deux chaines “lettre” et “nombre” (sachant qu’il peut y avoir plusieurs lettres et chiffres ex: AB20).

    Mais quand je vous vois faire des classements compliqués en une ligne de code Linq, je m’interroge… Ya-t-il une solution plus simple?

  10. Mitsu Furuta says:

    Ce que j’attendais était donc bien:

    var q =

       from c in db.Categories

       select c;

    var q2 =

       from c in q.AsEnumerable()

       select new { Name = c.CategoryName,

           Products = c.Products.ToDictionary(cat => cat.CategoryID) };

    q est bien une requête Linq to Sql car la source ‘db.Categories’ est bien une Table Linq to Sql. Linq to Sql tente donc de convertir l’intégralité de l’expression en SQL. Evidemment le select avec le dictionary n’est pas reconnu à l’exécution et la requête Linq échoue.

    La question porte donc bien sur l’isolation de la requête. Si vous voulez enchainer des requêtes Linq entre Sql, Xml, objets ou d’autres provider, pensez bien à isoler les requêtes.

    Pour ceci, deux solutions. Faire un q.ToList() afin de terminer la première requête et démarrer la seconde sur la liste résultante ou utiliser .AsEnumerable() qui ‘cassera’ la première expression et vous permettra d’être sûr que je reste définit une reqûete Linq to Object.

  11. “var q =

      from c in db.Categories

      select c;”

    Autant utiliser directement db.Categories.

  12. Mitsu Furuta says:

    Bien sûr Matthieu.

    Mais là ça permet à tout le monde de bien visualiser une requête simple que l’on peut facilement enrichire d’un ‘where’, un ‘order by’ ou autre.

    Tu comprends ? 🙂

  13. Olivier says:

    C’est pas du jeu ça… je viens de recevoir le flux rss que maintenant !

    Qqun sait-il pourquoi il y a tant de décalage ? Ou bien c’est une astuce de Matthieu pour éliminer la concurrence ? 🙂

Skip to main content