Consider the following code, where dbContext
is a SQL Server database context and Examples
is a DbSet
:
this.dbContext.Examples.Take(5).ToList();
Enumerable.Take(this.dbContext.Examples, 5).ToList();
The first line works as expected and is converted to SQL in the following manner:
SELECT TOP(5) * FROM Examples
However, the second line first fetches all rows and applies the Take
operator afterwards. Why is that?
Since I am using expressions to build a dynamic lambda I have to use the second approach ( Enumerable.Take
):
var call = Expression.Call(
typeof(Enumerable),
"Take",
new[]{ typeof(Examples) },
contextParam,
Expression.Constant(5)
);
Unfortunately, the first approach does not work when working with expressions and the current architecture of the program forces me to build a lambda dynamically.
Why does the second approach fetches all rows and how can I prevent it in order to use it in expressions efficiently?
You're not calling the same method. The first line is invoking Queryable.Take
, not Enumerable.Take
.
Since DbSet
implements both IQueryable<>
and IEnumerable<>
, but IQueryable<>
implements IEnumerable<>
, the compiler treats IQueryable<>
as a more specific type. So when it's resolving the Take
extension method to call, it determines that Queryable.Take(...)
is the right one, because it requires an IQueryable<>
as the first parameter.
This is important because the IQueryable<>
interface is what allows LINQ queries to be built as expression trees that get evaluated into SQL. The moment you switch to treating an IQueryable<>
as an IEnumerable<>
, you lose that behavior and switch to only being able to iterate over the results of whatever query had been built prior to that.
Try this:
Queryable.Take(this.dbContext.Examples, 5).ToList();
or this:
var call = Expression.Call(
typeof(Queryable),
"Take",
new[]{ typeof(Examples) },
contextParam,
Expression.Constant(5)
);
It works because in the first statement
dbContext.Examples.Take(5).ToList();
You are invoking the .Take(5)
on an IQueryable
interface, on which the LINQ to SQL provider can execute proper SQL statement against the database.
If you need the query to happen on the database side, you have to construct the query on the IQueryable
interface instance.
Enumerable.Take
is an IEnumerable
reference, the execution of the Take
method will happen in memory, after you have fetch all of the data from the database.
"this.dbContext.Examples" does get all the data then Enumerable.Take filter and take the top 5 from it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.