Entity Framework 3.0 包含无法像在 EF Core 2.2 中那样在 SQL 中进行翻译

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

我正在尝试将 Web API 从 .NET Core 2.2 迁移到 .NET Core 3.0,我偶然发现了以下内容:

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

这用于生成类似于此的 SQL 语句:

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

对于正确索引的列和少量 IN 值非常有效。

现在我收到以下错误提示 List<>.Contains 翻译不再受支持:

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.'

这表明 LINQ queries are no longer evaluated on the client 重大更改,但 AFAIK Contains 未在客户端上进行评估。

我明白发生了什么,为什么我的代码没有按预期运行(服务器端评估),以及为什么显式客户端评估实际上是一件好事:

IList<T>.Contains实际上是在客户端计算的,但是List<T>.Contains是在服务器端计算的。只需将 IList 替换为 List 即可使代码在没有显式客户端评估的情况下工作:

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

这是一个 3.0 错误,由 #17342: Contains on generic IList/HashSet/ImmutableHashSet will throw exception 跟踪。

已在 3.1 中修复。解决方法(如果您等不及)是强制使用 Enumerable.Contains,例如

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

或者改变变量的类型。