19

「トップ」オブジェクトが 0 ~ N 個の「サブ」オブジェクトを持つデータ モデルがあります。SQL では、これは外部キーで実現されますdbo.Sub.TopId

var query = context.Top
    //.Include(t => t.Sub) Doesn't seem to do anything
    .Select(t => new {
        prop1 = t.C1,
        prop2 = t.Sub.Select(s => new {
            prop21 = s.C3 //C3 is a column in the table 'Sub'
        })
        //.ToArray() results in N + 1 queries
    });
var res = query.ToArray();

Entity Framework 6 (遅延読み込みオフ) では、この Linq クエリは単一のSQL クエリに変換されます。結果は完全にロードされるため、res[0].prop2すでにIEnumerable<SomeAnonymousType>満たされた になります。

ただし、EntityFrameworkCore (NuGet v1.1.0) を使用する場合、サブコレクションはまだ読み込まれておらず、次のタイプです。

System.Linq.Enumerable.WhereSelectEnumerableIterator<Microsoft.EntityFrameworkCore.Storage.ValueBuffer, <>f__AnonymousType1<string>>.

データを反復処理するまでデータはロードされないため、N + 1 クエリが発生します。.ToArray()(コメントに示されているように) クエリに追加すると、データは に完全にロードされますがvar res、SQL プロファイラーを使用すると、これが 1 つの SQL クエリでは達成されないことがわかります。「トップ」オブジェクトごとに、「サブ」テーブルに対するクエリが実行されます。

最初に指定.Include(t => t.Sub)しても何も変わらないようです。匿名型の使用も問題ではないようです。new { ... }ブロックを置き換えてnew MyPocoClass { ... }も何も変わりません。

私の質問は次のとおりです。すべてのデータがすぐに読み込まれる EF6 と同様の動作を取得する方法はありますか?


:この例では、次のようにクエリを実行した、メモリ内に匿名オブジェクトを作成することで問題を解決できることを認識しています。

var query2 = context.Top
    .Include(t => t.Sub)
    .ToArray()
    .Select(t => new //... select what is needed, fill anonymous types

ただし、これは単なる例です。実際には、オブジェクトの作成を Linq クエリの一部にする必要があります。これは、AutoMapper がこれを使用してプロジェクトの DTO を埋めるためです。


更新:新しい EF Core 2.0 でテストしましたが、問題はまだ存在しています。(2017 年 8 月 21 日)

aspnet/EntityFrameworkCore問題はGitHub リポジトリで追跡されています:問題 4007

更新: 1 年後、この問題はバージョン で修正されました2.1.0-preview1-final。(2018-03-01)

更新: EF バージョン 2.1 がリリースされました。これには修正が含まれています。以下の私の答えを見てください。(2018-05-31)

4

2 に答える 2

1

私は同じ問題に直面しました。

あなたが提案した解決策は、比較的大きなテーブルでは機能しません。生成されたクエリを見ると、where 条件のない内部結合になります。

var query2 = context.Top .Include(t => t.Sub) .ToArray() .Select(t => new //... 必要なものを選択し、匿名型を入力します

より良い解決策を聞いてうれしいですが、データベースの再設計で解決しました。

私の場合、2つのテーブルAとBがあります.テーブルAはBと1対多です.あなたが説明したようにリストで直接解決しようとしたとき、私はそれを行うことができませんでした(.NETの実行時間LINQ は 0.5 秒でしたが、.NET Core LINQ は 30 秒の実行時間後に失敗しました)。

その結果、テーブル B の外部キーを作成し、内部リストなしでテーブル B の側から開始する必要がありました。

context.A.Where(a => a.B.ID == 1).ToArray();

その後、結果の .NET オブジェクトを簡単に操作できます。

于 2017-04-14T09:37:17.850 に答える