2010-07-27 2 views
34

Есть такой сценарий: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); 

Как это сделать, чтобы отобразить свойства, определенные в ClasA и ClassB

ответ

64

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

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

+0

Спасибо! Хорошее решение – Gringo

+0

Не стоит беспокоиться Гринго, рад, что это помогло. – Simon

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

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

2

По крайней мере, с последними версиями Automapper (?> 2,0) ваш код в порядке, если вы удалите IList<>: S вашего первого CreateMap заявления . И вам не нужно создавать определенные классы 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 обрабатывает это для вас.).
See this article о .Include.

На самом деле я задаюсь вопросом, скомпилирован ли код, как написано в вопросе?

+0

Привет. Спасибо за ответ. Да, он компилирует – Gringo

+0

. Это должен быть фактически правильный ответ для сопоставления наследования. Благодаря! –

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

Исходя из ответа Евгения 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()); 
Смежные вопросы