AutoMapper和继承 - 如何映射?

时间:2010-07-27 22:49:09

标签: 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;
}

我有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

中定义的属性

5 个答案:

答案 0 :(得分:74)

您需要创建与您的域类匹配的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();

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

答案 1 :(得分:4)

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

但是要具体说明继承并在扩展基类时避免冗余映射子句,可以使用.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!

然后你可以这样做:

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。自动映像为您处理此问题。)

See this article关于.Include

1 实际上我想知道代码是否按照问题编写?

答案 2 :(得分:2)

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

并用

替换他示例中的ConvertUsing
  expression.CreateMap<DTO, NumberBase>()
            .ConvertUsing(new NumberConverter());

答案 3 :(得分:0)

我这样做是为了解决问题

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

必须是更好的方法。

答案 4 :(得分:0)

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

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