代码之家  ›  专栏  ›  技术社区  ›  V0ldek

automapper覆盖递归类型

  •  3
  • V0ldek  · 技术社区  · 6 年前

    我有一个类似这样的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);
    

    第一个映射按预期工作,以递归方式映射子映射,填充 Id 名字 字段和离开 显示名称 等于 null 到处都是。然而,第二张地图填充了 显示名称 对于根对象,但在其子对象中,它会使 名字 字段。例如:

    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" 很明显。我应该如何配置映射器以获得想要的行为?

    我不能改变 Type1 Type2 类,它们来自外部API。

    automapper的版本是6.2.1,.NET Framework 4.5.1。

    2 回复  |  直到 6 年前
        1
  •  2
  •   V0ldek    6 年前

    摘自卢西安·巴加努的评论。

    这个 AutoMapper.Collection 包裹解决了我的问题。只需将此语句添加到配置:

    config.AddCollectionMappers();
    

    然后在两张地图上定义相等比较:

    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 收集并删除 D至儿童 包含,但 类型2Instance.Nested 没有。

        2
  •  1
  •   Alisson Reinaldo Silva    6 年前

    我认为这是一个可能的解决方案:

    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 为了手动循环子集合/嵌套集合,请查找其他集合的对应子集合(通过ID或您找到的任何其他相关属性),然后手动映射它。

    我基本上使用了 my answer to this question .