2017-11-12 15 views
0

属性ベースのAutoMapper機能を作成したいと思います。 ドメインモデルクラスをDTOにマップして逆にする必要がありますが、DTOの一部のプロパティをドメインクラスにマップし直すべきではありません。読み取り専用プロパティです。サーバーからクライアントへの方向のみが許可されます。属性駆動型のAutoMapper設定

[OK]を、ので、私は、属性を使用して方向を設定したいので、私はこの1つ

[Flags] 
public enum Direction 
{ 
    None = 0, 
    ServerToClient = 1, 
    ClientToServer = 2, 
    Both = 4 
} 

[AttributeUsage(AttributeTargets.Property)] 
public class MapperDirectionAttribute : Attribute 
{ 
    public Direction Direction { get; } 

    public MapperDirectionAttribute(Direction direction = Direction.Both) 
    { 
     Direction = direction; 
    } 
} 

を持っているこの

public class Person 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class PersonDTO 
{ 
    [MapperDirection(Direction.ClientToServer)] 
    public string Name { get; set; } 
    public string Metadata { get; set; } 
} 

を持っている私は、クライアントにサーバだけを許可するようにDTOクラスにNameプロパティを飾ら名前の方向。

私は

public static class MapperExpressions 
{ 
    public static IMappingExpression<TSource, TDestination> OnlyIfClientToServer<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression) 
    { 
     var sourceType = typeof(TSource); 
     var props = sourceType.GetMembers().Where(prop => prop.IsDefined(typeof(MapperDirectionAttribute), false)); 
     foreach (var property in props) 
     { 
      var attribute = property.GetCustomAttribute<MapperDirectionAttribute>(); 
      var allow = attribute.Direction == Direction.ClientToServer || attribute.Direction == Direction.Both; 
      if (!allow) 
      { 
       // THIS DOESN'T WORK FOR SOURCE MEMBER 
       // AND CAN'T BE USED FOR DESTINATION MEMBER (BECAUSE OF MISSING PROPERTIES) 
       expression.ForSourceMember(property.Name, opt => opt.Ignore()); 
      } 
     } 

     return expression; 
    } 
} 

属性設定に基づいて、ソースからターゲットにすべての値をコピーするには、この拡張子を持つマッパーためのヘルパーように、initは

 // Init mapper 
     Mapper.Initialize(cfg => 
     { 
      cfg.CreateMap<Person, PersonDTO>(); 

      cfg.CreateMap<PersonDTO, Person>() 
       .OnlyIfClientToServer(); 
      //.ForSourceMember(src => src.Name, src => src.Ignore()) // DOESN'T WORK 
      //.ForMember(dst => dst.Name, dst => dst.Ignore()); // WORKS, BUT DON'T WANT EXPLICIT DEFINITION 
     }); 

そして、コンソールアプリケーション

の一部であるマッパー
 // Map Domain -> ViewModel 
     var person = new Person { Id = 1, Name = "Default Name From Domain" }; 
     var personDto = Mapper.Map<PersonDTO>(person); 

     Console.WriteLine($"Person: Id = '{person.Id}', Name = '{person.Name}'"); 
     Console.WriteLine($"PersonDTO: Name = '{personDto.Name}', Metadata = '{personDto.Metadata}'"); 
     Console.WriteLine(); 

     // Map ViewModel -> Domain 
     personDto.Name = "Changed from DTO"; 
     personDto.Metadata = "Custom metadata from DTO"; 
     Mapper.Map(personDto, person); 

     Console.WriteLine($"PersonDTO: Name = '{personDto.Name}', Metadata = '{personDto.Metadata}'"); 
     Console.WriteLine($"Person: Id = '{person.Id}', Name = '{person.Name}'"); 

     Console.ReadLine(); 

これで問題が発生しました。

私は知りたい、なぜForSourceMemberが期待どおりに機能していないのですか? ソースメンバーを使用してマッピングを防止する方法?

ForMemberマッピングのため、私のためにソースが装飾されているため、宛先は単なるドメインモデルです。他の問題は、DTOクラスのすべてのプロパティがドメインクラスにあるわけではないので、私の拡張ではもちろん例外が発生します。

コピーされたプロパティを維持する方法は、CreateMap <>ですが、すべてのプロパティを設定コードに追加したくありません。属性を設定する方が良いです。

すべてのロジックをAutoMapperエクステンションに維持するために私のソリューションを修正する方法を知っている人はいますか?

答えて

0

ForSourceMemberは検証のためのもので、ForMember-> Ignoreのようなマッピングの目的では実際には無視されません。したがって、あなたが望むものを達成するために、逆マップをForMemberで設定する必要があります。 ForAllPropertyMapsを使用して一般的な方法でこれを行うことができます。

関連する問題