AutoMapper 2.1 AfterMap() Fires Multiple Times

I've been writing some complex mapping for the hierarchical data on my RustyShark site (the Media entities on the site consist of a lot of dynamic meta data, which can be added and removed when edited). I'm using Entity Framework and AutoMapper, and I've ran in to an issue where AfterMap() executes multiple times, when it should only really execute once. I just thought I'd post my workaround, which also helps to demonstrate how C# interfaces can be handy. Here's what my source entity looks like:

public class Medium : IAutoMapperAfterMapUsage {
    public int? MediumID { get; set; }
    public string Title { get; set; }
    public DateTime ReleaseDate { get; set; }
    public User CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }

    public List<Genre> Genres { get; set; }
    public List<MediaFormat> Formats { get; set; }
    public List<MetaData> MetaData { get; set; }

    #region IAutoMapperAfterMapUsage

    public bool Mapped { get; set; }

    #endregion
}

The destination entity isn't a concern here, so I won't post the code. As you can see, I've added a custom interface, IAutoMapperAfterMapUsage which has one property:

public interface IAutoMapperAfterMapUsage {
    bool Mapped { get; set; }
}

The boolean simply acts as a state manager, so whenever you need to use AfterMap(), implement the interface and add a single line of code at the top of your AfterMap method:

Mapper.CreateMap<Entities.Medium, Medium>()
    .ForMember(m => m.CreatedDate, o => o.Condition(s => (s.MediumID ?? 0) <= 0))
    .ForMember(m => m.CreatedDate, o => o.ResolveUsing(s => DateTime.Now))
    .ForMember(m => m.CreatedBy, o => o.Condition(s => (s.MediumID ?? 0) <= 0))
    .ForMember(m => m.CreatedBy, o => o.MapFrom(s => s.CreatedBy.UserID))
    .ForMember(m => m.Title, o => o.ResolveUsing(s => s.Title.Trim()))
    .ForMember(m => m.MediaType, o => o.Ignore())
    .ForMember(m => m.Genres, o => o.Ignore())
    .ForMember(m => m.MediaFormats, o => o.Ignore())
    .ForMember(m => m.MediaMetaData, o => o.Ignore())
    .AfterMap((source, dest) => {
        if (source.Mapped) return;
        // Do complex mapping for Genres, MediaFormats and MediaMetaData

        source.Mapped = true;
    });

Apparently this has been fixed, though the fix does say "Moved after map to execute less often, fixes #105", which isn't that reassuring. I also use this interface to expose my DbContext to the AfterMap() method, which is very handy for the complex scenarios I'm working with.

Popular posts from this blog

TDD and Unit Testing with Moq

Handling uploads with MVC4, JQuery, Plupload and CKEditor

Generating a self-signed SSL certificate for my QNAP NAS