Expression Trees: Part 3 – Setting Properties


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

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