Entity Framework 3.0 Contains cannot be translated in SQL as it was in EF Core 2.2

旧城冷巷雨未停 提交于 2020-04-10 14:04:07

问题


I am trying to migrate a Web API from .NET Core 2.2 to .NET Core 3.0 and I have stumbled across the following:

public Dictionary<int, Tag> GetTagMap(IList<int> tagIds = null)
{
    var tags = context.Tag.AsNoTracking();
    if (tagIds != null)
        tags = tags.Where(t => tagIds.Contains(t.TagId));

    return tags
       .ToList()       // explicit client evaluation in 3.0
       .ToDictionary(t => t.TagId, t => t);
}

This used to generate a SQL statement similar to this one:

SELECT TagId, Name FROM Tag WHERE TagId IN (1, 2, 3)

which worked very well for correctly indexed column and a small number of IN values.

Now I receive the following error suggesting that List<>.Contains translation is not supported anymore:

System.InvalidOperationException: 'The LINQ expression 'Where( source: DbSet, predicate: (t) => (Unhandled parameter: __tagIds_0).Contains(t.TagId))' 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 Client vs. Server Evaluation - EF Core for more information.'

This suggests the LINQ queries are no longer evaluated on the client breaking change, but AFAIK Contains was not evaluated on the client.


回答1:


It's a 3.0 bug, tracked by #17342: Contains on generic IList/HashSet/ImmutableHashSet will throw exception.

Already fixed in 3.1. The workaround (if you can't wait) is to force the usage of Enumerable.Contains, for instance

t => tagIds.AsEnumerable().Contains(t.TagId)

or changing the type of the variable.




回答2:


I understood what was happening and why my code was not behaving as expected (server side evaluation) and why the explicit client side evaluation is actually a very good thing:

IList<T>.Contains was actually evaluated on the client, but List<T>.Contains is evaluated on the server side. Simply replacing IList with List made the code work without explicit client evaluation:

public Dictionary<int, Tag> GetTagMap(List<int> tagIds = null)
{
    var tags = context.Tag.AsNoTracking();
    if (tagIds != null)
        tags = tags.Where(t => tagIds.Contains(t.TagId));

    return tags
       .ToList()       // explicit client evaluation in 3.0
       .ToDictionary(t => t.TagId, t => t);
}


来源:https://stackoverflow.com/questions/58690473/entity-framework-3-0-contains-cannot-be-translated-in-sql-as-it-was-in-ef-core-2

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