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:



