使用AutoMapper来合并对象

本文关键字:对象 合并 AutoMapper 使用 | 更新日期: 2023-09-27 18:16:43

我试图使用AutoMapper来合并来自多个对象的数据,我遇到了一些问题,我似乎无法解决。

我有一个这样的对象:

public class Parent
{
    public string Id { get; set; }
    public List<Child> Children { get; set; }
}
public class Child
{
    public string Key { get; set; }
    public int? Value1 { get; set; }
    public int? Value2 { get; set; }
    public int? Value3 { get; set; }
    public int? Value4 { get; set; }
    public int? Value5 { get; set; }
}

子属性显然不全是整型,但大多数都是可空的。

我在应用程序的两个不同层中有这些对象,所以我使用AutoMapper在它们之间进行转换:

{
    Mapper.CreateMap<Parent, ParentDTO>();
    Mapper.CreateMap<ParentDTO, Parent>();
    Mapper.CreateMap<Child, ChildDTO>();
    Mapper.CreateMap<ChildDTO, Child>();
}

转换工作得很好,我很高兴它所做的。但是,现在我需要合并多个相同类型的对象。我将有一个副本的对象,其中大部分或所有的属性填充,和另一个副本只有少数,其余为空。我希望第二个(部分)对象的任何非空属性映射到第一个(已经填写)对象中的相应字段。正如对这个答案的公认答案所述,我也应该能够使用AutoMapper来做到这一点,但它没有给出任何明确的示例。

然而,当我去执行操作时,我得到的对象与这两个对象中的任何一个都相同,而不是像我想要的那样组合在一起。

{
    var bigParent = new Parent
    {
        Id = "14",
        Children = new List<Child>
        {
            new Child
            {
                Key = "A",
                Value1 = 10,
                Value2 = 20,
                Value3 = 30,
                Value4 = 40,
                Value5 = 50
            }                   
        }
    };
    var merge = new Parent
    {
        Id = "14",
        Children = new List<Child>
        {
            new Child
            {
                Key = "A",
                Value1 = null,
                Value2 = null,
                Value3 = 100,
                Value4 = null,
                Value5 = null
            }
        }
    };
    var res = Mapper.Map(merge, bigParent);
}

我希望Child的值为10,20,100,40和50。然而,这取决于我是否把合并作为源或目标在Mapper。映射我得到null, null, 100, null, null或10,20,30,40,50。

有一种方法可以得到我的期望值吗?我认为列表是导致问题的原因,因为它不知道如何排列实体(以确定它们是否相同)。如果它回答了任何问题,我将能够通过查看一个或多个属性是否相同(在本例中为Key)来确定子记录是否相同。

使用AutoMapper来合并对象

如果您想忽略AutoMapper Profile中定义的映射中的空值,请使用:

public class MappingProfile : AutoMapper.Profile
{
  public MappingProfile()
  {
     this.CreateMap<Parent, Parent>()
         .ForAllMembers(o => o.Condition((source, destination, member) => member != null));
  }
}

Automapper能够做到这一点,您的映射器需要这样配置:

Mapper.Initialize(cfg =>
{
    // necessary if you are mapping parent to a parent
    cfg.CreateMap<Parent, Parent>()
        .ForAllMembers(options =>
        {
            options.Condition(src => src.DestinationValue == null);
        });
    // necessary if you are mapping your child to a child
    cfg.CreateMap<Child, Child>()
        .ForAllMembers(options =>
        {
            options.Condition(src => src.DestinationValue == null);
        });
});

然后你的用法如下:

var bigParent = new Parent
{
    Id = "14",
    Children = new List<Child>
    {
        new Child
        {
            Key = "A",
            Value1 = 10,
            Value2 = 20,
            Value3 = 30,
            Value4 = 40,
            Value5 = 50
        }
    }
};
var merge = new Parent
{
    Id = "14",
    Children = new List<Child>
    {
        new Child
        {
            Key = "A",
            Value1 = null,
            Value2 = null,
            Value3 = 100,
            Value4 = null,
            Value5 = null
        }
    }
};
var bigChild = new Child
{
    Key = "A",
    Value1 = 10,
    Value2 = 20,
    Value3 = 30,
    Value4 = 40,
    Value5 = 50
};
var mergeChild = new Child
{
    Key = "A",
    Value1 = null,
    Value2 = null,
    Value3 = 100,
    Value4 = null,
    Value5 = null
};
Mapper.Map(bigChild, mergeChild);
Debug.Assert(mergeChild.Value3 == 100);
Mapper.Map(bigParent, merge);
Debug.Assert(merge.Children[0].Value3 == 100);