Сопоставление наследования 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 — разные представления одного и того же класса Entity:

public class Entity
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
    public int Property3 { get; set; }
}

Я хочу повторно использовать одно и то же сопоставление для BaseViewModel для каждой ViewModel, например:

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.

Любые идеи? Мне нравится идея группировать сопоставления базовых классов вместе, но, похоже, это не работает.


person kdawg    schedule 21.12.2012    source источник
comment
Для будущих читателей этого вопроса - похоже, AutoMapper исправил это с тех пор, как был задан вопрос.   -  person Travis Illig    schedule 06.09.2014
comment
Я пытаюсь сделать то же самое здесь, но я пытаюсь сделать: var model = Mapper.Map<Entity, BaseViewModel>(entity) но он возвращает экземпляр ViewModelA, а не экземпляр BaseViewModel, даже несмотря на то, что я говорю функции Map вернуть тип BaseViewModel. Я использую Automapper 3.0, поэтому кажется, что исходная ошибка версии 2.2 устранена.   -  person njkremer    schedule 20.12.2014
comment
Этот пост SO помог мне с моей проблемой и дал желаемый эффект. stackoverflow.com/questions/27317719/   -  person njkremer    schedule 20.12.2014


Ответы (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));
person Jordão    schedule 21.12.2012
comment
Спасибо, но у меня не сработало. Не могли бы вы взглянуть на вопрос Использование AutoMapper для сопоставления базовых классов? - person Jack; 10.09.2016

Вы можете сделать, как здесь

            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)));
        }
    }

}
person Yelaman    schedule 13.11.2015