IQueryable
LINQのコンテキストでの使用は何ですか?
拡張メソッドの開発やその他の目的に使用されていますか?
IQueryable
LINQのコンテキストでの使用は何ですか?
拡張メソッドの開発やその他の目的に使用されていますか?
回答:
Marc Gravellの答えは非常に完全ですが、ユーザーの観点からもこれについて何か追加したいと思いました...
ユーザーの観点から見た主な違いは、IQueryable<T>
(物事を正しくサポートするプロバイダーと共に)を使用すると、多くのリソースを節約できることです。
たとえば、多くのORMシステムでリモートデータベースに対して作業している場合、2つの方法でテーブルからデータをフェッチするオプションがあります。1つはを返しIEnumerable<T>
、もう1つはを返しますIQueryable<T>
。たとえば、Productsテーブルがあり、コストが25ドルを超えるすべての製品を取得するとします。
もし、するなら:
IEnumerable<Product> products = myORM.GetProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
ここで何が起こるかというと、データベースがすべての製品をロードし、それらをネットワーク経由でプログラムに渡します。次に、プログラムはデータをフィルタリングします。本質的に、データベースはを実行し、SELECT * FROM Products
すべての製品をユーザーに返します。
IQueryable<T>
一方、適切なプロバイダーを使用すると、次のことができます。
IQueryable<Product> products = myORM.GetQueryableProducts();
var productsOver25 = products.Where(p => p.Cost >= 25.00);
コードは同じに見えますが、ここでの違いは、実行されるSQLがになることSELECT * FROM Products WHERE Cost >= 25
です。
開発者としてのPOVから、これは同じように見えます。ただし、パフォーマンスの観点からは、20,000の代わりに、ネットワーク全体で2つのレコードのみを返すことができます。
IQueryable<Product>
など、あなたのORMまたはリポジトリに固有のだろう-
foreach
、を使用する、またはを呼び出すToList()
)までは、実際にはDBにアクセスしません。
本質的に、その仕事はIEnumerable<T>
-クエリ可能なデータソースを表す- に似ていますが、さまざまなLINQメソッド(Queryable
)がより具体的であり、Expression
デリゲートではなくツリーを使用してクエリを構築します(これが何をEnumerable
使用するか)。
式ツリーは、選択したLINQプロバイダーによって検査され、実際のクエリに変換されます。
これは本当にダウンにありElementType
、Expression
そしてProvider
-しかし、現実には、あなたはめったにとしてこれを気にする必要はありませんユーザー。残酷な詳細を知る必要があるのはLINQ 実装者だけです。
コメントについて; 例として何が必要かはよくわかりませんが、LINQ-to-SQLを検討してください。ここで中心的なオブジェクトはでありDataContext
、これはデータベースラッパーを表します。これには通常、テーブルごとのプロパティ(たとえばCustomers
)があり、テーブルはを実装しIQueryable<Customer>
ます。しかし、それほど多くは直接使用しません。考慮してください:
using(var ctx = new MyDataContext()) {
var qry = from cust in ctx.Customers
where cust.Region == "North"
select new { cust.Id, cust.Name };
foreach(var row in qry) {
Console.WriteLine("{0}: {1}", row.Id, row.Name);
}
}
これは(C#コンパイラによって)次のようになります。
var qry = ctx.Customers.Where(cust => cust.Region == "North")
.Select(cust => new { cust.Id, cust.Name });
これも(C#コンパイラによって)次のように解釈されます。
var qry = Queryable.Select(
Queryable.Where(
ctx.Customers,
cust => cust.Region == "North"),
cust => new { cust.Id, cust.Name });
重要なのは、静的メソッドはQueryable
式ツリーを取得します。これは、通常のILではなく、オブジェクトモデルにコンパイルされます。たとえば、「どこ」を見るだけで、これは次のようなものになります。
var cust = Expression.Parameter(typeof(Customer), "cust");
var lambda = Expression.Lambda<Func<Customer,bool>>(
Expression.Equal(
Expression.Property(cust, "Region"),
Expression.Constant("North")
), cust);
... Queryable.Where(ctx.Customers, lambda) ...
コンパイラーは私たちのために多くのことをしませんでしたか?このオブジェクトモデルを分解して意味を調べ、TSQLジェネレーターで再び元に戻すことができます。
SELECT c.Id, c.Name
FROM [dbo].[Customer] c
WHERE c.Region = 'North'
(文字列はパラメータとして終わる可能性があります。思い出せません)
デリゲートを使用しただけでは、これは不可能です。そして、これがポイントですQueryable
/ IQueryable<T>
:それは式ツリーを使用するためのエントリポイントを提供します。
これらはすべて非常に複雑であるため、コンパイラーを使用して簡単に実行できるようにするのは良い仕事です。
詳細については、「C#in Depth」または「LINQ in Action」を参照してください。どちらもこれらのトピックをカバーしています。
がリードCopseyとマルクGravellはすでにについて説明したIQueryable
(ともIEnumerable
)十分な、MIは上の小さな例を提供することで、もう少しここに追加したいIQueryable
とIEnumerable
多くのユーザーがそれを求めて
例:データベースに2つのテーブルを作成しました
CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Gender] [nchar](1) NOT NULL)
CREATE TABLE [dbo].[Person]([PersonId] [int] NOT NULL PRIMARY KEY,[FirstName] [nvarchar](50) NOT NULL,[LastName] [nvarchar](50) NOT NULL)
PersonId
テーブルの主キー()もテーブルEmployee
の偽造キー(personid
)ですPerson
次に、アプリケーションにado.netエンティティモデルを追加し、その下にサービスクラスを作成します
public class SomeServiceClass
{
public IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable(IEnumerable<int> employeesToCollect)
{
DemoIQueryableEntities db = new DemoIQueryableEntities();
var allDetails = from Employee e in db.Employees
join Person p in db.People on e.PersonId equals p.PersonId
where employeesToCollect.Contains(e.PersonId)
select e;
return allDetails;
}
public IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable(IEnumerable<int> employeesToCollect)
{
DemoIQueryableEntities db = new DemoIQueryableEntities();
var allDetails = from Employee e in db.Employees
join Person p in db.People on e.PersonId equals p.PersonId
where employeesToCollect.Contains(e.PersonId)
select e;
return allDetails;
}
}
同じlinqが含まれています。program.cs
以下で定義されているように呼び出されました
class Program
{
static void Main(string[] args)
{
SomeServiceClass s= new SomeServiceClass();
var employeesToCollect= new []{0,1,2,3};
//IQueryable execution part
var IQueryableList = s.GetEmployeeAndPersonDetailIQueryable(employeesToCollect).Where(i => i.Gender=="M");
foreach (var emp in IQueryableList)
{
System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
}
System.Console.WriteLine("IQueryable contain {0} row in result set", IQueryableList.Count());
//IEnumerable execution part
var IEnumerableList = s.GetEmployeeAndPersonDetailIEnumerable(employeesToCollect).Where(i => i.Gender == "M");
foreach (var emp in IEnumerableList)
{
System.Console.WriteLine("ID:{0}, EName:{1},Gender:{2}", emp.PersonId, emp.Person.FirstName, emp.Gender);
}
System.Console.WriteLine("IEnumerable contain {0} row in result set", IEnumerableList.Count());
Console.ReadKey();
}
}
両方の出力は明らかに同じです
ID:1, EName:Ken,Gender:M
ID:3, EName:Roberto,Gender:M
IQueryable contain 2 row in result set
ID:1, EName:Ken,Gender:M
ID:3, EName:Roberto,Gender:M
IEnumerable contain 2 row in result set
だから問題は何が/どこに違いがあるのですか?違いはないようですね?本当に!!
これらの期間中にエンティティフレームワーク5によって生成および実行されたSQLクエリを見てみましょう
IQueryable実行部分
--IQueryableQuery1
SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])
--IQueryableQuery2
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Employee] AS [Extent1]
WHERE ([Extent1].[PersonId] IN (0,1,2,3)) AND (N'M' = [Extent1].[Gender])
) AS [GroupBy1]
IEnumerable実行部分
--IEnumerableQuery1
SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)
--IEnumerableQuery2
SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Gender] AS [Gender]
FROM [dbo].[Employee] AS [Extent1]
WHERE [Extent1].[PersonId] IN (0,1,2,3)
両方の実行部分に共通のスクリプト
/* these two query will execute for both IQueryable or IEnumerable to get details from Person table
Ignore these two queries here because it has nothing to do with IQueryable vs IEnumerable
--ICommonQuery1
exec sp_executesql N'SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
--ICommonQuery2
exec sp_executesql N'SELECT
[Extent1].[PersonId] AS [PersonId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Person] AS [Extent1]
WHERE [Extent1].[PersonId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3
*/
さて、あなたは今いくつかの質問を持っています、私がそれらを推測してそれらに答えようとしましょう
同じ結果に対して異なるスクリプトが生成されるのはなぜですか?
ここでいくつかのポイントを見つけましょう
すべてのクエリには1つの共通部分があります
WHERE [Extent1].[PersonId] IN (0,1,2,3)
どうして?関数IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable
と
IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable
の両方SomeServiceClass
がlinqクエリの1つの共通行を含むため
where employeesToCollect.Contains(e.PersonId)
なぜ実行部分にAND (N'M' = [Extent1].[Gender])
欠落しているのか
IEnumerable
、どちらの関数呼び出しでもWhere(i => i.Gender == "M") in
program.cs を使用したのか
今、私たちは違いが間に来た時点である
IQueryable
とIEnumerable
IQueryable
メソッドが呼び出されたときにエンティティフレームワークが行うことは、メソッド内に記述されたlinqステートメントを取得し、結果セットにさらにlinq式が定義されているかどうかを調べ、結果がより適切なsqlをフェッチして構築するまで、定義されているすべてのlinqクエリを収集します実行するクエリ。
それはのような多くの利点を提供します、
ここの例のように、SQLサーバーはIQueryableの実行後に2行だけをアプリケーションに返しましたが、IEnumerableクエリに対して3行を返しました。なぜですか?
IEnumerable
メソッドの場合 、エンティティフレームワークはメソッド内に記述されたlinqステートメントを取得し、結果をフェッチする必要があるときにSQLクエリを構築します。SQLクエリを構築するためのREST LINQ部分は含まれていません。このように、SQLサーバーの列ではフィルタリングは行われませんgender
。
しかし、出力は同じですか?'IEnumerableは、SQLサーバーから結果を取得した後、アプリケーションレベルで結果をさらにフィルター処理するため
だから、誰かが何を選ぶべきですか?私は個人的に関数の結果を定義することを好みます。IQueryable<T>
それは、それよりも多くの利点があるためIEnumerable
、SQLサーバーに特定のスクリプトを生成する2つ以上のIQueryable関数を結合できるからです。
ここの例では、私の見解ではIQueryable Query(IQueryableQuery2)
、より具体的なスクリプトが生成されていることがわかりIEnumerable query(IEnumerableQuery2)
ます。