Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq: Order of execution chain query

I want to understand how chain query is processed. For example, let us consider the following query

var sumOfRoots = numbers           //IEnum0
     .Where(x => x > 0)            //IEnum1
     .Select(x => Math.Sqrt(x))    //IEnum2
     .Select(x => Math.Exp(x))     //IEnum3
     .Sum();

where e.g. numbers={-1, 4, 9 }.

Is this what happends behind the scene:

1. Getting all enumerators (forward pass)

  • numbers calls GetEnumerator() which returns (let us denote it with) IEnum0 instance
  • IEnum0 calls GetEnumerator() which returns IEnum1 instance
  • IEnum1 calls GetEnumerator() which returns IEnum2 instance
  • IEnum2 calls GetEnumerator() which returns IEnum3 instance

2. Calling MoveNext (backward pass)

  • .Sum() calls MoveNext() on IEnum3
  • IEnum3 calls MoveNext() on IEnum2
  • IEnum2 calls MoveNext() on IEnum1
  • IEnum1 calls MoveNext() on IEnum0

3. Returning from MoveNext (forward-backward pass)

  • IEnum0 moves to element -1 and return true.
  • IEnum1 check if -1 satisfy condition (which is not true) so IEnum1 calls MoveNext() on IEnum0.
  • IEnum0 moves to element 4 and return true.
  • IEnum1 check if 4 satisfy condition (which is true) and returns true
  • IEnum2 does nothing, just return output of IEnum1 which is true
  • IEnum2 does nothing, just return output of IEnum2 which is true

4. Calling Current (backward pass)

  • .Sum() calls Current on IEnum3.
  • IEnum3 calls Current on IEnum2
  • IEnum2 calls Current on IEnum1
  • IEnum1 calls Current on IEnum0

5. Returning Current (forward pass)

  • IEnum0 returns 4
  • IEnum1 returns 4
  • IEnum2 returns sqrt(4)=2
  • IEnum3 returns exp(2)

6. Repeat steps 2.-5. until step 3. returns false

Please correct me if a chain query is processed in a different way.

like image 679
Dejan Avatar asked Aug 09 '18 10:08

Dejan


People also ask

How are LINQ queries executed?

LINQ queries are always executed when the query variable is iterated over, not when the query variable is created. This is called deferred execution. You can also force a query to execute immediately, which is useful for caching query results.

What is materialization in LINQ?

Materializing LINQ Queries Once When writing LINQ queries using IEnumerable or IQueryable interfaces, developers can materialize (call ToList , ToArray or similar methods) or not to materialize queries immediately (Lazy loading). The lack of materialization allows developers to work with collections lazily.

What is LINQ Deferred execution?

Deferred execution means that the evaluation of an expression is delayed until its realized value is actually required. It greatly improves performance by avoiding unnecessary execution.

How Deferred execution works?

LINQ uses a Deferred Execution model which means that resulting sequence is not returned at the time the Linq operators are called, but instead these operators return an object which then yields elements of a sequence only when we enumerate this object.


1 Answers

You can use delegates to understand the order of execution. Example:

static void Main(string[] args)
{
    var numbers = new []{ -1, 4, 9 };

    double sumOfRoots = numbers.Where(IsGreaterThanZero)   
                               .Select(ToSquareRoot)      
                               .Select(ToExp)              
                               .Sum(x => ToNumber(x));

    Console.WriteLine($"sumOfRoots = {sumOfRoots}");

    Console.Read();
}

private static double ToNumber(double number)
{
    Console.WriteLine($"SumNumber({number})");

    return number;
}

private static double ToSquareRoot(int number)
{
    double value =  Math.Sqrt(number);

    Console.WriteLine($"Math.Sqrt({number}): {value}");

    return value;
}

private static double ToExp(double number)
{
    double value =  Math.Exp(number);

    Console.WriteLine($"Math.Exp({number}): {value}");

    return value;
}

private static bool IsGreaterThanZero(int number)
{
    bool isGreater = number > 0;

    Console.WriteLine($"{number} > 0: {isGreater}");

    return isGreater;
}

Output:

-1 > 0: False

4 > 0: True

Math.Sqrt(4): 2

Math.Exp(2): 7.38905609893065

SumNumber(7.38905609893065)

9 > 0: True

Math.Sqrt(9): 3

Math.Exp(3): 20.0855369231877

SumNumber(20.0855369231877)

sumOfRoots = 27.4745930221183

like image 115
Rui Jarimba Avatar answered Oct 16 '22 14:10

Rui Jarimba