AutoMapper和inheritance – 如何映射?

有这种情况:

Public class Base { public string Name; } Public Class ClassA :Base { public int32 Number; } Public Class ClassB :Base { Public string Description;} Public Class DTO { public string Name; public int32 Number; Public string Description; } 

我有一个IList<Base>我的地图是:

 AutoMapper.Mapper.CreateMap<IList<Base>, IList<DTO>>() .ForMember(dest => dest.Number, opt => opt.Ignore()) .ForMember(dest => dest.Description, opt => opt.Ignore()); AutoMapper.Mapper.CreateMap<ClassA, DTo>() .ForMember(dest => dest.Description, opt => opt.Ignore()); AutoMapper.Mapper.CreateMap<ClassB, DTO>() .ForMember(dest => dest.Number, opt => opt.Ignore()) Mapper.AssertConfigurationIsValid(); //Is OK! 

但是,当我这样做时,ClassA或ClassB中的属性未映射:

 IList<DTO>= AutoMapper.Mapper.Map<IList<Base>,IList<DTO>>(baseList); 

我怎样才能映射在ClasAClassB中定义的属性

您将需要创build与您的域类匹配的DTO类,如下所示:

 public class DTO { public string Name; } public class DTO_A : DTO { public int Number { get; set; } } public class DTO_B : DTO { public string Description { get; set; } } 

然后你需要改变你的映射到这个:

  Mapper.CreateMap<Base, DTO>() .Include<ClassA, DTO_A>() .Include<ClassB, DTO_B>(); Mapper.CreateMap<ClassA, DTO_A>(); Mapper.CreateMap<ClassB, DTO_B>(); Mapper.AssertConfigurationIsValid(); 

一旦完成,那么以下将起作用:

  var baseList = new List<Base> { new Base {Name = "Base"}, new ClassA {Name = "ClassA", Number = 1}, new ClassB {Name = "ClassB", Description = "Desc"}, }; var test = Mapper.Map<IList<Base>,IList<DTO>>(baseList); Console.WriteLine(test[0].Name); Console.WriteLine(test[1].Name); Console.WriteLine(((DTO_A)test[1]).Number); Console.WriteLine(test[2].Name); Console.WriteLine(((DTO_B)test[2]).Description); Console.ReadLine(); 

不幸的是,这意味着你有一个不需要的演员,但我不认为你可以做的事情很多。

至less在最近的Automapper版本(> 2.0?)中,如果删除第一个CreateMap语句的IList<> :s,则代码是可以的。 你不必像@Simon在另一个答案中所build议的那样创build特定的DTO类(除非这是你想要的)。

但要具体说明inheritance,并在扩展基类时避免重复的映射子句,可以使用.Include方法指定inheritance。 所以,如果你创build这样的映射:

 Mapper.CreateMap<Base, DTO>() .Include<ClassA, DTO>() .Include<ClassB, DTO>() .ForMember(dest => dest.Description, opt => opt.Ignore()) .ForMember(dest => dest.Number, opt => opt.Ignore()); Mapper.CreateMap<ClassA, DTO>() .ForMember(dest => dest.Description, opt => opt.Ignore()); Mapper.CreateMap<ClassB, DTO>() .ForMember(dest => dest.Number, opt => opt.Ignore()); Mapper.AssertConfigurationIsValid(); //Is OK! 

那么你可以这样做:

 var baseList = new List<Base> { new Base {Name = "Base"}, new ClassA {Name = "ClassA", Number = 1}, new ClassB {Name = "ClassB", Description = "Desc"}, }; var test = Mapper.Map<IList<Base>, IList<DTO>>(baseList); Console.WriteLine(test[0].Name); Console.WriteLine(test[1].Name); Console.WriteLine((test[1]).Number); Console.WriteLine(test[2].Name); Console.WriteLine((test[2]).Description); Console.ReadLine(); 

(请注意,您不必专门映射IList,Automapper会为您处理。)
看到这篇文章 .Include

1 其实我想知道这个代码是不是写在这个问题上?

继Eugene Gorbovoy的回答之后,如果您使用configuration文件来configurationAutoMapper,则需要使用TypeConverter

像这样创build一个新的TypeConverter

  public class NumberConverter : ITypeConverter<DTO, NumberBase> { public NumberBase Convert(DTO source, NumberBase destination, ResolutionContext context) { if (source.Id % 2 == 0) { return context.Mapper.Map<EvenNumber>(source); } else { return context.Mapper.Map<OddNumber>(source); } } } 

并用他的示例replaceConvertUsing

  expression.CreateMap<DTO, NumberBase>() .ConvertUsing(new NumberConverter()); 

我这样做是为了解决这个问题

 IList<DTO> list1 = AutoMapper.Mapper.Map<IList<ClassA>,IList<DTO>>(baseList.OfType<ClassA>().ToList()); IList<DTO> list2 = AutoMapper.Mapper.Map<IList<ClassB>,IList<DTO>>(baseList.OfType<ClassB>().ToList()); list = list1.Union(list2); persons.OfType<T>().ToList() 

必须是一个更好的方法来做到这一点。

对于你的场景,你必须使用IMappingExpression.ConvertUsing方法。 通过使用它,您可以为新创build的对象提供适当的types。 请看看我的例子(非常适合你的情况):

 using System; using System.Linq; using AutoMapper; namespace ConsoleApplication19 { internal class Program { private static void Main(string[] args) { //mapping Mapper.Initialize(expression => { expression.CreateMap<DTO, NumberBase>() .ForMember(@class => @class.IdOnlyInDestination, configurationExpression => configurationExpression.MapFrom(dto => dto.Id)) .ConvertUsing(dto =>//here is the function that creates appropriate object { if (dto.Id%2 == 0) return Mapper.Map<EvenNumber>(dto); return Mapper.Map<OddNumber>(dto); }); expression.CreateMap<DTO, OddNumber>() .IncludeBase<DTO, NumberBase>(); expression.CreateMap<DTO, EvenNumber>() .IncludeBase<DTO, NumberBase>(); }); //initial data var arrayDto = Enumerable.Range(0, 10).Select(i => new DTO {Id = i}).ToArray(); //converting var arrayResult = Mapper.Map<NumberBase[]>(arrayDto); //output foreach (var resultElement in arrayResult) { Console.WriteLine($"{resultElement.IdOnlyInDestination} - {resultElement.GetType().Name}"); } Console.ReadLine(); } } public class DTO { public int Id { get; set; } public int EvenFactor => Id%2; } public abstract class NumberBase { public int Id { get; set; } public int IdOnlyInDestination { get; set; } } public class OddNumber : NumberBase { public int EvenFactor { get; set; } } public class EvenNumber : NumberBase { public string EventFactor { get; set; } } }