In September, Cory Isakson and I presented AutoMapper to the Boise .NET Developer User Group (NETDUG). As promised, I have included the sample code here. AutoMapper 1.0 RC1 is available on the CodePlex site.
At my company, we have embraced convention-over-configuration throughout our applications, from our data access framework (Fluent NHibernate) to our IoC container (StructureMap) to our build scripts to our…well, you get the picture. However, we had been hitting a pain point in our application when trying to serialize our domain objects for transport across the WCF service layer. We were also seeing some issues with using domain objects directly in our MVC views. The old way would have been to write that boring left-hand/right-hand code. We all know that left-hand/right-hand code is ugly and pollutes the methods that should be doing real work with ceremonious busy work.
Well, just as we were really getting stung by these pain points, Cory and I started talking about AutoMapper. Now, for those who have no idea what AutoMapper is, check out the Getting Started page on CodePlex. If you want to check out the source, go to the Google code site. Jimmy Bogard, the AutoMapper author, describes the AutoMapper as a convention-based object-to-object mapper.
Different Means to an End
Here is the basic idea… In the past, I would write this classic chunk of code:
var meeting = _repository.GetMeetingById(meetingId); var dto = new MeetingDto(); dto.Begins = meeting.Begins; dto.End = meeting.End; dto.Attendees = meeting.Attendees; dto.AttendeesCount = meeting.Attendees.Count; //do something meaningful
With AutoMapper, I can write this instead:
var meeting = _repository.GetMeetingById(meetingId); var dto = Mapper.Map<Meeting, MeetingDto>(meeting); //do something meaningful
Herein lies the beauty of AutoMapper. When your classes align themselves conventionally, your mapping configuration can be as simple as:
CreateMap<Meeting,MeetingDto>();
This is a trivial example, but where AutoMapper shines is in the ability to map lists and nested properties by convention. Or constructing an object based on another object’s properties. All without littering a method with ceremonial code.
Using an object-to-object mapper does have its drawbacks:
- The classic left-hand/right-hand code performs faster than mapped code, although Jimmy has made great strides in increasing performance.
- When defining unconventional mappings, you will still have left-hand/right-hand code
- There will be a learning curve for you (and/or your team) if you have not had experience with convention-based frameworks or some of the .NET 3.5 features.
However, we have found these drawbacks to be trivial when compared to the benefits.
Mapping Toolset
In addition to providing the convention mappings, Jimmy has provided a toolset for enhancing the mappings. Listed here are several tools we either use or find compelling.
Custom Converters: Code to convert from source type to destination type. Defined once and implicitly used for all mappings.
Custom Resolvers: Code to convert from source value to destination value. Defined once for use by any mapping, but must be explicitly used by a mapping.
Custom Formatters: Code to reformat a type for presentation. Like resolvers, formatters are defined once and explicitly used.
Before/After Map Functions: Provides the ability to run custom code before or after the mappings are performed.
Validation Assertion: Provides a method to short-circuit at runtime (or in tests) with an explanatory exception when a mapping may cause failures.
Mapping Profiles: Profile is a base class that helps you organize your code. If you are familiar with StructureMap’s Registry class, you will feel right at home with the Profile class.
Where We Use AutoMapper
We use AutoMapper to transform our object model to our presentation model. This happens in two places – in our MVC controllers and at our WCF service layer.
In the controllers, we will map from a domain model or DTO into the view model. We will also map from our view model into a message object for use by the service or domain.
At the service layer, we flatten our domain model into a DTO or map a message object to run a command against the domain.
Code Samples
I am including the sample code used at the NETDUG meeting. The first sample shows different ways to setup mappings (inline vs. profiles). The second sample shows some usages of custom resolvers, formatters, and before-maps.
Download the AutoMapper source. Jimmy has provided many sample classes and unit tests showing off the usage of different conventions.
Matt Hinze also posted a sample solution using AutoMapper with NerdDinner (of which I borrowed some custom formatter code – Thanks Matt!).
Download the CodeCampServer source. It is full of examples and conventions, including using an IoC/DI implementation of the AutoMapper engine.
What about the case when the domain object has, say 10 properties and the dto has 3?
_repository.GetMeetingById(meetingId; would generate a select with 10 fields and discard 7 of them…
Both sample links point to the same file.
But then, I will probably only have time to look at the first one anyway. Regardless, thanks for the work you did on these. AutoMapper has definitely become one of my favorite libraries.
@Dan If I only want 3 properties, then I would definitely drop the other 7, especially before sending data across a service boundary. If my identity map already has the entity in memory (1st or 2nd level cache), I don’t need to worry about a call to the db. It seems that you may be hinting at premature optimization. Remember, this is a sample project to show capability, not architecture. On a real project, I can optimize my queries when the need arises.
Richard
@KevDog
Thanks for pointing out the problem with the link to the second sample. It is fixed now.
Do you have Anemic Domain-Model?
@dario-g We have several domains/bounded contexts, with a mixture of anemic and strong models (by design).
Your question is a bit anemic…can you put some context around it so that I might provide a more relevant answer?
In Sample 2, AttendeeProfile.cs, line 21:
.BeforeMap((s, d) => attendee = new AttendeeRepository().GetById(s))
Can you show an example if we wanted to inject repository instance using IoC?( IAttendeeRepository.GetByIds(s))
Thanks.
@waveyus
Looks like you are really close to having it solved. Have you tried something similar to the following?
.BeforeMap((s,d) => attendee = Container.GetInstance<IAttendeeRepository>().GetByIds(s))