考虑以下代码,其中dbContext
是SQL Server数据库上下文,Examples
是DbSet
this.dbContext.Examples.Take(5).ToList();
Enumerable.Take(this.dbContext.Examples, 5).ToList();
第一行按照预期工作,并以以下方式转换为SQL:
SELECT TOP(5) * FROM Examples
但是,第二行首先获取所有行,然后应用Take
运算符。为什么会这样呢?
因为我使用表达式来构建动态lambda,所以我必须使用第二种方法(Enumerable.Take
):
var call = Expression.Call(
typeof(Enumerable),
"Take",
new[]{ typeof(Examples) },
contextParam,
Expression.Constant(5)
);
不幸的是,第一种方法在使用表达式时不起作用,程序的当前体系结构迫使我动态构建一个lambda。
为什么第二种方法获取所有行,我如何防止它以便在表达式中有效地使用它?
发布于 2020-05-06 03:15:27
您调用的不是同一个方法。第一行是调用Queryable.Take
,而不是Enumerable.Take
。
由于DbSet
实现了IQueryable<>
和IEnumerable<>
,而IQueryable<>
实现了IEnumerable<>
,因此编译器将IQueryable<>
视为更具体的类型。因此,当它解析要调用的Take
扩展方法时,它会确定Queryable.Take(...)
是正确的方法,因为它需要一个IQueryable<>
作为第一个参数。
这一点很重要,因为IQueryable<>
接口允许将LINQ查询构建为表达式树,并将其计算为SQL语句。当您切换到将IQueryable<>
视为IEnumerable<>
的那一刻,您就失去了这种行为,并且切换到只能迭代在此之前构建的任何查询的结果。
试试这个:
Queryable.Take(this.dbContext.Examples, 5).ToList();
或者这样:
var call = Expression.Call(
typeof(Queryable),
"Take",
new[]{ typeof(Examples) },
contextParam,
Expression.Constant(5)
);
发布于 2020-05-06 03:02:06
它之所以有效,是因为在第一条语句中
dbContext.Examples.Take(5).ToList();
-您正在IQuerable接口上调用.Take(5),LINQ to SQL提供程序可以在该接口上对数据库执行适当的SQL语句。
如果需要在数据库端执行查询,则必须在IQuerable接口实例上构造查询。
Enumerable.Take
是IEnumarble引用,在从db中获取所有数据后,将在内存中执行Take方法
发布于 2020-05-06 02:46:58
"this.dbContext.Examples“获取所有数据,然后进行Enumerable.Take过滤,并从中提取前5个数据。
https://stackoverflow.com/questions/61620258
复制相似问题