2016-10-05 87 views
2

TL; DR:我在多態映射方面遇到問題。我做了一個github回購測試套件,說明了我的問題。請在這裏找到它:LINK TO REPO使用AutoMapper的集合的多態映射

我正在實施保存/加載功能。爲了實現這一點,我需要確保序列化的域模型以序列化友好的方式表示。爲了達到這個目的,我創建了一套DTO,其中包含了執行有意義的保存或加載所需的最少量信息。

事情是這樣的域:

public interface IDomainType 
{ 
    int Prop0 { get; set; } 
} 

public class DomainType1 : IDomainType 
{ 
    public int Prop1 { get; set; } 
    public int Prop0 { get; set; } 
} 

public class DomainType2 : IDomainType 
{ 
    public int Prop2 { get; set; } 
    public int Prop0 { get; set; } 
} 

public class DomainCollection 
{ 
    public IEnumerable<IDomainType> Entries { get; set; } 
} 

...併爲DTO的

public interface IDto 
{ 
    int P0 { get; set; } 
} 

public class Dto1 : IDto 
{ 
    public int P1 { get; set; } 
    public int P0 { get; set; } 
} 

public class Dto2 : IDto 
{ 
    public int P2 { get; set; } 
    public int P0 { get; set; } 
} 

public class DtoCollection 
{ 
    private readonly IList<IDto> entries = new List<IDto>(); 
    public IEnumerable<IDto> Entries => this.entries; 
    public void Add(IDto entry) { this.entries.Add(entry); } 
} 

的想法是,DomainCollection表示應用程序的當前狀態。我們的目標是將DomainCollection映射到DtoCollection的結果是一個DtoCollection的實例,它包含IDto在映射到域時的適當實現。反之亦然。

這裏有一點額外的技巧是不同的具體域類型來自不同的插件程序集,所以我需要找到一個優雅的方法來讓AutoMapper(或類似的,如果你知道更好的映射框架)做繁重的工作爲了我。

使用結構圖,我已經能夠找到並加載插件中的所有配置文件,並使用它們配置應用程序IMapper。

我試圖創建這樣的配置文件...

public class CollectionMappingProfile : Profile 
{ 
    public CollectionMappingProfile() 
    { 
    this.CreateMap<IDomainType, IDto>().ForMember(m => m.P0, a => a.MapFrom(x => x.Prop0)).ReverseMap(); 

    this.CreateMap<DtoCollection, DomainCollection>(). 
     ForMember(fc => fc.Entries, opt => opt.Ignore()). 
     AfterMap((tc, fc, ctx) => fc.Entries = tc.Entries.Select(e => ctx.Mapper.Map<IDomainType>(e)).ToArray()); 

    this.CreateMap<DomainCollection, DtoCollection>(). 
     AfterMap((fc, tc, ctx) => 
       { 
        foreach (var t in fc.Entries.Select(e => ctx.Mapper.Map<IDto>(e))) tc.Add(t); 
       }); 
} 

public class DomainProfile1 : Profile 
{ 
    public DomainProfile1() 
    { 
    this.CreateMap<DomainType1, Dto1>().ForMember(m => m.P1, a => a.MapFrom(x => x.Prop1)) 
     .IncludeBase<IDomainType, IDto>().ReverseMap(); 
    } 
} 

public class DomainProfile2 : Profile 
{ 
    public DomainProfile2() 
    { 
    this.CreateMap<DomainType2, IDto>().ConstructUsing(f => new Dto2()).As<Dto2>(); 

    this.CreateMap<DomainType2, Dto2>().ForMember(m => m.P2, a => a.MapFrom(x => x.Prop2)) 
     .IncludeBase<IDomainType, IDto>().ReverseMap(); 
    } 
} 

我然後寫一個測試套件,以確保映射將像預期的那樣,當它的時間來此功能與應用程序整合。我發現每當DTO被映射到域(認爲加載)時,AutoMapper將創建IDomainType的代理,而不是將它們解析爲域。

我懷疑問題在於我的映射配置文件,但我已經用盡了人才。預先感謝您的意見。

Here's another link to the github repo

回答

0

我花了一點時間重組回購。我甚至模仿了一個核心項目和兩個插件。這確保了當測試最終開始時,我不會得到假陽性結果。

我發現該解決方案有兩個(ISH)部分。

1)我濫用AutoMapper的.ReverseMap()配置方法。我假設它會執行我正在做的任何自定義映射的相互作用。並非如此!它只做簡單的逆轉。很公平。一些SO關於它的問題/答案: 1,2

2)我沒有完全定義映射繼承。我會分解它。

2.1)我DomainProfiles遵循了這一模式:

public class DomainProfile1 : Profile 
{ 
    public DomainProfile1() 
    { 
    this.CreateMap<DomainType1, IDto>().ConstructUsing(f => new Dto1()).As<Dto1>(); 
    this.CreateMap<DomainType1, Dto1>().ForMember(m => m.P1, a => a.MapFrom(x => x.Prop1)) 
     .IncludeBase<IDomainType, IDto>().ReverseMap(); 

    this.CreateMap<Dto1, IDomainType>().ConstructUsing(dto => new DomainType1()).As<DomainType1>(); 
    } 
} 

所以,現在知道。ReverseMap()不是在這裏使用的東西,很明顯Dto1和DomainType1之間的映射定義不明確。另外,DomainType1和IDto之間的映射沒有鏈接回基本IDomainType以進行IDto映射。也是一個問題。最終結果如下:

public class DomainProfile1 : Profile 
{ 
    public DomainProfile1() 
    { 
    this.CreateMap<DomainType1, IDto>().IncludeBase<IDomainType, IDto>().ConstructUsing(f => new Dto1()).As<Dto1>(); 
    this.CreateMap<DomainType1, Dto1>().IncludeBase<DomainType1, IDto>().ForMember(m => m.P1, a => a.MapFrom(x => x.Prop1)); 

    this.CreateMap<Dto1, IDomainType>().IncludeBase<IDto, IDomainType>().ConstructUsing(dto => new DomainType1()).As<DomainType1>(); 
    this.CreateMap<Dto1, DomainType1>().IncludeBase<Dto1, IDomainType>().ForMember(m => m.Prop1, a => a.MapFrom(x => x.P1)); 
    } 
} 

現在映射的每個方向都被明確定義,並且繼承是受到尊重的。

2.2)IDomainType和IDto的最基本映射位於配置文件中,該配置文件還定義了「集合」類型的映射。這意味着一旦我將項目拆分爲模仿插件體系結構,那些僅以最新方式測試最簡單繼承的測試失敗 - 無法找到基本映射。我所要做的就是將這些映射放入他們自己的配置文件中,並在測試中使用該配置文件。這只是很好SRP

在將我自己的答案標記爲已接受的答案之前,我會將我所學到的知識應用到我的實際項目中。希望我已經得到它,並希望這將有助於其他人。

相關鏈接:

this

this one是一個很好的鍛鍊重構。我承認它是用來作爲我的榜樣的起點。所以,感謝@Olivier。