I love AutoMapper. I’ve used it in virtually every ASP.NET MVC application I’ve ever worked on. It has saved me countless lines of tedious code, and it’s quite smart at inferring the correct mappings. However, my one complaint is that the API for specifying the more complicated mappings is, in a word, ugly. It’s flexible, sure, but ugly. In this post, I’ll show you how a few simple extension methods that can make things much prettier.
First, let’s consider a very trivial (fictional) domain model and corresponding view model:
public class Widget { public string FirstName { get; set; } public string LastName { get; set; } } public class WidgetViewModel { public string FullName { get; set; } public string Placeholder { get; set; } }
Our mapping requirements here are quite simple: we want to concatenate FirstName and LastName and map it to the FullName property on the view model, and we want to ignore the Placeholder property entirely. Here’s a class that creates exactly this mapping:
public class WidgetViewModelMap { public void CreateMapping(IConfiguration mappingConfig) { mappingConfig.CreateMap<Widget, WidgetViewModel>() .ForMember(m => m.FullName, opt => opt.MapFrom(w => w.FirstName + " " + w.LastName)) .ForMember(m => m.Placeholder, opt => opt.Ignore()); } }
The two ForMember calls are what I was referring to when I said “ugly”. We have some nested lambdas going on here, which makes the code hard to read. The IMappingConfiguration interface (the ‘opt’ parameter) provides a lot of functionality, but I’ve yet to need to call multiple methods on it at once. I always use a single method, just like I’ve done in this example. What I’d like to see instead is syntax more like this:
public class WidgetViewModelMap { public void CreateMapping(IConfiguration mappingConfig) { mappingConfig.CreateMap<Widget, WidgetViewModel>() .ForMember(m => m.FullName).MapFrom(w => w.FirstName + " " + w.LastName) .ForMember(m => m.Placeholder).Ignore(); } }
It’s not a major change, but the nested lambdas are gone, and the code reads better IMO. I could submit a patch with this change, wait for it to be evaluated and possibly incorporated, but why wait? Thanks to the power of extension methods, I’ve already got all the tools I need to achieve this syntax right now!
First, we’ll need the ForMember extension method:
public static class AutoMapperExtensions { public static MemberMappingExpression<T1,T2> ForMember<T1,T2>(this IMappingExpression<T1,T2> expression, Expression<Func<T2,object>> member) { return new MemberMappingExpression<T1,T2>(expression, member); } }
Next, we’ll need to implement our MemberMappingExpression class:
public class MemberMappingExpression<T1,T2> : IMemberMappingExpression<T1, T2> { private readonly IMappingExpression<T1, T2> _mappingExpression; private readonly Expression<Func<T2, object>> _member; public MemberMappingExpression(IMappingExpression<T1,T2> mappingExpression, Expression<Func<T2, object>> member) { _mappingExpression = mappingExpression; _member = member; } public IMappingExpression<T1,T2> MapFrom<TResult>(Func<T1, TResult> sourceMember) { _mappingExpression.ForMember(_member, opt => opt.MapFrom(sourceMember)); return _mappingExpression; } public IMappingExpression<T1,T2> Ignore() { _mappingExpression.ForMember(_member, opt => opt.Ignore()); return _mappingExpression; } }
And TA-DA! You now have a simpler, more fluent interface for defining your mappings. Note that MemberMappingExpression should really be encapsulated behind an interface, and there are undoubtedly other methods that could be added besides the two I’ve implemented, but doing so is a trivial exercise.