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,填充 IdName 字段并使 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"。我应该如何配置映射器以获得所需的行为?

我无法更改 Type1Type2 类,它们来自外部 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 不包含。