Posts

....
Technical Blog for .NET Developers ©

Sunday, October 6, 2019

LINQ Expression Trees

An expression tree is an efficient data representation of a query operator lambda expression. This data representation is evaluated all simultaneously, so that an individual query can be built and launched on a data source in once

Consider the next query with two operators expecting delegates as arguments:


        private static decimal[] celsiusDegrees = new decimal[] 
            {0.0M, 6.0M, 14.0M, 25.0M, 28.0M, 34.0M, 36.0M};

        private static IEnumerable<decimal> enumerableTemps = celsiusDegrees
            .Where(i => i > 30.0M)
            .OrderBy(i => i).ToArray();

        private static IQueryable<decimal> queryableTemps = celsiusDegrees.Where(c => c > 30.0M)
            .OrderBy(c => c).AsQueryable();



When the first code is compiled .NET IL emits code with two anonymous methods, one for each of the query lambda expressions

This query is evaluated and launched linearly, first Where operator, then OrderBy operator. This linear evaluation has performance for this example, but consider a query on a large dataset. These scena is when Expression Trees become necessary. Expression trees will be generated if the operator is declared to accept an expression of a delegate

These are the two different implementations of the Where operator:

Standard query operator in LINQ to Objects API, in System.Linq.Enumerable class


        public static IEnumerable<T> Where<T>(
                this IEnumerable<T> source,
                Func<T, bool> predicate);



Implementation in the LINQ to SQL API, in the System.Linq.Queryable class


        public static IQueryable<T> Where<T>(
                this IQueryable<T> source,
                System.Linq.Expressions.Expression<Func<int, bool>> predicate);



The result of this code is the next


        Console.WriteLine(enumerableTemps);

        foreach (var temp in enumerableTemps)
            Console.WriteLine(temp);

        Console.WriteLine(Environment.NewLine);

        Console.WriteLine(queryableTemps);
        foreach (var temp in queryableTemps)
            Console.WriteLine(temp);





The ease way to create expression trees is by using advanced LINQ features, however, a tree made up only of constans would be converted automatically at compile time, the aim is build parametric expressions, such as the formula to convert celsius to kelvin degrees : K = C + 273.15


        Func<decimal, decimal> CelsiusToKelvin = (c) => c + 273.15M;

        Expression<Func<decimal, decimal>> CelsiusToKelvinExp = (c) => c + 273.15M;



The first delegate instance is no different from a regular function, you can get an expression tree representation instead of the delegate simply declaring CelsiusToKelvin as an Expression<TDelegate>

The result of this code is the next


            Console.WriteLine(CelsiusToKelvin.ToString());
            Console.WriteLine(CelsiusToKelvin(34.2M));

            Console.WriteLine(CelsiusToKelvinExp.ToString());
            Console.WriteLine(CelsiusToKelvinExp.Compile()(34.2M));