问题
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