【问题标题】:Linq TotalCount of "Grandchildren" with null checks and property filters带有空检查和属性过滤器的“孙子”的 Linq TotalCount
【发布时间】:2015-04-28 20:45:36
【问题描述】:

给出以下类:

[DebuggerDisplay("CustomerKey = {CustomerKey}")]
public class Customer
{
    public Customer()
    {
        this.Orders = new List<Order>();
    }
    public int CustomerKey { get; set; }
    public ICollection<Order> Orders { get; set; }
}

[DebuggerDisplay("OrderKey = {OrderKey}, OrderDateOffset='{OrderDateOffset}'")]
public class Order
{
    public Order()
    {
        this.OrderDetails = new List<OrderDetail>();
    }
    public int OrderKey { get; set; }
    public Customer ParentCustomer { get; set; }
    public DateTimeOffset OrderDateOffset { get; set; }
    public ICollection<OrderDetail> OrderDetails { get; set; }
}

[DebuggerDisplay("OrderDetailKey='{OrderDetailKey}', ProductKey='{ProductKey}', Quantity='{Quantity}', UnitPrice='{UnitPrice}, BackOrdered='{BackOrdered}'")]
public class OrderDetail
{
    public int OrderDetailKey { get; set; }
    public Order ParentOrder { get; set; }
    public int ProductKey { get; set; }
    public int Quantity { get; set; }
    public Decimal UnitPrice { get; set; }
    public bool BackOrdered { get; set; }
}

鉴于以下水合作用:

            ICollection<Customer> customers = new List<Customer>();

            Customer customerOne = new Customer() { CustomerKey = 11111 };
            /**/
            Order orderOne = null;
            customerOne.Orders.Add(orderOne);/* note, the item added to the collection is null) */
            /**/
            Order orderTwo = new Order() { OrderKey = 22222, OrderDateOffset = DateTimeOffset.Now };
            orderTwo.OrderDetails = null;/* Note, the child collection is null */
            customerOne.Orders.Add(orderTwo);
            /**/
            Order orderThree = new Order() { OrderKey = 22223, OrderDateOffset = DateTimeOffset.Now };
            orderThree.OrderDetails.Add(null); /* note, the item added to the collection is null) */
            customerOne.Orders.Add(orderThree);
            /**/
            Order orderFour = new Order() { OrderKey = 22221, OrderDateOffset = DateTimeOffset.Now };
            orderFour.OrderDetails.Add(new OrderDetail() { OrderDetailKey = 33333, ProductKey = 11, Quantity = 1, UnitPrice = 1M, BackOrdered = false });
            orderFour.OrderDetails.Add(new OrderDetail() { OrderDetailKey = 33334, ProductKey = 12, Quantity = 2, UnitPrice = 2M, BackOrdered = false });
            orderFour.OrderDetails.Add(new OrderDetail() { OrderDetailKey = 33335, ProductKey = 13, Quantity = 3, UnitPrice = 3M, BackOrdered = true });
            customerOne.Orders.Add(orderFour);

            customers.Add(customerOne);
            customers.Add(null);/* note, the item added to the collection is null) */

我正在尝试获取所有延期交货 OrderDetail(s) 的总数。

