Skip to content

Generate Func by using Expression Trees

November 8, 2011

I stumbled in a code change today which involved some value look-up in a dictionary based on some property name which is by convention (paramA + paramB + “property”).

So there was used to be a pre-calculated sum stored in the dictionary by a user control.

class SomeClass
{
    public Decimal DailyFunctionalAmount { get; set; }
    public Decimal MonthToDateFunctionalAmount { get; set; }
    ///..... have much more here
}

Decimal GetTotal(String fieldName, IDictionary<String, Decimal> lookups)
{
    // used to be as simple as getting the pre-calculated sum via dictionary (populated somewhere else by control)
    var totalByLookup = lookups[fieldName];
    return totalByLookup;
}

Now, I need to rely on the underlying data rows to calculate the sum.

var lists = new List<SomeClass> {
    new SomeClass { DailyFunctionalAmount = 10, MonthToDateFunctionalAmount = 100},
    new SomeClass { DailyFunctionalAmount = 10, MonthToDateFunctionalAmount = 100},
};

// I could not use the following code as this is compiled/pre-determined code where we actually need the selector in run-time based on the generated property name by convention
var totalByLinq = lists.Sum(r => r.DailyFunctionalAmount); 

Luckily, there is this expression trees which seems to be perfect for the job, rather than a nasty static function returning selector based on possible combinations for the property name by convention.

// returns -> dtoType => dtoType.propertyName
Func<SomeClass, Decimal> GetMyFunc(string propertyName)
{
    var dtoTypeParameter = Expression.Parameter(typeof(SomeClass), "dtoType");
    var dtoPropertySelector = Expression.Property(dtoTypeParameter, propertyName);
    var lamdaExpression = Expression.Lambda<Func<SomeClass, Decimal>>(dtoPropertySelector, new ParameterExpression[] { dtoTypeParameter });

    return lamdaExpression.Compile();
}

Decimal GetTotal(String fieldName, IEnumerable<SomeClass> lists)
{
    // solution - generate the func in run-time
    var runTimeSelector = GetMyFunc(fieldName);
    var totalByLinq = lists.Sum(runTimeSelector);
    return totalByLinq;
}

It’s surely world of possibilities with this expression trees ;)

Full source code below:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        var lists = new List<SomeClass> {
            new SomeClass { DailyFunctionalAmount = 10, MonthToDateFunctionalAmount = 100},
            new SomeClass { DailyFunctionalAmount = 10, MonthToDateFunctionalAmount = 100},
        };

        var lookups = new Dictionary<String, Decimal>(); // stored with the property name as key in the dictionary and the sum as value
        lookups.Add("DailyFunctionalAmount", 20);
        lookups.Add("MonthToDateFunctionalAmount", 200);

        var dailyFunctionalFieldName = GetFieldName("Daily", "Functional");
        var monthToDateFunctionalFieldName = GetFieldName("MonthToDate", "Functional");

        // old way
        Assert.AreEqual(20, GetTotal(dailyFunctionalFieldName, lookups));
        Assert.AreEqual(200, GetTotal(monthToDateFunctionalFieldName, lookups));

        // new way 
        Assert.AreEqual(20, GetTotal(dailyFunctionalFieldName, lists));
        Assert.AreEqual(200, GetTotal(monthToDateFunctionalFieldName, lists));
    }

    Decimal GetTotal(String fieldName, IDictionary<String, Decimal> lookups)
    {
        // used to be as simple as getting the pre-calculated sum via dictionary (populated somewhere else by control)
        var totalByLookup = lookups[fieldName];
        return totalByLookup;
    }

    Decimal GetTotal(String fieldName, IEnumerable<SomeClass> lists)
    {
        // now it needs to be manually calculated from the list

        //var totalByLinq = lists.Sum(r => r.DailyFunctionalAmount); // but this is compiled/pre-determined code where we actually need the selector in run-time

        // solution - generate the func in run-time
        var runTimeSelector = GetMyFunc(fieldName);
        var totalByLinq = lists.Sum(runTimeSelector);
        return totalByLinq;
    }

    String GetFieldName(String periodic, String currency)
    {
        // the field name is generated by some convention [Daily/MonthToDate] + [Functional/Transaction] + "Amount"
        return periodic + currency + "Amount";
    }

    // returns -> dtoType => dtoType.propertyName
    Func<SomeClass, Decimal> GetMyFunc(string propertyName)
    {
        var dtoTypeParameter = Expression.Parameter(typeof(SomeClass), "dtoType");
        var dtoPropertySelector = Expression.Property(dtoTypeParameter, propertyName);
        var lamdaExpression = Expression.Lambda<Func<SomeClass, Decimal>>(dtoPropertySelector, new ParameterExpression[] { dtoTypeParameter });

        return lamdaExpression.Compile();
    }
}

class SomeClass
{
    public Decimal DailyFunctionalAmount { get; set; }
    public Decimal MonthToDateFunctionalAmount { get; set; }
    ///..... have much more here
}

Advertisement
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.