2010-03-30 119 views
4

......除了明顯的循環列表和骯髒的大案例語句之外!有沒有辦法將鍵/值對列表轉換爲數據傳輸對象

我已經在我的頭上翻了幾個Linq查詢,但沒有任何東西似乎得到任何接近。

下面是一個例子DTO是否有幫助:

class ClientCompany 
    { 
     public string Title { get; private set; } 
     public string Forenames { get; private set; } 
     public string Surname { get; private set; } 
     public string EmailAddress { get; private set; } 
     public string TelephoneNumber { get; private set; } 
     public string AlternativeTelephoneNumber { get; private set; } 
     public string Address1 { get; private set; } 
     public string Address2 { get; private set; } 
     public string TownOrDistrict { get; private set; } 
     public string CountyOrState { get; private set; } 
     public string PostCode { get; private set; } 
    } 

我們有,我們正在作爲KV對,我怕獲取數據的事實無法控制。

雖然每個KV對有一個有效的映射到每個屬性,我事先知道鍵,他們沒有被命名爲DTO相同。

+0

通過KV對你的意思是你有像「Title」=>「something」,「Forenames」=>「別的東西」對的集合,它完全描述ClientCompany對象? – 2010-03-30 08:38:14

+0

Ooops忘了澄清。我會編輯。 (哦,並且在這裏回答你的問題,同時每個KV對都有一個映射,我事先知道這些鍵,他們的命名與DTO不一樣) – user129345 2010-03-30 08:48:08

+0

@Weevie - 事情是如何發生的? – 2010-04-11 17:24:03

回答

0

如果你能得到的數據看起來像['Title':'Mr', 'Forenames':'John', 'Surname':'Doe',...],那麼你應該能夠將JSON反序列化爲你的源對象的kvp。

3

這是一個優雅的,可擴展的,可維護的,速度非常快的從字典加載DTO的解決方案。

創建一個控制檯應用程序並添加這兩個文件。其餘的是自我記錄。

的要點:

  • 簡單,簡短和維護映射類。
  • 使用動態方法進行高效的對象補液。

注意:如果您複製了以前的DynamicProperties.cs,您將希望得到這一個。我添加了一個標誌來允許生成以前版本中沒有的私有setter。

乾杯。

Program.cs的

using System.Collections.Generic; 
using System.Diagnostics; 
using Salient.Reflection; 

namespace KVDTO 
{ 
    /// <summary> 
    /// This is our DTO 
    /// </summary> 
    public class ClientCompany 
    { 
     public string Address1 { get; private set; } 
     public string Address2 { get; private set; } 
     public string AlternativeTelephoneNumber { get; private set; } 
     public string CountyOrState { get; private set; } 
     public string EmailAddress { get; private set; } 
     public string Forenames { get; private set; } 
     public string PostCode { get; private set; } 
     public string Surname { get; private set; } 
     public string TelephoneNumber { get; private set; } 
     public string Title { get; private set; } 
     public string TownOrDistrict { get; private set; } 
    } 


    /// <summary> 
    /// This is our DTO Map 
    /// </summary> 
    public sealed class ClientCompanyMapping : KeyValueDtoMap<ClientCompany> 
    { 
     static ClientCompanyMapping() 
     { 
      AddMapping("Title", "Greeting"); 
      AddMapping("Forenames", "First"); 
      AddMapping("Surname", "Last"); 
      AddMapping("EmailAddress", "eMail"); 
      AddMapping("TelephoneNumber", "Phone"); 
      AddMapping("AlternativeTelephoneNumber", "Phone2"); 
      AddMapping("Address1", "Address1"); 
      AddMapping("Address2", "Address2"); 
      AddMapping("TownOrDistrict", "City"); 
      AddMapping("CountyOrState", "State"); 
      AddMapping("PostCode", "Zip"); 
     } 
    } 


