One of my favourite new features in C# 3 are lambda expressions. These nutty little functions are essential to the inner workings of Linq, and open many doors for the adventurous programmer.
It's important to know the difference between Func<>
expressions and Expression<>
expressions. If you have a lambda of type Func<>
, it's simply a function pointer - a strongly typed delegate to a (usually) anonymous method. However, if your expression is of type Expression<>
, it's actually a reference to an expression tree - an in-memory tree structure containing nodes that represent the operations that you have defined in your lambda.
This difference is crucial - if it is an Expression<>
, it's not actually a function to be executed but rather an abstract description of it - this means we can manipulate and construct them at will, then compile them into executable functions whenever we want to use them.
Consider the following lambda to square an integer:
Expression<Func<int, int>> f = (int x) => x * x;This gets translated into a statement to build the expression tree that looks like the following:
ParameterExpression x = Expression.Parameter(typeof(int), "x"); Expression<Func<int, int>> f = Expression.Lambda<Func<int, int>> ( Expression.Multiply(x, x), new ParameterExpression[] { x } );We can then compile it into a "real" delegate and call it using
DynamicInvoke()
.
// Compile it Func<int, int> func = f.Compile(); // Cast it to a Delegate type Delegate del = func; // Call it - prints "4" Console.Out.WriteLine(del.DynamicInvoke(2).ToString());So, armed with this technique, we can construct functions at runtime. In the following code sample, I've created a descendent of a
DataContext
class that prints out the type and primary key value of every updated row when SubmitChanges()
is called. It creates a lambda expression tree for each entity type that simply returns the entity's primary key value, and compiles that into a regular delegate.
public class MyNorthwindDataContext : NorthwindDataContext { private Dictionary<Type, Delegate> m_Keys = new Dictionary<Type,Delegate>(); public override void SubmitChanges(ConflictMode failureMode) { ChangeSet changes = this.GetChangeSet(); PrintChanges(changes.Updates); base.SubmitChanges(failureMode); } private void PrintChanges(IList<object> changes) { foreach (object entity in changes) { Delegate pk = null; if (m_Keys.ContainsKey(entity.GetType())) { pk = m_Keys[entity.GetType()]; } else { pk = CreatePrimaryKeyDelegate(entity.GetType()); m_Keys[entity.GetType()] = pk; } Console.Out.WriteLine(String.Format( "An entity of type {0} with primary key {1} was updated.", entity.GetType().Name, pk.DynamicInvoke(entity))); } } // This method returns a delegate that returns the primary key // value from a DLINQ data entity. private Delegate CreatePrimaryKeyDelegate(Type type) { Type col = typeof(ColumnAttribute); ParameterExpression x = Expression.Parameter(typeof(object), "x"); Expression<Func<object, object>> expression = Expression.Lambda<Func<object, object>> ( Expression.TypeAs ( Expression.Call ( Expression.TypeAs(x, type), ( // Get the primary key column's get_ method by // examining the attributes. from p in type.GetProperties (BindingFlags.Instance | BindingFlags.Public) where p.IsDefined(col, false) && (p.GetCustomAttributes(col, false)[0] as ColumnAttribute).IsPrimaryKey == true select p ).First().GetGetMethod() ), typeof(object) ), new [] { x } ); return expression.Compile(); } }Using the code above:
using (NorthwindDataContext ctx = new MyNorthwindDataContext()) { List<Order> orders = ( from o in ctx.Orders where o.CustomerID == "ALFKI" select o ).Take(5).ToList(); orders.ForEach(order => order.Freight = 1); ctx.SubmitChanges(); }When run, the above code yields the following output: