Сопоставление одного исходного класса с несколькими производными классами с помощью automapper
Предположим, что у меня есть исходный класс:
public class Source
{
//Several properties that can be mapped to DerivedBase and its subclasses
}
И некоторые целевые классы:
public class DestinationBase
{
//Several properties
}
public class DestinationDerived1 : DestinationBase
{
//Several properties
}
public class DestinationDerived2 : DestinationBase
{
//Several properties
}
Затем я хочу, чтобы производные целевые классы наследовали конфигурацию automapper базового класса, потому что я не хочу повторять его, есть ли способ достичь этого?
Mapper.CreateMap<Source, DestinationBase>()
.ForMember(...)
// Many more specific configurations that should not have to be repeated for the derived classes
.ForMember(...);
Mapper.CreateMap<Source, DestinationDerived1 >()
.ForMember(...);
Mapper.CreateMap<Source, DestinationDerived2 >()
.ForMember(...);
Когда я пишу его так, он не использует базовые сопоставления вообще, и включение, похоже, не помогает мне.
Изменить:
Это то, что я получаю:
public class Source
{
public string Test { get; set; }
public string Test2 { get; set; }
}
public class DestinationBase
{
public string Test3 { get; set; }
}
public class DestinationDerived1 : DestinationBase
{
public string Test4 { get; set; }
}
public class DestinationDerived2 : DestinationBase
{
public string Test5 { get; set; }
}
Mapper.CreateMap<Source, DestinationBase>()
.ForMember(d => d.Test3, e => e.MapFrom(s => s.Test))
.Include<Source, DestinationDerived1>()
.Include<Source, DestinationDerived2>();
Mapper.CreateMap<Source, DestinationDerived1>()
.ForMember(d => d.Test4, e => e.MapFrom(s => s.Test2));
Mapper.CreateMap<Source, DestinationDerived2>()
.ForMember(d => d.Test5, e => e.MapFrom(s => s.Test2));
AutoMapper.AutoMapperConfigurationException:
Найдены неподключенные члены. Просмотрите типы и элементы ниже.
Добавить настраиваемое выражение отображения, игнорировать, добавить настраиваемый преобразователь или изменить тип источника/назначения
Источник → DestinationDerived1 (список членов назначения)
Test3
Ответы
Ответ 1
Включить производные сопоставления в базовое сопоставление:
Mapper.CreateMap<Source, DestinationBase>()
.ForMember(d => d.Id, op => op.MapFrom(s => s.Id)) // you can remove this
.Include<Source, DestinationDerived1>()
.Include<Source, DestinationDerived2>();
Mapper.CreateMap<Source, DestinationDerived1>()
.ForMember(d => d.Name, op => op.MapFrom(s => s.Text))
.ForMember(d => d.Value2, op => op.MapFrom(s => s.Amount));
Mapper.CreateMap<Source, DestinationDerived2>()
.ForMember(d => d.Value, op => op.MapFrom(s => s.Amount));
Использование:
Mapper.AssertConfigurationIsValid();
var s = new Source() { Id = 2, Amount = 10M, Text = "foo" };
var d1 = Mapper.Map<DestinationDerived1>(s);
var d2 = Mapper.Map<DestinationDerived2>(s);
См. Сопоставление наследования в вики AutoMapper.
UPDATE: полный код классов, который работает так, как должен.
public class Source
{
public int Id { get; set; }
public string Text { get; set; }
public decimal Amount { get; set; }
}
public class DestinationBase
{
public int Id { get; set; }
}
public class DestinationDerived1 : DestinationBase
{
public string Name { get; set; }
public decimal Value2 { get; set; }
}
public class DestinationDerived2 : DestinationBase
{
public decimal Value { get; set; }
}
ОБНОВЛЕНИЕ (обход ошибки AutoMapper):
public static class Extensions
{
public static IMappingExpression<Source, TDestination> MapBase<TDestination>(
this IMappingExpression<Source, TDestination> mapping)
where TDestination: DestinationBase
{
// all base class mappings goes here
return mapping.ForMember(d => d.Test3, e => e.MapFrom(s => s.Test));
}
}
И все сопоставления:
Mapper.CreateMap<Source, DestinationBase>()
.Include<Source, DestinationDerived1>()
.Include<Source, DestinationDerived2>()
.MapBase();
Mapper.CreateMap<Source, DestinationDerived1>()
.MapBase()
.ForMember(d => d.Test4, e => e.MapFrom(s => s.Test2));
Mapper.CreateMap<Source, DestinationDerived2>()
.MapBase()
.ForMember(d => d.Test5, e => e.MapFrom(s => s.Test2));
Ответ 2
Для Automapper 8.0.
В текущей версии появился новый метод IncludeAllDerived
Вот рабочий пример:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, DestinationBase>()
.ForMember(dest => dest.Test3, opt => opt.MapFrom(src => src.Test))
.IncludeAllDerived();
cfg.CreateMap<Source, DestinationDerived1>()
.ForMember(dest => dest.Test4, opt => opt.MapFrom(src => src.Test2));
cfg.CreateMap<Source, DestinationDerived2>()
.ForMember(dest => dest.Test5, opt => opt.MapFrom(src => src.Test2));
});
var mapper = config.CreateMapper();
var source = new Source { Test = "SourceTestProperty", Test2 = "SourceTest2Property" };
var d1 = mapper.Map<DestinationDerived1>(source);
var d2 = mapper.Map<DestinationDerived2>(source);
Assert.Equal("SourceTestProperty", d1.Test3);
Assert.Equal("SourceTest2Property", d1.Test4);
Assert.Equal("SourceTestProperty", d2.Test3);
Assert.Equal("SourceTest2Property", d2.Test5);
Ответ 3
NB! Для тех, у кого проблемы с производными интерфейсами. AutoMapper не поддерживает регистрацию на основе производных интерфейсов. Только классы обрабатываются.
Чтобы это работало, вы должны изменить ссылку на тип для CreateMap на класс вместо интерфейса.
Пример:
interface Interface1 {}
class Class1: Interface1 {}
interface Interface2: Interface1 {}
class Class2: Class1, Interface2 {}
CreateMap<OtherClass, Interface1>().IncludeAllDerived();
CreateMap<OtherClass, Interface2>();
Любое сопоставление с Interface2 будет использовать только первый CreateMap. Вам нужно будет идентифицировать второй как
CreateMap<OtherClass, Class2>();