导航属性未评估EF核心

问题描述 投票:0回答:1

我在EF Core中遇到一个问题,我试图获得一个相关的实体及其所有的依赖结构,但是它没有取得多大成功。

目前,我有这样的查询:

var user = new Guid(id);
var userCustAffs = _data.UserCustomerAffiliation.Include(x => x.Customer)
                     .ThenInclude(x => x.Brand).Where(x => x.UserId.Equals(user)).ToList();
var result =  userCustAffs.Select(p => p.Customer).ToList();

当我应该能够做这样的事情来简化它(并删除本地评估的不必要的东西与db)

var user = new Guid(id);
var userCustAffs = _data.UserCustomerAffiliation.Include(x => x.Customer)
                               .ThenInclude(x => x.Brand).Where(x => x.UserId.Equals(user))
                               .Select(y => y.Customer).ToList();

但是,当我执行后一个查询时,我收到一个错误

The Include operation for navigation '[x].Customer.Brand' is unnecessary and was ignored 
because the navigation is not reachable in the final query results

但是,Brand非常重要,因为它驱动了一些客户模型的属性。重构此查询的正确方法是什么,以便获得我想要的结果(例如,客户及其相关品牌,受UserCustomerAffiliation表附属的userId限制)。

我之前看到过一个建议,即从客户“开始”查询而不是UserCustomerAffiliation,但这似乎与我从数据库优化角度看到的每一个本能相反(而且客户没有返回到UserCustomerAffiliation atm的导航属性)。

ef-core-2.0
1个回答
0
投票

为什么会发生这种情况(经过一些研究后)的答案非常有趣,并且为什么了解EF Core如何工作对于使用它很重要。

Linq总体上研究延迟执行的想法。简单地说,如果我在特定行上创建Linq语句,则在“需要”数据之前可能无法对其进行评估或执行。大多数情况下,我们使用强制立即执行的.ToList()来快捷。这里的一般想法是,有时不需要数据集(例如,如果在评估之前发生异常,但在“加载”之后发生异常)。

EF Core更进一步,将延迟执行的想法与数据库优化联系起来。例如,如果我从数据库中获取数据的子集:

var result = _context.SomeTable.Where(x => x.name == "SomeValue");

但后来我关心的是数据集的大小:

return result.Count;

可以优化DB调用

select count(*) from SomeTable where name = "SomeValue"; 

代替

select * from SomeTable where name = "SomeValue";

同样,我上面的查询正在被优化掉。因为我在评估之前将整个事物链接起来,所以EF Core优化器扔掉了我需要的表。

这有效的原因:

var user = new Guid(id);
var userCustAffs = _data.UserCustomerAffiliation.Include(x => x.Customer)
                                         .ThenInclude(x => x.Brand).Where(x => 
                                         x.UserId.Equals(user)).ToList();
var result =  userCustAffs.Select(p => p.Customer).ToList();

是因为我强制执行类似的查询

Select u.*, c.*, b.* from usercustomeraffiliation u, 
        inner join Customer c  on u.customerid = c.id 
        inner join Brand b on c.brandid = c.id 
        where u.userid = 'userId';

然后在内存中删除客户对象(及其下面的品牌对象)。能够生成如下查询会更有效:

Select c.*, b.* from Customer c  on u.customerid = c.id 
        inner join Brand b on c.brandid = c.id 
        where c.id in (select u.customerid from usercustomeraffiliation u 
                              where u.userid = 'userId');

但是,这会得到优化。

© www.soinside.com 2019 - 2024. All rights reserved.