【问题标题】:EF Core 3.1 throws an exception for ContainsEF Core 3.1 为包含引发异常
【发布时间】:2020-02-26 11:22:42
【问题描述】:

我最近将项目代码更新为 .NET Core 3.1 和 EF Core 3.1,现在我的大部分 linq 查询都停止了,EX。

public override ICollection<ContactDetailModel> GetAll(ICollection<int> ids)
{
            return _context
                .Set<TEntity>()
                .IgnoreDeletedEntities()                
                .Where(x => ids.Distinct().Contains(x.ContactId))                
                .Select(EntityToDTOMapper)
                .ToList();
}

此代码在我使用 Contains 时引发错误,我在其他一些帖子中看到此问题已作为错误修复,但仍然失败。

我得到的错误是“无法翻译。要么以可翻译的形式重写查询,要么通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或ToListAsync()。”

System.InvalidOperationException
  HResult=0x80131509
  Message=The LINQ expression 'DbSet<SupplierContactDetails>
    .Where(s => !(s.DeletedOn.HasValue) && !(s.DeletedBy.HasValue))
    .Where(s => __Distinct_0
        .Contains(s.ContactId))' 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.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|8_0(ShapedQueryExpression translated, <>c__DisplayClass8_0& )
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at BlueTag.DAL.Repositories.ContactRepository`1.GetAll(ICollection`1 ids) in D:\Projects\BlueTag Version 2\.net-core-server\DAL\Repositories\ContactRepository.cs:line 95
   at BlueTag.DAL.Repositories.SupplierRepository.ToDomain(IEnumerable`1 supplier) in D:\Projects\BlueTag Version 2\.net-core-server\DAL\Repositories\SupplierRepository.cs:line 216
   at BlueTag.DAL.Repositories.SupplierRepository.GetFilteredSuppliers(RequestPagingOptionsModel`1 options) in D:\Projects\BlueTag Version 2\.net-core-server\DAL\Repositories\SupplierRepository.cs:line 129
   at BlueTag.Supplier.Services.SupplierService.GetFilteredSupplierDetails(RequestPagingOptionsModel`1 options) in D:\Projects\BlueTag Version 2\.net-core-server\Supplier\Serrvices\SupplierService.cs:line 49
   at BlueTag.Supplier.Controllers.SupplierController.GetFilteredSuppliers(RequestPagingOptionsModel`1 options) in D:\Projects\BlueTag Version 2\.net-core-server\Supplier\Controllers\SupplierController.cs:line 54
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

【问题讨论】:

  • EF Core 查询翻译还有很多bug。但是您确定问题是上述Where 条件吗?因为它对我有用(EF Core 3.1.1 和 3.1.2)。您能否发布异常堆栈跟踪和/或完整的错误消息。
  • System.InvalidOperationException HResult=0x80131509 Message=LINQ 表达式 'DbSet .Where(s => !(s.DeletedOn.HasValue) && !(s.DeletedBy.HasValue)) .Where (s => __Distinct_0 .Contains(s.ContactId))' 无法翻译。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。请参阅go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。
  • 我部分删除了 linq 查询,它在 where 子句中失败
  • 注释掉.IgnoreDeletedEntities()怎么样?
  • 还是一样,正如我之前所说的那样,当添加 where 子句时它会失败。

标签: c# asp.net-core linq-to-sql asp.net-core-webapi ef-core-3.0


【解决方案1】:

Entity Framework 无法翻译每个查询,因此它有时必须加载所有数据并在内存中执行 LINQ 表达式,这称为客户端评估并且不需要,因为它更占用资源并且需要更长的时间.对于您的具体问题,有 2 种解决方案,错误消息中都列出了这两种解决方案。

1) 重写您的 LINQ 查询以包括对客户端评估的显式调用与隐式调用

2) 重写您的 LINQ 查询,使其不需要客户端评估

你可以这样做:

public override ICollection<ContactDetailModel> GetAll(ICollection<int> ids)
{
    return _context
        .Set<TEntity>()
        .IgnoreDeletedEntities()
        .ToList()                
        .Where(x => ids.Distinct().Contains(x.ContactId))                
        .Select(EntityToDTOMapper)
        .ToList();
}

注意在IgnoreDeletedEntities 之后对ToList 的显式调用,这需要显式切换到客户端评估,这样您的Where 语句才能正确执行并且不会引发任何错误。这是因为您的 EF 版本无法将 x =&gt; ids.Distinct().Contains(x.ContactId) 转换为 SQL(或其他)。

2 号可以这样解决:

public override ICollection<ContactDetailModel> GetAll(ICollection<int> ids)
{
    ids = ids.Distinct();
    return _context
        .Set<TEntity>()
        .IgnoreDeletedEntities()
        .Where(x => ids.Contains(x.ContactId))
        .Select(EntityToDTOMapper)
        .ToList();
}

注意我如何将 ids.Distinct() 的使用从 Where 移到顶部,因为那是 EF 无法翻译的部分

【讨论】:

  • 感谢您的快速回复,我清楚地了解客户端评估和服务器评估,我无法理解的部分是包含/区别从未在客户端进行评估。那么为什么它现在在.net core 3.x 中失败了?我已经尝试过 2 号方式,但仍然失败。甚至在 Where 子句之前和之后添加了 AsEnumerable,但它失败了。 1 号解决方案不会是有效的解决方案,因为它将全部加载到内存中。
  • @Shivanka 为什么是breaking changes in Entity Framework Core 3.0 (this one here to be specific) 上的第一个重大更改的原因
  • 将调用移动到Distinct() 之外的Where() 本身没有任何作用;它会创建一个需要评估的IEnumerable&lt;int&gt;,但仍然无法转换为 SQL。它还需要ToArray()ToList() 来创建EF 可以使用的结构。
  • 如果有人能提出一种重构 linq 查询以在服务器端评估的方法,我们将不胜感激
  • 是的,没有运气
猜你喜欢
  • 2021-01-21
  • 1970-01-01
  • 1970-01-01
  • 2019-12-10
  • 2021-08-09
  • 2021-02-25
  • 2021-12-10
  • 2021-04-08
  • 2020-04-10
相关资源
最近更新 更多