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