Advanced mapping with Automapper

Setting a manual mapping configuration

We can instruct Automapper to map a destination object’s member to a specific source object’s member. This is useful when the default mapping (which search for a member with the same name) doesn’t produce the correct result.

Mapper.CreateMap<MyRequestEntity, MyRequestModel>()
    .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.MyRequestId))

Using a custom resolver for complex mappings

Let’s consider this scenario: our MyRequestEntity object has an InterviewRequest collection which contains all together different kinds of interview requests, all associated with the main request. Each request has a InterviewTypeId field, which tells the type of the interview request. Furthermore a comment related to each interview request type is included in the main object.

On the other hand our MyRequestModel object has multiple objects, one for each interview request type, which contain the collection of the related interview requests together with the comment. So the destination object has a very different data structure compared with the source object, how can we perform a similar mapping?

We can use a custom resolver to generate on the fly an exchange object from the source object, which exposes data with the same structure as the destination object. Then Automapper can perform its mapping from this exchange object and the destination object.

Mapper.CreateMap<MyRequestEntity, MyRequestModel>()
    .ForMember(dest => dest.FaceToFaceInterviews, opts => opts.ResolveUsing<FaceToFaceInterviewResolver>())

Here we are telling Automapper that for our FaceToFaceInterviews destination property (which contains the collection of interview request of type “face-to-face”, together with the comment) we want to use a custom resolver of class FaceToFaceInterviewResolver. Here is the implementation of the resolver:

public class FaceToFaceInterviewResolver : ValueResolver<MyRequestEntity, InterviewRequestModel>
{
    protected override InterviewRequestModel ResolveCore(MyRequestEntity source)
    {
        var obj = new InterviewRequestModel()
        {
            Items = source.InterviewRequest.Where(
                i => i.InterviewTypeId == (int)RequestConstants.MyRequestInterviewType.FaceToFaceInterview),
            Comment = source.FaceToFaceInterviewNotes
        };
        return obj.Items.Any() ? obj : null;
    }
}

We are creating a InterviewRequestModel object (our exchange object) from the source object. Automapper will use this object as the source object when mapping the FaceToFaceInterviews member of the destination object. In other words we are manipulating the source object’s data to fit the destination data structure, in order to be able to perform the mapping. At this point we just need to define a mapping between the exchange object and the destination object:

Mapper.CreateMap<InterviewRequestModel, FaceToFaceInterviewsModel>();

The mapping is very simple because we defined our exchange object to have the same structure as the destination object, so the mapping can be performed just using the default behavior. Of course we could have an exchange object that is different in respect with the destination object. In that case we have to instruct Automapper with the appropriate mapping rules as always.


Executing other actions after the mapping

We can execute other operations on the destination object after the mapping has been performed (for example if we need the child objects to refer the parent). We can do this using the AfterMap() method in this way:

Mapper.CreateMap<MyRequestModel, MyRequestEntity>()
    .ForMember(dest => dest.MyRequestId, opts => opts.MapFrom(src => src.Id))
    ...
    .AfterMap((src, dest) =>
    {
        ...
    });
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s