2011-07-27 16 views
2

私は理解できない奇妙な振る舞いをしています。IEnumerable/IQueryable拡張による奇妙な動作(遅延読み込みなし)

以下の2つの方法は、明らかにIQueryableの場合と同じように動作しますが、間違っていると思われます。

最初にIQueryable(オブジェクトはIQueryableとして明示的に使用されるエンティティフレームワークのDbSetです)を呼び出すと、レイジーローディングを使用しないように見えます(データベースでスキャンを実行します)。私が同じオブジェクトを使って2番目のメソッドを呼び出すと、それは私が望むように動作しているように見えます(データベースに対してシークを実行します)。

ので、二つの質問:

  • なぜこの出来事はありますか?

  • (IEnumerableを使用して)最も一般的な方法を「正しく」動作させることはできますか? (私はより多くの拡張機能を持っていると私は過負荷を回避し、ちょうどコピーペーストする以下のようなメソッド本体をしたいコードを複製したくないので)

私はSQL Server Expressの不利に働いEF 4.1を使用していますlist以来2008データベース

public static TEntity GetByID<TEntity>(this IEnumerable<TEntity> list, long id) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(e => e.ID == id); 
} 

public static TEntity GetByID<TEntity>(this IQueryable<TEntity> list, long id) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(e => e.ID == id); 
} 
+0

IEnumerable拡張メソッドは、望むように機能しません。 – hatchet

答えて

5

IEnumerable<T>は、EFのLINQプロバイダにクエリ式を渡し、その代わりに、メモリにSingleOrDefault()を実行しません。これには、メモリへのテーブルの完全なロードが必要で、その後にSingleOrDefault()が続きます。 IQueryable<T>バージョンを使用することにより、プロバイダには正しい式ツリーが与えられ、それが必要なSQLに変換されます。それが違いの原因です。

+0

SingleOrDefaultは、インターフェイスの拡張メソッドです。したがって、IEnumerableの実装はIQueryableとはまったく異なります。差異の手がかりは、インターフェイスの名前です。IEnumerableはコレクションを列挙します(そしてそのコレクション全体を取得する必要があります).IQueryableは式のようにクエリを使用します。 – hatchet

2

はあなたの最初のケースでIEnumerableとして入力されたEntity Frameworkのクエリプロバイダは、(それがEnumerableにLINQのメソッドを代わりに使用するメモリでSingleOrDefault()方法を実行するためのヒントとしてこれを取りますQueryable) - そのため、完全なリストを具体化するためにデータベーススキャンが表示されます。

またAsEnumerable()方法およびジョンスキートによってこの記事を参照してください。"Reimplementing LINQ to Objects: Part 36 - AsEnumerable"

0

FirstOrDefaultはIEnumerableとIQueryableに対して別々に定義されています System.Linq.Queryable.FirstOrDefault()は2番目のケースで呼び出されています。

両方を組み合わせて1つのメソッドにする場合は、リストがIQueryableであるかどうかをテストし、代わりにQueryable拡張メソッドを使用できます。ここに来る人のため

public static TEntity GetByID<TEntity>(this IEnumerable<TEntity> list, long id) where TEntity : Identifiable 
{ 
    var query = list as IQueryable<TEntity>; 
    if (query != null) 
     return query.SingleOrDefault(e => e.ID == id); 
    return list.SingleOrDefault(e => e.ID == id); 
} 
0

、注意すべきもうひとつ:

あなたは、あなたがSingleOrDefault()方法に表現を提供している絶対的に確認する必要があり、次が発生するためにそれ以外の場合は可能性があります:

public static TEntity GetByID<TEntity>(this IEnumerable<TEntity> list, Func<TEntity,bool> selector) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(selector); 
} 

public static TEntity GetByID<TEntity>(this IQueryable<TEntity> list, Func<TEntity,bool> selector) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(selector); 
} 

これらのメソッドはまったく同じように動作します。どうして?IQueryable<>リストに指定していないため、リストはIEnumerable<>に自動的に変換され、セレクタはIEnumerable<>で実行されます。実際には、実際にはテーブル/リスト全体をDBからメモリにプルして、セレクタをメモリ内のリスト上で実行しています。

私はちょうどこれでやってしまいましたが、私は狂っていました。

Expression<Func<TEntity,bool>>IQueryable<>に渡すと、DB側で実行されます。

関連する問題