![](/img/trans.png)
[英]C# Linq NULLReferenceException when searching on value inside list
[英]Searching for an item in a list that's nested inside another list based on a property value in C# using LINQ?
有没有办法使用 LINQ 根据属性值在嵌套在另一个列表中的列表中搜索项目?
鉴于下面的模型,对于给定的订单(变量customerOrder
),我想返回最早的订单日期( Date
),其中 Day 是“Sunday”。
楷模:
public class Order
{
public int Id { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
public string Description { get; set; }
public List<OrderDate> OrderDates { get; set; }
}
public class OrderDate
{
public DateTime Date { get; set; }
public string Day { get; set; }
}
代码:
var dates = new List<DateTime>();
foreach(var a in customerOrder.OrderLines)
{
var orderDate = a.OrderDates.Where(x => x.DateTypeId.Equals("Sunday")).FirstOrDefault();
dates.Add(orderDate.ActualDate);
}
dates.OrderBy(d => d.Date);
return dates.FirstOrDefault();
编辑更优雅的查询
您可以使用 Linq 来实现您的结果。
这是一个与您的代码非常相似的查询。
customerOrder.OrderLines
.Select(ol => ol.OrderDates
.Where(x => x.Day.Equals("Sunday"))
.FirstOrDefault())
.Where(d => d != null)
.OrderBy(d => d.Date)
.FirstOrDefault();
可以更优雅地重写为:
customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.OrderBy(d => d.Date)
.FirstOrDefault(d => d.Day == "Sunday");
这是一个带有一些测试数据和转储的 Linqpad 查询供您尝试。 只需复制并粘贴到 Linqpad 中。
void Main()
{
var customerOrder = new Order
{
Id = 1,
OrderLines = Enumerable
.Range(0, 10)
.Select(i => new OrderLine
{
Description = $"Line Description {i}",
OrderDates = Enumerable.Range(0, 10)
.Select(j => new OrderDate
{
Date = DateTime.Now.AddDays(i+j),
Day = DateTime.Now.AddDays(i+j).DayOfWeek.ToString()
})
.ToList()
})
.ToList()
}
.Dump();
customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.OrderBy(d => d.Date)
.FirstOrDefault(d => d.Day == "Sunday")
.Dump();
}
// You can define other methods, fields, classes and namespaces here
public class Order
{
public int Id { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
public string Description { get; set; }
public List<OrderDate> OrderDates { get; set; }
}
public class OrderDate
{
public DateTime Date { get; set; }
public string Day { get; set; }
}
另一方面, OrderDate
class 不是必需的。 DateTime
类型有一个属性DayOfWeek
,您可以使用它来测试 Date 是否为 Sunday。
DayOfWeek
是一个枚举,因此您可以简单地测试MyDate.DayOfWeek == DayOfWeek.Sunday
而不是为此目的依赖string
。
首先,您的代码将无法按预期工作。 dates.OrderBy(d => d.Date);
不起作用: OrderBy
返回一个 IEnumerable,它不会更改原始集合。 您应该使用List.Sort
` 或这样做:
dates = dates.OrderBy(d => d.Date).ToList()
其次,您使用FirstOrDefault
:它有一个接受谓词进行搜索的重载; 所以不需要Where
调用。 此外,如果没有找到,FirstOrDefault 将返回null
。 如果这是可能的情况,您应该考虑检查orderDate
是否为 null:
var dates = new List<DateTime>();
foreach(var a in customerOrder.OrderLines)
{
var orderDate = a.OrderDates.FirstOrDefault(x => x.DateTypeId.Equals("Sunday"));
if (orderDate is {})
{
dates.Add(orderDate.ActualDate);
}
}
dates = dates.OrderBy(d => d.Date).ToList();
return dates.FirstOrDefault();
那应该可以正常工作。 但是很难猜测代码示例的行为的哪些方面是有意的,哪些不是。 您询问搜索,但对 OrderBy 部分只字未提。 你能澄清一下这部分吗?
回答这个问题,如果你的意思是better
紧凑的方式,你可以用这样的东西 go :
var result = customerOrder.OrderLines
.SelectMany(a => a.OrderDates)
.OrderBy(d => d.Date)
.FirstOrDefault(x => x.DateTypeId.Equals("Sunday"));
return result;
你现在不应该为better way
而烦恼; 首先,您应该至少从working way
开始。 我建议你学习如何使用 Linq 和不使用 Linq 来做这些事情。
Better 有点主观,但您可以使用Enumerable.SelectMany
扩展方法将OrderDate
实例扁平化为一个序列。
然后,您可以使用Enumerable.Where
扩展方法来过滤"Sunday"
的日期。
然后您可以使用Enumerable.Min
扩展方法来获取最小日期。
所有这些都可以链接到一个语句中。
DateTime earliestSunday = customeOrder
.OrderLines
.SelectMany(ol => ol.OrderDates)
.Where(od => od.Day == "Sunday")
.Min(od => od.Date);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.