What could be more preferable than simply stating X = Y in one place, then having the rest of the work transparently handled on your behalf?
Expression trees were one of the many language features introduced with C# 3.0 way back in 2007. Integration with the compiler allows us to nonchalantly ask it to emit data that represents a line of code instead of going about its usual business of readying the code for execution only. This leads to a convenient convergence of the benefits of reflection and those of compiler-checked code. An example that is further described here is that we no longer must use a string
literal to meta-identify a property; instead, we can use a lambda expression like o => o.SomeProperty
.
It took me much of this month of January 2013 to apply expression trees in a way that goes far beyond that simple property example. The result demonstrates not only the power of code as data, but also the applicability of the concept of data binding to coding in general instead of in the user-interface alone.
The project arose from the need to manage numerous interdependent properties on view-model and model classes. A property must be updated whenever one of the properties it depends on changes. Done on an individual basis, this would lead to a lot of event handlers. My undeviating aim to keep application code as succinct and declarative as possible led me to seek an alternative. What could be more preferable than simply stating X = Y in one place, then having the rest of the work transparently handled on your behalf? Nothing, of course. So I create the following public
interface and then implemented the guts to make it work:
public class PropertiesUpdater<TTarget>
{
public PropertiesUpdater( TTarget target )
{
...
}
public PropertiesUpdater<TTarget> AddProperty<TProperty>(
Expression<Func<TTarget, TProperty>> targetPropertyExp,
Expression<Func<TProperty>> valueExp,
bool setInitialValue = false )
{
...
}
}
I will present some usage examples to show the sort of advantage that PropertiesUpdater
provides. The restaurant POS system I am now working on includes a Table
domain object that represents the sort of table you sit at. Table
’s OpenOrder
property is bound in the user-interface and so its private
setter raises a property-changed event for notifying the UI of changes. Table
’s constructor contains the following code for determining OpenOrder
’s value:
new PropertiesUpdater<Table>( this )
.AddProperty(
t => t.OpenOrder,
() => DineInParts.Select( p => p.Owner ).SingleOrDefault( o => o != null && o.IsOpen )
);
PropertiesUpdater
recognizes most LINQ-to-Objects methods and can interpret the lambdas passed to them. After calling AddProperty
in the above example, each of the following causes the target Table.OpenOrder
property to be updated on this:
- changes to the
Table.DineInParts
collection property’s value on this, - add/removes of the collection instance’s elements, and
- changes to values of the
Owner
and IsOpen
properties referenced via collection elements.
Under the covers, PropertiesUpdater
relies on the referenced objects implementing INotifyPropertyChanged
and the referenced collections implementing INotifyCollectionChanged
.
Next, I will present two usages taken from the constructor of the order
detail screen’s view-model in the restaurant POS system:
new PropertiesUpdater<OrderDetailsScreenPM>( this )
.AddProperty(
pm => pm.PhoneNumber,
() => domainItem.Contact is VerifiedCustomer ?
((VerifiedCustomer)domainItem.Contact).PhoneNumber : null,
setInitialValue: true
).AddProperty(
pm => pm.Name,
() => domainItem.Contact != null ? domainItem.Contact.Name : null,
setInitialValue: true
);
.AddProperty(
dm => dm.Contact,
() => PhoneNumber != null
? (DataAccess.Instance.FindCustomer( PhoneNumber ) ?? new VerifiedCustomer
{ PhoneNumber = PhoneNumber, Name = Name })
: Name != null ? new Customer { Name = Name } : null
);
The first thing you may notice about this example is that the "one line" permitted for the value expression is actually spanning multiple lines. The "one line" limit just means the expression must not contain a semicolon. The expression can in fact be however complex is needed.
In the value expressions for the first instantiated PropertiesUpdater
, notice the presence of property paths that navigate across more than one property. These are domainItem.Contact.PhoneNumber
and domainItem.Contact.Name
. What if Contact
's value changes? The former value of Contact
is no longer of interest (in particular, PhoneNumber
's and Name
’s values on the former contact), therefore the PropertiesUpdater
stops observing the former contact. This is a similar situation as when an item is removed from a collection in which case the removed element is no longer observed. In the opposing case, PropertiesUpdater
will begin observing a newly introduced value.
The Wikipedia article on Reactive programming provides a canonical example that is in fact PropertyUpdater
’s primary purpose. So, I decided to call the namespace
for holding PropertiesUpdater
and its supporting classes "Qnomad.Reactive
", at least until someone who knows better tells me that’s an inappropriate classification. As for Microsoft’s Reactive Extensions (Rx), I didn’t see its connection to what PropertiesUpdater
provides until I came across an interesting article that fills in the gap: Data binding in code using Reactive Extensions. When I first saw the title, I worried my month-long work on PropertiesUpdater
may have been for naught. The single usage example that the article builds up to, however, falls far short of what PropertiesUpdater
can accomplish. The explained functionality reminds me of the basic building block with which PropertiesUpdater
observes referenced objects, which resembles Josh Smith’s PropertyObserver. Using Rx for this purpose, however, doesn’t seem necessary or even useful. This leaves me wondering how "reactive" Rx really is; although, I suppose it may just be reactive in a different sense. As with other CS concepts that become buzzwords, "reactive" is likely to go through a period of overuse before its actual meaning gets whittled down in the minds of the masses. Instead of waiting for that to happen, though, I would love to hear some clarification sooner.
Realizing the significant time expense of implementing PropertiesUpdater
about a week into it, I checked if something similar already exists. I came across the WhenAny
method of the ReactiveUI project, but it pales in comparison to what I ended up accomplishing with PropertiesUpdater
. It wasn’t until after completing PropertiesUpdater
and describing it in the above paragraphs that I finally found two projects, Bindable LINQ and Obtics, that appear to accomplish a similar feat. My initial impression, however, is that PropertiesUpdater
can more easily accomplish the same with its mere two public
methods and comparatively tiny implementation. Anyway, I’ll further explore these two alternatives later, if there is occasion.