为了账号安全,请及时绑定邮箱和手机立即绑定

在Linq上使用IQueryable

/ 猿问

在Linq上使用IQueryable

FFIVE 2019-10-23 12:44:25

什么是使用IQueryable在LINQ的背景下?

它是否用于开发扩展方法或任何其他目的?


查看完整描述

3 回答

?
牧羊人nacy


从用户的角度来看,主要区别在于,当您使用IQueryable<T>(与正确支持事物的提供程序一起)使用时,可以节省大量资源。


例如,如果要使用许多ORM系统处理远程数据库,则可以选择以两种方式从表中获取数据,一种返回IEnumerable<T>,一种返回IQueryable<T>。例如,假设您有一个“产品”表,并且想要获取所有成本大于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。


从您作为开发人员的观点来看,这看起来是一样的。但是,从性能的角度来看,您只能通过网络返回2条记录,而不是20,000条...。


查看完整回答
反对 回复 2019-10-23
?
叮当猫咪

从本质上讲,它的工作类似于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#深度 ”或“ LINQ in Action ”,这两种方法都涵盖了这些主题。


查看完整回答
反对 回复 2019-10-23
?
MM们

尽管Reed Copsey和Marc Gravell已经IQueryable(并且也IEnumerable)进行了足够的描述,但我还是想通过提供一个小示例IQueryable并在此基础上增加一些内容,IEnumerable因为许多用户都要求它


示例:我在数据库中创建了两个表


   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查询


可查询执行部分


--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

*/

所以,您现在有几个问题,让我猜出来并尝试回答


为什么为相同的结果生成不同的脚本?


让我们在这里找出一些要点,


所有查询都有一个共同的部分


WHERE [Extent1].[PersonId] IN (0,1,2,3)


为什么?因为函数IQueryable<Employee> GetEmployeeAndPersonDetailIQueryable和 IEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerable都SomeServiceClass包含linq查询中的一条公共行


where employeesToCollect.Contains(e.PersonId)


比为什么  AND (N'M' = [Extent1].[Gender])在IEnumerable执行部分中缺少该部分,而在两个函数调用中我们都使用了Where(i => i.Gender == "M") inprogram.cs`。


现在我们在点差之间来了IQueryable和  IEnumerable


当一个IQueryable方法被调用时,实体框架会做什么,它采用了写在方法内部的linq语句,并试图找出结果集上是否定义了更多的linq表达式,然后收集所有定义的linq查询,直到需要获取结果并构造更合适的sql。查询执行。


它提供了很多好处,例如,


只有那些由sql server填充的行在整个linq查询执行中可能是有效的

通过不选择不必要的行来帮助SQL Server性能

网络成本降低

像在这里例如SQL服务器返回给应用程序的IQueryable execution`后只有两行,但返回的三排为IEnumerable的查询,为什么?


对于  IEnumerable方法,实体框架采用在方法内部编写的linq语句,并在需要获取结果时构造sql查询。它不包括rest linq部分来构造sql查询。像这里一样,在sql server上没有对column进行过滤gender。


但是输出是一样的吗?因为从sql server检索结果后,“ IEnumerable会进一步在应用程序级别过滤结果”


SO,我应该有人选择呢?我个人更喜欢定义函数结果,IQueryable<T>因为它有很多好处,IEnumerable例如,您可以加入两个或多个IQueryable函数,这些函数将为SQL Server生成更特定的脚本。


在示例中,您可以看到一个IQueryable Query(IQueryableQuery2)生成的脚本比IEnumerable query(IEnumerableQuery2)我个人认为更可接受。


查看完整回答
反对 回复 2019-10-23

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信