Linq TotalCount of "Grandchildren" with null checks and 属性 filters
Linq TotalCount of "Grandchildren" with null checks and property filters
给出以下内容 类:
[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 的总数。
我的 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 friendly" 方法来获取子项的子项...?
你应该能够做这样的事情而不需要担心空值。
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);
没有 ICollection<T>
个属性是 null
。绝对没有理由在那里有 null
值,当你也可以有一个空集合(你甚至在构造函数中实例化它,所以你没有节省内存)。
重新设计您的 Customer
和 Order
类 以便 Orders
和 OrderDetails
属性 的 setter 是私人的。您的序列化应该能够处理这个(Entity Framework / NHibernate / Data Contracts 至少可以)。这可以防止其他代码将集合设置为 null
.
此外,为什么允许将 null
值插入到集合中? null
值代表什么。是否有未完成的订单,某物的占位符?如果是这种情况,请输入一些表示缺少订单/订单详细信息的值。
修改设计后,请使用 Gary。 S的原回答:
int count = customers.SelectMany(c => c.Orders)
.SelectMany(o => o.OrderDetails)
.Count(od => od.BackOrdered);
请注意,总是有一种 null 友好的方式来做所有事情,但这会使代码过于复杂且难以维护。
给出以下内容 类:
[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 的总数。
我的 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 friendly" 方法来获取子项的子项...?
你应该能够做这样的事情而不需要担心空值。
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);
没有 ICollection<T>
个属性是 null
。绝对没有理由在那里有 null
值,当你也可以有一个空集合(你甚至在构造函数中实例化它,所以你没有节省内存)。
重新设计您的 Customer
和 Order
类 以便 Orders
和 OrderDetails
属性 的 setter 是私人的。您的序列化应该能够处理这个(Entity Framework / NHibernate / Data Contracts 至少可以)。这可以防止其他代码将集合设置为 null
.
此外,为什么允许将 null
值插入到集合中? null
值代表什么。是否有未完成的订单,某物的占位符?如果是这种情况,请输入一些表示缺少订单/订单详细信息的值。
修改设计后,请使用 Gary。 S的原回答:
int count = customers.SelectMany(c => c.Orders)
.SelectMany(o => o.OrderDetails)
.Count(od => od.BackOrdered);
请注意,总是有一种 null 友好的方式来做所有事情,但这会使代码过于复杂且难以维护。