2011-09-30 8 views
23

このコード行をご覧ください。これはストアドプロシージャの呼び出しで、ObjectResult<long?>を返します。長い値を抽出するために、私は選択を追加しました:.NETエンティティフレームワーク - IEnumerable VS. IQueryable

はインテリセンスに基づいて
dbContext.FindCoursesWithKeywords(keywords).Select(l => l.Value); 

この選択はIEnumerable<long>を返します。私はいつもEF APIが返すときIEnumerable(とないIQueryable)、この結果は、マテリアライズされていることを意味していると考え -

私はそれがどこか多分ちょうどこの仮定に慣れ読んでいるかどうかはわかりません。彼らはデータベースから引き出されたという意味です。

今日私が間違っていたことが判明しました(あるいはバグかもしれません)。私は基本的には、このエラーは、あなたがいる間、変更を保存しようとしていることを示しています

「のセッションで実行されている他のスレッド があるので、新しいトランザクションは許可されていません」というエラーに

を取得保管しましたdbリーダーはまだレコードを読み込んでいます。

結局私は(私はロングショット考慮したもの)によってそれを解決しそうIEnumerable<long> ...

をマテリアライズするToArray()コールを追加 - ボトムラインを - 私はEFからIEnumerable結果は、その避難所の結果が含まれていることを期待すべきですまだ実現していない?はいの場合、IEnumerableが具体化されているかどうかを知る方法はありますか? IEnumerable<T>の作業

おかげで、これはそれらの「duhhhの質問の一つである場合に謝罪... :)

+0

EFのために具体的に書かれているものがあるかどうか分かりませんが、一般的にLinqの場合、 'IEnumerable 'は 'IQueryable 'よりも"マテリアライズ "されません。 –

+0

@Damien_The_Unbeliever:しかし、ObjectResult は、関数が呼び出されたときに常に実行されると思います...(この場合はFindCoursesWithKeywords) –

答えて

27

IQueryable Linq-to-entities =を使用している場合は、LINQプロバイダによってSQLとして解釈され、サーバー上で実行されるアプリケーションで宣言的LINQクエリを構築しています。クエリが実行(反復)されると、IEnumerableになり、オブジェクトは必要に応じて反復=即時にマテリアライズされます。

ストアドプロシージャを呼び出すと、アプリケーションに宣言型クエリが組み込まれていないため、Linqからエンティティへの変換は使用されません。クエリ/ SQLはすでにデータベースサーバー上に存在しており、呼び出すだけです。これによりIEnumerableが返されますが、すべての結果がすぐに反映されることはありません。結果は反復されて実体化されます。これは、明示的にオブジェクトの取得を要求するとき、データベースカーソル/または.NETデータリーダーの原則です。あなたがコース一つ一つを取得している

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords) 
           .Select(l => l.Value)) 
{ 
    ... 
} 

は、だから、このような何かを呼び出す場合(。あなただけのキーワードに興味を持っている場合は、ところで、なぜコース全体をロードします?)。ループを完了するか、または中断するまで、レコードをフェッチするためにデータリーダーが開かれます。あなたの代わりにこの呼び出した場合

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords) 
           .ToList() // or ToArray 
           .Select(l => l.Value)) 
{ 
    ... 
} 

をすぐにすべての結果を実体化するクエリを強制し、ループが代わりに開かれたデータベースリーダーのメモリ内コレクションを実行します。

IEnumerableIQueryableの間の相違は、IQueryableIEnumerableであるため、データの取り込み方法にはありません。違いは裏づけの構築です(これらのインターフェースを実装する必要があります)。

+0

こんにちは、この説明に感謝します!これはストアドプロシージャなので、クエリが行うすべての機能をサポートしていないため、IQueryableは返されません。 (btw、キーワードではないコースをフェッチしています):) – justabuzz

10

はすべて、さらに操作がC#のコード、すなわち、LINQツーオブジェクトで発生することを意味します。これは、クエリがすでに実行されていることを意味するものではありません。

linq-to-objectsに劣化したら、この時点で残っているすべてのデータをデータベースからフェッチし、.netに送信する必要があります。これにより、パフォーマンスが大幅に低下する可能性があります(たとえば、データベースインデックスはlinq-to-objectsによって使用されません)。一方、linq-to-objectsは柔軟性があります。 linqプロバイダはSQLに変換できます。

IEnumerable<T>は、遅延クエリでも、すでにマテリアライズされたデータでもかまいません。通常、標準のlinq演算子は延期され、ToArray()/ToList()は常にマテリアライズされます。

+0

この区別に感謝します!それは私が知っておくべきことですか?何か影響や悪夢はありますか?乾杯! – justabuzz

+0

最も明白な問題は、 'IEnumerable 'に縮退すると、パフォーマンスが大幅に低下することです。クエリの最初の段階でパフォーマンスが低下したとしたら、高速化のためにデータベース上のインデックスを使用してサーバー上のSQLを使用してフィルタリングする代わりに、テーブル全体をフェッチする必要があります。したがって、あなたが 'IEnumerable 'に劣化した時点では、可能な限り残っているアイテムはほとんど残さないようにします。 – CodesInChaos

+0

これが間違っているとは限りません。さらに私が行った+他の答えは、同様のことを言っています+ IEnumerable は、それを反復するときに後で実現されないかもしれません。これが正しいと分かっているのは、ストアドプロシージャを実行すると完全なIQueryableが得られるからです。なぜなら、これは完全なクエリ可能オブジェクトではないからです。意味がありますか? :) – justabuzz

-5

IEnumerable:LINQ to ObjectとLINQ to XML。

のIQueryable:LINQは、マテリアライズドまでのIEnumerableが水和しません

+0

IQueryableはまだ実行されていないタイプです。基本的には「クエリ」です。 http://msdn.microsoft.com/en-us/library/system.linq.iqueryable(v=vs.100).ASPX Virutally LINQ to SQL、またはLinq to Anythingとは関係ありません。任意の種類のデータプロバイダへのアクセスを提供するためのインタフェースです。 – Tony

0

SQLへ。ストアドプロシージャを呼び出す場合は、それ以上フィルターが必要ないと思います。ストアドプロシージャにパラメータを送信して、返されるデータのサブセットを生成することを意味します。 IEnumerableはストアドプロシージャに結び付けられています。しかし、テーブルの内容全体をフェッチして、アプリケーションでフィルタリングする場合は、戦略が必要です。同様に、テーブルのIEnumerableをToList()しないと、すべての行がマテリアライズされます。場合によっては、メモリ不足例外が発生することがあります。それ以外になぜメモリを消費するのか。 IQueryableをコンテキストに対して使用すると、アプリケーションではなくデータソースでテーブルをフィルタリングできます。答えは、それを実現させる必要があります。 IEnumerableはインターフェイスであり、それを実現するだけでその型を初期化し、私の理解で何かを生み出します。

関連する問題