Monday, November 8, 2010

LINQ Execution

How LINQ leverages differed execution and what benefits and impacts it can have to a developer.

Differed and Immediate Execution

By default, LINQ query expressions are not evaluated until you iterate over the contents. The benefit of this differed execution allows the same LINQ query to be executed multiple times, giving you the latest results. However, there would be times when differed execution behavior would not be acceptable due to performance reasons of the query being executed every time. To prevent this behavior, the .NET Framework defines a number of extension methods such as ToArray<T>(), ToDictionary<TSource, TKey>() and ToList<T>() to capture the results in a strongly typed container which would not reflect new changes made to the original collection. Here is an example:

public static void DifferedAndImmediateExecution()
    {
        int[] numbers = { 1,  2,  3,  4,  5,  6 };
        var lessthan4 = from n in numbers
                        where n < 4
                        select n;
 
        Console.WriteLine("Original array with items less than 4");
        foreach (int n in lessthan4)
        {
            Console.WriteLine("{0} <>, n);
        }
        //differed execution
        numbers[2] = 7;//assigning new value
        Console.WriteLine(
            "Results based on differed execution after change number[2] = 7");
        foreach (int n in lessthan4)
        {
            Console.WriteLine("{0} <>,  n);
        }
 
        //immediate execution
        numbers = new int[] { 1,  2,  3,  4,  5,  6 };
        var lessthan4immediate = numbers.Where(n => n < 4).Select(n => n).ToArray();
        numbers[2] = 7;//assigning new value
        Console.WriteLine(
            "Results based on immediate execution after change number[2] = 7");
        foreach (int n in lessthan4immediate)
        {
            Console.WriteLine("{0} <>,  n);
        }
    }


As you can see that, new changes are reflected on differed queries whereas new changes are not reflected on immediate execution because the results are cached in a strongly typed container. However, the query does not run until you iterate over the collection.

One of the downsides I have observed with differed execution is that bugs are harder to trace because enumerating the query could happen much further down in your code. This is where the exception would be thrown. However, you may forget that it is the original query that is the problem. So if differed execution is not the desired behavior you want, use the non-deferred extension methods like ToArray, ToList, ToDictionary or ToLookup to force the query to be executed. Let's see an example for that:


public static void DelayedExceptions()
    {
        string[] blob = {"Resharper", "is", "great"};
        IEnumerable sentence = blob.Select(s => s.Substring(0,  5));
        foreach (string word in sentence)
        {
            Console.WriteLine(word);
        }
    }


No comments: