AutoMapper-отображение наследования не работает, один и тот же источник, несколько назначений
Можно ли использовать отображение наследования в AutoMapper (v2. 2) для карт с одинаковым типом источника, но разными типами назначения?
У меня есть такая базовая ситуация (реальные классы имеют гораздо больше свойств):
public abstract class BaseViewModel
{
public int CommonProperty { get; set;}
}
public class ViewModelA : BaseViewModel
{
public int PropertyA { get; set; }
}
public class ViewModelB : BaseViewModel
{
public int PropertyB { get; set; }
}
ViewModelA и ViewModelB - это различные представления одного и того же класса сущностей:
public class Entity
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
}
Я хочу повторно использовать одно и то же отображение для BaseViewModel для каждой модели вида, например:
Mapper.CreateMap<Entity, BaseViewModel>()
.Include<Entity, ViewModelA>()
.Include<Entity, ViewModelB>()
.ForMember(x => x.CommonProperty, y => y.MapFrom(z => z.Property1));
Mapper.CreateMap<Entity, ViewModelA>()
.ForMember(x => x.PropertyA, y => y.MapFrom(z => z.Property2));
Mapper.CreateMap<Entity, ViewModelB>()
.ForMember(x => x.PropertyB, y => y.MapFrom(z => z.Property3));
, но, к сожалению, это не похоже на работу. Звонки вроде эти:
var model = Mapper.Map<Entity, ViewModelA>(entity);
Приводит к тому, что model имеет PropertyA отображение, но не CommonProperty. Я полагаю, что следую примерам в https://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritance правильно, но я боюсь, что наличие нескольких карт, созданных с одним и тем же типом источника, отключает AutoMapper.
Какие-нибудь идеи? Мне нравится идея группирования отображений базовых классов вместе, но это, кажется, не работает.
2 ответов:
К сожалению, в этом случае AutoMapper, похоже, регистрирует только одно отображение дочернего класса на исходный тип, последнее (
ViewModelB). Это, вероятно, было разработано для работы с параллельными иерархиями, а не с одним типом источника.Чтобы обойти это, вы можете инкапсулировать общие сопоставления в метод расширения:
public static IMappingExpression<Entity, TDestination> MapBaseViewModel<TDestination>(this IMappingExpression<Entity, TDestination> map) where TDestination : BaseViewModel { return map.ForMember(x => x.CommonProperty, y => y.MapFrom(z => z.Property1)); }И использовать его в отображениях отдельных подклассов:
Mapper.CreateMap<Entity, ViewModelA>() .MapBaseViewModel<ViewModelA>() .ForMember(x => x.PropertyA, y => y.MapFrom(z => z.Property2)); Mapper.CreateMap<Entity, ViewModelB>() .MapBaseViewModel<ViewModelB>() .ForMember(x => x.PropertyB, y => y.MapFrom(z => z.Property3));
Йо может сделать, как здесь
CreateMap<Entity, ViewModelA>() .InheritMapping(x => { x.IncludeDestinationBase<BaseViewModel>(); });Существует код расширения
public static class MapExtensions { public static void InheritMapping<TSource, TDestination>( this IMappingExpression<TSource, TDestination> mappingExpression, Action<InheritMappingExpresssion<TSource, TDestination>> action) { InheritMappingExpresssion<TSource, TDestination> x = new InheritMappingExpresssion<TSource, TDestination>(mappingExpression); action(x); x.ConditionsForAll(); } private static bool NotAlreadyMapped(Type sourceType, Type desitnationType, ResolutionContext r, Type typeSourceCurrent, Type typeDestCurrent) { var result = !r.IsSourceValueNull && Mapper.FindTypeMapFor(sourceType, desitnationType).GetPropertyMaps().Where( m => m.DestinationProperty.Name.Equals(r.MemberName)).Select(y => !y.IsMapped() ).All(b => b); return result; } public class InheritMappingExpresssion<TSource, TDestination> { private readonly IMappingExpression<TSource, TDestination> _sourcExpression; public InheritMappingExpresssion(IMappingExpression<TSource, TDestination> sourcExpression) { _sourcExpression = sourcExpression; } public void IncludeSourceBase<TSourceBase>( bool ovverideExist = false) { Type sourceType = typeof (TSourceBase); Type destinationType = typeof (TDestination); if (!sourceType.IsAssignableFrom(typeof (TSource))) throw new NotSupportedException(); Result(sourceType, destinationType); } public void IncludeDestinationBase<TDestinationBase>() { Type sourceType = typeof (TSource); Type destinationType = typeof (TDestinationBase); if (!destinationType.IsAssignableFrom(typeof (TDestination))) throw new NotSupportedException(); Result(sourceType, destinationType); } public void IncludeBothBases<TSourceBase, TDestinatioBase>() { Type sourceType = typeof (TSourceBase); Type destinationType = typeof (TDestinatioBase); if (!sourceType.IsAssignableFrom(typeof (TSource))) throw new NotSupportedException(); if (!destinationType.IsAssignableFrom(typeof (TDestination))) throw new NotSupportedException(); Result(sourceType, destinationType); } internal void ConditionsForAll() { _sourcExpression.ForAllMembers(x => x.Condition(r => _conditions.All(c => c(r))));//указываем что все кондишены истинны } private List<Func<ResolutionContext, bool>> _conditions = new List<Func<ResolutionContext, bool>>(); private void Result(Type typeSource, Type typeDest) { _sourcExpression.BeforeMap((x, y) => { Mapper.Map(x, y, typeSource, typeDest); }); _conditions.Add((r) => NotAlreadyMapped(typeSource, typeDest, r, typeof (TSource), typeof (TDestination))); } } }
Comments