Click here to Skip to main content
14,543,273 members

Expression API Cookbook

,
Rate this:
4.96 (46 votes)
Please Sign up or sign in to vote.
4.96 (46 votes)
10 Sep 2013CPOL
A how to do Expression API cookbook
Download the demo code here

Introduction 

This article is a strange one in a lot of ways, as there is no real end  product with it as such, it is more of a how do I do X / recipe type article. So  what are the recipes, and what will it show you, the reader how to do?

Well I am going to start with a wee story first (don't worry the point is one  its way). A while back I got this email out the blue from this guy in the states  who was creating a MVVM framework for Windows 8, and I have kept in contact with  this dude (and he really is a dude, if I can convince him to upload an image,  you will see what I mean) and we got talking about his IOC container within his  MVVM framework, which is like MEF for Windows 8.

Looking through Ian's code, it was immediately obvious to me, than Ian really  (and I do mean really) knew how to use the Expression API within .NET. This namespace has always  intrigued me, so I talked to Ian about the merits of writing a joint  kind of article, where we would effectively come up with some scenarios to solve  using the Expression API.

Ian said he would be more than happy to write the code  to any scenarios I could come up with, if I was happy to do the article writing  side of things. This seemed a very fair way to do it, so we have done just that.

Now you may be asking what is so cool about the Expression API, well one  thing I quite like is that you can literally write entire programs in it, and  another thing that you see time and time again, is creating compiled lambda  expressions that have been built up on the fly, which compile down to a  delegate, so provide uber fast performance when compared to reflection. That is  why the Expresson API can be useful (at least we feel that way).

That is essentially what this article is all about, we have a bunch of  scenarios that I came up with (which I hope are good ones) which Ian has coded  up. The examples range from simple property get/set through to some rather  complex examples where we show you how to do things like how to create  If-Then-Else Expressions and compute a HashCode for an object based of its  property values.

Here is a list of the scenarios we will be covering

  Change Tracker This example will show you how to use the
Expression
APIs to monitor an arbitrary object(s) for change  notifications, or  arbitrary collection(s) for change notifications. Where the object(s)  may be deeply nested within a tree like hierarchy
  Convert This example will show you how to use the
Expression.Convert
and Expression.TypeAs APIs to  carry out convert and casting operations
  HashCompute This example will show you how to create an expression that will be  able to generate a dynamic hash code based on examining a source input  objects properties.
  IfThenElse This example will show you how to create conditional Expression tree  logic using the Expression.IfThenEls<code>e  expression APIs
  MethodCall This example will show you how to call an obects method where there  are no parameters required, and shall also show a more complex example  where the method being called requires parameters
  PropertyGetSet This example will show you how to get/set an obects property, and  shall also show a more complex example where we show you how to get/set  a property value on a nested property deep in an objects hierarchy of  properties
  WhereClause This example will show you how to build a dynamic where clause that  can be applied to a IEnumerable<T> using the
Where(...)
extension method


We hope these examples will help to show how to do some of the more common  programming tasks that you may encounter, using the  Expression API.

 

Demo Code

In this section you will find all the examples. There is pretty much a 1-1  mapping between these sections and the actual demo code projects.


Change Tracker

This is by far the most complicated of all of the examples in what it  actually provides, but conversely the actual Expression API usage is not as  complicated as some of the other examples you will find here. The reason we have  chosen to start with this one, is that we honestly think it's a useful piece of  code (that is if you take the "Law Of Demeter" with a pinch of salt). It's a lot  to take in, but please bear with us it's a journey (we hope) that is worth  taking.

We think the best way to start with this one, is to explain in bullet points  what we wanted to create, so here is that original list that both Ian and I  agreed on before we started this article:

  • The tracker should allow the tracking of many objects at once
  • The tracker should either yield a IObservable<bool>  for the IsDirty property (we were originally going to use Rx),  or raise an overall IsDirty event that users of the tracker  could use. We opted for a simple event in the end, as we did not want to  burden non Rx users with an extra Dll, and extra learning.
  • The tracker should be deemed "Not Dirty" if a tracked value is set back  to its original value. This would only obviously work for simple properties,  such as Int, double, string etc etc
  • Would have some sort of method to mark the overall tracker as "Not  Dirty" to allow the change tracking to effectively be reset back to its  original state
  • There should be some sort of change tracker interface that allows a new  object to be tracked for changes by providing a Expression that could be  used to pull out the property. Where the object being requested to track  would have point to a object who supported change tracking via the use of  the INotifyPropertyChanged interface
  • There should be some sort of change tracker interface that allows a List  object(s) to be tracked for add/removing by providing a Expression that could be  used to pull out the property. Where the object being requested to track would  have point to a object who supported change tracking via the use of the
    INotifyCollectionChanged 
    interface
  • There should be some sort of change tracker interface that allows a List  object(s) to be tracked for changes by providing a Expression that could NOT  only be used to pull out the property, but could also supply a
    Predicate<T>
    
    that must be satisfied before the tracked list items  would be considered to contribute to the overall dirty state of the tracker.
So that was the brief for the change tracker, based on that Ian and I came up  with the following interface designs for the change tracker.
public class IsDiryChangedArgs : EventArgs
{
	/// <summary>
	/// Default constructor
	/// </summary>
	/// <param name="isDirty">the new value for is dirty</param>
	public IsDiryChangedArgs(bool isDirty)
	{
	IsDirty = isDirty;
	}

	/// <summary>
	/// The new is dirty value
	/// </summary>
	public bool IsDirty { get; private set; }
}




public interface ITracker : IDisposable
{
	/// <summary>
	/// Track all properties on the specified object
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="objectToTrack"></param>
	void TrackObject<T>(T objectToTrack);

	/// <summary>
	/// Track a specified property on an object or a child object
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <typeparam name="TProp"></typeparam>
	/// <param name="objectToTrack"></param>
	/// <param name="trackingExpression"></param>
	void TrackObject<T, TProp>(T objectToTrack, Expression<Func<T, TProp>> trackingExpression);

	/// <summary>
	/// True if any of the objects being tracked is dirty
	/// </summary>
	bool IsDirty { get; }

	/// <summary>
	/// Mark all components in the tracker as clean
	/// </summary>
	void MarkAsClean();

	/// <summary>
	/// List of component trackers associated with this tracker
	/// </summary>
	/// <returns></returns>
	IEnumerable<IComponentTracker> ComponentTrackers();

	/// <summary>
	/// Event is raised when the IsDirty flag is changed
	/// </summary>
	event EventHandler<IsDiryChangedArgs> IsDirtyChanged;
}
We feel the best place to learn how to use this would be by looking at the tests,  so let's see an example or 2 from the tests that we have provided with it:

If we consider the following tests files and there relationships:

Image 1

CLICK IMAGE FOR A LARGER VERSION

 

Demonstration Using Tests

Then examine some of the test cases, it should start to make a bit more sense.

Simple Test Cases

These test cases cover how you would add a tracker for  non list type properties that you wished to track. From these tests you should be able to see how you can track a object, or even a object which is deep down the property tree. (Remember through that the object(s) being tracked must implement the  INotifyPropertyChanged).

[TestClass]
public class SimpleTrackingTests
{
	[TestMethod]
	public void TrackFirstLayer()
	{
		bool eventCalled = false;
		Child1 child1 = new Child1{ TestInt = 10};
		Tracker tracker = new Tracker();

		Assert.IsFalse(tracker.IsDirty);
		tracker.IsDirtyChanged += (sender, args) => eventCalled = true;

		tracker.TrackObject(child1, x => x.TestInt);
		Assert.IsFalse(tracker.IsDirty);

		child1.TestInt = 5;
		Assert.IsTrue(tracker.IsDirty);
		Assert.IsTrue(eventCalled);

		eventCalled = false;
		child1.TestInt = 10;
		Assert.IsFalse(tracker.IsDirty); 
		Assert.IsTrue(eventCalled);
	}

	[TestMethod]
	public void TrackSecondLayer()
	{
		bool eventCalled = false;
		ChildA childA = new ChildA
			{
	            Child1 = new Child1
	              { 
	                  TestInt = 10
	              }
            };


		Tracker tracker = new Tracker();
		tracker.IsDirtyChanged += (sender, args) => eventCalled = true;

		Assert.IsFalse(tracker.IsDirty);
		tracker.TrackObject(childA, x => x.Child1.TestInt);
		Assert.IsFalse(tracker.IsDirty);
		childA.Child1.TestInt = 5;
		Assert.IsTrue(tracker.IsDirty);
		Assert.IsTrue(eventCalled);

		eventCalled = false;
		childA.Child1.TestInt = 10;

		Assert.IsFalse(tracker.IsDirty); 
		Assert.IsTrue(eventCalled);
	}




	[TestMethod]
	public void TrackThirdLayer()
	{
		bool eventCalled = false;
		RootObject rootObject = 
			new RootObject
		    {
			   ChildA = new ChildA
			   {
				    Child1 = new Child1
				    {
				       TestInt = 5
				    }
			    }
		    };


		Tracker tracker = new Tracker();
		tracker.IsDirtyChanged += (sender, args) => eventCalled = true;
		Assert.IsFalse(tracker.IsDirty);

		tracker.TrackObject(rootObject,x => x.ChildA.Child1.TestInt);
		Assert.IsFalse(tracker.IsDirty);

		rootObject.ChildA.Child1.TestInt = 10;
		Assert.IsTrue(tracker.IsDirty);
		Assert.IsTrue(eventCalled);


		eventCalled = false;
		rootObject.ChildA.Child1.TestInt = 5;
		Assert.IsFalse(tracker.IsDirty);
		Assert.IsTrue(eventCalled);
	}

	[TestMethod]
	public void MarkAsClean()
	{
		bool eventCalled = false;
		ChildA childA = new ChildA
		{
			Child1 = new Child1 { TestInt = 10 }
		};

		Tracker tracker = new Tracker();
		tracker.IsDirtyChanged += (sender, args) => eventCalled = true;
		Assert.IsFalse(tracker.IsDirty);
		tracker.TrackObject(childA, x => x.Child1.TestInt);
		Assert.IsFalse(tracker.IsDirty);

		childA.Child1.TestInt = 5;
		Assert.IsTrue(tracker.IsDirty);
		Assert.IsTrue(eventCalled);

		tracker.MarkAsClean();
		Assert.IsFalse(tracker.IsDirty);
		eventCalled = false;
		childA.Child1.TestInt = 10;

		Assert.IsTrue(tracker.IsDirty);
		Assert.IsTrue(eventCalled);

		childA.Child1.TestInt = 5;

		Assert.IsFalse(tracker.IsDirty);
		Assert.IsTrue(eventCalled);
	}
}

List Test Cases

These test cases cover how you would add a tracker for list type properties that  you wished to track. From these tests you should be able to see how you can  track a list for the following scenarios

  • Simple Add/Remove (item being tracked must implement  INotifyCollectionChanged)
  • Track a List where a certain Predicate<T> holds true
  • Track a nested List
[TestClass]
public class ListTrackingTests
{
	[TestMethod]
	public void TrackAllTest()
	{
		ObservableCollection<Child1> newList = new ObservableCollection<Child1>
			{
				new Child1 { TestInt = 5 },
				new Child1 { TestInt = 10 }
			};

		Tracker tracker = new Tracker();
		tracker.TrackObject(newList);

		Assert.IsFalse(tracker.IsDirty);
		newList[0].TestInt = 10;

		Assert.IsTrue(tracker.IsDirty);
		newList[0].TestInt = 5;
		Assert.IsFalse(tracker.IsDirty);
	}




	[TestMethod]
	public void FilterListTest()
	{
		ObservableCollection<Child1> testList = new ObservableCollection<Child1>();
		ListObject listContainer = new ListObject
		{
			Child1List = testList
		};

		Tracker tracker = new Tracker();

		tracker.TrackObject(listContainer, 
			c => c.Child1List.TrackList(x => x.TestInt > 0));

		testList.Add(new Child1 { TestInt = 0, TestString = "Start" });
		Assert.IsFalse(tracker.IsDirty);

		testList.Add(new Child1 { TestInt = 1, TestString = "Start" });

		Assert.IsTrue(tracker.IsDirty);
	}




	[TestMethod]
	public void NestedFilterListTest()
	{
		ObservableCollection<Child1> testList = new ObservableCollection<Child1>();
		ListObject listContainer = new ListObject
			{
				Child1List = testList
			};

		Tracker tracker = new Tracker();
		tracker.TrackObject(listContainer, 
			c => c.Child1List.TrackList(x => x.TestInt > 0).TestString);

		testList.Add(new Child1 { TestInt = 0, TestString = "Start" });
		Assert.IsFalse(tracker.IsDirty);
		testList.Add(new Child1 { TestInt = 1, TestString = "Start" });
		Assert.IsTrue(tracker.IsDirty);
	}




	[TestMethod]
	public void NestedListTest()
	{
		ObservableCollection<Child1> testList = new ObservableCollection<Child1>();
		ListObject listContainer = new ListObject
		{
			Child1List = testList
		};

		testList.Add(new Child1 { TestString = "Start" });
		Tracker tracker = new Tracker();

		tracker.TrackObject(listContainer, 
			c => c.Child1List.TrackList().TestString); 
			
		Assert.IsFalse(tracker.IsDirty);
		testList[0].TestString = "Hello";

		Assert.IsTrue(tracker.IsDirty);

		testList[0].TestString = "Start";
		Assert.IsFalse(tracker.IsDirty);
	}
}

So How Does The Tracker Work

One of the things that became obvious fairly quickly was that we would need to  have different type of trackers. So we came up with 3 of them which can be seen  in the following diagram

Image 2

Where the IComponentTracker interface looks like this:

/// <summary>
/// IComponentTracker is the interface all component trackers implements.
/// </summary>
public interface IComponentTracker : IDisposable
{
	/// <summary>
	/// The component being tracker
	/// </summary>
	object TrackedComponent { get; }

	/// <summary>
	/// True if the component or one of it's children is dirty
	/// </summary>
	bool IsDirty { get; }

	/// <summary>
	/// mark the component and all it's children as clean
	/// </summary>
	void MarkAsClean();

	/// <summary>
	/// A list of child trackers
	/// </summary>
	/// <returns></returns>
	IEnumerable<IComponentTracker> ChildTrackers();

	/// <summary>
	/// Event that is raised when the is dirty flag is changed
	/// </summary>
	event EventHandler<IsDiryChangedArgs> IsDirtyChanged;
}

We will try not to outline every aspect of these classes but rather  focus on the most salient points of each. However before we get into each of the  3 types of component tracker, let us just look at a common bit of code that all  3 of the component trackers make use of in order to build Expression(s) to  obtain getters for the properties to track.

Here is the relevant code that essentially builds up lookup delegates against  a property name for an object:

/// <summary>
/// ComponentTrackerHelper is used create property accessor and new component trackers. 
/// </summary>
public class ComponentTrackerHelper
{
	private readonly Dictionary<string, Func<object, object>> accessMethods = new Dictionary<string, Func<object, object>>();

	/// <summary>
	/// Access a named property on an object
	/// </summary>
	/// <param name="target"></param>
	/// <param name="propertyName"></param>
	/// <returns></returns>
	public object AccessProperty(object target, string propertyName)
	{
		Func<object, object> accessDelegate = GetPropertyAccessor(target, propertyName);

		if (accessDelegate != null)
		{
			return accessDelegate(target);
		}
		throw new Exception(string.Format("Could not access property {0} on {1}", propertyName, target.GetType().FullName));
	}

	/// <summary>
	/// Creates a new property accessor function
	/// </summary>
	/// <param name="target"></param>
	/// <param name="propertyName"></param>
	/// <returns></returns>
	public Func<object, object> GetPropertyAccessor(object target, string propertyName)
	{
		string fullPropertyName = target.GetType().FullName + "|" + propertyName;
		Func<object, object> accessDelegate;

		if (!accessMethods.TryGetValue(fullPropertyName, out accessDelegate))
		{
			PropertyInfo propertyInfo = target.GetType().GetProperty(propertyName);
			// create the parameter for the instance parameter of type object
			ParameterExpression inParameter = Expression.Parameter(typeof(object), "objectParam");
			// cast the instance to it's real type
			Expression castExpression = Expression.Convert(inParameter, target.GetType());
			// create the property access expression
			Expression propertyAccessExpression =
				Expression.Property(castExpression, propertyInfo);
			// cast the property access expression to an object
			Expression returnCastExpression = Expression.Convert(propertyAccessExpression, typeof(object));
			accessDelegate = Expression.Lambda<Func<object, object>>(returnCastExpression, inParameter).Compile();
			accessMethods.Add(fullPropertyName, accessDelegate);
		}
		return accessDelegate;
	}

	/// <summary>
	/// Creates a new component tracker based on the provided TrackerInfo
	/// </summary>
	/// <param name="target"></param>
	/// <param name="path"></param>
	/// <returns></returns>
	public IComponentTracker CreateTracker(object target, TrackerInfo path)
	{
		switch (path.TrackerType)
		{
			case ComponentTrackerType.Node:
				return new NodeComponentTracker(this, target, path);
			case ComponentTrackerType.Leaf:
				return new LeafComponentTracker(this, target, path);
			case ComponentTrackerType.List:
				return new ListComponentTracker(this, target as IEnumerable, path);
		}
		throw new Exception("Unknown tracker type: " + path.TrackerType);
	}
}

Now on with the different types of component trackers:

LeafComponentTracker

  • Tracks every change made on an object.
  • The object MUST be a INotifyPropertyChanged implementing  object
  • We will track old and new values such that we know if it is truly dirty  by comparing an "Original" value with a current value. This is handled by  the use of a WeakReference, to make sure the object is still  capable of being garbage collected.
  • Should also support a "Not Dirty" state if returned to the the original  value

Here is the the most relevant code for this type of tracker

/// <summary>
/// Initialize the object, setting up property trackers for all relavent properties
/// </summary>
/// <param name="helper"></param>
/// <param name="objectToTrack"></param>
/// <param name="trackerInfo"></param>
private void Initialize(ComponentTrackerHelper helper, object objectToTrack, TrackerInfo trackerInfo)
{
	propertyTrackers = new Dictionary<string, PropertyTracker>();
	this.helper = helper;
	this.objectToTrack = objectToTrack;
	this.trackerInfo = trackerInfo;
	// listen to property change events
	if (objectToTrack is INotifyPropertyChanged)
	{
		((INotifyPropertyChanged)objectToTrack).PropertyChanged += OnPropertyChanged;
	}
	// loop through all public properties
	foreach (PropertyInfo propertyInfo in objectToTrack.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
	{
		// only monitor properties that are write able
		if (propertyInfo.CanWrite)
		{
			bool trackWeakly = !(propertyInfo.PropertyType.IsValueType || propertyInfo.PropertyType == typeof(string));
			// create a new property tracking object and a property accessor delegate
			PropertyTracker propertyTracker = new PropertyTracker(trackWeakly)
	
			// use the new property access delegate to retrieve the property from the object
			object origValue = propertyTracker.PropertyAccess(objectToTrack);

			// store the original value 
			propertyTracker.SetOriginalValue(origValue);

			// save the new property tracker into the dictionary by property name
			propertyTrackers[propertyInfo.Name] = propertyTracker;
		}
	}
}


/// <summary>
/// This is the property changed handler for the object being tracker
/// </summary>
/// <param name="sender"></param>
/// <param name="propertyChangedEventArgs"></param>
private void OnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
	PropertyTracker propertyTrackingInfo;
	// Get the property tracker associated with the property that changed
	if (propertyTrackers.TryGetValue(propertyChangedEventArgs.PropertyName, out propertyTrackingInfo))
	{
		bool dirtyChanged = false;
		bool hasOriginalValue;
		object originalValue = propertyTrackingInfo.GetOriginalValue(out hasOriginalValue);
		object newValue = propertyTrackingInfo.PropertyAccess(sender);

		if (newValue != null)
		{
			// Property is reverting back to original value so setting is dirty to false
			if (newValue.Equals(originalValue))
			{
				propertyTrackingInfo.IsDirty = false;
				dirtyChanged = true;
			}
			// else if the property is clean we need to dirty it up
			else if (!propertyTrackingInfo.IsDirty)
			{
				propertyTrackingInfo.IsDirty = true;
				dirtyChanged = true;
			}
		}
		else if (!hasOriginalValue)
		{
			// the original value was null and the new value is null so the property is now clean
			if (propertyTrackingInfo.IsDirty)
			{
				propertyTrackingInfo.IsDirty = false;
				dirtyChanged = true;
			}
		}
		else // the new value is null and we have an original value
		{
			// only set to true if the property is clean
			if (!propertyTrackingInfo.IsDirty)
			{
				propertyTrackingInfo.IsDirty = true;
				dirtyChanged = true;
			}
		}


		// only check for dirty properties if the property dirty flag changed
		if (dirtyChanged)
		{
			IsDirty = propertyTrackers.Values.Any(x => x.IsDirty);
		}
	}
}

 

ListComponentTracker

  • Tracks changes to a ObservableCollection (or any other collection that  implenents INotifyCollectionChanged), such as  Add/Remove/Replace/Reset
  • The object MUST be a INotifyCollectionChanged implementing  object

Here is the most relevant code:

/// <summary>
/// Initialize the tracker
/// </summary>
/// <param name="helper"></param>
/// <param name="objectToTrack"></param>
/// <param name="trackerInfo"></param>
private void Initialize(ComponentTrackerHelper helper, IEnumerable objectToTrack, TrackerInfo trackerInfo)
{
	componentTrackers = new Dictionary<object, IComponentTracker>();
	this.helper = helper;
	this.objectToTrack = objectToTrack;
	this.trackerInfo = trackerInfo;

	// add all children to be tracked
	AddChildren(objectToTrack);

	// if this list implements the collection changed event we need to listen to it
	if (objectToTrack is INotifyCollectionChanged)
	{
		((INotifyCollectionChanged)objectToTrack).CollectionChanged += OnCollectionChanged;
	}
}

/// <summary>
/// Handler for collection changed. It adds and removes trackers as the list changes
/// </summary>
/// <param name="sender"></param>
/// <param name="notifyCollectionChangedEventArgs"></param>
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
	bool fireChanged = false;
	bool changed = false;

	if (!IsDirty)
	{
		fireChanged = true;
	}

	switch (notifyCollectionChangedEventArgs.Action)
	{
		case NotifyCollectionChangedAction.Add:
			changed = AddChildren(notifyCollectionChangedEventArgs.NewItems);
			break;
		case NotifyCollectionChangedAction.Remove:
			changed = RemoveChildren(notifyCollectionChangedEventArgs.OldItems);
			break;
		case NotifyCollectionChangedAction.Replace:
			changed = RemoveChildren(notifyCollectionChangedEventArgs.OldItems);
			changed |= AddChildren(notifyCollectionChangedEventArgs.NewItems);
			break;
		case NotifyCollectionChangedAction.Reset:
			changed = RemoveChildren(componentTrackers.Values.ToArray());
			break;
	}

	if (changed)
	{
		isDirty = true;
	}

	if (isDirty && fireChanged && IsDirtyChanged != null)
	{
		IsDirtyChanged(this, new IsDiryChangedArgs(isDirty));
	}
}




