AutoMapper — inheritance mapping not working, same source, multiple destinations

前端 未结 2 496
挽巷
挽巷 2020-12-31 10:12

Can I use inheritance mapping in AutoMapper (v2.2) for maps with the same Source type but different Destination types?

I have this basic situation (the real classes

相关标签:
2条回答
  • 2020-12-31 10:34

    Yo can do like here

                CreateMap<Entity, ViewModelA>()
                .InheritMapping(x =>
                {
                    x.IncludeDestinationBase<BaseViewModel>();
                });
    

    There is code of extension

    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)));
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-12-31 10:35

    Unfortunately in this case, AutoMapper seems to be registering only one child class mapping per source type, the last one (ViewModelB). This was probably designed to work with parallel hierarchies, not with a single source type.

    To work around this, you can encapsulate the common mappings in an extension method:

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

    And use it in the individual subclass mappings:

    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));
    
    0 讨论(0)
提交回复
热议问题