AutoMapper и наследование - как карта? - PullRequest
38 голосов
/ 28 июля 2010

Имеют такой сценарий:

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

Как я могу сделать для сопоставления свойств, которые определены в ClasA и ClassB

Ответы [ 5 ]

74 голосов
/ 13 октября 2010

Вам нужно будет создать классы 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();

К сожалению, это означает, что у вас есть нежелательный актерский состав, но я не думаю, что с этим можно что-то сделать.

4 голосов
/ 15 апреля 2014

По крайней мере с последними версиями Automapper (> 2.0?) Ваш код будет в порядке, если вы удалите IList<>: s из вашего первого CreateMap оператора 1 .И вам не нужно создавать конкретные классы 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. Automapper обрабатывает этодля вас.)
См. эту статью о .Include.

1 На самом деле мне интересно, скомпилирован ли код, как написано в вопросе?

2 голосов
/ 22 февраля 2017

Исходя из ответа Евгения Горбового, если вы используете профили для настройки вашего 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());
0 голосов
/ 20 июля 2016

Для вашего сценария вы должны использовать метод 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; }
    }
}
0 голосов
/ 29 июля 2010

Я сделал это, чтобы решить проблему

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

Должно быть, лучший способ сделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...