Update December 2015: This little utility class has continued to get traffic and comments so I took some time to update it with some new features and a proper implementation of the Ignore functionality. I've rewritten this post to reflect the changes I recently made. I've also moved the source to a GiHub repository and will be adding it to NuGet soon.
Several years ago I found myself in a situation where I wanted to take an object, add some additional information, and return it to a caller as JSON. This was in an ASP.NET MVC site using AJAX. While there are several ways to accomplish this at the time I looked around and found an interesting class written by Mark Miller. The class was called TypeMerger and would take two objects and emit a new class into the assembly that was a merge of properties from both objects. This would work with Anonymous Types or regular classes. I liked the approach but I wanted a little more out of it so I extended it to allow you to ignore properties of each class. At first I did this in a
lazy simplistic way just using the property value as a way to know what to ignore. Some pointed out that this wasn't a good approach and I agree but it worked at the time for what I wanted. I decided to take the time to update the class to ignore properties in a way that you'd expect plus handle the situation of Property Name Collisions that was also pointed out in the comment on Stack Overflow.
Since Anonymous Types were added to .NET they've been used for lots of things and anyone familiar with ASP.NET MVC that they were used a great deal with various routing methods. With my situation mentioned above the 'additional information' I wanted to combine with an existing class happens to be represented by an Anonymous Type. My first thought was to create something that would take a couple of objects (concrete or anonymous) and merge them into a new anonymous type that I create on the fly, then I could serialize that Anonymous Type to JSON to return. Some of you may be thinking "Wait, you can't pass around Anonymous Types, they have method scope!" Actually, yes you can pass them around all you want, the thing is you only get an Object to work with and can only access it's members using Reflection or you can cast that Object back into an Anonymous Type which shares the same signature. Check out this post for more info on this.
So before doing any coding myself I did a quick search to see if anyone else had solved this problem. I stumbled across Mark's post Extending Anonymous Types using Reflection.Emit. When I saw this I thought "Perfect, exactly the direction I was thinking!" I tested it out and it worked as expected. It's also written basically how I was thinking so all is good right? Well, not completely. One thing I wanted from the solution was the ability to ignore some properties from the source objects. Basically think AutoMapper CreateMap / ForMember / option to Ignore certain properties while merging two objects into a completely new object created on the fly. If you're wondering why not just use AutoMapper for this, well unfortunately it doesn't work with Anonymous Types, or at least I haven't figured out how to make it (and even if it can the syntax would be pretty clumsy).
In coming up with this solution I looked at the way AutoMapper does it's thing and wanted something similar that was very simple and fluid. So as any TDD'er I started out with a unit test and just started typing the syntax I wanted. Here is what I came up with:
var result = Merger.Ignore(() => obj1.SomeProperty).Ignore(() => obj2.AnotherProperty).Merge(obj1, obj2);
I thought this was pretty simple and easily showed what the code was doing. The idea here is to use Method Chaining in order to have a smooth syntax. (yes, I've been using JQuery a lot lately and if you're familiar with StructureMap you may recognize the pattern too.) If you're not familiar with the Method Chaining pattern it's more common than you think. Here is Martin Fowler's explanation of the pattern:
"Make modifier methods return the host object so that multiple modifiers can be invoked in a single expression."
Method Chaining is used throughout the JQuery framework as well as in the .NET Framework. It's a pretty easy pattern to build into your classes and provide fluid interaction with your classes.
For my situation I am dealing with a static class (Merger), which makes the Method Chaining setup a little bit more complex but not much. In the most basic form Method Chaining will return the object for which the call was made on. This doesn't work for Static classes so we have to use an Expression Builder. So what is an Expression Builder? I basically think of it as a buddy class for the target object that will be used to complete the Method Chaining process. For this situation that means keeping track of the items we want to ignore when we merge two types. For this setup I created the TypeMergerPolicy class seen below.
The TypeMergerPolicy class simply holds the information that is used in the main Merger class to know how to handle merging. It has an internal List of objects which store the Type and Property of the items to exclude while merging. The Merge method at the end is what allows us to stop the chain. This method simply calls Merge on the Static class Merger passing itself as an argument.
In order to make this all work of course I had to modify the original TypeMerger class that Mark wrote to use the new TypeMergerPolicy class. Here are the changes I had to make:
- Add a private static TypeMergerPolicy object to store the items to ignore.
- Overload the constructor to take a TypeMergerPolicy class (used by the TypeMergerPolicy.MergeTypes method).
- Modify a few of the methods to use the TypeMergerPolicy class to exclude properties.
- And finally the Ignore method that kicks off the Method Chaining.
That's it! As you can see the Merger.Ignore method kicks off the process when you want to ignore a property and the TypeMergerPolicy.Merge method finishes it off.
While updating this I decided to address the issue of Property Collision that was mentioned on SO. Basically the question is what happens when you have two objects that have the same property names? Which value gets used? I made a simple modification so that the first object passed into the Merge method gets priority. I also added a Use() method that will allow you to specify which property you want to use when there is a collision. Here's an example of how this would work:
var result = Merger.Use(() => obj2.SomeProperty).Merge(obj1, obj2) ;In this situation both objects have a property "SomeProperty" and we've told the Merger that we want to use the value from obj2 even though obj1 is passed in first. You can see the implementation details of this in the source but it's basically the same as how I handled Ignore.