AutoMapper:将集合中的项目值从一个实例合并到另一个实例

时间:2012-03-15 17:21:45

标签: asp.net collections merge automapper

我试图弄清楚如何使用AutoMapper合并两个复杂的对象实例。父对象有一个属性,它是子对象的集合:

public class Parent
{
    public List<Child> Children { get; set; }
}

public class Child
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

我有两个Parent实例:

var parentOne = new Parent()
{
    Children = new List<Child>() { new Child() { A = "A", B = "B", C = "C" } }
};

var parentTwo = new Parent()
{
    Children = new List<Child>() { new Child() { C = "Updated value" } }
};

我希望能够合并parentOneparentTwo的值,而不会覆盖CparentTwo的值。我创建的地图如下:

Mapper.CreateMap<Parent, Parent>()
    .ForMember(parent => parent.Children, opt => opt.UseDestinationValue());

Mapper.CreateMap<Child, Child>()
    .ForMember(child => child.C, opt => opt.Ignore());

Mapper.Map(parentOne, parentTwo);

据我了解,除非使用UseDestinationValue()选项,否则AutoMapper将创建复杂属性的新实例。但是,在执行上述代码后,parentTwo.C等于"C"而不是"Updated value"

我认为它保留了List<Child>的实例,但它正在列表中创建Child 的新实例。不幸的是,我正在努力想出一张能够保留Child每个实例的地图。

非常感谢任何帮助!

3 个答案:

答案 0 :(得分:6)

据我所知,AutoMapper不支持这一点,我相信这是因为支持这种情况的复杂性。

UseDestinationValue确实只对您推测的集合实例起作用 - 而不是集合元素。在您的场景中,每个列表中只有一个项目(我假设)您希望子项列表对象“同步”更新(即第一个元素更新第一个元素,第二个更新第二个等等)。但是如果一个列表为3而另一个列表有5个呢?如果没有目标值,“UseDestinationValue”是什么意思?那么如何在两个子列表中选择要映射的desintation对象?在数据库场景中(听起来像是问题的基础),会有唯一的ID或某种外键,可以让你匹配子对象以知道要映射到哪一个。在这种情况下,编写一个支持UseDestinationValue的通用映射太难(IMO)。

答案 1 :(得分:4)

看起来越来越有可能我无法想出映射来处理这种情况;真的很公平,因为AutoMapper从未打算用这种方式合并值。

所以我已经在循环中手动处理集合。出于本答案的目的,我假设Parent.A是唯一标识属性的:

Mapper.CreateMap<Parent, Parent>()
    .ForMember(parent => parent.Children, opt => opt.Ignore());

Mapper.CreateMap<Child, Child>()
    .ForMember(child => child.C, opt => opt.Ignore());

Mapper.Map(parentOne, parentTwo);

foreach (var childTwo in parentTwo.Children)
{
    var childOne = parentOne.Children.Where(child => child.A == childTwo.A).Single();
    Mapper.Map(childOne, childTwo);
}

答案 2 :(得分:2)

从ViewModel(DTO)映射到EntitySet时遇到了同样的问题。这是我为解决问题而编写的方法。它将ViewModel集合同步到实体集合中。

在您的automapper映射中,您需要完全忽略该集合。

public void SyncronizeEntitySet<TViewModel, TEntity>(IEnumerable<TViewModel> modelSet, ICollection<TEntity> entitySet,
        Func<TViewModel, int> sourceKey, Func<TEntity, int> destinationKey, Action<TEntity> setParentKey)
    where TViewModel : class, new()
    where TEntity : class, new()
{
    var toDelete = new List<TEntity>();
    foreach (var entityItem in entitySet)
    {
        var modelItem = modelSet.FirstOrDefault(i => sourceKey(i) == destinationKey(entityItem));
        if (modelItem == null)
        {
            toDelete.Add(entityItem);
        }
        else
        {
            Mapper.Map(modelItem, entityItem);
        }
    }

    toDelete.ForEach(i => Delete(i));

    foreach (var modelItem in modelSet)
    {
        if (sourceKey(modelItem) == 0)
        {
            var entityItem = Mapper.Map<TEntity>(modelItem);
            setParentKey(entityItem);
            Add(entityItem);
        }
    }
}