setup reversemap memberlist forpath custom c# inheritance automapper

c# - reversemap - custom automapper



AutoMapper y herencia: cómo mapear? (5)

Tener este escenario:

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

Tengo un IList<Base> mis mapas son:

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!

Pero las propiedades que están en ClassA o ClassB no se asignan cuando hago esto:

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

¿Cómo puedo hacer para mapear propiedades que están definidas en ClasA y ClassB


Al menos con las versiones recientes de Automapper (> 2.0?) Su código está bien si elimina los IList<> : s de su primera instrucción CreateMap 1 . Y no tiene que crear clases de DTO específicas como sugiere @Simon en otra respuesta (a menos que eso sea lo que desea).

Pero para ser específico acerca de la herencia y para evitar cláusulas de mapeo redundantes cuando extiende la clase base, puede especificar la herencia utilizando el método .Include . Por lo tanto, si crea sus asignaciones de esta manera:

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!

entonces puedes hacer esto:

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

(Tenga en cuenta que no tiene que asignar IList específicamente. Automapper maneja esto para usted).
Vea este artículo sobre .Include .

1 En realidad, me pregunto si el código compilado como está escrito en la pregunta?


Hice esto para resolver el problema

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

Debe ser una mejor forma de hacer esto.


Necesitarás crear clases de DTO que coincidan con tus clases de dominio como esta:

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

Luego necesita cambiar sus asignaciones a esto:

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

Una vez hecho esto, entonces lo siguiente funcionará:

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

Desafortunadamente, esto significa que tienes un elenco no deseado, pero no creo que haya mucho que puedas hacer al respecto.


Para su escenario, debe usar el método IMappingExpression.ConvertUsing. Al usarlo, puede proporcionar el tipo apropiado para el objeto recién creado. Por favor, mira mi ejemplo (se ajusta bastante bien a tu situación):

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


Siguiendo con la respuesta de Eugene Gorbovoy, si está usando perfiles para configurar su AutoMapper, necesita usar un TypeConverter .

Crea un nuevo TypeConverter como este

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

y reemplaza la línea ConvertUsing en su ejemplo con

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