有这种情况:
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
答案 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; }
}
}