/// <summary>
/// Handler for child tracker is dirty changing
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
private void ChildTrackerIsDirtyChanged(object sender, IsDiryChangedArgs eventArgs)
{
	// short circuit if we are already dirty
	if (isDirty)
	{
		return;
	}

	IComponentTracker childTracker = (IComponentTracker)sender;
	bool newValue = childTracker.IsDirty;
	bool fireChange = true;

	foreach (KeyValuePair<object, IComponentTracker> kvp in componentTrackers)
	{
		if (kvp.Key == sender)
		{
			continue;
		}

		if (kvp.Value.IsDirty != newValue)
		{
			fireChange = false;
			break;
		}
	}

	if (fireChange && IsDirtyChanged != null)
	{
		IsDirtyChanged(this, eventArgs);
	}
}


/// <summary>
/// Add children to be tracked
/// </summary>
/// <param name="childObjects"></param>
/// <returns></returns>
private bool AddChildren(IEnumerable childObjects)
{
	bool returnValue = false;
	IListComponentFilter listFilter = trackerInfo.ListFilter;

	// if we have child tracker info create a new tracker for the object
	if (trackerInfo.ChildTrackerInfo != null)
	{
		foreach (object child in childObjects)
		{
			// filter the object if a filter exists
			if (listFilter != null && !listFilter.FilterComponent(child))
			{
				continue;
			}

			IComponentTracker childTracker =
				helper.CreateTracker(child, trackerInfo.ChildTrackerInfo);
			childTracker.IsDirtyChanged += ChildTrackerIsDirtyChanged;
			componentTrackers.Add(child, childTracker);
			returnValue = true;
		}
	}
	else 
	{
		// Create a leaf tracker for each child in the list
		foreach (object child in childObjects)
		{
			if (listFilter != null && !listFilter.FilterComponent(child))
			{
				continue;
			}

			IComponentTracker childTracker =
				helper.CreateTracker(child, new TrackerInfo { TrackerType = ComponentTrackerType.Leaf });

			childTracker.IsDirtyChanged += ChildTrackerIsDirtyChanged;
			componentTrackers.Add(child, childTracker);
			returnValue = true;
		}
	}
	return returnValue;
}




