代码之家  ›  专栏  ›  技术社区  ›  Marshall Tigerus

导航属性未评估ef core

  •  0
  • Marshall Tigerus  · 技术社区  · 6 年前

    我有一个问题,在英孚核心,我试图获得一个相关的实体和它的所有依赖结构,但没有太大的成功与它。

    目前,我有一个这样的查询:

    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();
    

    当我能够做这样的事情来简化它的时候(并删除在本地对数据库进行评估的不必要的事情)

    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
    

    然而,品牌是非常重要的,因为它将一些特性从客户模型中剔除。重新构造此查询以便获得所需结果的正确方法是什么(例如,具有相关品牌的客户,受用户客户关联表上的用户ID的限制)。

    我以前看到过一个建议,建议从客户而不是用户自定义查询“开始”查询,但从数据库优化的角度来看,这似乎与我的直觉相反(而且客户没有返回到用户自定义查询ATM的导航属性)。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Marshall Tigerus    6 年前

    为什么会发生这种情况(在一些研究之后)的答案是非常有趣的,并且是一个很好的例子,说明为什么知道ef核心如何工作对于使用它是很重要的。

    Linq一般都在研究延迟执行的思想。简单地说,如果我在一个特定的行上创建一个LINQ语句,在数据“需要”之前,它可能不会被计算或执行。这里的一般思想是,有时不需要数据集(例如,如果异常在评估之前发生,但在“加载”之后发生)。

    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核心优化器丢弃了一个我需要的表。

    这样做的原因是:

    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');
    

    但是,这会被优化掉。