Skip to content
haiduc32 edited this page May 14, 2011 · 15 revisions

Before you begging you should get a compiled version of NextMap included in your project. You can compile it from source or get the latest compiled version in downloads.

Simple auto mapping

Before being able to do any mappings a mapping configuration must be defined. this is done by Mapper.CreateMap<TSource,TDestination>() where TSource is the source type and TDestination the destination type. When you want to be able to map object of type A to objects of type B you must create a mapping configuration as follows:

Mapper.CreateMap<A,B>();

After that you can actualy map an object of type A to an object of type B (assuming both A and B have a property called Name:

A a = new A { Name = "Big A" };
B b = Mapper.Map<A,B>(a);
Console.WriteLine(b.Name); //will output "Big A"

In this case the mapping has been done by convention, the rule put simple would be: each member in the destination class is mapped from the member with same name in the source class if exists.

NextMap also supports "copy" mappings where the destination object is already instantiated and only the mapped fields must be updated:

A a = new A { Name = "Big A" };
B b = new B { Name = "Big B" };
Console.WriteLine(b.Name); //will output "Big B"
Mapper.Map(a, b);
Console.WriteLine(b.Name); //will output "Big A"

The generic parameters can be skipped as they will be inferred by the compiler.

Defining explicit mappings for members

Mapping by convention is cool as you can just have two classes with members of same name and the mapping will be done automatically. But what about if there is a property in the destination class that has a different name in the source, but is actually the same? Let's suppose we have a Person class and a Customer class:

public class Person
{
  public string Name { get; set; }
  public string Surname { get; set; }
  // and a lot more other fields...
}

public class Customer
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  // and a lot more other fields...
}

The "a lot more other fields" are of the same name in both Person and Customer and we want to take advantage of auto mapping but Name/FirstName and Surname/LastName still have to be mapped. For that we can define manual mapping rules:

Mapper.CreateMap<Person,Customer>()
  .ForMember(dest => dest.FirstName, rule => rule.MapFrom(src.Name))
  .ForMember(dest => dest.LastName, rule => rule.MapFrom(src.Surname));

That might look cumbersome at a first glance but gives us a great opportunity to make validations against the configuration later, bringing more safety into the code.

Ignoring members

In cases when a destination member should not be mapped by convention it can be ignored. Also if validating the configuration the destination members that have to corresponding member in the source object must be ignored.

Ignoring can be done on a member for both "create" and "copy" mappings or only on the "copy" mapping. That comes handy when there is a need to copy into an instantiated destination only the update-able values, like price, quantity, but the id must be the same, as for the create all mapped values must be copied. A very good example is the Entity Framework generated entities.

A simple ignore rule looks like that:

Mapper.CreateMap<Person,Customer>()
  .ForMember(dest => dest.FirstName, rule => rule.MapFrom(src.Name))
  .ForMember(dest => dest.LastName, rule => rule.Ignore())

Now in any mapping the LastName will not be mapped with any value. To ignore only the "copy" mapping the ignoreOnCreate parameter of the Ignore method must be set to false so it will have the rule of ignore but will "ignore" the rule when creating the destination object, but not for the already initialized object:

Mapper.CreateMap<Person,Customer>().ForMember(dest => dest.Id, rule => rule.Ignore(ignoreOnCreate: false));

Person p = new Person { Id = 100 };
Customer c = new Customer { Id = 200 };

Mapper.Map(p, c);
Console.WriteLine(c.Id); //the output will be "200"

That helps

Mapping complex classes

Mapping simple classes seems.. simple. But how about complex classes with aggregation? Suppose we have the following classes:

public class A
{
  //..
}

public class B
{
  public A PropertyA { get; set; }
}

public class ADto //class similar to A
{
  //..
}

public class BDto //class similar to B
{
  public ADto PropertyA { get; set; }
}

How about mapping B to BDto, and more specifically the PropertyA property, can that be mapped automatically? Nothing more simpler:

Mapper.CreateMap<B, BDto>();
Mapper.CreateMap<A, ADto>();

BDto bTransportObject = Mapper.Map<B, BDto>(bSourceObject);

All that was required was both classes to have a mapping configuration defined. It doesn't even matter the order in which they were defined as long as both were defined before the actual mapping request.

Another subject regarding the aggregated objects is the support for types implementing IEnumerable<>, that's List<>, Queue<>, etc. At the moment only generic types defined in the .NET framework are supported only (excluding Dictionary).

Mapping from a list of A to a list of ADto is not supported and most probably will not be supported as it can be written as a single line of code:

List<ADto> aDtoList = aSourceList.Select(x => Mapper.Map<A, ADto>(x)).ToList();

On the other hand aggregated classes with lists of another classes that must be mapped is a common scenario. Let's take the previous example and say that B has a list of As:

public class A
{
  //..
}

public class B
{
  public List<A> PropertyA { get; set; }
}

public class ADto //class similar to A
{
  //..
}

public class BDto //class similar to B
{
  public List<ADto> PropertyA { get; set; }
}

In this case, the mapping configuration code from the previous example needs no update, it is still correct. NextMap figures the List<> as standard .NET framework class and knows how to handle it. It only needs tips on how to map the A to ADto.

Validating the mappings

TODO: explain why validating is useful and how to use it for Unit Testing. An important method that was inspired by AutoMapper is the AssertConfigurationIsValid()

Implementation suggestions

After all the basics on usage are covered a few aspects are worth mentioning. Attention must be payed where the Mapper.CreateMap is defined. A suggestion would be to define them in a static constructor in the class where the mappings are done, or in some initialization place if the mappings for the types are reused in more classes. Defining the mapping configurations after they were defined will not throw any exception but might imply a significant performance penalty and there is a higher risk of bugs.

Now that you know the basics check the Advanced topics for a better understanding of the mapping mechanisms.