This is part of a series of blog post about expression trees in .NET 4.0. The past blog entries are:
In this article, I’ll cover using Expression Trees (ET) for setting properties.
I’ll use the same object model I’ve used in the last article for examples:
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public Address Address { get; set; }
public Project Project { get; set; }
}public struct Address
{
public int StreetNumber { get; set; }
public string StreetName { get; set; }
}public class Project
{
public string ProjectName { get; set; }
public decimal Budget { get; set; }
}
Let’s show something quite powerful, although surprisingly simple to implement: transforming a fetching (get) expression into a setter.
Basically, we want to implement the following method:
private static Expression<Action<M, R>> MakeSet<M, R>(Expression<Func<M, R>> fetcherExp)
That is, we want to take an expression taking an object of type M and returning an object of type R by fetching a property (or property path) on object M and transform it into an action able to take an object of type M and set the same property value.
As mentioned, this is surprisingly straightforward. The first thing to realize is that even if we have a long property path (e.g. m.p1.p2.p3.p4.p5), we only need to turn the last getter into a setter.
private static Expression<Action<M, R>> MakeSet<M, R>(Expression<Func<M, R>> fetcherExp)
{
if (fetcherExp.Body.NodeType != ExpressionType.MemberAccess)
{
throw new ArgumentException(
"This should be a member getter",
"fetcherExp");
}// Input model
var model = fetcherExp.Parameters[0];
// Input value to set
var value = Expression.Variable(typeof(R), "v");
// Member access
var member = fetcherExp.Body;
// We turn the access into an assignation to the input value
var assignation = Expression.Assign(member, value);
// We wrap the action into a lambda expression with parameters
var assignLambda = Expression.Lambda<Action<M, R>>(assignation, model, value);return assignLambda;
}
And voilà! If you want to see the results, simply print-out the resulting expressions:
var lastNameExp = MakeSet((Employee e) => e.LastName);
var streetNameExp = MakeSet((Employee e) => e.Address.StreetName);
var projectBudgetExp = MakeSet((Employee e) => e.Project.Budget);Console.WriteLine(lastNameExp);
Console.WriteLine(streetNameExp);
Console.WriteLine(projectBudgetExp);
As expected, this prints out:
- (e, v) => (e.LastName = v)
- (e, v) => (e.Address.StreetName = v)
- (e, v) => (e.Project.Budget = v)
You can even run them. First, create an instance of an object and compile the expressions into delegates:
var lastNameSetter = lastNameExp.Compile();
var streetNameSetter = streetNameExp.Compile();
var projectBudgetSetter = projectBudgetExp.Compile();var employee = new Employee
{
FirstName = "John",
LastName = "Smith",
DateOfBirth = DateTime.Now,
Address = new Address { StreetName = "Baker Street", StreetNumber = 222 },
Project = new Project { Budget = 6000, ProjectName = "POC" }
};
Then try it out:
lastNameSetter(employee, "Bob");
Console.WriteLine(employee.LastName);
streetNameSetter(employee, "Sainte-Catherine");
Console.WriteLine(employee.Address.StreetName);
projectBudgetSetter(employee, 54);
Console.WriteLine(employee.Project.Budget);
This last code snippet outputs:
- Bob
- Baker Street
- 54
This is all as expected, except maybe for the second one. Why didn’t the new street name (i.e. Sainte-Catherine) stuck on the employee’s address?
Well, that’s because Address is a struct, hence a value type, hence, when you do something like e.Address, what you get is a copied value. Therefore, if you change your copy, you don’t change the original, as we did here.
2 thoughts on “Expression Trees: Part 3 – Setting Properties”