Why does .Where() with a Func parameter executes the query?

时间:2015-06-15 14:17:45

标签: c# oracle linq entity-framework

Here's how my DataAccessLayer works :

 public Foo GetFooBy(Func<Foo, bool> filter)
 {
     var query = from item in this.DataService.FooSet select item;
     var where = query.Where(filter);
     var first = where.First();

     return first;
 }

I assumed the query would be run when First() is called but it's actually executed by the Where(). From the MSDN, I realized that .Where(Func) is an extension method defined by Enumerable, so it makes sense, but I don't understand how is it different from calling .Where() with a lambda expression.

A very easy way to be sure that .Where() materialized the data is to check query's, where's and first's type.

  1. query is IQueryable, meaning nothing happend in database yet
  2. where is IEnumerable, meaning the data has been materialized*
  3. first is Foo

Debug and SQL traces also make it very clear that data is fetched by the .Where()

EDIT : * possibly not true since IQueryable implements IEnumerable

2 个答案:

答案 0 :(得分:9)

There is a very important difference between Enumerable.Where and Queryable.Where:

Enumerable.Where takes a Func<T, bool>.

Queryable.Where takes an Expression.

Your filter variable is not an Expression, it is a Func<T, bool>, therefore, the compiler uses Enumerable.Where.

What happens then is that all rows of your FOO table are transferred to your client and are then filtered in memory.

As others correctly noted, the execution still happens on the call to First().

UPDATE:
Your SQL trace doesn't prove that the materialization happens on the call to Where. It only proves that the filtering of the Where is not happening on the server side. And the reason for that is explained in my answer above.

How to fix:
You can easily fix this by changing your method to take an Expression instead:

public Foo GetFooBy(Expression<Func<Foo, bool>> filter)
{
    var query = from item in this.DataService.FooSet select item;
    var where = query.Where(filter);
    var first = where.First();

    return first;
}

答案 1 :(得分:0)

I assumed the query would be run when First() is called

That's a correct assumption.

but it's actually executed by the Where()

That's either false, or you're not using a Where method from System.Linq.

I don't understand how is it different from calling Where() with a lambda expression.

It's not. Well, unless the Queryable.Where would have been the one executed had you provided a lambda, but in both cases execution would be deferred.


On a side note, your entire method body could be re-written as:

return this.DataService.FooSet.First(filter);

It'd behave identically.