Teaching AutoMapper about our conventions

I often need to send data from my entities to the client in JSON format to enable rich AJAX functionality. It isn’t practical to serialize the entities directly, so they are first flattened to a data transfer object (DTO) before serialization. For example, the Case entity has a reference to a Contact entity, which has a FirstName property. On the Case DTO, I might have a ContactFirstName property to hold that value. We use AutoMapper to handle the chore of populating the properties of the DTO using values from the original entity.

Conventions for mapping our DTOs

AutoMapper requires that we declare up front which which entities will be mapped to which DTOs. It has a lot of intelligent defaults, so once we tell it that a Case can be mapped to a CaseDTO, it can figure out how to populate the ContactFirstName property on the DTO from Case.Contact.FirstName. However, there are some properties on the DTO which aren’t so straightforward, so we have to explicitly configure how they are mapped.

Our DTOs generally follow these rules:

  • For every entity reference property on the source entity, the DTO will have a property of the same name, but of type GUID, which holds the entity identifier. Ex: Case.Contact of type Contact maps to CaseDTO.Contact of type GUID.
  • For every entity reference property on the source entity, the DTO will have a property with the name plus the suffix “ViewURL”, which holds the URL for viewing the referenced entity. Ex: CaseDTO.ContactViewUrl holds the URL for the Case.Contact.
  • For every “list value” (think values that show in a drop-down) property on the source entity, the DTO will have a property with the name plus the suffix “Display”, which holds the localized display value. Ex: Case.Contact.Country will map the value “USA” to CaseDTO.ContactCountry, and the value “United States” to CaseDTO.ContactCountryDisplay.

Implementing our mapping rules with AutoMapper

It was very easy to teach AutoMapper about the first rule. It took just a single line of code in our configuration:

cfg.CreateMap<Entity, Guid>().ConvertUsing(entity => entity.Id);

This tells Automapper that any time it tries to map a property of a type that derives from Entity to a property of type GUID, execute the lambda to get the Id.

The other two rules were not so easy to apply. AutoMapper currently has no built-in support for defining conventions to build mappings based on aspects of the types or their properties (although I hear this is something being considered for the future). Instead, you have to explicitly declare the custom relationship between each type, and each custom property resolution. You end up with code that looks like this:

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<Entity, Guid>().ConvertUsing(entity => entity.Id);
    cfg.CreateMap<Case, CaseDTO>()
        .ForMember(d => d.ContactViewUrl, map => map.ResolveUsing<UrlValueResolver>().FromMember(s => s.Contact))
        .ForMember(d => d.SiteViewUrl, map => map.ResolveUsing<UrlValueResolver>().FromMember(s => s.Site))
        .ForMember(d => d.InstalledPartViewUrl, map => map.ResolveUsing<UrlValueResolver>().FromMember(s => s.InstalledPart))
        // ... and the rest of the ViewUrl properties
        .ForMember(d => d.CaseTypeDisplay, map => map.ResolveUsing<ListValueResolver>().FromMember(s => s.CaseType))
        .ForMember(d => d.StatusDisplay, map => map.ResolveUsing<ListValueResolver>().FromMember(s => s.Status))
        // ... and the rest of the Display properties
        ;
    cfg.CreateMap<Site, SiteDTO>()
        .ForMember(d => d.SupportSiteViewUrl, map => map.ResolveUsing<UrlValueResolver>().FromMember(s => s.SupportSite))
        // ... and the rest of the ViewUrl properties
        .ForMember(d => d.PrimaryAddressCountryDisplay, map => map.ResolveUsing<ListValueResolver>().FromMember(s => s.PrimaryAddress.Country))
        // ... and the rest of the Display properties
        ;
    cfg.CreateMap<Part, PartDTO>()
        // ... all of the ViewUrl properties
        // ... all of the Display properties
        ;
    // ... and the rest of the Entities
});

The sample is abbreviated because showing all of the entities, and all of the ViewUrl and Display property mappings for each of those entities is just too repetitive to be interesting. Aside from the tedium of having to type it up the first time, it makes maintenance a lot more painful. Any time we add an entity or list value property to an entity, we would have to remember to go add the corresponding mapping to the AutoMapper configuration, or risk introducing bugs.

Declaring mapping rule conventions