/// <summary>
/// Remove child object that need to stop being tracked
/// </summary>
/// <param name="childObjects"></param>
/// <returns></returns>
private bool RemoveChildren(IEnumerable childObjects)
{
	bool returnValue = false;
	// foreach child object try and find a tracker and dispose it
	foreach (object child in childObjects)
	{
		IComponentTracker oldTracker;
		if (componentTrackers.TryGetValue(child, out oldTracker))
		{
			componentTrackers.Remove(child);
			oldTracker.IsDirtyChanged -= ChildTrackerIsDirtyChanged;
			oldTracker.Dispose();

			returnValue = true;
		}
	}




	return returnValue;
}

 

NodeComponentTracker

  • Tracks a specific property on an object
    • The object MUST be a
      INotifyCollectionChanged 
      implementing  object

Here are the most relevant methods:

/// <summary>
/// Initialize the new tracker
/// </summary>
/// <param name="helper"></param>
/// <param name="objectToTrack"></param>
/// <param name="trackerInfo"></param>
private void Initialize(ComponentTrackerHelper helper, object objectToTrack, TrackerInfo trackerInfo)
{
	this.helper = helper;
	this.objectToTrack = objectToTrack;
	this.trackerInfo = trackerInfo;
	// calculate if we should track the property weakly
	bool trackWeakly = !(trackerInfo.PropertyType.IsValueType || trackerInfo.PropertyType == typeof(string));
	object currentValue = helper.AccessProperty(objectToTrack, trackerInfo.PropertyName);

	propertyTracker = new PropertyTracker(trackWeakly);
	propertyTracker.SetOriginalValue(currentValue);
	// if we have a current value and child tracker info start tracking
	if (currentValue != null && trackerInfo.ChildTrackerInfo != null)
	{
		childComponentTracker = helper.CreateTracker(currentValue, trackerInfo.ChildTrackerInfo);
		childComponentTracker.IsDirtyChanged += ChildComponentTrackerOnIsDirtyChanged;
	}

	INotifyPropertyChanged propertyChangeObject = objectToTrack as INotifyPropertyChanged;
	if (propertyChangeObject != null)
	{
		propertyChangeObject.PropertyChanged += PropertyChangedOnTrackedObject;
	}
}