    internal class Program 
    { 
     private const string Address1 = "1243 Easy Street"; 
     private const string CountyOrState = "Az"; 
     private const string EmailAddress = "[email protected]"; 
     private const string Forenames = "Sky"; 
     private const string PostCode = "85282"; 
     private const string Surname = "Sanders"; 
     private const string TelephoneNumber = "800-555-1212"; 
     private const string Title = "Mr."; 
     private const string TownOrDistrict = "Tempe"; 

     private static void Main(string[] args) 
     { 
      // this represents our input data, some discrepancies 
      // introduced to demonstrate functionality of the map 

      // the keys differ from the dto property names 
      // there are missing properties 
      // there are unrecognized properties 
      var input = new Dictionary<string, string> 
       { 
        {"Greeting", Title}, 
        {"First", Forenames}, 
        {"Last", Surname}, 
        {"eMail", EmailAddress}, 
        {"Phone", TelephoneNumber}, 
        // missing from this input {"Phone2", ""}, 
        {"Address1", Address1}, 
        // missing from this input {"Address2", ""}, 
        {"City", TownOrDistrict}, 
        {"State", CountyOrState}, 
        {"Zip", PostCode}, 
        {"SomeOtherFieldWeDontCareAbout", "qwerty"} 
       }; 


      // rehydration is simple and FAST 

      // instantiate a map. You could store instances in a dictionary 
      // but it is not really necessary for performance as all of the 
      // work is done in the static constructors, so no matter how many 
      // times you 'new' a map, it is only ever built once. 

      var map = new ClientCompanyMapping(); 

      // do the work. 
      ClientCompany myDto = map.Load(input); 





      // test 
      Debug.Assert(myDto.Address1 == Address1, "Address1"); 
      Debug.Assert(myDto.Address2 == null, "Address2"); 
      Debug.Assert(myDto.AlternativeTelephoneNumber == null, "AlternativeTelephoneNumber"); 
      Debug.Assert(myDto.CountyOrState == CountyOrState, "CountyOrState"); 
      Debug.Assert(myDto.EmailAddress == EmailAddress, "EmailAddress"); 
      Debug.Assert(myDto.Forenames == Forenames, "Forenames"); 
      Debug.Assert(myDto.PostCode == PostCode, "PostCode"); 
      Debug.Assert(myDto.Surname == Surname, "Surname"); 
      Debug.Assert(myDto.TelephoneNumber == TelephoneNumber, "TelephoneNumber"); 
      Debug.Assert(myDto.Title == Title, "Title"); 
      Debug.Assert(myDto.TownOrDistrict == TownOrDistrict, "TownOrDistrict"); 
     } 
    } 

    /// <summary> 
    /// Base mapper class. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    public class KeyValueDtoMap<T> where T : class, new() 
    { 
     private static readonly List<DynamicProperties.Property> Props; 
     private static readonly Dictionary<string, string> KvMap; 

     static KeyValueDtoMap() 
     { 
      // this property collection is built only once 
      Props = new List<DynamicProperties.Property>(DynamicProperties.CreatePropertyMethods(typeof(T))); 
      KvMap=new Dictionary<string, string>(); 
     } 

     /// <summary> 
     /// Adds a mapping between a DTO property and a KeyValue pair 
     /// </summary> 
     /// <param name="dtoPropertyName">The name of the DTO property</param> 
     /// <param name="inputKey">The expected input key</param> 
     protected static void AddMapping(string dtoPropertyName,string inputKey) 
     { 
      KvMap.Add(dtoPropertyName,inputKey); 
     } 

     /// <summary> 
     /// Creates and loads a DTO from a Dictionary 
     /// </summary> 
     /// <param name="input"></param> 
     /// <returns></returns> 
     public T Load(Dictionary<string, string> input) 
     { 
      var result = new T(); 
      Props.ForEach(p => 
       { 
        string inputKey = KvMap[p.Info.Name]; 
        if (input.ContainsKey(inputKey)) 
        { 
         p.Setter.Invoke(result, input[inputKey]); 
        } 
       }); 
      return result; 
     } 
    } 
} 

