2009-10-15 8 views
2

Je suis nouveau à linq et j'ai du mal à écrire deux requêtes simples. Pour une raison quelconque, je ne peux pas envelopper ma tête autour d'elle.Comment interroger une collection d'objets dans LINQ par une propriété enfant?

sa structure simple: une commande a OrderItems. Chaque orderItem a un productID.

Je voudrais:

  1. obtenir toutes les commandes qui ont ordonné productId 3

  2. obtenir toutes les commandes qui ont ordonné productId 4 et 5 du même ordre.

Je l'ai essayé de plusieurs façons. les deux requêtes sont au bas de la petite application de test.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      OrderService svc = new OrderService(); 

      //find all orders that purchased ProductID 3 
      IEnumerable<Order> data = svc.GetOrdersWithProduct(3); 

      //find all orders that purchase product 4 AND 5 
      IEnumerable<Order> data2 = svc.GetOrdersWithProduct(new int[] { 4, 5}); 
     } 
    } 

    public class Order 
    { 
     public int OrderId { get; set; } 
     public IEnumerable<OrderItem> Items { get; set; } 
    } 

    public class OrderItem 
    { 
     public int OrderItemId { get; set; } 
     public int OrderId { get; set; } 
     public int ProductId { get; set; } 
    } 

    public class OrderService 
    { 
     private static List<Order> GetTestData() 
     { 
      List<Order> orders = new List<Order>(); 

      //5 Orders, 3 items each (every orderitem has a unique product in this test set) 
      int orderitemid = 1; 
      int productid = 1; 
      for (int orderid = 1; orderid < 6; orderid++) 
      { 
       orders.Add(new Order 
       { 
        OrderId = orderid, 
        Items = new List<OrderItem> 
               { 
                new OrderItem() { OrderId = orderid, OrderItemId = orderitemid++, ProductId = productid ++ }, 
                new OrderItem() { OrderId = orderid, OrderItemId = orderitemid++, ProductId = productid ++ }, 
                new OrderItem() { OrderId = orderid, OrderItemId = orderitemid++, ProductId = productid ++ } 
               } 
       }); 

      } 
      return orders; 
     } 

     public IEnumerable<Order> GetOrdersWithProduct(int productId) 
     { 
      List<Order> orders = OrderService.GetTestData(); 

      // ?? not really what i want, since this returns only if all items are the same 
      var result = orders.Where(o => o.Items.All(i => i.ProductId == productId)); 

      return result.ToList(); 
     } 

     public IEnumerable<Order> GetOrdersWithProduct(IEnumerable<int> productIds) 
     { 
      List<Order> orders = OrderService.GetTestData(); 

      //?? 
      var result = orders.Where(o => o.Items.All(productIds.Contains(i => i.ProductId))); 

      return result.ToList(); 
     } 
    } 
} 

Répondre

6

je le ferais comme ceci:

  1. Obtenez toutes les commandes qui ont ordonné productId 3

    var result = orders.Where(o => o.Items.Any(item => item.ProductId == 3)); 
    
  2. Obtenez toutes les commandes qui ont ordonné productId 4 et 5

    var result = orders.Where(o => o.Items.Any(item => item.ProductId == 4)) 
            .Where(o => o.Items.Any(item => item.ProductId == 5)); 
    

Ou:

public static IEnumerable<Order> GetOrdersWithProduct(int id) 
{ 
    return orders.Where(o => o.Items.Any(item => item.ProductId == productId)); 
} 

Puis:

var result1 = GetOrdersWithProduct(3); 
var result2 = GetOrdersWithProduct(4).Intersect(GetOrdersWithProduct(5)); 

Une autre alternative:

public static IEnumerable<Order> GetOrdersWithProducts(params int[] ids) 
{ 
    return GetOrdersWithProducts((IEnumerable<int>) ids); 
} 

public static IEnumerable<Order> GetOrdersWithProducts(IEnumerable<int> ids) 
{ 
    return orders.Where(o => !ids.Except(o.Items.Select(p => p.ProductId)) 
           .Any()); 
} 

var result1 = GetOrdersWithProducts(3); 
var result2 = GetOrdersWithProduct(4, 5); 
+0

parfait, merci! Je suppose que je n'étais pas trop loin. Existe-t-il un moyen pour la collection OrderItems d'avoir uniquement les ID de produit correspondants? disons que je voulais une collection de commandes où les seuls articles de commande dans chaque commande étaient égaux aux produits que j'ai passés. –

+0

Voulez-vous dire que vous voulez filtrer toutes les commandes qui ont eu d'autres produits, ou que vous voulez créer une nouvelle copie de la commande mais avec seulement les produits pertinents? –

+0

Créer une nouvelle copie avec seulement les produits pertinents. –

1

Sans Linq "Language-Integrated Query" Syntaxe:

public IEnumerable<Order> GetOrdersWithProduct(int productId) 
    { 
     List<Order> orders = OrderService.GetTestData(); 

     var result = orders.Where(o => o.Items.Any(i => i.ProductId == productId)); 

     return result.ToList(); 
    } 

    public IEnumerable<Order> GetOrdersWithProduct(IEnumerable<int> productIds) 
    { 
     List<Order> orders = OrderService.GetTestData(); 

     var result = orders.Where(o => productIds.All(id => o.Items.Any(i => i.ProductId == id))); 

     return result.ToList(); 
    } 

que le canard Semblent Boiteux fait la version "Langue-Integrated Query" je ne vais pas le faire.

+1

De quelle façon est que "sans LINQ"? Où/All/Any sont toutes les méthodes LINQ. Voulez-vous dire "ne pas utiliser la syntaxe d'expression de requête"? –

+0

Oui désolé, je voulais dire sans les requêtes intégrées linq. J'ai déjà eu de telles méthodes dans la plupart de mon code C# 2.0 (Seulement avec certaines différences de nom comme Map/Reduce comme Select/Aggregate et évidemment pas comme méthodes d'extension) ils sont simplement une définition par défaut pratique de certaines fonctions de base. Le fait qu'ils soient dans l'espace de noms Sytem.Linq me semble un peu anecdotique. –

0

Vérifiez-les:

1.

from order in orders 
where order.Items.Exists(item => item.OrderItemId == 3) 
select order 
  1. Très similaire:

    de l'ordre dans les ordres où order.Items.Exists (item => item.OrderItemId == 3) & &
    order.Items.Exists (item => item.OrderItemId == 4) sélectionner un ordre

0
public IEnumerable<Order> GetOrdersWithProduct(int productId) 
    { 
     List<Order> orders = OrderService.GetTestData(); 

     // ?? not really what i want, since this returns only if all items are the same 
     //var result = orders.Where(o => o.Items.All(i => i.ProductId == productId)); 
     var result = orders.Where(o => o.Items.Any(ii => ii.ProductId == productId)); 

     return result.ToList(); 
    } 
Questions connexes