如何使用Include()方法仅获取第一个元素?

5
我想获取所有汽车的信息,但只需要每辆汽车的最新报告,而不是全部汽车和所有报告。一辆汽车可能有多个报告。
// with this i would get all cars with all reports for each car.
// How can i get all cars with only the last report for each car?
context.Cars.Include("Reports");

我尝试了类似这样的代码:

我尝试了类似这样的代码:

context.Cars.Include(c => c.Reports.OrderByDescending(r => r.Id).FirstOrDefault())
.Take(10)
.ToList();

但是这并没有起作用。


2
我认为编写Include()函数的人不太可能认为这种优化值得实现。请使用两个查询。 - Gary McGill
请使用.Select(...)代替。 - Gene R
@GeneR,能否请您提供一个示例?即使是更抽象的示例也可以。 - Jo Smo
@GaryMcGill,你能给我展示一个例子吗?因为我想不出一个“合理”的例子。 :/ - Jo Smo
3个回答

8

我同意@GaryMcGill的观点,Include扩展方法无法部分加载导航属性。你可以使用显式加载来代替:

var car=yourPreviousQuery.FirstOrDefault(); // This is just an example to have a Car instance
context.Entry(car) 
    .Collection(b => b.Reports) 
    .Query().OrderByDescending(r => r.Id).Take(1) 
    .Load(); 

4
.Select(p => new
{
    Car = p,
    Report = p.Reports.OrderByDescending(r => r.Id).FirstOrDefault()
})

这将提供一个匿名对象列表,您可以将其转换为 IEnumerable<Cars>

1
虽然这个答案和@octavioccl的答案都能达到相同的结果,但是@octavioccl的更好,因为它明确了正在发生的事情。在这里,包含隐式地发生以满足Select,但很容易忘记或忽视这一点。此外,使用带有过滤器的显式加载可以让您保留实体类型,而无需转换一组匿名对象。 - Chris Pratt

1
假设您有这样的Dto / Viewmodels:

public class CarDto
{
    public int Id { set; get; }
    public string Name { set; get; }
}
public class ReportDto
{
    public int Id { set; get; }
    public string Name { set; get; }
}

public class CatSimpleDto
{
    public CarDto Car { set; get; }
    public ReportDto FirstPost { set; get; }
}

您可以按照 报告 的降序(按ID或插入时间戳等)进行排序,并获取第一项。
var carsWithFirstReport= db.Cars.
Select(s => new CatSimpleDto
{
    Car = new CarDto { Id = s.Id, Name = s.Name },
    FirstPost = s.Reports.OrderByDescending(f => f.Id).Take(1)
    .Select(x => new ReportDto
    {
        Id = x.Id,
        Name = x.Name
    }).FirstOrDefault()
}).ToList();

将投影到DTO / POCO将消除延迟执行(因此在访问导航属性时执行更多的数据库查询)与选择由实体框架创建的实体相比。了解更多这里

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