C# EF Core 5-查询中Include()/Where()方法的顺序重要吗?
我正在从.NET Core 2.2升级到.NET 5.0,一次只升级一个主要版本(2.2->3.0->3.1->5.0),我有一个LINQ查询到一个MySQL数据库,在升级到5.0之后,它的工作方式有所不同。查询在2.2、3.0和3.1中运行良好,但在升级到5.0之后,其中一个C# EF Core 5-查询中Include()/Where()方法的顺序重要吗?,c#,entity-framework,linq,entity-framework-core,.net-5,C#,Entity Framework,Linq,Entity Framework Core,.net 5,我正在从.NET Core 2.2升级到.NET 5.0,一次只升级一个主要版本(2.2->3.0->3.1->5.0),我有一个LINQ查询到一个MySQL数据库,在升级到5.0之后,它的工作方式有所不同。查询在2.2、3.0和3.1中运行良好,但在升级到5.0之后,其中一个Include()调用似乎没有任何效果。查询是: var adminClient = (this._context.AdminUserClients .Include(f => f.Client) .
Include()
调用似乎没有任何效果。查询是:
var adminClient = (this._context.AdminUserClients
.Include(f => f.Client)
.Where(f => f.User.Id == user.Id && f.Client.Id != 1)
.Include(f => f.Client.ClientUserRoles) // This Include does not seem to have an effect
.ThenInclude(f => f.User)
.FirstOrDefault())?.Client;
(有关模型,请参见下文。)
当它在EF Core 3.1中运行时(2.2和3.0类似),它会生成一个包含两个子查询的SQL语句,一个子查询用于连接AdminUserClient
、AspNetUsers
、和Client
,另一个子查询用于连接ClientUserRoles
和AspNetUsers
。然后,它连接两个子查询以生成结果。在EF Core 5.0中,生成的SQL语句不引用ClientUserRoles
——它本质上只是3.1 SQL语句中的第一个子查询
如果我修改查询以在Include()
调用之后移动Where()
调用,它将工作:
var adminClient = (this._context.AdminUserClients
.Include(f => f.Client)
.Include(f => f.Client.ClientUserRoles) // This Include runs fine
.ThenInclude(f => f.User)
.Where(f => f.User.Id == user.Id && f.Client.Id != 1)
.FirstOrDefault())?.Client;
在本例中,生成的SQL语句实际上与3.1中生成的SQL语句相同
我不知道这为什么会有不同。在Where()
方法中引用User
对象之前,可能需要包含该对象?但这对我来说没有意义,因为(1)它在2.2、3.0和3.1中工作,(2)我的理解是方法的顺序相对于Include()
和Where()
方法的顺序不应该影响返回集(尽管我知道它会影响性能)
问题
Include()
和Where()
方法的顺序是否有问题UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)
来模拟2.2行为。但是,我测试了:
AsSplitQuery()
添加到查询中useQuerySplitingBehavior(QuerySplitingBehavior.SingleQuery)
useQuerySplitingBehavior()
。
在所有这些情况下,行为都是一样的公共类AdminUserClient
{
公共长Id{get;set;}
[必需]
公共应用程序用户{get;set;}
[必需]
公共客户端{get;set;}
[必需]
public DateTime CreatedOn{get;set;}
}
公共类应用程序用户:IdentityUser
{
公共用户通知设置通知设置{get;set;}
[JsonIgnore]
公共ClientUserRole ClientUserRole{get;set;}
公共布尔锁定{get;set;}
公共字符串名称{get;set;}
}
公共类客户端
{
公共长Id{get;set;}
公共字符串名称{get;set;}
公共字符串说明{get;set;}
公共bool RequireTwoFactor{get;set;}
公共应用程序用户由{get;set;}创建
public DateTime CreatedOn{get;set;}
[JsonIgnore]
公共ICollection ClientUserRoles{get;set;}
公共布尔被删除{get;set;}
公共布尔锁定{get;set;}
}
公共类ClientUserRole
{
公共长Id{get;set;}
[必需]
公共长ClientId{get;set;}
[JsonIgnore]
公共客户端{get;set;}
[必需]
公共字符串用户标识{get;set;}
公共应用程序用户{get;set;}
[必需]
公共应用程序用户由{get;set;}创建
[必需]
public DateTime CreatedOn{get;set;}
[必需]
[列(TypeName=“nvarchar(15)”)]
公共用户角色{get;set;}
}
更新
这已被确认为来自其中一个贡献者的EF Core 5.0缺陷:请参见。根据定义(设计),与其他LINQ查询运算符的包含的相对顺序应该无关紧要-唯一的要求是启动查询的实体必须是包含
因此,在上述EF核心版本之间,在这方面没有有意的变更。然而,定义/设计是一回事,实现是另一回事。简单地说,您遇到了EF Core 5.x错误,所以最好向他们的GitHub问题跟踪者报告
问题并不总是出现,而且似乎与Where
谓词中使用的表达式有关(通常Include
不应影响过滤器、排序等LINQ运算符中导航属性的使用),更具体地说,与此处的导航属性Client
有关
f.Client.Id != 1
结合
.Include(f => f.Client)
如果您从Where
中删除该条件,或将包含
移动到具有该条件的Where
之后,则剩余的包括工作。而有了这个组合,他们就不会了
所以,报告它,让他们知道它,并最终解决它。在此之前,由于无法删除标准(显然),请对包含的内容重新排序
如果您希望安全(并且不会遇到类似的意外错误),即使定义中没有要求,也可以将所有包含的放在查询的开头,紧跟在DbSet
之后,然后是所有其他操作符。@Magnetron Good point。我应该说得更清楚些——我是专门指Include()
和Where()
方法之间的相对顺序。谢谢Ivan。我打开了这个问题,并将根据该问题的任何反馈更新这个问题。EF核心贡献者之一已经确认这是一个bug。
.Include(f => f.Client)