暗黙のキャストを削除します。
public IQueryable<Item> GetItemsFromRepository()
{
var setA = from a in this.dataContext.TableA
where /* criteria */
select a.Prop;
return DoSubquery(setA);
}
private IQueryable<Item> DoSubQuery(IQueryable<DateTimeOffset> set)
{
return from item in set
where /* criteria */
select item;
}
IQueryable<Item>
from からto への暗黙のキャストIEnumerable<Item>
は、基本的AsEnumerable()
にはIQueryable<Item>
. もちろん、それが必要な場合もありますが、デフォルトのままにしておく必要があります。これにより、メモリ内で実行される残りの部分IQueryable
だけではなく、データベースに対してクエリ全体を実行できるようになります。GetItemsFromRepository()
二次的な質問:
1) System.Linq.Table インスタンスをメソッドに渡すと、クエリが実行されるのはいつですか?
Max()
、などの最終結果が必要な場合ToList()
、クエリ可能なオブジェクトでも、ロードされたままの列挙型でもありません。
ただし、AsEnumerable()
クエリの実行は発生しませんがAsEnumerable()
、ソース データソースに対して が実行される前にのみ実行が発生した場合、オンデマンドのインメモリ データソースが生成され、それに対して残りが実行されることを意味します。 .
2) 別のクエリで System.Linq.Table を使用すると、いつ実行されますか?
上と同じ。Table<T>
実装しIQueryable<T>
ます。たとえば、それらのうちの 2 つを結合しても、まだ何も実行されません。
3) パラメータをメソッドに渡した System.Linq.Table に適用できる操作の種類 (Take、First、Last、order by など) に制限はありますか?
によって定義されるものIQueryable<T>
。
編集: と の相違点と類似点に関するいくつかのIEnumerable
説明IQueryable
。
でできることは何でも で実行できIQueryable
、IEnumerable
その逆も同様ですが、実行方法は異なります。
どの実装も linq クエリで使用でき、、IQueryable
などの linqy 拡張メソッドがすべて含まれます。Take()
Select()
GroupBy
これがどのように行われるかは、実装によって異なります。たとえばSystem.Linq.Data.Table
、クエリを SQL クエリに変換することでこれらのメソッドを実装し、その結果をロード時にオブジェクトに変換します。mySource
がテーブルの場合:
var filtered = from item in mySource
where item.ID < 23
select new{item.ID, item.Name};
foreach(var i in filtered)
Console.WriteLine(i.Name);
次のような SQL に変換されます。
select id, name from mySourceTable where id < 23
そして、それから列挙子が作成され、MoveNext()
別の行への呼び出しごとに結果から読み取られ、そこから新しい匿名オブジェクトが作成されます。
一方、mySource
where aList
または a HashSet
、または実装するIEnumerable<T>
が独自のクエリ エンジンを持たないその他の場合、linq-to-objects コードはそれを次のように変換します。
foreach(var item in mySource)
if(item.ID < 23)
yield return new {item.ID, item.Name};
これは、そのコードをメモリ内で実行できるのとほぼ同じくらい効率的です。結果は同じですが、取得方法が異なります。
ここで、すべてIQueryable<T>
を同等のものに変換できるためIEnumerable<T>
、必要に応じて、最初のものmySource
(データベースで実行が行われる場所) を取得し、代わりに次のことを行うことができます。
var filtered = from item in mySource.AsEnumerable()
where item.ID < 23
select new{item.ID, item.Name};
ここでは、結果を反復処理するか、これらの結果をすべて調べる何かを呼び出すまで、データベースに対して何も実行されませんが、一度実行すると、実行を 2 つの別々のステップに分割したようになります。
var asEnum = mySource.AsEnumerable();
var filtered = from item in asEnum
where item.ID < 23
select new{item.ID, item.Name};
最初の行の実装は SQL を実行することSELECT * FROM mySourceTable
であり、残りの実行は上記の linq-to-objects の例のようになります。
データベースに ID が 23 未満のアイテムが 10 個、ID が 23 より大きいアイテムが 50,000 個含まれている場合、パフォーマンスが大幅に低下することは容易に理解できます。
明示的なメソッドを提供するだけでなくAsEnumerable()
、すべてIQueryable<T>
を暗黙的に にキャストできますIEnumerable<T>
。これにより、それらを処理し、foreach
を処理する他の既存のコードでそれらを使用できますIEnumerable<T>
が、誤って不適切なタイミングで実行すると、クエリが大幅に遅くなる可能性がありDoSubQuery
ますIEnumerable<DateTimeOffset>
。IEnumerable<Item>
; _ AsEnumerable()
yourIQueryable<DateTimeOffset>
と yourを暗黙的に呼び出しIQueryable<Item>
、データベースで実行できたことがメモリ内で実行されるようにしました。
このため、99% の確率で、IQueryable
最後の瞬間まで取引を続けたいと考えています。
ただし、反対の例として、それとAsEnumerable()
キャストIEnumerable<T>
が狂気から存在しないことを指摘するために、2 つのことを考慮する必要があります。1 つ目は、IEnumerable<T>
お互いを知らない 2 つの完全に異なるソース (たとえば、2 つの異なるデータベース、データベースと XML ファイルなど) を結合するなど、他の方法ではできないことを実行できるようにすることです。
もう1つは、IEnumerable<T>
実際にはより効率的である場合もあるということです。検討:
IQueryable<IGrouping<string, int>> groupingQuery = from item in mySource select item.ID group by item.Name;
var list1 = groupingQuery.Select(grp => new {Name=grp.Key, Count=grp.Count()}).ToList();//fine
foreach(var grp in groupingQuery)//disaster!
Console.WriteLine(grp.Count());
これは、何らかのグループ化を行うクエリ可能として設定されていますgroupingQuery
が、とにかく実行されていません。list1 を作成するときは、最初にそれにIQueryable
基づいて新しいものを作成します。クエリ エンジンは、最適な SQL を見つけ出し、次のようなものを考え出します。
select name, count(id) from mySourceTable group by name
これはかなり効率的に実行されます。次に、行がオブジェクトに変換され、リストに入れられます。
一方、2 番目のクエリでは、group by
グループ化されていないすべてのアイテムに対して集計メソッドを実行しない の SQL 変換は自然ではありません。したがって、クエリ エンジンが思いつく最善の方法は、最初に行う:
select distinct name from mySourceTable,
そして、受け取ったすべての名前に対して、次のことを行います。
select id from mySourceTable where name = '{name found in last query goes here}'
など、これは 2 つの SQL クエリ、つまり 200,000 を意味するはずです。
この場合、最初にテーブル全体をメモリに取り込む方が効率的であるmySource.AsEnumerable()
ため、はるかにうまく処理できます。(データベースから必要な列のみを取得し、その時点でメモリ内に切り替えるため、作業を行う方がさらに良いでしょう)。mySource.Select(item => new {item.ID, item.Name}).AsEnumerable()
IQueryable<T>
最後の部分は覚えておく価値があります。これは、できるだけ長く一緒にいるべきであるという私たちのルールに反するからです。あまり心配する必要はありませんが、グループ化を行ってクエリが非常に遅い場合は、注意する価値があります。