Search code examples
c#lambdaexpression-trees

Expression<Func<T>> parameter causes calling code to use Expression.Lambda


I have a generic extension method which takes an Expression parameter. The parameter is used to build, compile and cache an expression tree. That part all works well (expression compiled only once).

But when I profile the application I see time spent in Expression.Lambda and Expression.Property in the calling code. Is this expected or did I do something wrong in defining/compiling my expressions?

Here's a simplified version of the code involved:

namespace Example
{
    public class Caller
    {
        public void SetPropertyX(MyTypedDataRow dataRow, string value)
        {
            // Inside this method, the profiler shows time spent in Expression.Lambda and Expression.Property
            // even when the same property is reused and the compiled expression stored in the dictionary of
            // DataRowExpressionHelper is used.
            dataRow.Set(x => x.PropertyX, value);
        }
    }

    public static class DataRowExtensions
    {
        public static void Set<TRow, TValue>(this TRow row, Expression<Func<TRow, TValue>> property, TValue value) where TRow : DataRow
        {
            DataRowExpressionHelper<TRow>.Set(row, property, value);
        }
    }

    internal static class DataRowExpressionHelper<TRow> where TRow : DataRow
    {
        private static Dictionary<string, object> _propertySetters = new Dictionary<string, object>();

        internal static void Set<TValue>(TRow row, Expression<Func<TRow, TValue>> property, TValue value)
        {
            var propertyInfo = (PropertyInfo) ((MemberExpression) property.Body).Member;
            var propertyName = propertyInfo.Name;

            Action<TRow, TValue> propertySetter;

            object untypedSetter;
            if (_propertySetters != null && _propertySetters.TryGetValue(propertyName, out untypedSetter))
            {
                propertySetter = (Action<TRow, TValue>)untypedSetter;
            }
            else
            {
                var targetRow = Expression.Parameter(typeof(TRow), "targetRow");
                var newValue = Expression.Parameter(typeof(TValue), "newValue");

                propertySetter = Expression.Lambda<Action<TRow, TValue>>(
                    Expression.Block(/* Logic here */),
                    // Input parameters
                    targetRow,
                    newValue
                ).Compile();

                var updatedPropertySetters = new Dictionary<string, object>(_propertySetters);
                updatedPropertySetters[propertyName] = propertySetter;
                _propertySetters = updatedPropertySetters;
            }

            propertySetter.Invoke(row, value);
        }
    }

Solution

  • If you would look at the decompiled code of your caller, you would see something like the following:

     public void SetPropertyX(MyTypedDataRow dataRow, string value)
        {
            var parameter = Expression.Parameter("x", typeof(MyTypedDataRow));
            dataRow.Set(Expression.Lambda<Func<MyTypedDataRow, string>>(Expression.Property(parameter, "PropertyX"), parameter) , value);
        }
    

    As you can see your lambda is constructed inline.

    Unfortunately for a method that is called a lot, this has a really big performance impact. In this case either save the expression tree (e.g x=>x.PropertyX) to a static field, or if there is too many of them, then cache them by property name ( and if you have a limited amount of columntypes, then also by column types)