2009-09-04 18 views
3

Voici une requête LINQ simple (basée sur NorthWind) qui renvoie une liste de clients. Chaque client contient une liste de commandes.Requêtes hiérarchiques dans LINQ

from c in Customers 
join o in Orders on c.CustomerID equals o.CustomerID into CO 
select new {c, CO} 

Cela fonctionne très bien et le SQL généré est également bon. Maintenant, je veux aller un peu plus loin. Je veux que chaque objet Order contienne une liste de OrderDetails. J'utilise la requête suivante:

from c in Customers 
join od in (
    from o in Orders 
    join od in OrderDetails on o.OrderID equals od.OrderID into OD 
    select new { o.CustomerID, o, OD } 
) 
on c.CustomerID equals od.CustomerID into COD 
select new { c, COD } 

Cette requête fonctionne mais génère un code SQL horrible. Une requête distincte est émise pour chaque client. Lorsque vous regardez le code lambda, nous avons:

Customers 
    .GroupJoin (
     Orders 
     .GroupJoin (
      OrderDetails, 
      o => o.OrderID, 
      od => od.OrderID, 
      (o, OD) => 
       new 
       { 
        CustomerID = o.CustomerID, 
        o = o, 
        OD = OD 
       } 
     ), 
     c => c.CustomerID, 
     od => od.CustomerID, 
     (c, COD) => 
     new 
     { 
      c = c, 
      COD = COD 
     } 
    ) 

Les GroupJoins imbriquées semblent être la cause des multiples stataments SQL. Cependant, j'ai essayé différentes combinaisons sans succès. Des idées?

EDIT: J'ai peut-être été un peu peu clair sur ce que j'essayais de réaliser. Je veux que l'objet OrderDetail soit une propriété de l'objet Order, qui est à son tour une propriété de l'objet Customer. Je ne veux pas que l'Order & OrderDetail soit une propriété du Client. J'essaie d'obtenir la liste des clients uniques. Pour chaque client, je m'attends à une liste de commandes et pour chaque commande, je veux une liste de OrderDetails. Je souhaite que la hiérarchie soit plus profonde que ma requête initiale.

Répondre

0

Pourquoi ne pas simplement utiliser plusieurs jointures:

from c in Customers 
join o in Orders on c.CustomerID equals o.CustomerID 
join od in OrderDetails on o.OrderID equals od.OrderID 
select new {c, o, od} 
+0

Bien que je pense que cette requête pourrait être post-traitement pour obtenir ce dont il a besoin, la question initiale implique qu'il veut un seul résultat par 'Client', où chaque résultat contient une liste de' OrderDetails'. – jeremyalan

0

Vous pouvez essayer de rejoindre jusqu'à la OrderDetails sur le serveur, puis « regrouper » sur le client pour obtenir votre structure de données hiérarchique complète:

var q = from c in Customers 
     join o in Orders on c.CustomerID equals o.CustomerID 
     join od in OrderDetails on o.OrderID equals od.OrderID into OD 
     select new { c, o, OD }; 

var res = from x in q.AsEnumerable() 
      group x by x.c.CustomerID into g 
      select new 
      { 
       Customer = g.First().c, 
       Orders = g.Select(y => new 
         { 
          Order = y.o, 
          OrderDetails = y.OD 
         }) 
      }; 
1

Si vous voulez forcer une seule requête, vous pouvez effectuer le regroupement sur le côté client non sur Serverside:

from a in (from c in Customers 
    join o in Orders on c.CustomerID equals o.CustomerID 
    join od in OrderDetails on o.OrderID equals od.OrderID 
    select new {c, o, od}).AsEnumerable() 
group a by a.c into g 
select new { Customer = g.Key, Orders = g.Select(o => o.o) , OrderDetails = g.Select(od => od.od)} 

Le SQL généré est:

SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax], [t1].[OrderID], [t1].[CustomerID] AS [CustomerID2], [t1].[EmployeeID], [t1].[OrderDate], [t1].[RequiredDate], [t1].[ShippedDate], [t1].[ShipVia], [t1].[Freight], [t1].[ShipName], [t1].[ShipAddress], [t1].[ShipCity], [t1].[ShipRegion], [t1].[ShipPostalCode], [t1].[ShipCountry], [t2].[OrderID] AS [OrderID2], [t2].[ProductID], [t2].[UnitPrice], [t2].[Quantity], [t2].[Discount] 
FROM [Customers] AS [t0] 
INNER JOIN [Orders] AS [t1] ON [t0].[CustomerID] = [t1].[CustomerID] 
INNER JOIN [Order Details] AS [t2] ON [t1].[OrderID] = [t2].[OrderID] 

Ours à l'esprit que c'est pas plus rapide que plusieurs requêtes et en même temps, il augmente la charge du réseau et du serveur.

Ma recommandation serait d'utiliser une requête comme suit:

DataLoadOptions opt = new DataLoadOptions(); 
opt.LoadWith<Orders>(o => o.OrderDetails); 
this.LoadOptions = opt; 

from c in Customers 
select new {c, Orders = c.Orders, OrderDetails = c.Orders.SelectMany(o=> o.OrderDetails)}