使用IQueryable的使用LINQ(Using IQueryable with Linq)

2019-08-19 05:53发布

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

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

Answer 1:

马克Gravell的答案是非常完整的,但我想我会添加一些关于这个但从用户的角度,以及...


主要的区别,从用户的角度来看,就是,当你使用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

从您的POV作为一个开发者,这看起来是一样的。 然而,从性能的角度来看,你可能只在网络上,而不是20,000返回2条记录....



Answer 2:

在本质上它的工作是非常相似IEnumerable<T> -代表一个可查询的数据源-不同之处在于各LINQ方法(上Queryable )可以是更具体的,使用以构建查询Expression树,而不是代表(这是有什么Enumerable用途)。

表达式树可以由您选择LINQ提供商进行检查和变成了一个实际的查询-尽管这本身就是一种黑色艺术。

这是真的下到ElementTypeExpressionProvider -但在现实中你很少需要关心这个作为一个用户 。 只有LINQ 实施者需要知道的血淋淋的细节。


回复意见; 我不太确定要通过举例的方式是什么,但考虑LINQ到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在行动 ”,两者都提供这些主题的报导。



Answer 3:

虽然里德科普塞和马克Gravell约已经描述IQueryable (也IEnumerable )不够,MI希望在这里提供一个小例子添加更多一点IQueryableIEnumerable尽可能多的用户自找的

例如 :我已经创建了数据库中的两个表

   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也是forgein键( 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

所以,问题是什么/哪里是区别? 它似乎没有有什么区别吗? 真!!

让我们对在这期间产生的,并通过实体framwork 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
*/

所以,你现在有几个问题,让我猜那些并尽量回答

为什么同样的结果产生不同的脚本?

让我们在这里找到了一些分,

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

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

为什么? 因为这两个功能IQueryable<Employee> GetEmployeeAndPersonDetailIQueryableIEnumerable<Employee> GetEmployeeAndPersonDetailIEnumerableSomeServiceClass包含LINQ查询一个公共线

where employeesToCollect.Contains(e.PersonId)

比为什么是AND (N'M' = [Extent1].[Gender])部分缺失的IEnumerable执行部分,而在这两个函数调用,我们使用Where(i => i.Gender == "M") in计划.cs`

现在我们在点差之间来了IQueryableIEnumerable

当一个什么实体framwork做IQueryable调用的方法,它tooks方法里面写LINQ语句,并试图找出如果有更多的LINQ表达式的结果集定义,它就会收集到的结果中定义的所有的LINQ查询需要获取并构建更合适SQL查询来执行。

它提供了许多像好处,

  • 仅由SQL Server填充那些行可能由整个LINQ查询执行是有效的
  • 通过不选择不必要的行可以帮助SQL Server的性能
  • 网络成本得到降低

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

在的情况下, IEnumerable方法,实体框架接过方法里面写LINQ语句和SQL构造查询时,结果需要获取。 它不包括休息LINQ部分构造SQL查询。 喜欢这里没有过滤是SQL Server做了列gender

但是,输出是一样的吗? 由于“IEnumerable的从SQL Server检索结果过滤后的结果在应用层面进一步

那么,究竟应该有人选择呢? 我个人比较喜欢自定义函数的结果IQueryable<T>因为有很多有过好处IEnumerable一样,你可以连接两个或更多的IQueryable功能,产生更具体的脚本到SQL Server。

下面例子中,你可以看到一个IQueryable Query(IQueryableQuery2)产生比一个更具体的脚本IEnumerable query(IEnumerableQuery2)这是我的观点更易被人接受。



Answer 4:

它允许进一步查询进一步向下行。 如果这是超出了服务边界的说法,那么这个IQueryable的对象的用户将被允许用它做更多。

对于如果你使用延迟加载与NHibernate例如,这可能会导致图形,如果在需要的时候/加载。



文章来源: Using IQueryable with Linq