从EF Core 2迁移到EF Core 3

时间:2019-09-30 11:26:02

标签: c# sql-server linq entity-framework-core

将我的项目从(dotnet核心2 / ef核心2)升级到(dotnet核心3 / ef核心3)后,几乎所有实体框架LINQ查询都被破坏了。虽然我已经读过this,但仍不清楚该怎么做。

以下是一些我遇到问题的示例:

var league = await dbContext.League.LastAsync();

虽然此代码在ef内核2中运行良好,但在ef内核3中引发了异常。我能找到的唯一解决方法是以下代码,因为它与以前不一样,所以仍然不是我想要的。

var league = dbContext.League.AsEnumerable().Last();

另一个引发相同异常的示例是以下代码:

var user = await dbContext.User.FirstOrDefaultAsync(u =>
                u.UserId == userId && string.Equals(u.Token, token, StringComparison.InvariantCulture));

我仍然可以使用AsEnumerable(),但是那里没有可用的FirstOrDefault异步版本,所以这不是一个选择。有人可以指导我吗?

编辑
例外:

System.InvalidOperationException: The LINQ expression 'Last<League>(DbSet<League>)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

2 个答案:

答案 0 :(得分:6)

您的问题的答案将非常冗长,因为它对应于EF Core 3.0中发生的一些不同更改;因此,我们只考虑其中的一小部分。

正如您在问题中提到的那样,Microsoft对this post中的3.0版所做的更改令人费解。

以上文章的第一部分是:“ 不再在客户端上评估LINQ查询”。它说,在开发人员用来编写包含两个部分的查询之前一部分是对数据库的查询,另一部分是仅对客户端代码了解的表达式。在这种情况下,client evaluation of potentially expensive expressions only triggered a warning。但是在新版本中,EF内核仅允许在客户端上评估最后一次Select()调用,并且在存在无法转换为SQL或参数的表达式时引发异常。

为清楚起见,让我们看一下迭戈·维加(Diego Vega)在其EF Core 3.0 announcement blog post中描述的示例。

  

显式切换到客户端评估:如果查询基于无法转换为SQL的表达式过滤数据,则可能需要通过插入对AsEnumerable()的调用来显式切换到客户端评估。 ,AsAsyncEnumerable(),ToList()或ToListAsync()放在查询中间。例如,以下查询将在EF Core 3.0中不再起作用,因为where子句中的谓词之一需要客户端评估:

var specialCustomers = context.Customers
    .Where(c => c.Name.StartsWith(n) && IsSpecialCustomer(c));
  

但是,如果您知道在客户端上处理部分过滤器是合理的,则可以将查询重写为:

var specialCustomers = context.Customers
    .Where(c => c.Name.StartsWith(n)) 
    .AsEnumerable() // Start using LINQ to Objects (switch to client evaluation)
    .Where(c => IsSpecialCustomer(c));

在上面的示例中,IsSpecialCustomer(c)是一种不能转换为SQL的方法,因为它是C#方法,仅在客户端代码中可用。因此,开发人员应该以可以翻译的形式重写查询,或者在数据库上查询,然后使用.AsEnumerable()向客户端评估数据库结果,然后可以根据返回的IsSpecialCustomer(c)过滤结果。 这就是为什么您仍然可以在代码中访问AsEnumerable()的原因。

现在,让我们来看看为什么FirstOrDefaultAsync()方法不可用?

嗯,有两种原因导致这种情况。

我之前回答过第一个原因:在3.0版中删除了用于检测不可组合SQL的代码。

第二个是:查询管道不了解表达式树中的异步可查询运算符(例如:当您尝试在 EF.CompileQuery() 上访问它时)。

总而言之,您可以阅读一些有趣的帖子:

40 breaking bad changes in ef core 3

Announcing entity framework core 3.0 preview 9 and entity framework 6.3 preview 9

EF core issues on github

答案 1 :(得分:2)

  1. 使用OrderByDesc()来获取某些属性,然后使用FirstAsync()。 (https://github.com/aspnet/EntityFrameworkCore/issues/18211

  2. 不变比较未翻译,很可能是客户端先前评估过的。根据您的数据库排序规则设置,您可能可以在此处做一个普通的等于。

AsEnumerable()上调用DbSet而没有任何过滤器将在本地提取所有数据,而不是您要在生产中执行的操作。尝试重写到上面,并监视生成的SQL,以确保您获得高性能的查询。