-
Notifications
You must be signed in to change notification settings - Fork 1
Maps
MemberMapper
, the default implementation of IMemberMapper
is the central class in ThisMember. It's used for the creation of maps and the mapping itself.
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.
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.
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.
There's a number of ways to create a map, depending on what you want.
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 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);
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();
}
});
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.
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();
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.
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
.