/// <summary>
/// Handler for child component tracker changing is dirty
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
private void ChildComponentTrackerOnIsDirtyChanged(object sender, IsDiryChangedArgs eventArgs)
{
	IComponentTracker componentTracker = sender as IComponentTracker;
	if (!propertyTracker.IsDirty && IsDirtyChanged != null)
	{
		IsDirtyChanged(this, eventArgs);
	}
}

/// <summary>
/// The property change handler for the object being tracked
/// </summary>
/// <param name="sender"></param>
/// <param name="propertyChangedEventArgs"></param>
private void PropertyChangedOnTrackedObject(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
	if (childComponentTracker != null)
	{
		childComponentTracker.IsDirtyChanged -= ChildComponentTrackerOnIsDirtyChanged;
		childComponentTracker.Dispose();
		childComponentTracker = null;
	}

	bool hasOriginalValue;
	object originalValue = propertyTracker.GetOriginalValue(out hasOriginalValue);
	object newValue = helper.AccessProperty(objectToTrack, trackerInfo.PropertyName);

	if (newValue != null)
	{
		if (originalValue != null &&& newValue.Equals(originalValue))
		{
			IsDirty = false;
		}
		else
		{
			IsDirty = true;
		}
	}
	else
	{
		if (hasOriginalValue)
		{
			IsDirty = true;
		}
		else
		{
			IsDirty = false;
		}
	}

	// if there is a new value and we have a child tracker info we need to create a new tracker and listen to it
	if (newValue != null &&&& trackerInfo.ChildTrackerInfo != null)
	{
		childComponentTracker = helper.CreateTracker(newValue, trackerInfo.ChildTrackerInfo);
		childComponentTracker.IsDirtyChanged += ChildComponentTrackerOnIsDirtyChanged;
	}
}



Now we know there is a lot of code here, and it may be hard to take in, but have  a play with the unit tests, the core API is actually very simple, and we feel  this one is actually very very useful.

Convert

Expression.Convert

This simple example shows you how to use Cast (achieved using  Expression.Convert) which takes an object and casts to type of  T

Here is the relevant code:

/// <summary>
/// Creates a new function that cast an object to a type T.
/// it will throw an exception if the cast is invalid
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Func<object, T> CreateStaticCastStatement<T>()
{
	ParameterExpression inObject = Expression.Parameter(typeof(object));

	// static cast expression (T)inObject
	Expression convertExpression = Expression.Convert(inObject, typeof(T));
			
	return Expression.Lambda<Func<object,T>>(convertExpression,inObject).Compile();
}

Which we could use like this. If the cast call fails an

Exception
will be thrown.

Func<object,int> intStaticCastMethod = ConvertStatementCreater.CreateStaticCastStatement<int>();
int testValue = intStaticCastMethod(8);

Expression.TypeAs

We can also use Expression.TypeAs to return null, if the cast  fails instead of throwing an Exception which the previous example  will do for an invalid cast.

/// <summary>
/// Creates a new function that casts an object it will return null if the cast fails
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Func<object, T> CreateAsCastStatement<T>()
{
	ParameterExpression inObject = Expression.Parameter(typeof(object));
			
	// cast using as statement   inObject as T
	Expression convertExpression = Expression.TypeAs(inObject, typeof(T));

	return Expression.Lambda<Func<object, T>>(convertExpression, inObject).Compile();
}

Which we could use like this

Func<object, string> stringAsCastMethod = ConvertStatementCreater.CreateAsCastStatement<string>();

if (stringAsCastMethod("Hello") != "Hello")
{
	throw new Exception("Cast did not work");	
}

if (stringAsCastMethod(789) != null)
{
	throw new Exception("Should have been null");
}




Hash Compute

In this example we kind of had a bit of fun, where we show you how you could  create a generic GetHashCode function for any object, which is achieved by  examining all the public properties of the T object type that is passed to the GenericHashCalculator<T>. For each property value a new  Expression.Property is  setup and then an Expression.Call is done to call the GetHashCode() method for  the property object instance. The results of which are appended to an ongoing  StringBuilder string concatenation (where the int value of the individual property GetHashCode() is passed to the StringBuilder.Append() method). At the end of iterating all the public  properties the final StringBuilder string,

GetHashCode()
method is called which  is effectievly the overall hash value for the object.

So if we use this demo class
public class Person
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public string EmailAddress { get; set; }
	public int Age { get; set; }
	public int NumberOfSiblings { get; set; }
	public int NumberOfChildren { get; set; }
}

We can build up a single properties hash value that is appended to the ongoing  StringBuilder string value (as an int) which will be used as the final hash value

private Expression CreatePropertyHashExpression(
    PropertyInfo propertyInfo,
	ParameterExpression stringBuilderParameter,
	ParameterExpression objectToHashParameter)
{
	Expression returnExpression = null;
	Expression propertyExpression = Expression.Property(objectToHashParameter, propertyInfo);
	Expression propertyHashCodeExpression = Expression.Call(propertyExpression, getHashCodeMethod);
	Expression stringBuildAppendExpression = Expression.Call(
        stringBuilderParameter,
	stringBuilderAppendMethod,
	propertyHashCodeExpression);
			
    if (propertyInfo.PropertyType.IsValueType)
	{
	returnExpression = stringBuildAppendExpression;
	}
	else
	{
	Expression notEqualToNullExpress = Expression.NotEqual(propertyExpression, Expression.Constant(null));
	returnExpression = Expression.IfThen(notEqualToNullExpress, stringBuildAppendExpression);
	}
	return returnExpression;
}

And this is the part that creates the final hash by calling the accumulated string value (from the  StringBuilder that was used to create the indvidual property part  hashcode values) GetHashCode() method

