我试图弄清楚如何使用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" } }
};
我希望能够合并parentOne
到parentTwo
的值,而不会覆盖C
中parentTwo
的值。我创建的地图如下:
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
每个实例的地图。
非常感谢任何帮助!
答案 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);
}
}
}