06 February 2011

It’s been a long time since I haven’t posted anything technical, but here is the weekend, and I finally have time to write another article on my laptop. I started writing about AutoMapper 2 months ago, but figured the PoC I was going to demo was faulty so I gave up. Till now. I think I got it right this time, and I hurry to share with you my latest thoughts.

I’ll go with a short description of the tool, just to give you a heads up what AutoMapper is all about. Suppose you are working in an n-layered solution where you have Model entities and DTO entities (Data Transfer Object). The Model entities are the classes that are loaded from the DB, and used in the Business Logic Layer. The DTO entities are used for communication with the outside world, say web service contracts. Naturaly you’ll be writing mapping code for creating DTO objects from Model objects, and Model from DTO. Supposedly a lot of the DTO entities have the same fields as Model entities, with some exceptions. You can use AutoMapper to map the data between the Model and DTO entities in just a few lines compared to manual mapping:
[csharp]
public class Model
{
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
}

public class DTO
{
public string Name { get; set; }
public string Surname { get; set; }
}

..

//create the source object:
Model model = new Model
{
Name = "John",
Surname = "Black",
Age = 35
};

//manual mapping:
DTO dto = new DTO
{
Name = model.Name,
Surname = model.Surname
};

//mapping using AutoMapper:
//this two lines can be set up in a static constructor,
//or in an Init block
Mapper.CreateMap<Model, DTO>();
Mapper.AssertConfigurationIsValid();

//mapping from Model object to DTO
DTO dto = Mapper.Map<Model, DTO>(model);

[/csharp]
You can read about basic usage in more details in this blog

Why this is good? Because you won’t have to worry about adding mappings manually each time you add new fields. We are all humans, we sometimes forget that we must also update some mapping class in some forgotten project.

But there is still chance for mistakes. If you create the target field but forget to create the source field, you will end with default value for that target field.

Don’t worry, with a correct usage of AutoMapper you can create 2 way mappings that can be verified easily with unit tests, or at least spotted immediately when you run the application on your dev machine.

Now using the previous Model and DTO classes let’s do a two way mapping and validate it with AssertConfigurationIsValid():
[csharp]
//mapping using AutoMapper:
Mapper.CreateMap<Model, DTO>();
Mapper.CreateMap<DTO, Model>();
Mapper.AssertConfigurationIsValid();

...

Model model = Mapper.Map<DTO, Model>(dto);

[/csharp]
An exception will be thrown when validating the configuration. When mapping from DTO to Model, there is the Age property that AutoMapper does not know where to map from. Ups.. Is this a mistake? Did I forget to add the Age field to the DTO class? If it’s not a mistake I will have to specify an ignore rule on this field, and when mapping from DTO to Model the property will be set to the default value, 0 in this case.
[csharp]
//mapping using AutoMapper:
Mapper.CreateMap<Model, DTO>();
Mapper.CreateMap<DTO, Model>()
.ForMember(x=>x.Age, y=>y.Ignore());
Mapper.AssertConfigurationIsValid();

...

Model model = Mapper.Map<DTO, Model>(dto);

[/csharp]
Now we have a two way mapping set up with validation. If to the DTO class a new property is added, but not to the Model class a runtime exception will be thrown when validating the configuration. As long as properties with same name and convertible types (AutoMapper can also convert automatically from one type to another) are added to both classes validation will run successfully, and you’ll have automatic mapping hassle free.

Right, but there is one more thing to cover. Where do you write the map configuration code, and how to use the mapping code in a more elegant way?

Starting with C# 3.0 Microsoft gave us Extension methods, which is a great thing I must say. Using extension methods, we can create a class that is used for mapping methods, that way we can call methods on DTO and Model classes like if it was part of them, but keep the code in a separate place, in such a way that DTO and Model mustn’t know about the existence of each other. Isn’t that confusing?

But this article is not about creating n-layer applications, and how to decouple the layers but about using AutoMapper, so I’ll skip to the code first and discuss it after you have a sneak peek:
[csharp]
public static class MappingExtensions
{
static MappingExtensions()
{
Mapper.CreateMap<Model, DTO>();
Mapper.CreateMap<DTO, Model>()
.ForMember(x => x.Age, y => y.Ignore());
Mapper.AssertConfigurationIsValid();
}

public static Model ToModel(this DTO dto)
{
return Mapper.Map<DTO, Model>(dto);
}

public static DTO ToDTO(this Model model)
{
return Mapper.Map<Model, DTO>(model);
}
}

...

//create the source object:
Model model = new Model
{
Name = "John",
Surname = "Black",
Age = 35
};

//map to DTO
DTO dto = model.ToDTO();
[/csharp]
So now we have a MappingExtensions class with extension methods. We have to add a using to it’s namespace and we can call ToModel() and ToDTO() method on the Model and DTO classes. The mapping configuration is done in the static constructor. The only drawback of this method is that if the validations is unsuccessful you get a “The type initializer for 'Mapping.MappingExtensions' threw an exception.” Exception which is a bit confusing, and the AutoMapper exception is wrapped in the inner exception, so you must always investigate the inner exceptions (and I don’t mean only when working with AutoMapper).

Now you know not only how to make automatic mappings that are being validated at runtime, but also how to make it elegantly. There is one more thing you must know, it has a price to pay. You can’t use AutoMapper for high performance applications. There is a big penalty for all the under the hood operations that AutoMapper does. But its usage is more then motivated when you are dealing with moderate quantities of data to be transferred or when you have to build a stable system.



blog comments powered by Disqus