2012-12-21 10 views
12

同じソースタイプで異なる送り先タイプのマップに対して、AutoMapper(v2.2)で継承マッピングを使用できますか?AutoMapper - 継承マッピングが機能しない、同じソース、複数の送り先

私は、この基本的な状況(実際のクラスはより多くの性質を持っている)があります。

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

ViewModelAViewModelBは同じEntityクラスの異なる表現です:

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

私は再利用したいです

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)); 
01のように、それぞれのViewModelの BaseViewModelの同じマッピング。

残念ながら、これはうまくいかないようです。これらのようなコール:model

var model = Mapper.Map<Entity, ViewModelA>(entity); 

結果はPropertyAがマッピングされた、ではないCommonProperty。私はhttps://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritanceの例を正しく実行していると思いますが、同じソースタイプで作成された複数のマップがAutoMapperを起動していることが心配です。

洞察?私はベースクラスのマッピングをグループ化するというアイデアが大好きですが、これはうまくいかないようです。

+1

この質問の将来の読者のために、AutoMapperは問題が尋ねられて以来これを修正しているようです。 –

+0

私はここに同じことをやろうとしているが、私はやろうとしている: 'VARモデル= Mapper.Map <エンティティ、BaseViewModel>(エンティティ)' しかし、それは、ViewModelAのインスタンスをしませ戻っていますBaseViewModelのインスタンス、MapView関数がBaseViewModel型を返すように指示しているとは思っていました。私はAutomapper 3.0を使用していますので、2.2からの元のバグが解決されたようです。 – njkremer

+0

このSOのポストは私の問題を助け、働くのに望ましい効果を得ました。 http://stackoverflow.com/questions/27317719/automapper-how-to-not-repeat-mapping-config-from-complex-type-to-base-class – njkremer

答えて

14

残念ながら、この場合、AutoMapperはソースタイプごとに1つの子クラスマッピングのみを登録しているようです(最後の1つは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)); 
+4

+1コードの再利用! – kdawg

+0

ありがとうございますが、私のために働いていません。 [AutoMapperを使用してベースクラスをマップする](http://stackoverflow.com/questions/39425775/using-automapper-to-map-base-classes)の質問をご覧ください。 –

+0

@ClintEastwoodあなたのURLが機能していない – huoxudong125

0

ヨがここ

のように行うことができます
  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))); 
     } 
    } 

} 
関連する問題