如何通过LINQ查询子属性来查询对象集合?

3

我对linq不熟悉,写两个简单的查询有困难。出于某种原因,我无法理解。

这是一个简单的结构:订单有订单项。每个订单项都有一个产品ID。

我想要:

  1. 获取所有订购productId 3的订单

  2. 获取在同一订单上订购了productId 4和5的所有订单。

我已经尝试了多种方法。两个查询在测试应用程序的底部。

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();
        }
    }
}
4个回答

7
我会像这样做:

我会这样做:

  1. Get all orders that ordered productId 3

    var result = orders.Where(o => o.Items.Any(item => item.ProductId == 3));
    
  2. Get all orders that ordered productId 4 and 5

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

或者:

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

然后:

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

另一种选择:
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);

有没有一种方法让OrderItems集合只包含匹配的productIDs?比如说,我想要一个Orders集合,其中每个订单中的OrderItems都等于我传入的productIds。谢谢!我猜我离答案不远了。 - user190792
你的意思是要过滤掉任何包含其他产品的订单,还是要创建一个只包含相关产品的新订单副本? - Jon Skeet
创建一个只包含相关产品的新副本。 - user190792
好的,我会尽量找时间稍后处理。 - Jon Skeet

1

没有 Linq "语言集成查询" 语法:

    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();
    }

看起来 The Lame Duck 正在做“语言集成查询”的版本,所以我不会做了。


1
那个“没有使用LINQ”的意思是什么?Where/All/Any都是LINQ的方法。你是指“不使用查询表达式语法”吗? - Jon Skeet
是的,抱歉我指的是没有Linq集成查询。我已经在我的大多数C# 2.0代码中使用了这样的方法(只有一些名称不同,如Map/Reduce作为Select/Aggregate,显然不是作为扩展方法),所以很难对“linq”有什么具体看法,它们只是一些基本函数的方便默认定义。它们在Sytem.Linq命名空间中的事实对我来说似乎有点轶闻趣闻。 - Julien Roncaglia

0

请看这些:

1.

from order in orders 
where order.Items.Exists(item => item.OrderItemId == 3)
select order

非常相似: 从订单中的顺序 其中订单.项.存在 ( 项 => 项.订购项ID == 3 ) && 订单.项.存在 ( 项 => 项.订购项ID == 4 ) 选择订单

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( );
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接