我曾多次尝试寻找差异,并且有多个答案。
一个常见的区别是在服务器端IEnumerable
过滤内存中的数据。IQueryable
这到底是什么意思?
在内存数据上发生的过滤仍然不是服务器端的事情吗?
我试图在多个地方寻找它们的用途,但我无法用简单的语言找到它。
谢谢你。
我曾多次尝试寻找差异,并且有多个答案。
一个常见的区别是在服务器端IEnumerable
过滤内存中的数据。IQueryable
这到底是什么意思?
在内存数据上发生的过滤仍然不是服务器端的事情吗?
我试图在多个地方寻找它们的用途,但我无法用简单的语言找到它。
谢谢你。
IEnumerable<T>
表示产生一系列结果的事物。但是,它不会公开有关如何生成序列的任何信息。
IQueryable<T>
在Expression
属性中以表达式树的形式公开有关如何生成序列的信息。然后可以轻松地将这些信息映射到不同的指令集。
如果您调用Enumerable.Where
,IEnumerable<T>
您将传入一个与 . 兼容的编译方法Func<T, bool>
。理论上,我们可以解析编译方法的 IL 以找出该方法的作用,并使用它来映射到另一组指令;但这非常复杂。不可避免地,解决此问题的唯一方法是将所有对象从服务器/提供者/数据源加载到内存中,并将编译后的方法应用于每个对象。
如果你调用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 库编写的。免责声明:我是作者。)
Zev Spitz 已经很好地说明了这种差异,但我想补充一个极端情况下发生的例子,以突出差异。
假设您的数据在一台服务器上,而应用程序在另一台服务器上运行。这些是通过互联网连接的。
假设您在数据中有一个包含 100 万行的表,并且您想要一个具有给定 ID 的表,即 thatId。
忽略您的连接方式,让变量 Table 成为代码中该数据的表示形式。
现在,我们可以通过请求获得我们想要的行Table.Where(x => x.Id == thatId)
。
如果 Table 是 IQueryable,那么这将被转换为对该行的 Table 请求,该请求将从服务器返回,该服务器将数据存储在运行应用程序的服务器的内存中。
另一方面,如果我们将其视为 IEnumerable,例如通过这样做,Table.AsEnumerable().Where(x => x.Id == thatId)
那么这将变成对表中所有行的请求,然后将其传输到运行应用程序的服务器上的内存中。只有在互联网上传输完所有这些数百万行之后,应用程序才会对数据进行排序并挑选出您真正想要的行。
一个常见的区别是 IEnumerable 从内存中过滤数据,而 IQueryable 在服务器端进行
你是对的,一切都在服务器中发生。但在上面的句子中,server side
指的是 SQL Server,而不是运行 .NET 应用程序的服务器。所以主要思想是IQueryable
可以将an翻译成将在SQL Server中执行的SQL脚本,与将数据从SQL Server带到应用程序内存然后在内存中执行查询相比,这通常会带来显着的性能提升.
不是 100% 肯定,但我认为这意味着使用 IQueryable,在我们在 BE 中使用 EF 和 SQLike 作为 DB 的示例中,其中编写的任何 Linq 过程都将在 SQL 中转换并发送到DB,它将详细说明此类 sql 代码,然后返回结果。
虽然 IEnumelable 缺少此功能,例如,如果您将整个实体转换为 IEnumerable,您将始终在 BE 内详细说明任何过滤器。
我希望我是对的,我已经很清楚了,祝你有个愉快的会议