c# - Entity Framework Core 2.2 编译查询结构参数在本地评估

标签 c# entity-framework ef-core-2.2 linq.compiledquery

我一直在研究使用 Entity Framework Core 编译的查询。我正在使用当前最新的稳定版本 2.2.2。我正在阅读这篇文章 ( https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/compiled-queries-linq-to-entities ) 以了解编译查询,并试图了解这是 EF Core 中的错误还是他们尚未完成的事情。我知道这篇文章是为 EF6 编写的,但我期望编译后的查询会以相同的方式工作,并且找不到任何相反的东西。

这是我的 DbContext 设置和一个用于分页选项的简单结构:

public class BuggyDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
}

public struct PagingOptions
{
    public int Skip;
    public int Take;
}

[Table("User")]
public class User
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
}

这是我编译的查询。第一个选择基于结构参数的用户“页面”(与文章中的示例非常相似)。第二个做同样的事情,但接受“skip”和“take”作为基本 int32 类型的单独参数。

var badQuery = EF.CompileQuery<BuggyDbContext, PagingOptions, IEnumerable<User>>((context, paging) =>
     context.Users
         .OrderBy(u => u.LastName)
         .Skip(paging.Skip)
         .Take(paging.Take));

var goodQuery = EF.CompileQuery<BuggyDbContext, int,int, IEnumerable<User>>((context, skip, take) =>
     context.Users
         .OrderBy(u => u.LastName)
         .Skip(skip)
         .Take(take));

下面是演示问题的用法:

 using (var db = new BuggyDbContext())
 {

     var pagingOptions = new PagingOptions {
         Skip = 0,
         Take = 25
     };
     var firstPage = badQuery.Invoke(db, pagingOptions).ToList();
     var alternateFirstPage = goodQuery.Invoke(db, pagingOptions.Skip, pagingOptions.Take).ToList();
 }

goodQuery 运行时,一切都按预期进行。以下内容在日志中显示为我所期望的生成的 SQL:

SELECT [u].[UserId], [u].[FirstName], [u].[LastName]
FROM [User] AS [u]
ORDER BY [u].[LastName]
OFFSET @__skip ROWS FETCH NEXT @__take ROWS ONLY

但是,当 badQuery 运行时,它会选择所有记录,然后计算内存中的 Skip 和 Take,这会导致糟糕的性能。

SELECT [u].[UserId], [u].[FirstName], [u].[LastName]
FROM [User] AS [u]
ORDER BY [u].[LastName]

warn: Microsoft.EntityFrameworkCore.Query[20500]
  => Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor
  The LINQ expression 'Skip(__paging.Skip)' could not be translated 
and will be evaluated locally.
warn: Microsoft.EntityFrameworkCore.Query[20500]
  => Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor
  The LINQ expression 'Take(__paging.Take)' could not be translated 
and will be evaluated locally.

出于几个非常重要的原因,我更愿意使用复杂类型(引用或值结构,不关心)作为编译查询的参数:

  1. Lambda 函数具有最大数量的输入参数。如果我有一个需要多个输入的复杂过滤、排序和分组的查询,我将被迫走其他路线。
  2. 输入参数对于调用查询的开发人员来说更加清晰。即使在此示例中,调用查询的开发人员也会开始键入 query.Invoke,然后在智能感知中盯着 2 个未命名的整数参数。他们知道他们的意思的唯一方法是查看查询。如果输入参数改变了顺序或含义,对查询的更改将是极其危险的。

EF Core 3.0 路线图 (https://learn.microsoft.com/en-us/ef/core/what-is-new/roadmap) 确实表示他们正在研究一般的 LINQ 查询策略(以避免这种可怕的运行查询,或者至少让您在运行前意识到或碰巧在您的日志中捕获警告) 但我希望 struct 参数起作用。

如果我做错了什么或者这是正在进行中的事情,有人在这里有任何见解吗?您是否也认为这是一个错误?

最佳答案

我在 https://github.com/aspnet/EntityFrameworkCore/issues/14857 向 EF 团队提交了错误报告它已关闭并标记为 https://github.com/aspnet/EntityFrameworkCore/issues/13976 的副本

已移至积压。以下是回应:“根据正常的分类,这是一个有合理解决方法的功能,我们还没有看到大量需求,所以我们现在将其移至待办事项列表中。”

关于c# - Entity Framework Core 2.2 编译查询结构参数在本地评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54909758/

相关文章:

c# - .Net 代码捕获来自 .asmx 网络服务的 HTTP 500 错误响应

c# - 轮询 SSIS 执行状态

c# - 在不创建新的 Edmx 文件的情况下在 EF 4 上映射外键

c# - EFCore Postgres 时间戳丢失刻度精度

sql-server - Entity Framework Core + 空间数据正在引发 SRID 无效错误

c# - 我怎样才能减慢我的 for 循环以便我可以看到颜色?

c# - App_Global.asax.compiled 和 App_Global.asax.dll 丢失了吗? WebApi .NET 4.5 项目

c# - 像 EntityFramework 6 中那样在 System.Data.Linq.DataContext 中记录执行时间

entity-framework - 自定义 IdentityUserRole 主键

c# - EntityFrameworkCore 键列从特定值自动递增