7

チームによるLoadingRelatedEntitiesの投稿を読みましたEntity Frameworkが、最後の段落で少し混乱しました。

すべてのエンティティをロードするコストを実際に負担することなく、データベース内の別のエンティティに関連するエンティティの数を知ることが役立つ場合があります。これを行うには、LINQCountメソッドを使用したQueryメソッドを使用できます。例えば:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Count how many posts the blog has 
    var postCount = context.Entry(blog)
                          .Collection(b => b.Posts)
                          .Query()
                          .Count();
}

Query+Countメソッドがここで必要なのはなぜですか?代わりに
LINQの方法を簡単に使用することはできませんか?COUNT

var blog = context.Blogs.Find(1);
var postCount = blog.Posts.Count();

これにより遅延読み込みがトリガーされ、すべてのコレクションがメモリに読み込まれ、目的のスカラー値が取得されますか?

4

3 に答える 3

6

ボットの場合、目的のスカラー値を取得します。しかし、何が起こっているかの違いを考慮してください。

フォームのデータベースでクエリを.Query().Count()実行し、SELECT COUNT(*) FROM Postsその値を整数変数に割り当てます。

を使用すると、データベースで(の.Posts.Countようなものを)実行SELECT * FROM Postsします(すでにはるかに高価です)。次に、結果の各行がフィールドごとにC#オブジェクトタイプにマップされ、コレクションが列挙されてカウントが検出されます。このようにカウントを要求することで、C#がカウントできるように、すべてのデータを強制的にロードすることになります。

うまくいけば、(実際にすべての行を返すことなく)データベースに行数を要求する方がはるかに効率的であることは明らかです!

于 2012-09-20T10:42:38.120 に答える
2

最初のメソッドはCountから呼び出されるため、すべての行をロードしていませんIQueryableが、2番目のメソッドは、から呼び出されるため、すべての行をロードしていますICollection

私はそれを検証するためにいくつかのテストを行いました。Table1にはPK"Id"があり、Table2にはFK "Id1"(1:N)があるTable1とTable2でテストしました。ここhttp://efprof.com/からEFプロファイラーを使用しました。

最初の方法:

var t1 = context.Table1.Find(1);

var count1 = context.Entry(t1)
                        .Collection(t => t.Table2)
                        .Query()
                        .Count();

いいえSelect * From Table2

SELECT TOP (2) [Extent1].[Id] AS [Id]
FROM   [dbo].[Table1] AS [Extent1]
WHERE  [Extent1].[Id] = 1 /* @p0 */

SELECT [GroupBy1].[A1] AS [C1]
FROM   (SELECT COUNT(1) AS [A1]
        FROM   [dbo].[Table2] AS [Extent1]
        WHERE  [Extent1].[Id1] = 1 /* @EntityKeyValue1 */) AS [GroupBy1]

2番目の方法:

var t1 = context.Table1.Find(1);
var count2 = t1.Table2.Count(); 

Table2はメモリにロードされます:

SELECT TOP (2) [Extent1].[Id] AS [Id]
FROM   [dbo].[Table1] AS [Extent1]
WHERE  [Extent1].[Id] = 1 /* @p0 */

SELECT [Extent1].[Id]  AS [Id],
       [Extent1].[Id1] AS [Id1]
FROM   [dbo].[Table2] AS [Extent1]
WHERE  [Extent1].[Id1] = 1 /* @EntityKeyValue1 */

なぜこうなった?

の結果Collection(t => t.Table2)は、を実装するクラスですが、ICollectionすべての行をロードしているわけではなく、という名前のプロパティがありますIsLoadedQueryメソッドの結果はであり、これにより、行をプリロードせずにIQueryable呼び出すことができます。Count

の結果はでt1.Table2ありICollection、カウントを取得するためにすべての行をロードしています。ちなみに、t1.Table2カウントを要求せずに使用した場合でも、行はメモリにロードされます。

于 2012-09-20T12:38:56.443 に答える
1

最初のソリューションは、コレクションプロパティに直接アクセスすることはほとんどないため、遅延読み込みをトリガーしません。このメソッドは、委任だけでなく、Collectionを受け入れます。Expressionこれは、マッピング情報にアクセスして正しいクエリを作成するために使用されるプロパティの名前を取得するためにのみ使用されます。

コレクションプロパティにアクセスする場合でも、EFの他の内部部分(検証など)と同じ戦略を使用して、ナビゲーションプロパティにアクセスする前に一時的に遅延読み込みをオフにして、予期しない遅延読み込みを回避できます。

ところで。これは、クエリの構築にナビゲーションプロパティへのアクセスが必要であり、遅延読み込みをトリガーする可能性があるObjectContextAPIとは対照的に大幅に改善されています。

これら2つのアプローチにはもう1つの違いがあります。

  • 1つ目は常にデータベースへのクエリを実行し、データベース内のアイテムの数を返します
  • 2つ目は、データベースへのクエリを1回だけ実行してすべてのアイテムをロードし、データベースの状態をチェックせずにアプリケーション内のアイテムの数を返します。

3番目の非常に興味深いオプションとして、追加の読み込みを使用できます。Arthur Vickersによる実装は、ナビゲーションプロパティを使用して、アイテムを遅延ロードせずにデータベースからカウントを取得する方法を示しています。

于 2012-09-20T12:15:32.030 に答える