次の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
_
Selectで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句がないことに注意してください。
これは、「インポート」テーブル全体がメモリにキャッシュされることを意味しますか?テーブルに大量のレコードが含まれている場合、このパフォーマンスはまったく低下しますか?
ここで舞台裏で実際に何が起こっているのかを理解してください。
AsEnumerable の理由は
AsEnumerable(TSource)(IEnumerable(TSource))は、シーケンスがIEnumerable(T)を実装するが、利用可能なパブリッククエリメソッドの異なるセットがある場合、クエリ実装から選択するために使用できます。
したがって、以前にWhere
メソッドを呼び出していたときは、IEnumerable.Where
とは異なるWhere
メソッドを呼び出していました。 Where
ステートメントはLINQがSQLに変換するためのものでした。新しいWhere
はIEnumerable
を取り、それを列挙して一致するアイテムを生成するIEnumerable
のものです。これが、異なるSQLが生成されるのを見る理由です。コードの2番目のバージョンでWhere
拡張が適用される前に、テーブルはデータベースから完全に取得されます。これにより、テーブル全体がメモリ内に存在する必要があるか、さらに悪いことにテーブル全体がサーバー間を移動する必要があるため、深刻なボトルネックが発生する可能性があります。 SQLサーバーがWhere
を実行できるようにし、最善を尽くします。
列挙が列挙される時点で、データベースが照会され、結果セット全体が取得されます。
部分的な解決策が道になります。検討する
var res = (
from result in SomeSource
where DatabaseConvertableCriterion(result)
&& NonDatabaseConvertableCriterion(result)
select new {result.A, result.B}
);
また、NonDatabaseConvertableCriterionには結果のフィールドCが必要だとしましょう。 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が列挙、照会、または使用されると、可能な限り多くの作業がデータベースに渡され、ジョブを続行するのに十分な量が返されます。すべての作業をデータベースに送信できるように書き換えることは実際には不可能であると仮定すると、これは適切な妥協案となる可能性があります。
AsEnumerable
には3つの実装があります。
_DataTableExtensions.AsEnumerable
_
DataTable
を拡張してIEnumerable
インターフェイスを与えるため、DataTable
に対してLinqを使用できます。
_Enumerable.AsEnumerable<TSource>
_ および _ParallelEnumerable.AsEnumerable<TSource>
_
AsEnumerable<TSource>(IEnumerable<TSource>)
メソッドは、ソースのコンパイル時の型を_IEnumerable<T>
_を実装する型から_IEnumerable<T>
_自体に変更する以外には効果がありません。
AsEnumerable<TSource>(IEnumerable<TSource>)
は、シーケンスが_IEnumerable<T>
_を実装するが、利用可能なパブリッククエリメソッドの異なるセットがある場合、クエリ実装から選択するために使用できます。たとえば、Table
、Where
、Select
などの_IEnumerable<T>
_を実装し、独自のメソッドを持つ汎用クラスSelectMany
Where
を呼び出すと、Where
のpublicTable
メソッドが呼び出されます。データベーステーブルを表すTable
型には、式ツリーとして述語引数を取り、リモート実行のためにツリーをSQLに変換するWhere
メソッドを含めることができます。たとえば、述語がローカルメソッドを呼び出すためにリモート実行が望ましくない場合は、_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));
_
サーバーはすべてのデータを返し、Linqからオブジェクトへの_Enumerable.Where
_がクエリプロバイダーの実装の代わりに使用されます。
Entity FrameworkがSomeUnusualPredicate
の解釈方法を知らなくても構いません。私の関数は直接使用されます。 (ただし、これはすべての行がサーバーから返されるため、非効率的なアプローチになる可能性があります。)
AsEnumerableはコンパイラに使用する拡張メソッド(この場合はIQueryableの代わりにIEnumerableに定義されているメソッド)を伝えるだけだと思います。クエリの実行は、ToArrayを呼び出すか、列挙するまで延期されます。