2010-06-16 45 views
2

有人遇到過任何需要將一個對象與另一個相同類型的對象合併的情況,合併完整的對象圖。例如對於例如 如果我有一個person對象,並且一個person對象具有名字和其他姓氏,則可以通過某種方式將這兩個對象合併到一個對象中。合併.net對象圖

public class Person 
{ 
    public Int32 Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

public class MyClass 
{ 
    //both instances refer to the same person, probably coming from different sources 
    Person obj1 = new Person(); obj1.Id=1; obj1.FirstName = "Tiju"; 
    Person obj2 = new Person(); ojb2.Id=1; obj2.LastName = "John"; 


    //some way of merging both the object 
    obj1.MergeObject(obj2); //?? 
    //obj1.Id // = 1 
    //obj1.FirstName // = "Tiju" 
    //obj1.LastName // = "John" 
} 

我遇到過這種類型的需求,我寫了一個擴展方法來做同樣的事情。

public static class ExtensionMethods 
{ 
    private const string Key = "Id"; 



    public static IList MergeList(this IList source, IList target) 
    { 
     Dictionary itemData = new Dictionary(); 

     //fill the dictionary for existing list 
     string temp = null; 
     foreach (object item in source) 
     { 
      temp = GetKeyOfRecord(item); 
      if (!String.IsNullOrEmpty(temp)) 
       itemData[temp] = item; 
     } 

     //if the same id exists, merge the object, otherwise add to the existing list. 

     foreach (object item in target) 
     { 
      temp = GetKeyOfRecord(item); 
      if (!String.IsNullOrEmpty(temp) && itemData.ContainsKey(temp)) 
       itemData[temp].MergeObject(item); 
      else 
       source.Add(item); 
     } 

     return source; 
    } 




    private static string GetKeyOfRecord(object o) 
    { 
     string keyValue = null; 
     Type pointType = o.GetType(); 
     if (pointType != null) 
      foreach (PropertyInfo propertyItem in pointType.GetProperties()) 
      { 
       if (propertyItem.Name == Key) 
       { keyValue = (string)propertyItem.GetValue(o, null); } 
      } 
     return keyValue; 
    } 




    public static object MergeObject(this object source, object target) 
    { 
     if (source != null && target != null) 
     { 
      Type typeSource = source.GetType(); 
      Type typeTarget = target.GetType(); 

      //if both types are same, try to merge 
      if (typeSource != null && typeTarget != null && typeSource.FullName == typeTarget.FullName) 
       if (typeSource.IsClass && !typeSource.Namespace.Equals("System", StringComparison.InvariantCulture)) 
       { 
        PropertyInfo[] propertyList = typeSource.GetProperties(); 

        for (int index = 0; index < propertyList.Length; index++) 
        { 
         Type tempPropertySourceValueType = null; 
         object tempPropertySourceValue = null; 
         Type tempPropertyTargetValueType = null; 
         object tempPropertyTargetValue = null; 

         //get rid of indexers 
         if (propertyList[index].GetIndexParameters().Length == 0) 
         { 
          tempPropertySourceValue = propertyList[index].GetValue(source, null); 
          tempPropertyTargetValue = propertyList[index].GetValue(target, null); 
         } 
         if (tempPropertySourceValue != null) 
          tempPropertySourceValueType = tempPropertySourceValue.GetType(); 
         if (tempPropertyTargetValue != null) 
          tempPropertyTargetValueType = tempPropertyTargetValue.GetType(); 



         //if the property is a list 
         IList ilistSource = tempPropertySourceValue as IList; 
         IList ilistTarget = tempPropertyTargetValue as IList; 
         if (ilistSource != null || ilistTarget != null) 
         { 
          if (ilistSource != null) 
           ilistSource.MergeList(ilistTarget); 
          else 
           propertyList[index].SetValue(source, ilistTarget, null); 
         } 

         //if the property is a Dto 
         else if (tempPropertySourceValue != null || tempPropertyTargetValue != null) 
         { 
          if (tempPropertySourceValue != null) 
           tempPropertySourceValue.MergeObject(tempPropertyTargetValue); 
          else 
           propertyList[index].SetValue(source, tempPropertyTargetValue, null); 
         } 
        } 
       } 
     } 
     return source; 
    } 


} 

但是,當source屬性爲null時,如果target有它,它會將其複製到源代碼。 當存在不一致時,IT仍然可以改進以合併。如果FirstName =「Tiju」和FirstName =「John」

讚賞的所有內容。

感謝 TJ

+1

我不明白你的問題是什麼。你想評論你的代碼如何改進?或者代碼有問題嗎? – 2010-06-16 09:20:48

+0

我的問題是,有沒有其他人面對這樣的要求,他們提出了什麼想法。 – 2010-06-17 06:13:16

回答

0

你的代碼看起來非常難吃。你確定你需要這樣一個通用的模型嗎?您是否打算單獨合併多個Person對象?如果是這樣,合併要求是否完全一樣?如果您需要合併其他類型,則更改是必須以不同方式合併。也許你應該選擇一個沒有反思的設計。下面是一個其他的想法:

public interface IMergable<T> 
{ 
    T MergeWith(T other); 
} 

public interface IEntity 
{ 
    object EntityId { get; } 
} 

public class Person : IMergable<Person>, IEntity 
{ 
    public int Id { get; set; } 

