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

Alexei - check Codidact picture Alexei - check Codidact · Nov 4, 2019 · Viewed 9.2k times · Source

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.

Answer

Ivan Stoev picture Ivan Stoev · Nov 4, 2019

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.