Skip to content
JulianR edited this page Jun 28, 2011 · 5 revisions

MemberMapper

MemberMapper, the default implementation of IMemberMapper is the central class in ThisMember. It's used for the creation of maps and the mapping itself.

ProposedMap

A ProposedMap (or ProposedMap<TSource, TDestination>) is what ThisMember thinks your map should look like, based on its conventions and configuration. This map can still be modified.

MemberMap

If you're happy with your ProposedMap you can 'finalize' it by calling FinalizeMap on it. The result of that will be a MemberMap which is the object that does the actual mapping. After you have called FinalizeMap, the map can no longer be changed. It's now a fast, compiled mapping function.

Map lifetime

Maps are owned by the MemberMapper. Creating a MemberMapper is cheap but maps are expensive to create. You can create multiple MemberMappers in your application if they have multiple purposes (the MapCollection class is meant for this) but create them once and cache them.

Creation

There's a number of ways to create a map, depending on what you want.

Implicitly

If you don't need to do anything custom, there's no need to explicitly create a map. Just call Map and if the map does not yet exist, it will be created based on standard conventions.

var mapper = new MemberMapper();

var source = ....

// Map is implicitly created
var result = mapper.Map<Source, Destination>(source);

Explicitly

Explicitly can be done with CreateMap, but this serves no real purpose of you don't do anything custom.

var mapper = new MemberMapper();

mapper.CreateMap<Source, Destination>(source);

var source = ....

// Map is already created
var result = mapper.Map<Source, Destination>(source);

Explicitly with MappingOptions

MappingOptions is a delegate type that you fill in with a function, which gets called for every property that ThisMember wants to map. The property can then be ignored if you want.

An example:

mapper.CreateMap<UserDto, User>(options: (source, destination, option) =>
{
  // You can make this as complicated as you want
  if (destination.Name == "Password")
  {
    option.IgnoreMember();
  }

  // For example, check for the presence of an attribute 
  // that determines if a user has rights to map this property
  var attr = destination
    .PropertyOrFieldType
    .GetCustomAttributes(typeof(MappingRequiresPermissionAttribute), false)
    .FirstOrDefault() as MappingRequiresPermissionAttribute;

  if (attr != null && !attr.HasPermission)
  {
    option.IgnoreMember();
  }

});

Explicitly with a custom mapping

You can specify a custom mapping by providing an expression that shows how to map the type:

mapper.CreateMap<Customer, CustomerDto>(source => new CustomerDto
{
  FullName = source.FirstName + " " + source.LastName
});

It's not needed to specify the members that ThisMember can map on its own based on convention.

Create a proposal first

var proposal = mapper.CreateMapProposal<CustomerDto, Customer>(source => new Customer
{
  LastName = source.FirstName
});  

proposal.ForMember(c => c.ID)
        .OnlyIf(c => c.ID > 0)
        .ForMember(c => c.FirstName).MapAs(c => c.LastName);

proposal.FinalizeMap();

Map re-creation

Every time you call CreateMap on MemberMapper, it will take it literally and recreate the entire map, not utilizing any maps in its cache. Only call CreateMap once unless you want to change an existing map.

MapRepository

Because map creation is expensive and should only be done once per MemberMapper lifetime it would be nice if there were a central place where you can specify all your custom maps. This is what the IMapRepository is for. Every IMemberMapper also has an optional IMapRepository. When it's not null, the IMapRepository is first checked if it has a definition for a certain map.

It can be used in this way:

public class TestRepository : MapRepositoryBase
{
  protected override void InitMaps()
  {
    DefineMap<SourceType, DestinationType>((mapper, options) =>
    {
      return mapper
      .CreateMapProposal<SourceType, DestinationType>(customMapping: src => new DestinationType
      {
        Test = int.Parse(src.ID)
      });
    });

    DefineMap<SourceTypeNested, DestinationTypeNested>((mapper, options) =>
    {
      return mapper
      .CreateMapProposal<SourceTypeNested, DestinationTypeNested>(customMapping: src => new DestinationType
      {
        Test = int.Parse(src.ID)
      });
    });
  }
}

var mapper = new MemberMapper();

var repo = new TestRepository();

mapper.MapRepository = repo;

var result = mapper.Map<SourceTypeWithNested, DestinationTypeWithNested>(new SourceTypeWithNested 
{ 
  Foo = new SourceTypeNested 
  { 
    ID = "1" 
  } 
});

If you make use of a map repository, you are strongly recommended to not implement IMapRepository yourself, but to make use of the abstract base class MapRepositoryBase.