我最近将项目代码更新到了.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时引发错误,我在其他一些文章中看到此问题已作为bug修复,但失败了。
我得到的错误是“无法翻译。要么以可以翻译的形式重写查询,要么通过插入对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()
Entity Framework无法转换每个查询,因此,有时它必须加载所有数据并在内存中执行LINQ表达式,这被称为客户端评估,这是不希望的,因为它占用更多的资源并且需要更长的时间。对于您的特定问题,有2个解决方案,错误消息中都列出了这两种解决方案。
1)重写您的LINQ查询,以包括对客户端评估的显式调用与隐式调用
或
2)重写您的LINQ查询,不需要客户端评估
您可以像这样做1号:
public override ICollection<ContactDetailModel> GetAll(ICollection<int> ids)
{
return _context
.Set<TEntity>()
.IgnoreDeletedEntities()
.ToList()
.Where(x => ids.Distinct().Contains(x.ContactId))
.Select(EntityToDTOMapper)
.ToList();
}
[请注意在ToList
之后显式调用IgnoreDeletedEntities
,这需要显式切换到客户端评估,以便您的Where
语句将正确执行且不会引发任何错误。这是因为x => ids.Distinct().Contains(x.ContactId)
无法通过您的EF版本转换为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无法翻译的部分