Often enough we have frameworks do heavy lifting for us. That's usually a good thing. But I always found it kind of sedating to let "the man" provide me with too much comfort. Especially when the framework or other SOUP library fails to perform exactly what you want. That usually leads to some grumpiness followed by some code-acrobatics to punch that square peg into a round hole – often negating the elegance and usefulness of the library call altogether.
One task a framework often performs is binding. Binding – as defined here – is taking an object and assigning values to it's properties from a property bag of sorts. For example, a property bag could be a hash-table or list of name-value pairs and an object would be a POCO (Plain old class object). It's common in web and ORM contexts, also in web services to receive a dictionary of values and have to attach them to your domain model. The plain straightforward way would be to pluck key value pairs one by one by name and assign to your object. But that's no fun and error prone. What we would like is to be able to auto-attach values to the target object.
How do we achieve that? Steve Bearman and I actually presented this very briefly as part of a larger talk on advanced C# at SoCal Code Camp last weekend. A bit of reflection, a bit of generics and extensions, and viola:
1: public static T Populate<T>(this T target, IEnumerable<KeyValuePair<string, object>> values)
3: Type targetType = target.GetType();
4: foreach (var kvp in values)
6: PropertyInfo pi = targetType.GetProperty(kvp.Key);
7: pi.SetValue(target, kvp.Value, null);
9: return target;
The extension method Populate() above takes an IEnumerable of KeyValuePair as a parameter. It then iterates a these pairs and for each key finds a property by that name and assigns the value from the dictionary to the target object. Reflection comes in at lines 6 and 7. A property is found based on the name alone, and assigned to. Usage of this extension can look something like this:
1: Dictionary<string, object> assignments = new Dictionary<string, object>();
2: assignments.Add("Name", "Paris Hilton");
3: assignments.Add("ID", 42);
4: assignments.Add("HomePhone", "(310)-555-1212");
5: assignments.Add("WorkPhone", "(310)-777-FILM");
6: Person paris = new Person();
8: Person result = paris.Populate(assignments);
Simple, granted. Simplistic perhaps. But let's consider that the average programmer doesn't write much code like this. There can be many bells and whistles added here: A property getter might only assign if the prperty is writable, attempt to match without case sensitivity, only match if property is public or adorned with an attribute. The main thinks is that you can write this method into your solution in very few lines and shape it as you wish. You do not have to depend on default MVC binders or ORMs being open source or anything like that to have binding performed. Further utilizing this bit (!) of code you might create a simple object factory that returns a new populated type from any object given a property bag of values. In fact, let's do this right here:
1: public static class MyFactory<T>
3: public static T Create(IEnumerable<KeyValuePair<string, object>> values)
5: T result;
6: ConstructorInfo ctor = typeof(T).GetConstructor(System.Type.EmptyTypes);
7: result = (T)ctor.Invoke(null);
9: return result;
The generic factory MyFactory has a Create() method which takes a property bag as a parameter and returns a populated instance with the values provided. The usage of the factory eliminates the need for creating a new empty instance before populating it. Well, actually , it does it so you don't have to – code was not eliminated, just consolidated. The usage then becomes:
1: Dictionary<string, object> values = new Dictionary<string, object>();
2: values.Add("Name", "Paris Hilton");
3: values.Add("ID", 42);
4: values.Add("HomePhone", "(310)-555-1212");
5: values.Add("WorkPhone", "(310)-777-FILM");
7: Person actual = MyFactory<Person>.Create(values);
So there we have it folks. Another DIY tidbit that would hopefully help you take control of the common binding task away from framework writers and back into your hands - where it belongs.
In production, you would probably want to also take a look at whether there's a Populate() or TryPopulate() -exception or return boolean - and handle the whole error pathing in an explicit way that fits your own style of coding. Similarly you should consider whether an unassigned property (missing value in the bag) is cause for error and whether extra unmatched values are cause for error. In this example, an extra value will cause an exception and an unassigned property will not.