2013-08-08 10 views
5

GetFoo().Count()をDBクラス以外のメソッドから呼び出すと、Entity Framework 5はCOUNTではなくむしろ非効率なSELECTクエリを実行します。 thisのようないくつかの質問を読んで、これが期待される動作であることがわかります。私はそのため正しくCOUNTクエリを実行私のDBクラスにカウント方法を追加しましたエンティティフレームワーク - SELECTではなくCOUNT

public IEnumerable<DbItems> GetFoo() 
{ 
    return context.Items.Where(d => d.Foo.equals("bar")); 
} 

:複数回のクエリ指定から私を保存するには

public int GetFooCount() 
{ 
    return context.Items.Where(d => d.Foo.equals("bar")).Count(); 
} 

を、私はこれを変更したいのですが以下のとおりです。ただし、これはDBクラス内にあってもSELECTを再度実行します。なぜこれが - そしてどうすればそれを避けることができますか?あなたはIEnumerable<T>IQueryable<T>の拡張機能の違いを理解する必要があり、この動作を理解するために

public int GetFooCount() 
{ 
    return this.GetFoo().Count(); 
} 
+1

Context.Items.Count(D => d.Foo == "バー");あなたはどこに電話が必要ではありません:) –

答えて

14

IEnumerable<DbItems>を返すクエリをSELECTとして実行され、その後Countは、オブジェクトのコレクションに適用され、SQLに投影されていません。

1つのオプションは、代わりにIQueryable<DbItems>を返して:

public IQueryable<DbItems> GetFoo() 
{ 
    return context.Items.Where(d => d.Foo.equals("bar")); 
} 

しかし、それはコスプレイ@ダイナミックセーラーがロードされるように期待している他の発信者(IQueryableでレイジーロードされます)の動作を変更することがあります。特に、.Where呼び出しを追加するメソッドは、SQLに変換できません。残念ながら、コンパイル時にはそれらについてはわかりませんので、徹底的なテストが必要になります。

私の代わりにIQueryableを返し新しい方法作成します。

public IQueryable<DbItems> GetFooQuery() 
{ 
    return context.Items.Where(d => d.Foo.equals("bar")); 
} 

ので、既存の用途は影響を受けません。あなたがそのコードを再利用したい場合は、へGetFoo変更することができます:

public IEnumerable<DbItems> GetFoo() 
{ 
    return GetFooQuery().AsEnumerable(); 
} 
13

。最初のものは、メモリ内のクエリであるLinqとObjectで動作します。これは単純な.NETコードなので、このクエリはSQLに変換されません。

public static int Count<TSource>(this IEnumerable<TSource> source) 
{ 
    int num = 0; 
    foreach(var item in source) 
     num++; 

    return num; 
} 

しかしIQueryable<T>拡張子を持つ物語は完全に異なるあり:だから、あなたには、いくつかのIEnumerable<T>値を持っている、とあなたはCount()にこれを実行している場合のようなものであるEnumerable.Count拡張メソッドを呼び出します。これらのメソッドは、基になるLINQプロバイダ(あなたの場合はEF)によって.NETコード以外のものに変換されます。例えば。 SQLへ。この変換は、クエリを実行するときに発生します。すべてのクエリが分析され、いい(よくないとは限りませんが)SQLが生成されます。このSQLはデータベースで実行され、結果はクエリの実行結果として返されます。

したがって、メソッドはIEnumerable<T>を返します。つまり、メモリ内で実行するEnumerable.Count()メソッドを使用しています。したがって、次のクエリは、SQL実行

context.Items.Where(d => d.Foo.equals("bar")) // translated into SELECT WHERE 

にEFで翻訳され、次いで、上記方法で、メモリ計算アイテムの数。あなたはIQueryable<T>に戻り値の型を変更する場合はしかし、その後、すべてが今Queryable<T>.Count()が実行される

public IQueryable<DbItems> GetFoo() 
{ 
    return context.Items.Where(d => d.Foo.equals("bar")); 
} 

を変更します。これはクエリが構築を継続していることを意味します(実際にはCount()はクエリの実行を強制する演算子ですが、Count()はこのクエリの一部になります)。そして、EFはサーバ側で実行されるSQLクエリに、

context.Items.Where(d => d.Foo.equals("bar")).Count() 

を変換します。 GetFoo()ので

+0

こんにちは!私は.AsQueryable()、Count()、.AsEnumerable()、Count()、パフォーマンス統計を同じように使いこなしてみました。例えば、 など。 - My Oracle Workbenchで "リンゴからカウント(*)を選択"〜1ms - EF linq "dbContext.apples.Count()"〜800ms – Ross

+1

@Ross必要な情報をすべて入れて新しい質問を作成する必要があります。例えば。あなたの最初のクエリであれば、EFはedmをビルドするのに時間がかかります –