このコード行を参照してください。これは、_ObjectResult<long?>
_を返すストアドプロシージャの呼び出しです。長い値を抽出するために、Select:を追加しました。
_dbContext.FindCoursesWithKeywords(keywords).Select(l => l.Value);
_
インテリセンスに基づいて、このSelectは_IEnumerable<long>
_を返します。
どこかで読んだのか、この仮定に慣れただけなのかはわかりません-EFAPIがIEnumerable
(IQueryable
ではなく)を返す場合、これは結果が具体化されました。それらがデータベースからプルされたことを意味します。
私は今日、自分が間違っていることを知りました(またはそれはバグですか?)。エラーが発生し続けました
「セッションで他のスレッドが実行されているため、新しいトランザクションは許可されません」
基本的に、このエラーは、dbリーダーがまだレコードを読み取っている間に変更を保存しようとしていることを示しています。
最終的に私はそれを(私がロングショットと考えたもの)で解決し、ToArray()
呼び出しを追加して_IEnumerable<long>
_ ..を実現しました。
つまり、最終的には、EFからのIEnumerable
の結果に、まだ具体化されていない結果が含まれることを期待する必要がありますか?はいの場合、IEnumerable
が具体化されているかどうかを知る方法はありますか?
これがそれらの「duhhh」質問の1つである場合、感謝と謝罪... :)
IQueryable
は、Linq-to-entitiesを使用している場合に使用されます= LINQプロバイダーによってSQLとして解釈され、サーバー上で実行される宣言型LINQクエリをアプリケーションで構築します。クエリが実行(反復)されると、IEnumerableになり、オブジェクトは必要に応じて反復=すぐにではなく実体化されます。
ストアドプロシージャを呼び出すと、アプリケーションに宣言型クエリが組み込まれていないため、Linq-to-entitiesは使用されません。クエリ/ SQLはデータベースサーバーにすでに存在し、呼び出しているだけです。これはIEnumerable
を返しますが、すべての結果がすぐに実現されるわけではありません。結果は反復として具体化されます。これは、オブジェクトのフェッチを明示的に要求する場合のデータベースカーソル/または.NETデータリーダーの原則です。
したがって、このようなものを呼び出すと:
foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
.Select(l => l.Value))
{
...
}
コースを1つずつ取得しています(ところで、キーワードだけに関心があるのに、なぜコース全体をロードするのですか?)。ループを完了するか中断するまで、データリーダーが開いてレコードをフェッチします。
代わりにこれを呼び出す場合:
foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
.ToList() // or ToArray
.Select(l => l.Value))
{
...
}
クエリですべての結果をすぐに実体化するように強制すると、開いているデータベースリーダーではなく、メモリ内のコレクションでループが実行されます。
IEnumerable
とIQueryable
の違いは、IQueryable
がIEnumerable
であるため、データのフェッチ方法にありません。違いはバッキング構造にあります(何かがこれらのインターフェースを実装する必要があります)。
_IEnumerable<T>
_で作業するということは、それ以降のすべての操作がC#コード、つまりlinq-to-objectsで行われることを意味します。クエリがすでに実行されているという意味ではありません。
Linq-to-objectsに分解したら、この時点で残っているすべてのデータをデータベースからフェッチして.netに送信する必要があります。これにより、パフォーマンスが大幅に低下する可能性があります(たとえば、データベースインデックスはlinq-to-objectsによって使用されません)が、一方で、linq-to-objectsは、何に制限されるのではなく、任意のC#コードを実行できるため、より柔軟性があります。 linqプロバイダーはSQLに変換できます。
_IEnumerable<T>
_は、遅延クエリまたはすでにマテリアライズされたデータの両方にすることができます。通常、標準のlinq演算子は延期され、ToArray()
/ToList()
は常に実体化されます。
IEnumerableは、具体化されるまで水和しません。ストアドプロシージャを呼び出す場合、これ以上のフィルタは必要ないと思います。つまり、パラメータをストアドプロシージャに送信して、返されるデータの目的のサブセットを生成します。ストアドプロシージャに関連付けられたIEnumerableは問題ありません。ただし、テーブルの内容全体をフェッチする場合は、アプリケーションでフィルタリングするための戦略が必要です。同様に、テーブルのIEnumerableをToList()しないでください。すべての行がマテリアライズされます。これにより、メモリ不足の例外が発生する場合があります。それに加えて、理由もなくメモリを消費します。コンテキストに対してIQueryableを使用します。これにより、アプリケーションではなく、データソースでテーブルをフィルタリングできます。答えとして、あなたはそれを具体化する必要があります。 IEnumerableはインターフェースであり、それを具体化するだけでその型が「初期化」され、私の理解では何かが生成されます。