    object IEntity.EntityId { get { return this.Id; } } 

    public Person MergeWith(Person other) 
    { 
     var mergedperson = new Person(); 

     // Do merging here, and throw InvalidOperationException 
     // when objects can not be merged. 

     return mergedPerson; 
    } 
} 

YUR擴展方法會是這個樣子:

public static IEnumerable<T> MergeList<T>(this IEnumerable<T> left, 
    IEnumerable<T> right) 
    where T : IMergable<T>, IEntity 
{ 
    return 
     from leftEntity in left 
     from rightEntity in right 
     where leftEntity.EntityId.Equals(rightEntity.EntityId) 
     select leftEntity.MergeWith(rightEntity); 
} 
+0

不錯的設計。有沒有理由「IEntity.EntityId」是對象類型而不是int?我猜這不會工作,因爲底層類型實際上是一個值類型,因爲'leftEntity.EntityId == rightEntity.EntityId'是盒裝值參考的比較,總是評估爲false。 – 2010-06-16 10:42:52

+0

@ 0xA3:好點。我解決這個問題。我將該行更改爲'leftEntity.EntityId.Compare(rightEntity.EntityId)'。爲什麼'Entityid'是一個對象,因爲我不知道OP的實體是否總是類型爲int的地方。如果是這樣的話,代碼會變得更簡單一些。 – Steven 2010-06-16 10:46:42

+1

您可以使'IEquatable '類型的'EntityId'並使用'Equals()'方法。請注意,'object'沒有'Compare()'方法。 – 2010-06-16 10:55:02

0

尼斯辦法史蒂芬,

是的,我將合併相同類型的對象。 是的,我需要一個更通用的模型。

原因是我的架構不允許我繼承關係,因爲這些對象將是簡單的DTO的只是發送到UI clint綁定到的輕量級對象。所以這種關係毫無疑問。

這個邏輯在客戶端是必需的,而且確切的說,我的數據來自不同的數據源,比如來自system1的數據和來自system2的數據,兩者都會返回具有不同值的相同類型對象,我只是想要一種合併兩個對象的方式,以便將完整的信息顯示給客戶端UI。用戶界面不會有任何保存回系統。代碼應該是通用的,不應該包含任何類型特定的合併邏輯。 當一個屬性在一個對象中爲空而沒有在另一個對象中時沒有問題。當兩者具有相同屬性的不同值時出現問題。

爲例如一位消息人士說,住在美國的人和其他人說住在法國的人。在這種情況下該怎麼做。雖然這基本上是對廣管局的一個問題,但是對於每個來源可能都有某種可信度指數,機制可能會起作用。

如果Type包含其他Dto的列表,則MergeList不僅會合並匹配記錄,還會將不匹配的記錄添加到列表中。

上使代碼更好的:-)

+0

這是否意味着即使在這些DTO上實現接口也不成問題?那麼用一個屬性來裝飾它們是好的嗎?或者是否有任何改變? (它們是否自動生成?)我提供的'MergeList'方法有點幼稚,它會進行連接。你只需要在左邊記錄,只記錄在右邊? – Steven 2010-06-17 07:31:09

+0

你是對的,沒有接口,而且它們是自動生成的。由於這些類型通過WCF Web服務公開,所以我無法使用屬性,因爲這些DTO被創建爲客戶端的代理(我的案例silverlight)。糾正我,如果我錯了,我認爲.NET特定的信息,如屬性不能傳遞給代理。 我希望所有可能的記錄,只要他們有不同的關鍵 – 2010-06-17 13:17:10

0

沒有直接回答任何評論,但匿名 類型是值得思考的:

return new { 
    FirstName = "Peter", 
    LastName = "Pen" 
}; 

欲瞭解更多信息,this article很好地解釋特徵;還有更多關於msdnwikipedia

綜上所述:

  • 他們只能從對象繼承,
  • 他們唯一成員是每一個匹配的讀/寫性能的私人領域。
+0

請你詳細說明一下.. – 2010-06-17 13:10:32