EF Core 3.1 throws an exception for Contains

柔情痞子 提交于 2020-07-09 12:05:22

问题


I recently updated the project code into .NET Core 3.1 and EF Core 3.1, now most of my linq queries brake, EX.

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

This code throws an error where I use Contains, I saw in some other posts this issues has been fixed as a bug, but yet it fails.

Error I get is "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()."

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()


回答1:


Entity Framework cannot translate every query, and as such it sometimes has to load all the data and perform the LINQ expression in-memory, this is called Client Side Evaluation and is not desired, as it is more resource intensive and takes longer. For your specific problem, there are 2 solutions, both of which are outlined in the error message.

1) Rewrite your LINQ queries to include explicit calls to client side evaluation versus implicit calls

or

2) Rewrite your LINQ queries to not need client side evaluation

You could do number 1 like so:

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

Notice the explicit call to ToList after IgnoreDeletedEntities, this needs to be done to explicitly switch to client side evaluation so your Where statement will properly execute and not throw any errors. This is because x => ids.Distinct().Contains(x.ContactId) cannot be translated to SQL (or whatever) by your version of EF.

Number 2 could be solved like so:

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();
}

Notice how I moved the use of ids.Distinct() from the Where to the top, as that was the part EF couldn't translate



来源:https://stackoverflow.com/questions/60412799/ef-core-3-1-throws-an-exception-for-contains

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!