DynamicProperties.cs

/*! 
* Project: Salient.Reflection 
* File : DynamicProperties.cs 
* http://spikes.codeplex.com 
* 
* Copyright 2010, Sky Sanders 
* Dual licensed under the MIT or GPL Version 2 licenses. 
* See LICENSE.TXT 
* Date: Sat Mar 28 2010 
*/ 

using System; 
using System.Collections.Generic; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace Salient.Reflection 
{ 
    /// <summary> 
    /// Gets IL setters and getters for a property. 
    /// </summary> 
    public static class DynamicProperties 
    { 
     #region Delegates 

     public delegate object GenericGetter(object target); 

     public delegate void GenericSetter(object target, object value); 

     #endregion 

     public static IList<Property> CreatePropertyMethods(Type T) 
     { 
      var returnValue = new List<Property>(); 

      foreach (PropertyInfo prop in T.GetProperties()) 
      { 
       returnValue.Add(new Property(prop)); 
      } 
      return returnValue; 
     } 


     public static IList<Property> CreatePropertyMethods<T>() 
     { 
      var returnValue = new List<Property>(); 

      foreach (PropertyInfo prop in typeof (T).GetProperties()) 
      { 
       returnValue.Add(new Property(prop)); 
      } 
      return returnValue; 
     } 


     /// <summary> 
     /// Creates a dynamic setter for the property 
     /// </summary> 
     /// <param name="propertyInfo"></param> 
     /// <returns></returns> 
     /// <source> 
     /// http://jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/ 
     /// </source> 
     public static GenericSetter CreateSetMethod(PropertyInfo propertyInfo) 
     { 
      /* 
      * If there's no setter return null 
      */ 
      MethodInfo setMethod = propertyInfo.GetSetMethod(true); 
      if (setMethod == null) 
       return null; 

      /* 
      * Create the dynamic method 
      */ 
      var arguments = new Type[2]; 
      arguments[0] = arguments[1] = typeof (object); 

      var setter = new DynamicMethod(
       String.Concat("_Set", propertyInfo.Name, "_"), 
       typeof (void), arguments, propertyInfo.DeclaringType); 
      ILGenerator generator = setter.GetILGenerator(); 
      generator.Emit(OpCodes.Ldarg_0); 
      generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); 
      generator.Emit(OpCodes.Ldarg_1); 

      if (propertyInfo.PropertyType.IsClass) 
       generator.Emit(OpCodes.Castclass, propertyInfo.PropertyType); 
      else 
       generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); 

      generator.EmitCall(OpCodes.Callvirt, setMethod, null); 
      generator.Emit(OpCodes.Ret); 

      /* 
      * Create the delegate and return it 
      */ 
      return (GenericSetter) setter.CreateDelegate(typeof (GenericSetter)); 
     } 


     /// <summary> 
     /// Creates a dynamic getter for the property 
     /// </summary> 
     /// <param name="propertyInfo"></param> 
     /// <returns></returns> 
     /// <source> 
     /// http://jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/ 
     /// </source> 
     public static GenericGetter CreateGetMethod(PropertyInfo propertyInfo) 
     { 
      /* 
      * If there's no getter return null 
      */ 
      MethodInfo getMethod = propertyInfo.GetGetMethod(true); 
      if (getMethod == null) 
       return null; 

      /* 
      * Create the dynamic method 
      */ 
      var arguments = new Type[1]; 
      arguments[0] = typeof (object); 

      var getter = new DynamicMethod(
       String.Concat("_Get", propertyInfo.Name, "_"), 
       typeof (object), arguments, propertyInfo.DeclaringType); 
      ILGenerator generator = getter.GetILGenerator(); 
      generator.DeclareLocal(typeof (object)); 
      generator.Emit(OpCodes.Ldarg_0); 
      generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); 
      generator.EmitCall(OpCodes.Callvirt, getMethod, null); 

      if (!propertyInfo.PropertyType.IsClass) 
       generator.Emit(OpCodes.Box, propertyInfo.PropertyType); 

      generator.Emit(OpCodes.Ret); 

      /* 
      * Create the delegate and return it 
      */ 
      return (GenericGetter) getter.CreateDelegate(typeof (GenericGetter)); 
     } 

     #region Nested type: Property 

     public class Property 
     { 
      public GenericGetter Getter; 
      public PropertyInfo Info; 
      public GenericSetter Setter; 

      public Property(PropertyInfo info) 
      { 
       Info = info; 
       Setter = CreateSetMethod(info); 
       Getter = CreateGetMethod(info); 
      } 
     } 

     #endregion 
    } 
} 
+0

