274

IQueryableLINQのコンテキストでの使用は何ですか?

拡張メソッドの開発やその他の目的に使用されていますか?

4

4 に答える 4

546

Marc Gravellの答えは非常に完全ですが、ユーザーの観点からもこれについて何かを追加すると思いました...


ユーザーの観点から見た主な違いは、IQueryable<T>(物事を正しくサポートするプロバイダーを使用して) を使用すると、多くのリソースを節約できることです。

たとえば、多くの ORM システムを使用してリモート データベースに対して作業している場合、 を返す方法と を返す方法の 2 つの方法で、テーブルからデータをフェッチするオプションがありIEnumerable<T>ます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.

開発者としての視点から見ると、これは同じように見えます。ただし、パフォーマンスの観点からは、ネットワーク全体で 20,000 ではなく 2 つのレコードしか返されない可能性があります....

于 2009-10-16T16:08:43.667 に答える
17

Reed CopseyMarc Gravellは についてIQueryable(および も) 十分に説明しましたが、多くのユーザーが求めたとIEnumerableの小さな例を提供することで、ここにもう少し追加したいと思います。IQueryableIEnumerable

: データベースに 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)

なぜ?functionIQueryable<Employee> GetEmployeeAndPersonDetailIQueryableIEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerableof の両方SomeServiceClassに linq クエリの共通行が 1 つ含まれているため

where employeesToCollect.Contains(e.PersonId)

両方の関数呼び出しでprogram.cs`を使用しているのに、なぜ実行部分でその AND (N'M' = [Extent1].[Gender])部分が欠落しているのですか?IEnumerableWhere(i => i.Gender == "M") in

IQueryable今、私たちは と の間に 違いが生じたところにいますIEnumerable

メソッドが呼び出されたときにエンティティ フレームワークが行うことは、IQueryableメソッド内に記述された linq ステートメントを取得し、結果セットでさらに linq 式が定義されているかどうかを調べようとします。次に、定義されたすべての linq クエリを収集し、結果をフェッチしてより適切な sql を構築する必要があるまで収集します。実行するクエリ。

次のような多くのメリットを提供します。

  • linqクエリの実行全体で有効になる可能性がある、SQLサーバーによって入力された行のみ
  • 不要な行を選択しないことで、SQL Server のパフォーマンスを向上させます
  • ネットワーク コスト

この例のように、SQL Server は IQueryable の実行後に 2 行だけアプリケーションに返しましたが、IEnumerable クエリに対して3行を返したのはなぜですか?

メソッドの場合 IEnumerable、エンティティ フレームワークはメソッド内に記述された linq ステートメントを取得し、結果を取得する必要がある場合に SQL クエリを構築します。SQLクエリを構築する残りのlinq部分は含まれていません。ここのように、列のSQLサーバーではフィルタリングは行われませんgender

しかし、出力は同じですか?'IEnumerable は、SQL Server から結果を取得した後、アプリケーション レベルで結果をさらにフィルタリングするため

だから、誰かが何を選ぶべきですか?私は個人的に関数の結果を定義することを好みます。これには、より具体的なスクリプトを SQL サーバーに生成する 2 つ以上の IQueryable 関数を結合できるなど、IQueryable<T>多くの利点があるためです。IEnumerable

ここの例では、私の観点からははるかに受け入れられるIQueryable Query(IQueryableQuery2)よりも具体的なスクリプトを生成することがわかります。IEnumerable query(IEnumerableQuery2)

于 2016-02-25T14:00:44.587 に答える