LINQ to SQL の .AsEnumerable() について
-
27-09-2020 - |
質問
次の LINQ to SQL クエリがあるとします。
var test = from i in Imports
where i.IsActive
select i;
解釈された SQL ステートメントは次のとおりです。
SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1
SQL に変換できないアクションを選択で実行したいとします。これを達成する従来の方法は次のとおりであると理解しています AsEnumerable()
したがって、それを実行可能なオブジェクトに変換します。
この更新されたコードを考えると、次のようになります。
var test = from i in Imports.AsEnumerable()
where i.IsActive
select new
{
// Make some method call
};
そして更新された SQL:
SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0]
実行された SQL ステートメントに where 句が欠落していることに注意してください。
これは、「Imports」テーブル全体がメモリにキャッシュされることを意味しますか?テーブルに大量のレコードが含まれている場合、パフォーマンスは少しでも低下しますか?
ここの舞台裏で実際に何が起こっているのかを理解するのを手伝ってください。
解決
の理由 列挙可能として することです
Asenumerable(TSource)(IENumerable(TSource))は、シーケンスがiNumerable(t)を実装する場合のクエリ実装を選択するために使用できますが、利用可能なパブリッククエリメソッドのセットも異なります
それで、あなたが電話していたとき、 Where
以前は別のメソッドを呼び出していました Where
からのメソッド IEnumerable.Where
. 。それ Where
ステートメントは LINQ を SQL に変換するためのもので、新しい Where
それは IEnumerable
かかるもの IEnumerable
, 、それを列挙し、一致する項目を生成します。これが、異なる SQL が生成される理由の説明になります。テーブルは、実行前にデータベースから完全に取得されます。 Where
拡張子は、コードの 2 番目のバージョンに適用されます。これにより、テーブル全体がメモリ内に存在する必要があり、さらに悪いことにテーブル全体がサーバー間を移動する必要があるため、深刻なボトルネックが発生する可能性があります。SQL サーバーに実行を許可します。 Where
そしてそれが最も得意なことを行います。
他のヒント
列挙が列挙されている時点で、データベースは照会され、ResultSet全体が取得されます。
部品と部品の解決策は方法である可能性があります。
を検討してくださいvar res = (
from result in SomeSource
where DatabaseConvertableCriterion(result)
&& NonDatabaseConvertableCriterion(result)
select new {result.A, result.B}
);
.
NondataBaseConvertableCriterionにフィールドCが必要な場合は、結果からもSPERで指定しましょう。NondataBaseConvertableCriterionはその名前を示唆しているものをしているため、これは列挙として実行されなければなりません。ただし、
を検討してくださいvar partWay =
(
from result in SomeSource
where DatabaseConvertableCriterion(result)
select new {result.A, result.B, result.C}
);
var res =
(
from result in partWay.AsEnumerable()
where NonDatabaseConvertableCriterion select new {result.A, result.B}
);
.
この場合、RESが列挙されている場合、照会、またはその他の方法で使用されている場合は、できるだけ多くの作業がデータベースに渡されます。これはジョブを続行するのに十分なほど戻ります。すべての作業をデータベースに送信できるように再書き換えることが本当に不可能であると仮定すると、これは適切な妥協点かもしれません。
実装は 3 つあります AsEnumerable
.
DataTableExtensions.AsEnumerable
を延長します DataTable
それに与える IEnumerable
インターフェースに対して Linq を使用できるようにする DataTable
.
Enumerable.AsEnumerable<TSource>
そして ParallelEnumerable.AsEnumerable<TSource>
の
AsEnumerable<TSource>(IEnumerable<TSource>)
メソッドは、コンパイル時間タイプのソースを実装するタイプから変更する以外に影響を及ぼしませんIEnumerable<T>
にIEnumerable<T>
自体。
AsEnumerable<TSource>(IEnumerable<TSource>)
シーケンスが実装されたときにクエリの実装を選択するために使用できますIEnumerable<T>
また、利用可能な異なる一連のパブリッククエリメソッドもあります。たとえば、ジェネリック クラスがあるとします。Table
実装するIEnumerable<T>
などの独自のメソッドがありますWhere
,Select
, 、 そしてSelectMany
, 、への電話Where
国民を呼び起こすだろうWhere
の方法Table
. 。あTable
データベーステーブルを表す型には、Where
述語引数を式のツリーとして取得し、リモート実行のためにツリーをSQLに変換する方法。たとえば、述語がローカルメソッドを呼び出すため、リモート実行が望ましくない場合、AsEnumerable<TSource>
メソッドを使用して、カスタムメソッドを非表示にし、代わりに標準のクエリ演算子を利用できるようにすることができます。
言い換えると。
私が持っているなら
IQueryable<X> sequence = ...;
Entity Framework などの LinqProvider から、私はそうします。
sequence.Where(x => SomeUnusualPredicate(x));
そのクエリはサーバー上で作成され、実行されます。EntityFramework が変換方法を知らないため、これは実行時に失敗します。 SomeUnusualPredicate
SQLに変換します。
代わりに Linq to Objects を使用してステートメントを実行したい場合は、次のようにします。
sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x));
これで、サーバーはすべてのデータを返します。 Enumerable.Where
from Linq to Objects は、クエリ プロバイダーの実装の代わりに使用されます。
Entity Framework が解釈方法を知らなくても問題ありません SomeUnusualPredicate
, 、私の関数は直接使用されます。(ただし、すべての行がサーバーから返されるため、これは非効率的なアプローチになる可能性があります。)
ASENUMERABLEは、使用する拡張方法(この場合、IQueryableの代わりにIEnumerableに定義されているもの)をコンパイラに指示すると考えています。 クエリの実行は、それを解析または列挙するまで依然として延期されます。