webdevqa.jp.net

Entity Framework 3.0 Containsは、EF Core 2.2のようにSQLで変換できません

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: 'LINQ式' Where(source:DbSet、predicate:(t)=>(Unhandled parameter:__tagIds_0).Contains(t.TagId)) 'は翻訳できませんでした。変換可能な形式でクエリを書き直すか、AsEnumerable()、AsAsyncEnumerable()、ToList()、またはToListAsync()への呼び出しを挿入して、クライアント評価に明示的に切り替えます。詳細については、クライアントとサーバーの評価-EFコアを参照してください。

これは LINQクエリがクライアントで評価されなくなった 重大な変更を示唆していますが、クライアントではAFAIK Containsは評価されていません。

6
Alexei

私は何が起こっているのか、なぜ私のコードが期待どおりに動作しなかったのか(サーバー側の評価)、そして明示的なクライアント側の評価が実際に非常に優れているのかを理解しました。

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