繁体   English   中英

EF Core 5 - 查询中 Include()/Where() 方法的顺序是否重要?

[英]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 语句,一个用于加入AdminUserClientsAspNetUsersClients ,另一个用于加入ClientUserRolesAspNetUsers 然后,它连接两个子查询以生成结果。 在 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()方法的顺序不应该影响返回集(尽管我知道它会影响性能)。

问题

  1. 原始查询中Include()Where()方法的排序是否存在问题?
  2. 从 EF Core 3.1 到 EF Core 5.0 的更改是否会导致此查询的行为发生更改?

拆分查询注意事项

因为我是从 2.2 升级到 5.0,所以我使用UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)来模拟 2.2 的行为。 但是,我已经测试过:

  1. AsSplitQuery()添加到查询中。
  2. 使用UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)
  3. 完全删除UseQuerySplittingBehavior() 在所有这些情况下,行为都是相同的。

Model

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM