[英]EF Core 5 - Does the order of Include()/Where() methods in queries matter?
I am upgrading from .NET Core 2.2 to .NET 5.0 one major release at a time (2.2 -> 3.0 -> 3.1 -> 5.0), and I have a LINQ query into a MySQL database that works differently after the upgrade to 5.0. 该查询在 2.2、3.0 和 3.1 中运行良好,但在升级到 5.0 之后,其中一个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;
(model 见下文。)
当它在 EF Core 3.1(2.2 和 3.0 类似)中运行时,它会生成一个带有两个子查询的 SQL 语句,一个用于加入AdminUserClients
、 AspNetUsers
和Clients
,另一个用于加入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 中生成的语句相同。
我不知道为什么这会有所不同。 也许在Where()
方法中引用User
object 之前需要包含它? 但这对我来说没有意义,因为 (1) 它适用于 2.2、3.0 和 3.1,以及 (2) 我的理解是
方法
的顺序相对于Include()
和Where()
方法的顺序不应该影响返回集(尽管我知道它会影响性能)。
Include()
和Where()
方法的排序是否存在问题?因为我是从 2.2 升级到 5.0,所以我使用UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)
来模拟 2.2 的行为。 但是,我已经测试过:
AsSplitQuery()
添加到查询中。UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)
。UseQuerySplittingBehavior()
。 在所有这些情况下,行为都是相同的。public class AdminUserClient
{
public long Id { get; set; }
[Required]
public ApplicationUser User { get; set; }
[Required]
public Client Client { get; set; }
[Required]
public DateTime CreatedOn { get; set; }
}
public class ApplicationUser : IdentityUser
{
public UserNotificationsSetting NotificationSetting { get; set; }
[JsonIgnore]
public ClientUserRole ClientUserRole { get; set; }
public bool Locked { get; set; }
public string Name { get; set; }
}
public class Client
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool RequireTwoFactor { get; set; }
public ApplicationUser CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
[JsonIgnore]
public ICollection<ClientUserRole> ClientUserRoles { get; set; }
public bool IsDeleted { get; set; }
public bool Locked { get; set; }
}
public class ClientUserRole
{
public long Id { get; set; }
[Required]
public long ClientId { get; set; }
[JsonIgnore]
public Client Client { get; set; }
[Required]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
[Required]
public ApplicationUser CreatedBy { get; set; }
[Required]
public DateTime CreatedOn { get; set; }
[Required]
[Column(TypeName = "nvarchar(15)")]
public UserRole Role { get; set; }
}
这已被其中一位贡献者确认为 EF Core 5.0 错误:请参阅https://github.com/dotnet/efcore/issues/24953 。
根据定义(设计), Include
与其他 LINQ 查询运算符的相对顺序应该无关紧要 - 唯一的要求是Include
是用于启动查询的实体。
因此,上述 EF Core 版本之间在这方面没有有意更改。 然而,定义/设计是一个故事,实施是另一回事。 简单地说,您遇到了 EF Core 5.x 错误,因此最好将其报告给他们的 GitHub 问题跟踪器。
该问题并不总是出现,并且似乎与Where
谓词中使用的表达式有关(通常Include
不应影响在过滤器、排序等 LINQ 运算符中使用导航属性),更具体地说,与导航属性Client
这里
f.Client.Id != 1
结合
.Include(f => f.Client)
如果从Where
中删除该条件,或将Include
移到具有该条件的Where
之后,则包含工作的 rest。 虽然使用这种组合,但他们没有。
所以报告它以便让他们知道并最终修复它。 在那之前,由于您无法删除条件(显然),请重新排序Includes
。
而且,如果您想安全(并且也不会遇到类似的意外错误),即使它不是定义所必需的,也请将所有Includes
放在查询的开头,紧跟在DbSet
之后,然后是所有其他运算符。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.