我的 pre-linq 代码:

            int totalBackOrderedCount = 0;
            if (null != customers)
            {
                foreach (Customer cust in customers)
                {
                    if (null != cust)
                    {
                        if (null != cust.Orders)
                        {
                            foreach (Order ord in cust.Orders)
                            {
                                if (null != ord)
                                {
                                    if (null != ord.OrderDetails)
                                    {
                                        foreach (OrderDetail ordDet in ord.OrderDetails)
                                        {
                                            if (null != ordDet)
                                            {
                                                if (ordDet.BackOrdered)
                                                {
                                                    totalBackOrderedCount++;
                                                }
                                            }
                                            else
                                            {
                                                Console.WriteLine("ordDet was null, good thing I coded for it");
                                            }
                                        }
                                    }
                                    else
                                    {
                                        Console.WriteLine("ord.OrderDetails was null, good thing I coded for it");
                                    }
                                }
                                else
                                {
                                    Console.WriteLine("ord was null, good thing I coded for it");
                                }
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("cust was null, good thing I coded for it");
                    }
                }
            }

            Console.WriteLine("totalBackOrderedCount={0}", totalBackOrderedCount);

我在 linq 替代方案上的尝试:

            int linqyTotalBackOrderedCountOne =
               (
                 from cust in customers
                 from ord in (cust.Orders ?? Enumerable.Empty<Order>()).DefaultIfEmpty()
                 from ordDet in (ord.OrderDetails.Where(od => od.BackOrdered == true) ?? Enumerable.Empty<OrderDetail>()).DefaultIfEmpty()
                 where cust != null && (null != ord) && (null != ordDet)
                 select ordDet
               ).Count();

            int linqyTotalBackOrderedCountTwo =
                   (
                     from cust in customers
                     from ord in (cust.Orders ?? Enumerable.Empty<Order>()).DefaultIfEmpty()
                     from ordDet in (ord.OrderDetails.Where(od => od.BackOrdered == true) ?? Enumerable.Empty<OrderDetail>()).DefaultIfEmpty()
                     where cust != null && (null!=cust.Orders) && (null!=ord) && (null!=ord.OrderDetails) && (null!=ordDet)
                     select ordDet
                   ).Count();

在 linq 中是否有一种“null 友好”的方式来获取孩子的孩子...?

【问题讨论】:

    标签: c# linq c#-4.0 linq-to-objects


    【解决方案1】:

    你应该能够做这样的事情而不必担心空值。

    var count = customers.Where (c => c.Orders != null)
        .SelectMany (c => c.Orders.Where(o => o.OrderDetails != null)
            .SelectMany (o => o.OrderDetails))
        .Count (c => c.BackOrdered);
    

    以上内容很接近......这是最终答案:

                int itWorksCount = customers.Where(c => null != c && null != c.Orders)
                        .SelectMany(c => c.Orders.Where(o => null != o && null != o.OrderDetails)
                            .SelectMany(o => o.OrderDetails.Where(ordDet => null != ordDet)))
                        .Count(c => c.BackOrdered);
    

    【讨论】:

    • 对不起,伙计。 “对象引用未设置为对象的实例。”
    • @granadaCoder 你是对的,它是固定的。看起来需要进行空检查。
    • (更改后,不再重复评论) ::: 对不起老兄。仍然得到:“对象引用未设置为对象的实例。”
    【解决方案2】:

    没有ICollection&lt;T&gt; 属性是null。绝对没有理由在其中包含 null 值,因为您也可以有一个空集合(您甚至可以在构造函数中实例化它,因此您不会节省内存)。

    重新设计CustomerOrder 类,使OrdersOrderDetails 属性的设置器是私有的。您的序列化应该能够处理这个(实体框架/NHibernate/数据契约至少可以)。这可以防止其他代码将集合设置为null

    另外,为什么你允许在集合中插入 null 值? null 值代表什么。是否有未完成的订单或某事的占位符?如果是这种情况,请输入一些代表缺失订单/订单详细信息的值。

    修复您的设计后,使用 Gary。 S的原答案:

    int count =  customers.SelectMany(c => c.Orders)
                          .SelectMany(o => o.OrderDetails)
                          .Count(od => od.BackOrdered);
    

    请注意,总是有一种对 null 友好的方式来处理所有事情,但这会使代码过于复杂且难以维护。

    【讨论】:

    • 这是遗留代码老兄......显然没有空值是理想的情况......但它并不总是可用。我编辑了 Gary 的代码......现在它可以工作......所有的 null 和所有。
    • @granadaCoder 听起来像错误代码,如果集合被重置为 null 并且 null 值被插入到集合中。它可能隐藏了一些更大的问题。只是说,您总是可以为所有内容添加空检查,但这很可能隐藏了问题。
    猜你喜欢
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2017-03-24
    相关资源
    最近更新 更多