不錯的代碼在那裏,但因爲我知道鍵和它們的屬性提前映射,我不認爲我需要動態屬性訪問提供。儘管歡呼。 – user129345 2010-03-30 08:52:09

+0

@ weevie - 是的,你有一半的映射,但在這種情況下,這個類的重點是速度寶貝! – 2010-03-30 08:56:17

+0

這會教會我加快閱讀代碼。 _可能需要這是高性能的,所以這可能最終變得更有用。我會試試看看它的不同之處。我正在按照Sergej在下面建議的路線走下去,如果我不需要性能,我仍然可以使用它,因爲它簡潔和簡單。 – user129345 2010-03-30 10:12:17

-1

或者,如果你不想用反射去,你可以使用這個(這不會」工作速度極快):

var ccd = new List<KeyValuePair<string, string>>(); 
ccd.Add(new KeyValuePair<string, string>("Title", "")); 
ccd.Add(new KeyValuePair<string, string>("Forenames", "")); 
ccd.Add(new KeyValuePair<string, string>("Surname", "")); 
ccd.Add(new KeyValuePair<string, string>("EmailAddress", "")); 
ccd.Add(new KeyValuePair<string, string>("TelephoneNumber", "")); 
ccd.Add(new KeyValuePair<string, string>("AlternativeTelephoneNumber", "")); 
ccd.Add(new KeyValuePair<string, string>("Address1", "")); 
ccd.Add(new KeyValuePair<string, string>("Address2", "")); 
ccd.Add(new KeyValuePair<string, string>("TownOrDistrict", "")); 
ccd.Add(new KeyValuePair<string, string>("CountyOrState", "")); 
ccd.Add(new KeyValuePair<string, string>("PostCode", "")); 

var data = new List<List<KeyValuePair<string, string>>> { ccd, ccd, ccd }; 

var companies = from d in data 
       select new ClientCompany { 
        Title = d.FirstOrDefault(k => k.Key == "Title").Value, 
        Forenames = d.FirstOrDefault(k => k.Key == "Forenames").Value, 
        Surname = d.FirstOrDefault(k => k.Key == "Surname").Value, 
        EmailAddress = d.FirstOrDefault(k => k.Key == "EmailAddress").Value, 
        TelephoneNumber = d.FirstOrDefault(k => k.Key == "TelephoneNumber").Value, 
        AlternativeTelephoneNumber = d.FirstOrDefault(k => k.Key == "AlternativeTelephoneNumber").Value, 
        Address1 = d.FirstOrDefault(k => k.Key == "Address1").Value, 
        Address2 = d.FirstOrDefault(k => k.Key == "Address2").Value, 
        TownOrDistrict = d.FirstOrDefault(k => k.Key == "TownOrDistrict").Value, 
        CountyOrState = d.FirstOrDefault(k => k.Key == "CountyOrState").Value, 
        PostCode = d.FirstOrDefault(k => k.Key == "PostCode").Value, 
       }; 
+0

您是否認爲這將與問題中顯示的私人套餐一起使用? – 2010-04-01 20:36:03

+0

沒有反射,沒有什麼能與私人定製者一起工作。這只是爲了讓你一個想法。 – 2010-04-07 08:00:56

0

在這種情況下,我會使用反射將Key-Value對映射到對象屬性。舉例來說,檢查這個接受的答案SO question

相關問題