11

I read this question's answers that explain the order of the LINQ to objects methods makes a difference. My question is why?

If I write a LINQ to SQL query, it doesn't matter the order of the LINQ methods-projections for example:

session.Query<Person>().OrderBy(x => x.Id)
                       .Where(x => x.Name == "gdoron")
                       .ToList();

The expression tree will be transformed to a rational SQL like this:

  SELECT   * 
  FROM     Persons
  WHERE    Name = 'gdoron'
  ORDER BY Id; 

When I Run the query, SQL query will built according to the expression tree no matter how weird the order of the methods.
Why it doesn't work the same with LINQ to objects?
when I enumerate an IQueryable all the projections can be placed in a rational order(e.g. Order By after Where) just like the Data Base optimizer does.

Community
  • 1
  • 1
gdoron
  • 147,333
  • 58
  • 291
  • 367
  • Very close, has good answers: [does-the-order-of-linq-functions-matter](http://stackoverflow.com/questions/7499384/does-the-order-of-linq-functions-matter) – nawfal Nov 29 '13 at 08:50

5 Answers5

15

Why it doesn't work this way with LINQ to objects?

LINQ to Objects doesn't use expression trees. The statement is directly turned into a series of method calls, each of which runs as a normal C# method.

As such, the following in LINQ to Objects:

   var results = collection.OrderBy(x => x.Id)
                   .Where(x => x.Name == "gdoron")
                   .ToList();

Gets turned into direct method calls:

   var results = Enumerable.ToList(
                   Enumerable.Where(
                     Enumerable.OrderBy(collection, x => x.Id),
                     x => x.Name = "gdoron"
                   )
                 );

By looking at the method calls, you can see why ordering matters. In this case, by placing OrderBy first, you're effectively nesting it into the inner-most method call. This means the entire collection will get ordered when the resutls are enumerated. If you were to switch the order:

   var results = collection
                   .Where(x => x.Name == "gdoron")
                   .OrderBy(x => x.Id)
                   .ToList();

Then the resulting method chain switches to:

   var results = Enumerable.ToList(
                   Enumerable.OrderBy(
                     Enumerable.Where(collection, x => x.Name = "gdoron"),
                     x => x.Id
                   )
                 );

This, in turn, means that only the filtered results will need to be sorted as OrderBy executes.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Thanks! So why LINQ to objects doesn't use expression tree to enhance performance? – gdoron Dec 21 '11 at 20:12
  • @EtiennedeMartel, (ordering, filtering) => (filtering, ordering) == Performance enchantment... – gdoron Dec 21 '11 at 20:18
  • @gdoron Expression trees wouldn't necessarily help. With `IQueryable`, the provider can turn the expression tree into something that executes *on the server* and returns the appropriate results. With LINQ to Objects, everything's always going to execute in memory. The overhead of computing the expression (which is real) wouldn't likely be recovered by any increase in performance... – Reed Copsey Dec 21 '11 at 20:20
  • @gdoron In some cases, it ~could~ make a difference. However, even if your case, the difference could be huge or very minimal, or non-existent, depending on the filter. If the filter returns most results, it would make little difference... – Reed Copsey Dec 21 '11 at 20:21
  • the minimun i think of is to organize the calls! think of joined ienumerable with a filter, a good optimizer would have excute the where before the join. those scenarios are real! – gdoron Dec 22 '11 at 08:41
8

Linq to objects's deferred execution works differently than linq-to-sql's (and EF's).

With linq-to-objects, the method chain will be executed in the order that the methods are listed—it doesn't use expression trees to store and translate the whole thing.

Calling OrderBy then Where with linq-to-objects will, when you enumerate the results, sort the collection, then filter it. Conversely, filtering results with a call to Where before sorting it with OrderBy will, when you enumerate, first filter, then sort. As a result the latter case can make a massive difference, since you'd potentially be sorting many fewer items.

Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
  • 1
    Linq to objects does use deferred execution... Until you enumerate the results, no processing occurs in Linq to Objects either. It just doens't use expression trees to convert the entire method chain into a single operation, but rather directly executes the methods, each of which returns an enumerable which effectively runs as enumerated. – Reed Copsey Dec 21 '11 at 19:39
  • @Reed - thanks. I did know that - not sure why I was thinking so stupidly. I +1'd you answer and corrected mine. – Adam Rackis Dec 21 '11 at 19:43
  • 1
    It's important to know - that's part of why people so often (even though it's not always good) add .ToList() - it forces an enumeration, which causes the entire operation to execute immediately. However, you can test this easily - just write collection.OrderBy(...) on a large collection, and ignore the results. You'll find it has nearly no execution cost. – Reed Copsey Dec 21 '11 at 19:47
  • @Reed - for sure. Thanks again - enjoy your badges :) – Adam Rackis Dec 21 '11 at 19:50
  • @AdamRackis is there any documentation that guarantees the execution order of successive methods in a method chain? – Peter Majeed Jan 19 '17 at 16:07
4

Because, with LINQ for SQL, the SQL grammar for SELECT mandates that the different clauses occur in a particular sequence. The compiler must generate grammatically correct SQL.

Applying LINQ for objects on an IEnumerable involves iterating over the IEnumerable and applying a sequence of actions to each object in the IEnumerable. Order matters: some actions may transform the object (or the stream of objects itself), others may throw objects away (or inject new objects into the stream).

The compiler can't divine your intent. It builds code that does what you said to do in the order in which you said to do it.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
3

It's perfectly legal to use side-effecting operations. Compare:

"crabapple"
    .OrderBy(c => { Console.Write(c); return c; })
    .Where(c => { Console.Write(c); return c > 'c'; })
    .Count();
"crabapple"
    .Where(c => { Console.Write(c); return c > 'c'; })
    .OrderBy(c => { Console.Write(c); return c; })
    .Count();
Jimmy
  • 89,068
  • 17
  • 119
  • 137
  • Just to add and prevent people from shooting their own feet. Having side effects on a predicate or especially a comparer will cause unpredictable behavior. The amount of times the comparer is called may even be dependent on a randomly chosen pivot point, and even for the same operation the method may be called multiple times. --- A predicate in general is safer, since it'll run either for all items, for every item until a match, or some other predefined condition. However it may still be unpredictable depending on the input, or input order. --- It's probably best to just have no side-effects. – Aidiakapi Sep 01 '15 at 13:10
1

Linq to Objects does not reorder to avoid a would-be run-time step to do something that should be optimized at coding time. The resharpers of the world may at some point introduce code analysis tools to smoke out optimization opportunities like this, but it is definitely not a job for the runtime.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 2
    That's actually not true. While there is a conceptual model of how a SELECT query executes (e.g. compute the cartesian product of all involved tables, apply any JOIN criteria, apply the WHERE criteria, perform any GROUPing, apply the HAVING criteria, apply any ORDERing), but in the Real World, it would be a very naive (and poorly performing) SQL implementation that actually operated according to the conceptual model. So long as the results are equivalent, SQL implementations are free to act on a query as they see fit. – Nicholas Carey Dec 21 '11 at 19:51