AutoMapper 覆盖递归类型
AutoMapper overwrites recursive type
我有一个看起来有点像这样的 Dto:
class TypeDto
{
int Id {get; set;}
string Name {get; set;}
string DisplayName {get; set;}
IEnumerable<TypeDto> Children {get; set;}
}
现在我需要从两个不同的来源映射到它。那是因为其中一个包含 Name
而另一个包含 DisplayName
。所以类型:
class Type1
{
int Id {get; set;}
string Name {get; set;}
IEnumerable<Type1> Children {get; set;}
}
class Type2
{
int Id {get; set;}
string DisplayName {get; set;}
IEnumerable<Type2> Nested {get; set;}
}
请注意 Children
/Nested
可枚举的名称差异。
现在我会做的地图:
config.CreateMap<Type1, TypeDto>();
config.CreateMap<Type2, TypeDto>()
.ForMember(dest => dest.Children, opts => opts.MapFrom(src => src.Nested));
var dto = _mapper.Map<TypeDto>(type1Instance);
_mapper.Map(type2Instance, dto);
第一个映射按预期工作,递归映射 children,填充 Id
和 Name
字段并使 DisplayName
等于 null
到处。但是,第二个映射正确地为根 object 填充了 DisplayName
,但随后在其 children 中,它使 Name
字段无效。例如:
var type1Instance = new Type1
{
Id = 1,
Name = "root",
Children = new[] { new Type1
{
Id = 2,
Name = "child"
}}
};
var type2Instance = new Type2
{
Id = 1,
DisplayName = "Root",
Children = new[] { new Type2
{
Id = 2,
DisplayName = "Child"
}}
};
映射以下实例后,结果的字段设置为:
Id = 1,
Name = "root",
DisplayName = "Root",
Children = { TypeDto { Id = 2, Name = null, DisplayName = "Child", Children = null } }
所以 child 的 Name
被取消了,这不是我想要的。显然,我希望它是 "child"
。我应该如何配置映射器以获得所需的行为?
我无法更改 Type1
或 Type2
类,它们来自外部 API。
AutoMapper 版本为 6.2.1,.NET Framework 4.5.1。
这是一种可能的解决方案:
config.CreateMap<Type2, TypeDto>()
.ForMember(dest => dest.Children, opts => opts.Ignore())
.AfterMap((d,e) => AddNestedChildren(d, e));
private void AddNestedChildren(Type2 type, TypeDto dto)
{
foreach (var child in type.Nested)
{
var childDto = dto.Children.SingleOrDefault(c => c.Id == child.Id);
// keep old properties
Mapper.Map(child, childDto);
}
}
如评论中所述,集合默认无效。我可以找到解决您问题的一种方法是使用 AfterMap
手动循环遍历 children/nested 集合,为另一个集合找到相应的子集合(通过 Id 或任何其他 属性 你找到相关的),然后手动映射它。
我基本上使用了与相同的想法。
摘自 Lucian Bargaoanu 的评论。
AutoMapper.Collection
包解决了我的问题。所需要的只是将此语句添加到配置中:
config.AddCollectionMappers();
然后在我的两个地图上定义 EqualityComparison:
config.CreateMap<Type1, TypeDto>()
.EqualityComparison((src, dest) => src.Id == dest.Id);
config.CreateMap<Type2, TypeDto>()
.EqualityComparison((src, dest) => src.Id == dest.Id)
.ForMember(dest => dest.Children, opts => opts.MapFrom(src => src.Nested));
之后,集合将得到正确更新。引用来自 here 的文档,第二个映射,即
_mapper.Map(type2Instance, dto);
现在将递归映射到任何具有匹配 Id
的集合成员,添加到集合并映射 type2Instance.Nested
中未出现在 dto.Children
集合中的任何项目并删除任何项目dto.Children
包含,但 type2Instance.Nested
不包含。
我有一个看起来有点像这样的 Dto:
class TypeDto
{
int Id {get; set;}
string Name {get; set;}
string DisplayName {get; set;}
IEnumerable<TypeDto> Children {get; set;}
}
现在我需要从两个不同的来源映射到它。那是因为其中一个包含 Name
而另一个包含 DisplayName
。所以类型:
class Type1
{
int Id {get; set;}
string Name {get; set;}
IEnumerable<Type1> Children {get; set;}
}
class Type2
{
int Id {get; set;}
string DisplayName {get; set;}
IEnumerable<Type2> Nested {get; set;}
}
请注意 Children
/Nested
可枚举的名称差异。
现在我会做的地图:
config.CreateMap<Type1, TypeDto>();
config.CreateMap<Type2, TypeDto>()
.ForMember(dest => dest.Children, opts => opts.MapFrom(src => src.Nested));
var dto = _mapper.Map<TypeDto>(type1Instance);
_mapper.Map(type2Instance, dto);
第一个映射按预期工作,递归映射 children,填充 Id
和 Name
字段并使 DisplayName
等于 null
到处。但是,第二个映射正确地为根 object 填充了 DisplayName
,但随后在其 children 中,它使 Name
字段无效。例如:
var type1Instance = new Type1
{
Id = 1,
Name = "root",
Children = new[] { new Type1
{
Id = 2,
Name = "child"
}}
};
var type2Instance = new Type2
{
Id = 1,
DisplayName = "Root",
Children = new[] { new Type2
{
Id = 2,
DisplayName = "Child"
}}
};
映射以下实例后,结果的字段设置为:
Id = 1,
Name = "root",
DisplayName = "Root",
Children = { TypeDto { Id = 2, Name = null, DisplayName = "Child", Children = null } }
所以 child 的 Name
被取消了,这不是我想要的。显然,我希望它是 "child"
。我应该如何配置映射器以获得所需的行为?
我无法更改 Type1
或 Type2
类,它们来自外部 API。
AutoMapper 版本为 6.2.1,.NET Framework 4.5.1。
这是一种可能的解决方案:
config.CreateMap<Type2, TypeDto>()
.ForMember(dest => dest.Children, opts => opts.Ignore())
.AfterMap((d,e) => AddNestedChildren(d, e));
private void AddNestedChildren(Type2 type, TypeDto dto)
{
foreach (var child in type.Nested)
{
var childDto = dto.Children.SingleOrDefault(c => c.Id == child.Id);
// keep old properties
Mapper.Map(child, childDto);
}
}
如评论中所述,集合默认无效。我可以找到解决您问题的一种方法是使用 AfterMap
手动循环遍历 children/nested 集合,为另一个集合找到相应的子集合(通过 Id 或任何其他 属性 你找到相关的),然后手动映射它。
我基本上使用了与
摘自 Lucian Bargaoanu 的评论。
AutoMapper.Collection
包解决了我的问题。所需要的只是将此语句添加到配置中:
config.AddCollectionMappers();
然后在我的两个地图上定义 EqualityComparison:
config.CreateMap<Type1, TypeDto>()
.EqualityComparison((src, dest) => src.Id == dest.Id);
config.CreateMap<Type2, TypeDto>()
.EqualityComparison((src, dest) => src.Id == dest.Id)
.ForMember(dest => dest.Children, opts => opts.MapFrom(src => src.Nested));
之后,集合将得到正确更新。引用来自 here 的文档,第二个映射,即
_mapper.Map(type2Instance, dto);
现在将递归映射到任何具有匹配 Id
的集合成员,添加到集合并映射 type2Instance.Nested
中未出现在 dto.Children
集合中的任何项目并删除任何项目dto.Children
包含,但 type2Instance.Nested
不包含。