2016-10-05 3 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; } 
} 

... и для DTOS

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

Я тогда написал набор тестов, чтобы убедиться, что отображение будет вести себя, как ожидается, когда его время, чтобы интегрировать эту функцию с приложением , Я обнаружил, что когда DTOs сопоставлялись с Domain (think Load), AutoMapper создавал бы прокси из IDomainType вместо того, чтобы разрешать их в домене.

Я подозреваю, что проблема связана с моими профилями профиля, но у меня закончились таланты. Спасибо заранее за ваш вклад.

Here's another link to the github repo

ответ

0

Я провел немного времени реорганизовать репо. Я дошел до того, чтобы подражать основному проекту и двум плагинам. Это гарантировало, что я не получу ложноположительный результат, когда тесты, наконец, начнут проходить.

Я обнаружил, что решение имеет две (иш) части.

1) Я злоупотреблял методом конфигурации AutoMapper .ReverseMap(). Я предполагал, что он выполнил бы обратное любое пользовательское сопоставление, которое я делал. Не так! Это просто делает простые развороты. Справедливо. Некоторые ответы на вопросы и ответы: 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.