2013-05-21 50 views
4

我使用AutoMapper將UI模型轉換爲POCO,我稍後使用DataContractSerializer將其序列化爲XML以保留它們之間的引用。AutoMapper,如何保持映射對象之間的引用?

問題是,映射時,這些實體之間的引用丟失

的UI類相互引用,但映射過程,使新實例爲每個參考,所以原來的關係被打破:(

讓我解釋一下:

我有2個實體型人

Person 
    { 
     List<House> OwnedHouses 
    } 

而這2個對象

約翰 誰擁有

  • House1

威爾 誰也擁有

  • House1

當AutoMapper正確映射每個人,但是當它也將House1映射爲兩個不同的實例時!

所以我有兩個House1的副本。約翰擁有他的House1(#1),Will擁有他的House1(#2)。

他們沒有鏈接了。

有什麼辦法保持原來存在的關係嗎?

謝謝。

編輯:其實我有是這樣的:

文檔中包含ChildDocuments的列表。每個ChildDocument都有一個Designables(矩形,線條,橢圓...)和一個特別設計的名爲ChildDocumentAdapter的列表,其中包含ANOOTHER ChildDocument。這是麻煩,它可以引用另一個ChildDocument。

The diagram

回答

2

如果我理解的問題,您要執行兩個獨立的映射操作 - 一個是約翰,另一個用於威爾。

@孫尼是對的。 AutoMapper不是爲了做到這一點而設計的。您致電Mapper.Map()的每個電話通常都是獨立於其他電話的。通過使用HouseListConverter的相同實例,您可以在字典中緩存所有映射的房屋。但是您必須全局註冊或將其作爲選項傳遞給要分組在一起的映射調用。這不僅僅是額外的工作,它隱藏了轉換器內部非常重要的實現細節。

如果您將John和Will映射到一個操作中,通過將它們放入集合中,輸出將是您想要的,而不需要自定義轉換器或解析器。

對於其他類似問題的人來說,這可能更容易。

public void MapListOfPeopleWithSameHouse() 
    { 
     Mapper.CreateMap<Person, PersonDTO>(); 
     Mapper.CreateMap<House, HouseDTO>(); 

     var people = new List<Person>(); 
     var house = new House() { Address = "123 Main" }; 
     people.Add(new Person() { Name = "John", Houses = new List<House>() { house } }); 
     people.Add(new Person() { Name = "Will", Houses = new List<House>() { house } }); 

     var peopleDTO = Mapper.Map<List<PersonDTO>>(people); 
     Assert.IsNotNull(peopleDTO[0].Houses); 
     Assert.AreSame(peopleDTO[0].Houses[0], peopleDTO[1].Houses[0]); 
    } 
2

雖然Automapper是不是跟這個設計的想法,這是強大到足以讓你做到這一點,利用custom type converters。您需要創建自己的轉換器從IList<House>IList<HouseDto>,並使用工廠注入它:

using System; 
using System.Collections.Generic; 
using AutoMapper; 
using NUnit.Framework; 
using SharpTestsEx; 

namespace StackOverflowExample 
{ 
    public class House 
    { 
     public string Address { get; set; } 
    } 

    public class Person 
    { 
     public IList<House> OwnedHouse { get; set; } 
    } 

    public class HouseDto 
    { 
     public string Address { get; set; } 
    } 

    public class PersonDto 
    { 
     public IList<HouseDto> OwnedHouse { get; set; } 
    } 

    [TestFixture] 
    public class AutomapperTest 
    { 
     public interface IHouseListConverter : ITypeConverter<IList<House>, IList<HouseDto>> 
     { 
     } 

     public class HouseListConverter : IHouseListConverter 
     { 
      private readonly IDictionary<House, HouseDto> existingMappings; 

      public HouseListConverter(IDictionary<House, HouseDto> existingMappings) 
      { 
       this.existingMappings = existingMappings; 
      } 

      public IList<HouseDto> Convert(ResolutionContext context) 
      { 
       var houses = context.SourceValue as IList<House>; 
       if (houses == null) 
       { 
        return null; 
       } 

       var dtos = new List<HouseDto>(); 
       foreach (var house in houses) 
       { 
        HouseDto mapped = null; 
        if (existingMappings.ContainsKey(house)) 
        { 
         mapped = existingMappings[house]; 
        } 
        else 
        { 
         mapped = Mapper.Map<HouseDto>(house); 
         existingMappings[house] = mapped; 
        } 
        dtos.Add(mapped); 
       } 

       return dtos; 
      } 
     } 

     public class ConverterFactory 
     { 
      private readonly IHouseListConverter resolver; 
      public ConverterFactory() 
      { 
       resolver = new HouseListConverter(new Dictionary<House, HouseDto>()); 
      } 

      public object Resolve(Type t) 
      { 
       return t == typeof(IHouseListConverter) ? resolver : null; 
      } 
     } 

     [Test] 
     public void CustomResolverTest() 
     { 
      Mapper.CreateMap<House, HouseDto>(); 
      Mapper.CreateMap<IList<House>, IList<HouseDto>>().ConvertUsing<IHouseListConverter>(); 
      Mapper.CreateMap<Person, PersonDto>(); 

      var house = new House {Address = "any"}; 
      var john = new Person {OwnedHouse = new List<House> {house}}; 
      var will = new Person { OwnedHouse = new List<House> { house } }; 

      var converterFactory = new ConverterFactory(); 
      var johnDto = Mapper.Map<PersonDto>(john, o=>o.ConstructServicesUsing(converterFactory.Resolve)); 
      var willDto = Mapper.Map<PersonDto>(will, o=>o.ConstructServicesUsing(converterFactory.Resolve)); 

      johnDto.OwnedHouse[0].Should().Be.SameInstanceAs(willDto.OwnedHouse[0]); 
      johnDto.OwnedHouse[0].Address.Should().Be("any"); 
     } 
    } 
} 
+0

我得檢查一下,但我認爲你明白了道理並給出了正確的解決方案。非常感謝。讓我看看我是否可以將它應用到我的上下文中(這有點複雜)。非常感謝,夥計! – SuperJMN

+0

我剛剛更新了原始帖子以顯示真實的上下文。更詳細的描述在這裏的官方AutoMapper郵件列表的主題:https://groups.google.com/forum/?fromgroups#!topic/automapper-users/htuzpz6ZKbA – SuperJMN