AutoMapper和继承 - 如何映射?

Gri*_*ngo 38 c# inheritance automapper

有这种情况:

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;
}
Run Code Online (Sandbox Code Playgroud)

我的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!
Run Code Online (Sandbox Code Playgroud)

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

IList<DTO>= AutoMapper.Mapper.Map<IList<Base>,IList<DTO>>(baseList);
Run Code Online (Sandbox Code Playgroud)

如何映射在ClasA和中定义的属性ClassB

Sim*_*mon 76

您需要创建与您的域类匹配的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; }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要将映射更改为:

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

        Mapper.CreateMap<ClassA, DTO_A>();

        Mapper.CreateMap<ClassB, DTO_B>();

        Mapper.AssertConfigurationIsValid();
Run Code Online (Sandbox Code Playgroud)

完成后,以下内容将起作用:

        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();
Run Code Online (Sandbox Code Playgroud)

不幸的是,这确实意味着你有一个不受欢迎的演员阵容,但我认为你无法做到这一点.


Ulf*_*edt 6

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

但是要具体说明继承并在扩展基类时避免冗余映射子句,您可以使用.Include方法指定继承。因此,如果您像这样创建映射:

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!
Run Code Online (Sandbox Code Playgroud)

那么你可以这样做:

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();
Run Code Online (Sandbox Code Playgroud)

(请注意,您不必专门映射 IList。Automapper 会为您处理此问题。)
请参阅有关.Include.

1其实我想知道是否按照问题中编写的代码编译?


pod*_*ska 5

继 Eugene Gorbovoy 的回答之后,如果您使用配置文件来配置 AutoMapper,则需要使用TypeConverter.

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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

并将ConvertUsing其示例中的行替换为

  expression.CreateMap<DTO, NumberBase>()
            .ConvertUsing(new NumberConverter());
Run Code Online (Sandbox Code Playgroud)