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:
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:
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:
Apparently this has been fixed, though the fix does say "#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.
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 "#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.