2012-03-15 33 views
4

我想弄清楚如何使用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,不在parentTwo中覆蓋C的值。是我所創建的映射如下:

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); 

據我瞭解,AutoMapper將除非你使用UseDestinationValue()選項來創建複雜屬性的新實例。但是,執行上面的代碼後,parentTwo.C等於"C"而不是"Updated value"

在我看來,它保留了List<Child>的實例,但它在列表中創建了Child的新實例。不幸的是,我正在努力想出一張地圖,以保持每個實例Child

任何幫助將不勝感激!

回答

6

據我所知,AutoMapper不支持這一點,我相信這是因爲支持複雜的這樣一個場景。

UseDestinationValue確實只能在您猜測的集合實例上工作 - 而不是集合元素。在你的場景中,每個列表中只有一個項目,並且(我假設)你希望子列表對象「同步」更新(即第一個元素更新第一個元素,第二個更新第二個等等)。但是如果一個列表爲3,另一個列表爲5,那該怎麼辦呢?如果沒有目標值,「UseDestinationValue」是什麼意思?你如何選擇在兩個子列表中映射哪個desintation對象?在數據庫場景中(它聽起來像是問題的基礎),將會有唯一的id或某種外鍵,這將允許您匹配子對象以知道映射到哪個對象。在這種情況下,編寫一個支持集合中各個元素的UseDestinationValue的通用映射是非常困難的(IMO)。

+0

謝謝帕特里克 - 你當然很對。我在做什麼,假設每個列表中都有相同的元素。事情發生的時候,我正在處理的是這種情況,但我認爲這對大多數情況並不適用。我可能會充實我的代碼以保持集合同步,並使AutoMapper爲匹配的元素合併屬性。 – 2012-03-20 09:27:49

+0

謝謝帕特里克:我有一個類似的問題[這裏](http://stackoverflow.com/questions/14229798/)和你的評論「但是如果一個列表有3個,另一個列表有5個呢?已經說明我想做的事可能沒有意義。 – Merenzo 2013-01-09 09:19:32

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

嗨@Gareth - 我實現您的解決方案在我的應用程序,有輕微的調整:家長對家長映射後,我叫:'.AfterMap((SRC,DEST)=> {_Gareth的的foreach loop_}) ;'。這允許我使用映射定義來保持循環,而不必在每次調用Map()後都明確地對其進行編碼。 – Merenzo 2013-01-09 11:44:57

+0

非常好 - 感謝發佈。 – 2013-01-14 14:36:23

+0

Gareth - 你也可以回答我的類似問題[這裏](http://stackoverflow.com/questions/14229798/)作爲你的方法可能是最適合我的情況。 – Merenzo 2013-01-15 04:13:14

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); 
     } 
    } 
} 
相關問題