The benefit of establishing conventions in your code is that different parts of the system can rely on those conventions, so you get a lot of functionality that just works the way you would expect. Since we consistently name our DTOs the same way, and we consistently name properties on the DTO the same way, we should be able to rely on the fact that these will always be populated the same way.

With a couple patches to AutoMapper (now available in the latest source), I was able to build some extension methods that allowed me to express our conventions. The previous (which had to be edited for length) code can now be rewritten as:

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<Entity, Guid>().ConvertUsing(entity => entity.Id);
    cfg.CreateMapsForSourceTypes(x => x.CanBeCastTo<Entity>(),
        type => Type.GetType(typeof (ListItemMethodDTO).Namespace + "." + type.Name + "DTO"),
        (map, source, destination) =>
        {
            map.MapViewUrls(source, destination);
            map.MapDisplayValues(source, destination);
        });
});

Now the code expresses our convention: for every type that derives from Entity, map it to a type (in a given namespace) with the same name with a DTO suffix, and map all the ViewUrl and Display members appropriately.

I’ve posted the full source for our extension methods, but the CreateMapsForSourceTypes method is probably the only one that is generally applicable:

public static void CreateMapsForSourceTypes(this IConfiguration configuration, Func<Type, bool> filter, Func<Type, Type> destinationType, Action<IMappingExpression, Type, Type> mappingConfiguration)
{
    var typesInThisAssembly = typeof (AutoMapperExtensions).Assembly.GetExportedTypes();
    CreateMapsForSourceTypes(configuration, typesInThisAssembly.Where(filter), destinationType, mappingConfiguration);
}

public static void CreateMapsForSourceTypes(this IConfiguration configuration, IEnumerable<Type> typeSource, Func<Type, Type> destinationType, Action<IMappingExpression, Type, Type> mappingConfiguration)
{
    foreach (var type in typeSource)
    {
        var destType = destinationType(type);
        if (destType == null) continue;
        var mappingExpression = configuration.CreateMap(type, destType);
        mappingConfiguration(mappingExpression, type, destType);
    }
}

It simply takes a criteria for identifying your source types, a function for determining the destination type based on the source type, and a set of configuration steps that should be applied to each mapping. Goodbye repetitive code!

Related Articles:

This entry was posted in automapper, conventions. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://craigcav.wordpress.com/ CraigCav

    Hi Joshua,

    I’ve struggled with similar repetitive mappings myself and hadn’t achieved a way to make my conventions explicit, so I found this a really interesting read.

    I’m really hoping that AutoMapper will soon be extended to natively support conventions to build mappings for type information, as although this work-around is much better than where we started, it still feels a little clunky.

    I guess I’d like to be able to see full, easily testable, convention classes for MapViewUrls and MapDisplayValues, rather than extension methods off of IMappingExpression.

    Another thing I’m thinking was that it took me longer than it should for me to figure out what was going on in the CreateMapsForSourceTypes method. Now, that might just be me being a little slow…but I couldn’t figure out what the parameters were doing at a glance. I’m wondering if this method should be broken up into smaller chunks? Maybe something like:

    cfg.ForSourceTypes(x => x.CanBeCastTo())
    .MapToDestinationType(sourceType => Type.GetType(typeof (ListItemMethodDTO).Namespace + “.” + sourceType.Name + “DTO”))
    .CreateMaps(map, source, destination =>
    {
    map.MapViewUrls(source, destination);
    map.MapDisplayValues(source, destination);
    });

    What do you think?

    The only other part that wasn’t particularly clear (without looking at the implementation) was where exactly the source types were coming from.

    Hmm…Reading my comment back I sound really critical, but actually I’m just excited to see where this is going. Keep up the good work.

  • http://www.lostechies.com/members/jflanagan/default.aspx Joshua Flanagan

    I didn’t read it as critical at all, its all valid feedback, and I pretty much agree with everything you said.
    The truth is, I started to go down the path of a method-chained DSL, but it makes the implementation a lot more complicated. It wasn’t worth the effort to solve my problem, since I saw it as a one-off solution. I know Jimmy is re-thinking some of the underlying infrastructure to enable a better solution for the next major version of AutoMapper. My solution is just some spackle to hold us over (which is also why I didn’t propose it as a patch to AM). I posted it, not because the code is impressive, but to share the idea.