If you need a full-featured object mapper with minimal set up, I recommend you take a look at AutoMapper on Codeplex. If you need a quick-and-dirty solution, maybe the following code could help you out.
First off, why am I not using AutoMapper? At my current client, there are strict rules as to the use of open source software. There's a process in place for requesting the use of a particular open source tool, but with the red-tape of the approval process (reviews, signatures, justification, etc…), it could literally take 3 – 6 months. It's just not worth it for what I need right now. So I rolled my own.
This mapper is super-simple, not very smart and may have a bug or two in it, but it works for what I need it to do and reduces a lot of hand coding. USE AT YOUR OWN RISK!
It uses two simple rules to map data between two objects:
- If a property name and type on the source match the name and type of a destination property, the value is copied.
- If the user has defined a custom mapping action, use that to copy data (but rule #1 is always executed first).
Let's dig into the details.
First, I set up a generic class that takes in a couple of types – my source and destination types. I added a clause on the destination type that it must be 'new-able' so that I could provide a utility function that would create a destination object, map it's values from a source and return it to you.
When copying data from a source object to a destination object, we get all public instance properties of the destination and see if they have a matching (same name and same type) property on the source. If so, we set the value on our destination object:
I want the ability to define my own transformation for special cases. This is simply a list of Action<TSource, TDest> delegates:
Again, simple yet functional.
The last thing we need is a couple of methods to execute the actual mapping:
You'll see that "CreatedMappedObject" was the reason we needed to have the new-able clause on the TDest generic parameter.
Now let's put this into action! Given a simple domain object and view model:
As you can see, our view model only needs the Name and Age. Our mapping code looks like this:
if the view model is created somewhere else and pre-populated with other data, we would use the MapObject method instead of creating a new instance of ViewModel:
Now let's assume we want to add the user's birth year to the view:
Yes, we could pass along the entire date of birth, but this way the view model is getting only what it needs and doesn't need to do any additional processing to get the year:
To encourage re-use and centralize the setup of any custom transformations, I create a subclass of my Mapper class: