什么时候应该使用IEnumerable和IQueryable?

问题描述

我已经尝试寻找差异很多次,并且有多个答案。

一个常见的区别是IEnumerable从内存中过滤数据,而IQueryable在服务器端进行数据过滤。

这到底是什么意思?

在内存数据上进行过滤还不是服务器端的事情吗?

我试图在多个地方寻找它们的用途,但是我无法用简单的文字找到它。

谢谢。

解决方法

IEnumerable<T>代表具有获得一系列结果所需的所有信息的事物。但是,它没有公开有关序列产生方式的信息。

IQueryable<T> expression tree的形式在Expression属性处公开有关如何构建序列的信息。然后,可以轻松地将此信息映射到一组不同的指令。

如果您在Enumerable.Where上调用IEnumerable<T>,则意味着传入的是已编译的方法Func<T,bool>,该方法不易解析和翻译;不可避免地,唯一的解决方法是将所有对象从服务器/提供者/数据源加载到内存中,并将编译后的方法应用于每个对象。

如果您在Queryable.Where上调用IQueryable<T>,则会传入一个表示不同代码操作的对象-Expression<Func<T,bool>>。例如:

IQueryable<Person> qry = /* ... */;
qry = qry.Where(x => x.LastName.StartsWith("A"));

编译器将x => x.LastName.StartsWith("A")转换为代表其各个部分的对象:

Lambda expression,returning a `bool`
    with an `x` parameter of type `Person`
    Call the `StartsWith` method,passing in a constant string `"A"`,on
        Result of the `LastName` property,of
            the `x` element

更多,调用Queryable.Where本身也会修改基础的表达式树:

Call to Queryable.Where,passing in
    The object at `qry`,and
    The previous lambda expression (see above)

枚举查询时(通过foreach或对ToList的调用或类似的方式),信息将被映射为另一种形式,例如SQL:

SELECT *
FROM Persons
WHERE LastName LIKE N'A%'

或网络请求:

http://example.com/Person?lastname[0]=a

如果可以使用构造函数以及对象和集合初始化程序构造表达式树,则调用Queryable.Where之后的最终表达式树看起来类似于此对象图:

var x = new ParameterExpression {
    Type = typeof(Person),Name = "x"
};

new MethodCallExpression {
    Type = typeof(IQueryable<Person>),Arguments = new ReadOnlyCollection<Expression> {
        new ConstantExpression {
            Type = typeof(EnumerableQuery<Person>)
        },new UnaryExpression {
            NodeType = ExpressionType.Quote,Type = typeof(Expression<Func<Person,bool>>),Operand = new Expression<Func<Person,bool>> {
                NodeType = ExpressionType.Lambda,Type = typeof(Func<Person,bool>),Parameters = new ReadOnlyCollection<ParameterExpression> {
                    x
                },Body = new MethodCallExpression {
                    Type = typeof(bool),Object = new MemberExpression {
                        Type = typeof(string),Expression = x,Member = typeof(Person).GetProperty("LastName")
                    },Arguments = new ReadOnlyCollection<Expression> {
                        new ConstantExpression {
                            Type = typeof(string),Value = "A"
                        }
                    },Method = typeof(string).GetMethod("StartsWith",new[] { typeof(string) })
                },ReturnType = typeof(bool)
            }
        }
    },Method = typeof(Queryable).GetMethod("Where",new[] { typeof(IQueryable<Person>),typeof(Expression<Func<Person,bool>>) })
}

(注意。这是使用ExpressionTreeToString library编写的。免责声明:我是作者。)

,

一个常见的区别是,IEnumerable从内存中过滤数据,而IQuerable在服务器端进行过滤

是的,一切都在服务器中进行。但是在上面的句子中,import concurrent.futures import requests import functools def get_info(session,symbol): """ r = session.get('https://somewebsite.com?symbol=' + symbol) return r.text """ return 'symbol answer: ' + symbol symbols = ['abc','def','ghi','jkl'] NUMBER_THREADS = min(30,len(symbols)) with requests.Session() as session: get_info_with_session = functools.partial(get_info,session) # this will be the first argument with concurrent.futures.ThreadPoolExecutor(max_workers=NUMBER_THREADS) as executor: results = executor.map(get_info_with_session,symbols) for result in results: print(result) 的意思是server side,而不是运行.NET应用程序的服务器。因此,主要思想是可以将IQueryable转换为将在SQL Server中执行的SQL脚本,与将数据从SQL Server导入应用程序相比,通常可以带来显着的性能提升内存,然后在内存中执行查询。

,

Zev Spitz已经很好地说明了这种差异,但是我想补充一个极端情况下发生的事例,以突出差异。

假设您的数据在一台服务器上,而应用程序在另一台服务器上运行。这些通过互联网连接。
假设您的数据中有一个表,其中有100万行,并且您想要一个具有给定ID(即id)的表。
忽略连接的精确程度,让变量Table作为代码中数据的表示形式。

现在,我们可以通过请求appsettings.{environment}.json来获得所需的行。

如果Table是一个IQueryable,则将其转换为对Table的那一行请求,该请求将从将数据存储在运行应用程序的服务器的内存中的服务器返回。

另一方面,如果我们将其视为IEnumerable,例如,将其作为<PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.1</TargetFramework> <RootNamespace>ConsoleApplication3</RootNamespace> <AssemblyName>ConsoleApplication3</AssemblyName> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.7" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.7" /> </ItemGroup> <ItemGroup> <None Update="appsettings.Development.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> <None Update="appsettings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> 进行处理,则该请求将变为对表所有行的请求,然后将其转移到运行应用程序的服务器上的内存。只有在通过互联网传输了所有这100万行之后,应用程序才会对数据进行排序并挑选出您真正想要的行。

,

不能100%确定,但是我认为这意味着使用IQueryable,在我们正在BE中使用EF并使用SQLike as DB的示例中,其中编写的任何Linq过程都将在SQL和发送到数据库,它将详细说明此类sql代码,然后返回结果。

当IEnumelable缺少此功能时,例如,如果将整个实体转换为IEnumerable,则将始终在BE内详细说明所有过滤器。

我希望我是对的,我已经很清楚了, 会议愉快