private void CreateHashDelegate()
{
	ParameterExpression objectToHashParameter = Expression.Parameter(typeof(T), "objectToHash");
	ParameterExpression stringBuilderParameter = Expression.Variable(typeof(StringBuilder), "stringBuilder");
	Expression assignAndCreateExpression = Expression.Assign(stringBuilderParameter,
		Expression.New(stringBuilderConstructor,
		Expression.Constant(typeof(T).FullName)));

	List<Expression> computeHashStatements = new List<Expression>();
	computeHashStatements.Add(assignAndCreateExpression);

	foreach (PropertyInfo propertyInfo in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
	{
		computeHashStatements.Add(
			CreatePropertyHashExpression(propertyInfo, 
                stringBuilderParameter, objectToHashParameter));
	}

	ParameterExpression tempStringVariable = Expression.Variable(typeof(string), "tempString");
	Expression toStringForBuilder = Expression.Call(stringBuilderParameter, stringBuildToStringMethod);
	Expression assignToStringToTempString = Expression.Assign(tempStringVariable, toStringForBuilder);
	Expression finalGetHashCodeFromString = Expression.Call(tempStringVariable, getHashCodeMethod);
	computeHashStatements.Add(assignToStringToTempString);
	computeHashStatements.Add(finalGetHashCodeFromString);
	BlockExpression finalBlock = BlockExpression.Block(typeof(int),
		new[] { stringBuilderParameter, tempStringVariable },
		computeHashStatements);

	hashDelegate = Expression.Lambda<Func<T, int>>(finalBlock, objectToHashParameter).Compile();
}

This is how you might use this GenericHashCalculator<T> class

GenericHashCalculator<Person> hasher = new GenericHashCalculator<Person>();
Person testPerson1 = new Person();
Person testPerson2 = new Person();
int hashValue1 = hasher.Compute(testPerson1);
int hashValue2 = hasher.Compute(testPerson2);
testPerson1.FirstName = "Ian";
testPerson2.FirstName = "Ian";
int hashValue3 = hasher.Compute(testPerson1);
int hashValue4 = hasher.Compute(testPerson2);
if (hashValue1 != hashValue2)
{
	throw new Exception("Hashes need to match");
}
if (hashValue1 == hashValue3)
{
	throw new Exception("Hashes must not match");
}
if (hashValue3 != hashValue4)
{
	throw new Exception("Hashes need to match");
}
testPerson1.LastName = "Johnson";
testPerson2.LastName = "IsDementedExpressionMonster";
hashValue1 = hasher.Compute(testPerson1);
hashValue2 = hasher.Compute(testPerson2);
if (hashValue1 == hashValue2)
{
	throw new Exception("Hashes should not match");
}






If-Then-Else

In this example we will look at how we can construct If-Then-Else flow  control using Expression trees.

In pseudo code this is what we are trying to do

.Block() {
  .If ($var1 > 0) {
    .Return returnLabel { "Positive" }
  } .Else {
     .If ($var1 < 0) {
         .Return returnLabel { "Negative" }
     } .Else {
         .Return returnLabel { "Zero" }
     }
  };
  .Label
      ""
  .LabelTarget returnLabel:

It turns our that there is actually an Expression.IfThenElse API  so translating this pseudo code into to an Expression API isn't that bad  actually, here is the relevant code:

public static Func<int, string> CreatePositiveNegative()
{
	// Create the int parameter that is passed into the function
	ParameterExpression intParameter = Expression.Parameter(typeof(int));
	LabelTarget returnTarget = Expression.Label(typeof(string),"returnLabel");
	// create expression that tests the int parameter is > 0
	Expression testForPositive = Expression.GreaterThan(intParameter, Expression.Constant(0));
	// create expression that tests the int parameter is < 0
	Expression testForNegative = Expression.LessThan(intParameter, Expression.Constant(0));
	// Creates a statement that returns the string "Negative" if the int
	// parameter is less than 0 else it returns the string "Zero"
	Expression ifNegativeElseZeroExpression =
		Expression.IfThenElse(testForNegative, 
			Expression.Return(returnTarget, Expression.Constant(NegativeString)),
			Expression.Return(returnTarget, Expression.Constant(ZeroString)));
	// creates a statement that returns the string Positive if the parameter is 
	// greater than 0 else run the negative if/then/else expression
	Expression ifPositiveElseStatement =
		Expression.IfThenElse(testForPositive,
			Expression.Return(returnTarget, Expression.Constant(PositiveString)),
			ifNegativeElseZeroExpression);
	// creates a block that contains the if statement and a return label (GoTo statement)
	Expression expressionBody = Expression.Block(ifPositiveElseStatement,Expression.Label(returnTarget,Expression.Constant("")));

	return Expression.Lambda<Func<int, string>>(expressionBody, intParameter).Compile();
}

Here is an example of how we would use this

Func<int, string> intTestFunc = IfThenElseStatementCreater.CreatePositiveNegative();

if (intTestFunc(1) != IfThenElseStatementCreater.PositiveString)
{
	throw new Exception("Wrong value, should have been positive");
}

if (intTestFunc(-1) != IfThenElseStatementCreater.NegativeString)
{
	throw new Exception("Wrong value, should have been negative");
}

if (intTestFunc(0) != IfThenElseStatementCreater.ZeroString)
{
	throw new Exception("Wrong value, should have been zero");
}

We could of course replace the constant string values shown below with some more meaningful data structures

public const string PositiveString = "Positive";
public const string NegativeString = "Negative";
public const string ZeroString = "Zero";




Method Call

Methods are something that every programmer will have to call in their every  day programming existence, so it stands to reason that you may occassionally  have to create a delegate to call a method that you want to call a lot. Recall  at the beginning of this article I stated that you could do a lot of this with  Reflection, however using Reflection we take the cost of looking up a method  each time, a far better option is to create some delegate that we can use when  we need it, Ok, it still takes time to create the comiled Expression tree - to  delegate (Func<TR>) the 1st time but after that it should be nearly  as fast as calling a delegate that was known at compile time.

We have created 2 examples of how to construct method call delegate  Expression trees, both of these will make use of this object to call the methods  on

public class SomeBasicClass
{
    public string SomeMethod()
    {
       return "Hello World";
    }

    public string MoreComplexMethod(int param1, double param2, string param3)
    {
	return "Hello World " + param1 + " " + param2 + " " + param3;
    }
}

Simple Example

The simple case is fairly straight forward as there are no parameters for the  example method (SomeMethod) to deal  with, we just need to obtain a MethodInfo an create an Expression.Lambda to call  the correct method. Here is the relevant code for this:

protected void Initialize(MethodInfo methodInfo)
{
	Type declaringType = methodInfo.DeclaringType;

	// the input parameter to the function
	ParameterExpression inputObject = Expression.Parameter(typeof(object));

	// loval variable of type declaringType
	ParameterExpression tVariable = Expression.Variable(declaringType);

	// cast the input object to be declaring type
	Expression castExpression = Expression.Convert(inputObject, declaringType);

	// assign the cast value to the tVaraible variable
	Expression assignmentExpression = Expression.Assign(tVariable, castExpression);
			
	// call the specified method on tVariable
	Expression callExpression = Expression.Call(tVariable, methodInfo);

	// create a block statement that contains the body of the linq statement
	BlockExpression body = Expression.Block(new[] { tVariable }, assignmentExpression, callExpression);

	// create a new delegate that takes in an object and returns an object
	invokeMethod = Expression.Lambda<Func<object, object>>(body, inputObject).Compile();
}

Compex Example

The complex case is a little more tricky as we need to deal with creating a  bunch of parameters for the method that will be called (MoreComplexMethod), as such there is a bit  more code, the guts of it is pretty similar stuff though, we are just buildng up  an array of parameters that will be supplied to the final method call (Expression.Call)

protected void Initialize(MethodInfo methodInfo)
{
	// set the number of parameters so we can do a sanity check when we call it.
	parameterCount = methodInfo.GetParameters().Count();

	Type declaringType = methodInfo.DeclaringType;

	// the parameter to call the method on
	ParameterExpression inputObject = Expression.Parameter(typeof(object), "inputObject");
	// the input parameters for the method
	ParameterExpression parameterArray = Expression.Parameter(typeof(object[]), "parameters");
	// loval variable of type declaringType
	ParameterExpression tVariable = Expression.Variable(declaringType);

	// keep a list of the variable we declare for use when we define the body
	List<ParameterExpression> variableList = new List<ParameterExpression> { tVariable };
	// cast the input object to be declaring type
	Expression castExpression = Expression.Convert(inputObject, declaringType);

	// assign the cast value to the tVaraible variable
	Expression assignmentExpression = Expression.Assign(tVariable, castExpression);

	List<Expression> bodyExpressions = new List<Expression> { assignmentExpression };

	Expression callExpression = null;
	if (parameterCount == 0)
	{
		// call the specified method on tVariable
		callExpression = Expression.Call(tVariable, methodInfo);
	}
	else // we need to build up the parameters
	{
		int parameterNum = 0;
		List<ParameterExpression> callArguements = new List<ParameterExpression>();
		// we need to convert each parameter in the array to match the method signature
		foreach (ParameterInfo parameterInfo in methodInfo.GetParameters())
		{
			// create new local variable of the same type as the paramete
			ParameterExpression newVariable = Expression.Variable(parameterInfo.ParameterType, "param" + parameterNum);
			// add it to the list of local variable we have
			callArguements.Add(newVariable);

			// create an expression to access the parameter array
			// using a constant indexer [parameterNum]
			Expression arrayAccess = Expression.ArrayAccess(parameterArray, Expression.Constant(parameterNum));
			// cast the array access expression to be the parameter type
			Expression castArrayValue = Expression.Convert(arrayAccess, parameterInfo.ParameterType);

			// assign the casted value to the local variable
			Expression variableAssign = Expression.Assign(newVariable, castArrayValue);
			// add the variable assignment expression to the list of expression that is the body
			bodyExpressions.Add(variableAssign);
			// move to the next parameter in the array
			parameterNum++;
		}
		// add all the new local variables to the variable list
		variableList.AddRange(callArguements);

		// call the method on tVaraible with the specified callArguement list
		callExpression = Expression.Call(tVariable, methodInfo, callArguements);
	}
	// add the call expression to the body
	bodyExpressions.Add(callExpression);

	// create a block statement that contains the body of the linq statement
	BlockExpression body = Expression.Block(variableList, bodyExpressions);




	// create a new delegate that takes in an object and object array and returns an object
	invokeMethod = Expression.Lambda<Func<object, object[], object>>(body, inputObject,parameterArray).Compile();
}




Property Get/Set

A very common requirement that seems to happen all the time is to get/set  property values on an object. To illustrate this, we will work through a simple  case, and then move on to a more complex case.

Simple-Example

This is a bulk standard property get/set where we will be using this source  object

public class BusinessObject
{
public ChildObjectA A { get; set; }
public int IntProp { get; set; }
public string StringProp { get; set; }
}

Getter

So how do we code up a getter expression?

/// <summary>
/// Creates a new Func that takes an instance object and returns the property value
/// 
/// (System.Object)((PropertyGetSetExample.BusinessObject)$objectParam).IntProp
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
private Func<object,object> CreateGetMethod(PropertyInfo propertyInfo)
{
	// create the parameter for the instance parameter of type object
	ParameterExpression inParameter = Expression.Parameter(typeof(object), "objectParam");


	// cast the instance to it's real type
	Expression castExpression = Expression.Convert(inParameter, objectType);


	// create the property access expression
	Expression propertyAccessExpression =
		Expression.Property(castExpression, propertyInfo);

	// cast the property access expression to an object
	Expression returnCastExpression = Expression.Convert(propertyAccessExpression, typeof(object));

	return Expression.Lambda<Func<object, object>>(returnCastExpression, inParameter).Compile();
}

Where we would use this as shown below. This would be something like this  expressed using Reflection var x = (object)typeof(BusinessObject).GetProperty("IntProp").GetValue(bo, null); the difference being with the Expression API  example is that it would be a compiled Func<object,object> so would  be very quick when used again, whilst doing the same thing via Reflection would  be slow each time.

BusinessObject bo = new BusinessObject { IntProp = 80 };
PropertyInfo propertyInfo = objectType.GetProperty("IntProp",BindingFlags.Public | BindingFlags.Instance);
var getMethod = CreateGetMethod(propertyInfo);
if ((int)getMethod(bo) != 80)
{
	throw new Exception("Value should have been 80");
}

Setter

So how about a setter, lets have a look at that now shall we.

/// <summary>
/// Creates a new Action that takes the instance object and the new value for the property and sets it
/// 
/// ((PropertyGetSetExample.BusinessObject)$objectParam).IntProp = (System.Int32)$newValue
/// </summary>
/// <param name="propertyInfo"></param>
/// <returns></returns>
private Action<object,object> CreateSetMethod(PropertyInfo propertyInfo)
{
	// define the first parameter to the action the object parameter
	ParameterExpression objectParameter = Expression.Parameter(typeof(object),"objectParam");




	// define the new value parameter for the action
	ParameterExpression newValueParameter = Expression.Parameter(typeof(object),"newValue");

	// cast the instance to it's proper type
	Expression castExpression = Expression.Convert(objectParameter, objectType);

	// cast the new value to be the correct property type
	Expression newValueCastExpression = Expression.Convert(newValueParameter, propertyInfo.PropertyType);

	// create a new property access expression and assign the cast new value to it
	Expression assignExpression = Expression.Assign(Expression.Property(castExpression, propertyInfo),
			                                        newValueCastExpression);

	return Expression.Lambda<Action<object, object>>(assignExpression, objectParameter, newValueParameter).Compile();
}

Which you would use like this

BusinessObject bo = new BusinessObject { IntProp = 80 };
PropertyInfo propertyInfo = objectType.GetProperty("IntProp",BindingFlags.Public | BindingFlags.Instance);
var setMethod = CreateSetMethod(propertyInfo);
setMethod(bo, 160);
if ((int)getMethod(bo) != 160)
{
	throw new Exception("Value should have been 160");
}

Complex-Example

There is also a more complex example, which uses these demo objects, and  allows us to drill deep into an objects nested properties

public class BusinessObject
{
public ChildObjectA A { get; set; }
public int IntProp { get; set; }
public string StringProp { get; set; }
}

public class ChildObjectA
{
public ChildObjectB B { get; set; }
}

public class ChildObjectB
{
public ChildObjectC C { get; set; }
}

public class ChildObjectC
{
public int IntProp { get; set; }
public string StringPro { get; set; }
}

There is no doube that this example is a lot more complicated, as such I will  just concentrate on the getter, for nested properties. The bulk of the work is  finding and Expression that could turn something like "A.B.C.IntProp"  into an actual Expression delegate. As you can imagine we need to recursively  examine each of these values that are seperated by a "." and create a Expression  portion that represents that part of the property tree expression that we are  trying to construct. This part is done using this code:

// <summary>
/// Creates the linq expression the goes down the dotted property chain (A.B.C)
/// It will recursively work it's way down the chain
/// </summary>
/// <param name="valueParameter">the current object in the chain</param>
/// <param name="throwParameter">if thrown parameter</param>
/// <param name="objectType">object type</param>
/// <param name="propertyName">property name (a.b.c)</param>
/// <param name="createAction">function that will be called once the final property is reached. 
/// This the part that is different between get and set</param>
/// <returns></returns>
private static Expression CreateAccessPathCode(
        ParameterExpression valueParameter,
		ParameterExpression throwParameter,
		Type objectType,
		string propertyName,
		Func<Type, string, Expression, Expression> createAction)
{
	Expression returnExpression = null;
	int firstPeriod = propertyName.IndexOf('.');

	// there are more properties in the chain
	if (firstPeriod > 0)
	{
		string currentPropertyName = propertyName.Substring(0, firstPeriod);
		string theRest = propertyName.Substring(firstPeriod + 1);
		Type propertyOrFieldType = GetPropertyOrFieldType(objectType, currentPropertyName);
		// create local variable to hold the property we are about to inspect
		ParameterExpression newValue = Expression.Variable(propertyOrFieldType);

		// assign the property to the local variable
		Expression assignExpression = Expression.Assign(newValue,
			Expression.PropertyOrField(valueParameter, currentPropertyName));
	
		// recurse back up the chain passing the local variable in as the starting point
		// and theRest of the string for the property name
		Expression recurse =
			CreateAccessPathCode(newValue,throwParameter,
				propertyOrFieldType,
				theRest,
				createAction);

		// if the property type is not a value type we need to test it for null
		if (!propertyOrFieldType.GetTypeInfo().IsValueType)
		{
			// expression to create a new Exception object with the message provided
			Expression newExceptionExpression = Expression.New(
				exceptionConstructor,
				Expression.Constant(string.Format("Could not find property {1} on type {0}",
										objectType.FullName,
										currentPropertyName)));




			// create a statement that will throw a new exception if the throwParameter is true
			Expression throwIfMissingTrueExpression =
				Expression.IfThen(Expression.IsTrue(throwParameter),
					Expression.Throw(newExceptionExpression));

			// create if statement that tests the local variable for null
			Expression ifExpression =
				Expression.IfThenElse(
					Expression.NotEqual(newValue, Expression.Constant(null)),
						recurse, throwIfMissingTrueExpression);

			// create a new block containing the new local variable and if expression
			returnExpression =
				Expression.Block(new[] { newValue }, new[] { assignExpression, ifExpression });
		}
		else
		{
			// create a new block containing the recurse statement and local variable
			returnExpression =
				Expression.Block(new[] { newValue }, new[] { assignExpression, recurse });
		}
	}
	else
	{
		// execute create action and return it.
		returnExpression = createAction(objectType, propertyName, valueParameter);
	}
	return returnExpression;
}

The next step is the actual getter. Which looks like this.

public static GetPropertyDelegate CreateGetPropertyDelegate(Type instanceType, string propertyName, Type indexType)
{
	// create input parameters for the method.
	ParameterExpression valueObjectParameter = Expression.Parameter(typeof(object), "valueObject");
	ParameterExpression indexParameter = Expression.Parameter(typeof(object), "index");
	ParameterExpression throwParameter = Expression.Parameter(typeof(bool), "throwIfMissing");

	// define a return variable at the base of 
	ParameterExpression returnValueExpression = Expression.Variable(typeof(object), "returnValue");

	// local variable that is the proper type
	ParameterExpression castValue = Expression.Variable(instanceType, "castValue");
	// assign the value object to the local castValue
	Expression castExpression =
		Expression.Assign(castValue, Expression.Convert(valueObjectParameter, instanceType));
	Expression accessBlock
		= CreateAccessPathCode(castValue,
			throwParameter,
			instanceType,
			propertyName,
			(type, name, expression) =>
			{
				Expression returnValue = null;
				if (indexType != null)
				{
					PropertyInfo propertyInfo = type.GetProperty(propertyName);
					if (propertyInfo != null)
					{
						// assign the property to the returnValue using the index
						returnValue = Expression.Assign(
							returnValueExpression,
							Expression.Convert(
								Expression.Property(expression,
								propertyInfo,
								Expression.Convert(indexParameter, indexType)),
								typeof(object)));
					}
					else
					{
						throw new Exception(
							string.Format("Could not find property {0} on type {1}",
								type.FullName,
								name));
					}
				}
				else
				{
				// assign the final property to the returnValue
					returnValue = Expression.Assign(
						returnValueExpression,
						Expression.Convert(
							Expression.PropertyOrField(expression, name), typeof(object)));
				}

				return returnValue;
			});

	// create body of method
	BlockExpression returnBlock =
		Expression.Block(new[] { castValue, returnValueExpression },
			castExpression,
			accessBlock,
			returnValueExpression);

	return Expression.Lambda<GetPropertyDelegate>(
		returnBlock, valueObjectParameter, indexParameter, throwParameter).
									Compile();
}

Granted this code is quite hairy, but when you think about what it is doing  it is quite cool. It is created a delegate that allows you to create a property  getter delegate against an objects nested properties, like this:

BusinessObject bo = new BusinessObject
{
	A = new ChildObjectA
	{
		B = new ChildObjectB
		{
			C = new ChildObjectC { IntProp = 90 }
		}
	}
};var getValue =
	ComplexPropertyAccessor.CreateGetPropertyDelegate(typeof(BusinessObject), "A.B.C.IntProp", null);

//returnValue will be 90
var returnValue = getValue(bo, null, true);

It can be seen that this code, actually calls the recursive method

CreateAccessPathCode 
where  it passes in a callback action that is called when the recursion is over, where  the callback will be called with the final object Type, the final  property name and the final value parameter. The callback is then used as part  of a Expression.Block that is in turn used as the final output that is used to  create a LambdaExpression that is finally retuned to the user to  use as the property getter delegeate.





Dynamic Where

The idea behind this one is a simple one, which is a very common requirement.  We have a IEnumerable<T> where T is some object and we wish to filter it.  However we want to build up the filter dynamically.

Say the IEnumerable<T> was made up of Person objetct, so we have  IEnumerable<T> where Person looked like this:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

If were not buiding up a filter dynamically we could simple do something like  this

var x = people.Where(x => x.FirstName="sam" && LastName.Contains("Bree"));

If however we want to build up the filter at runtime based on some input  criteria, such as letting the user pick which fields / methods to use in the  filter, we would need to come up with a where builder. What we thought was that  the where part could be considering its own class, and when we come to build up  the entire dynamic where clause filter we would be combining a number of where  clause part objects.

The WhereClausePart is shown below:

public enum CompareMethod
{
	Equal,
	GreaterThan,
	GreaterThanOrEqual,
	LessThan,
	LessThanOrEqual,
	Contains,
	StartsWith,
	EndsWith
}


/// <summary>
/// This class represents one part of a complex where statement
/// </summary>
public class WhereClausePart
{
	public WhereClausePart()
	{
			
	}

	public WhereClausePart(string propertyName, CompareMethod compareMethod, object compareValue)
	{
		PropertyName = propertyName;
		CompareMethod = compareMethod;
		CompareValue = compareValue;
	}

	public string PropertyName { get; set; }
	public object CompareValue { get; set; }
	public CompareMethod CompareMethod { get; set; }
}

Which we could then use to construct a complex dynamic where filter using 2  maiin helper methods, namely:

  • CreateAndWhereClause : Which effectively ANDs all the  provided WhereClausePart objects together using the  ExpressionAPI Expression.AndAlso
  • CreateOrWereClause : Which effectively ORs all the  provided WhereClausePart objects together using the  ExpressionAPI Expression.OrElse

That is what the bulk of the following code is doing. There are also helper  methods to do the actual comparison based on the comparison type that was  provided as part of the WhereClausePart object(s)

public static class WhereClauseCreator
{
	private static readonly MethodInfo containsMethod;
	private static readonly MethodInfo endsWithMethod;
	private static readonly MethodInfo startsWithMethod;
		
	static WhereClauseCreator()
	{
		containsMethod = typeof(WhereClauseCreator).GetMethod("Contains",BindingFlags.NonPublic | BindingFlags.Static);
		endsWithMethod = typeof(WhereClauseCreator).GetMethod("EndsWith", BindingFlags.NonPublic | BindingFlags.Static);
		startsWithMethod = typeof(WhereClauseCreator).GetMethod("StartsWith", BindingFlags.NonPublic | BindingFlags.Static);
	}

	/// <summary>
	/// Creates a Func(T,bool) that can be used as part of a Where statement in Linq
	/// The comparission statements will be linked using an or operator
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="whereClause"></param>
	/// <returns></returns>
	public static Func<T, bool> CreateOrWhereClause<T>(IEnumerable<WhereClausePart> whereClause)
	{
		ParameterExpression inParameter = Expression.Parameter(typeof(T));
		Expression whereStatement = null;

		// foreach part of the where clause create the compare expression then or it with the current where statement
		foreach (WhereClausePart part in whereClause)
		{
			Expression propertyEqualStatement = CompareExpression<T>(inParameter, part);
			if (whereStatement == null)
			{
				whereStatement = propertyEqualStatement;
			}
			else
			{
				whereStatement = Expression.OrElse(whereStatement, propertyEqualStatement);
			}
		}

		if (whereStatement == null)
		{
			whereStatement = Expression.Constant(true);
		}
		return Expression.Lambda<Func<T, bool>>(whereStatement, inParameter).Compile();
	}

	public static Func<T, bool> CreateAndWhereClause<T>(IEnumerable<WhereClausePart> whereClause)
	{
		ParameterExpression inParameter = Expression.Parameter(typeof(T));
		Expression whereStatement = null;




		foreach (WhereClausePart part in whereClause)
		{
			Expression propertyEqualStatement = CompareExpression<T>(inParameter, part);
			if (whereStatement == null)
			{
				whereStatement = propertyEqualStatement;
			}
			else
			{
				whereStatement = Expression.AndAlso(whereStatement, propertyEqualStatement);
			}
		}
		if (whereStatement == null)
		{
			whereStatement = Expression.Constant(true);
		}
		return Expression.Lambda<Func<T, bool>>(whereStatement, inParameter).Compile();
	}

	/// <summary>
	/// Creates an expression where it compares a property to a particular value
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="inParameter"></param>
	/// <param name="part"></param>
	/// <returns></returns>
	private static Expression CompareExpression<T>(ParameterExpression inParameter, WhereClausePart part)
	{
		PropertyInfo propertyInfo = typeof(T).GetProperty(part.PropertyName);
		// based on the comparission method we change the Expression we return back
		switch (part.CompareMethod)
		{
			case CompareMethod.Equal:
				return Expression.Equal(Expression.Property(inParameter, propertyInfo), 
					Expression.Constant(part.CompareValue));
			case CompareMethod.GreaterThan:
				return Expression.GreaterThan(Expression.Property(inParameter, propertyInfo), 
					Expression.Constant(part.CompareValue));
			case CompareMethod.GreaterThanOrEqual: 
				return Expression.GreaterThanOrEqual(Expression.Property(inParameter, propertyInfo), 
					Expression.Constant(part.CompareValue));
			case CompareMethod.LessThan:
				return Expression.LessThan(Expression.Property(inParameter, propertyInfo), 
					Expression.Constant(part.CompareValue));
			case CompareMethod.LessThanOrEqual:
				return Expression.LessThanOrEqual(Expression.Property(inParameter, propertyInfo), 
					Expression.Constant(part.CompareValue));
			case CompareMethod.Contains:
				return Expression.Call(containsMethod,
					Expression.Property(inParameter, propertyInfo),
					Expression.Constant(part.CompareValue));
			case CompareMethod.StartsWith:
				return Expression.Call(startsWithMethod,
				Expression.Property(inParameter, propertyInfo),
					Expression.Constant(part.CompareValue));
			case CompareMethod.EndsWith:
				return Expression.Call(endsWithMethod,
					Expression.Property(inParameter, propertyInfo),
					Expression.Constant(part.CompareValue));

		}
		return null;
	}

	/// <summary>
	/// I've created a wrapper around Contains so that I don't need to check for null in the linq expression 
	/// </summary>
	/// <param name="testString"></param>
	/// <param name="containsValue"></param>
	/// <returns></returns>
	private static bool Contains(string testString, string containsValue)
	{
		if (testString != null)
		{
			return testString.Contains(containsValue);
		}
		return false;
	}


	/// <summary>
	/// I've created a wrapper around EndsWith so that I don't need to check for null in the linq expression 
	/// </summary>
	/// <param name="testString"></param>
	/// <param name="endsWithValue"></param>
	/// <returns></returns>
	private static bool EndsWith(string testString, string endsWithValue)
	{
		if (testString != null)
		{
			return testString.EndsWith(endsWithValue);
		}
		return false;
	}

	/// <summary>
	/// I've created a wrapper around StartsWith so that I don't need to check for null in the linq expression 
	/// </summary>
	/// <param name="testString"></param>
	/// <param name="startsWithValue"></param>
	/// <returns></returns>
	private static bool StartsWith(string testString, string startsWithValue)
	{
		if (testString != null)
		{
			return testString.StartsWith(startsWithValue);
		}

		return false;
	}
}


The actual usage of this may look something like this:

List<Person> persons = CreatePersonList();
Func<Person, bool> clause =
	WhereClauseCreator.CreateAndWhereClause<Person>(new[]
		{
			new WhereClausePart("LastName",CompareMethod.Equal, "Doe"),
 			new WhereClausePart("Age",CompareMethod.GreaterThan, 20) 
		});

if (persons.Where(clause).Count() != 4)
{
	throw new Exception("There should be only 4.");
}

Func<Person, bool> orClause =
	WhereClauseCreator.CreateOrWhereClause<Person>(new[]
		{
			new WhereClausePart("LastName",CompareMethod.Equal, "Johnson"),
 			new WhereClausePart("FirstName",CompareMethod.StartsWith, "J") 
		});

if (persons.Where(orClause).Count() != 8)
{
	throw new Exception("Result should be 8.");
}




 

 

That's It

Anayway hope you enjoyed this one. We both feel that this sort of  Expression API cookbook approach can be a pretty useful way of learning more  about the Expression API. I personally salute Ian for his Expression skills, which far  outway my own, and we both hope that this article may prove useful to some of  you. I know I have learned quite a bit just from working through Ians examples.

As always if you enjoyed the article, feel free to leave a comment/vote.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Authors

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Ian P Johnson
Software Developer (Senior)
United States United States
Author of StyleMVVM - http://StyleMVVM.codeplex.com

Comments and Discussions

 
QuestionList-tracking fails on list operators Pin
Sven Bardos22-Jan-15 0:52
MemberSven Bardos22-Jan-15 0:52 
AnswerRe: List-tracking fails on list operators Pin
Sacha Barber22-Jan-15 1:44
MemberSacha Barber22-Jan-15 1:44 
AnswerRe: List-tracking fails on list operators Pin
Sacha Barber22-Jan-15 19:57
MemberSacha Barber22-Jan-15 19:57 
GeneralRe: List-tracking fails on list operators Pin
Sven Bardos23-Jan-15 8:54
MemberSven Bardos23-Jan-15 8:54 
GeneralRe: List-tracking fails on list operators Pin
Ian P Johnson23-Jan-15 9:02
professionalIan P Johnson23-Jan-15 9:02 
AnswerRe: List-tracking fails on list operators Pin
Ian P Johnson24-Jan-15 3:23
professionalIan P Johnson24-Jan-15 3:23 
GeneralRe: List-tracking fails on list operators Pin
Sven Bardos25-Jan-15 2:39
MemberSven Bardos25-Jan-15 2:39 
GeneralRe: List-tracking fails on list operators Pin
Ian P Johnson25-Jan-15 5:09
professionalIan P Johnson25-Jan-15 5:09 
GeneralRe: List-tracking fails on list operators Pin
Sven Bardos25-Jan-15 5:28
MemberSven Bardos25-Jan-15 5:28 
GeneralMy vote of 5 Pin
Prasad Khandekar1-Apr-14 23:31
professionalPrasad Khandekar1-Apr-14 23:31 
GeneralRe: My vote of 5 Pin
Sacha Barber1-Apr-14 23:47
MemberSacha Barber1-Apr-14 23:47 
QuestionVery helpful Pin
BillW337-Oct-13 3:16
professionalBillW337-Oct-13 3:16 
AnswerRe: Very helpful Pin
Sacha Barber8-Oct-13 23:53
MemberSacha Barber8-Oct-13 23:53 
GeneralMy vote of 5 Pin
Renju Vinod6-Oct-13 19:27
professionalRenju Vinod6-Oct-13 19:27 
GeneralRe: My vote of 5 Pin
Sacha Barber6-Oct-13 21:55
MemberSacha Barber6-Oct-13 21:55 
GeneralMy vote of 5 Pin
SteveTheThread27-Sep-13 2:41
MemberSteveTheThread27-Sep-13 2:41 
GeneralRe: My vote of 5 Pin
Sacha Barber27-Sep-13 2:44
MemberSacha Barber27-Sep-13 2:44 
GeneralRe: My vote of 5 Pin
SteveTheThread27-Sep-13 3:48
MemberSteveTheThread27-Sep-13 3:48 
SuggestionAnother way to learn expression trees.... Pin
Andrew Rissing17-Sep-13 6:25
MemberAndrew Rissing17-Sep-13 6:25 
GeneralRe: Another way to learn expression trees.... Pin
Sacha Barber17-Sep-13 9:18
MemberSacha Barber17-Sep-13 9:18 
GeneralRe: Another way to learn expression trees.... Pin
Andrew Rissing18-Sep-13 4:25
MemberAndrew Rissing18-Sep-13 4:25 
GeneralRe: Another way to learn expression trees.... Pin
Andrew Rissing18-Sep-13 4:37
MemberAndrew Rissing18-Sep-13 4:37 
GeneralRe: Another way to learn expression trees.... Pin
Sacha Barber18-Sep-13 5:05
MemberSacha Barber18-Sep-13 5:05 
GeneralRe: Another way to learn expression trees.... Pin
Andrew Rissing19-Sep-13 5:37
MemberAndrew Rissing19-Sep-13 5:37 
GeneralRe: Another way to learn expression trees.... Pin
Sacha Barber19-Sep-13 7:47
MemberSacha Barber19-Sep-13 7:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 10 Sep 2013

Stats

63.5K views
439 downloads
89 bookmarked