2013-11-23 168 views
0

我打電話彭博服務器API(在股市中的數據),並早在Dictionary<string, object>Key到字典中是彭博的身邊Field Name獲取數據,對象包含來自彭博的數據值,可以是stringdecimalDateTime,boolean當對象屬性和字典鍵名稱不同時如何將對象/類屬性映射到字典?

當我得到彭博數據後,我需要用返回的值填充我的強類型實體/類。根據我向彭博社的請求發送的字段名稱,返回的字典可能具有不同的鍵值。我遇到的問題是,彭博現場的名字和我的.NET實體的屬性名稱不匹配,所以我不知道我能做到使用AutoMapper或類似的庫這種映射。

我也嘗試過使用Tuple<string,string,object>其中第一個元組項是bloomberg字段名,第二個元組項是我的實體的屬性名,第三個元組項是從bloomberg返回的數據值。即不工作或者(到目前爲止),所以我想知道是否有維護這個bloombergfield <的一個簡單直接的方式 - > EntityProperty映射和填充使用彭博的數據價值的各個領域實體的價值。通用(即使用C#泛型)解決方案會更好!

我已經粘貼下面的示例控制檯應用程序代碼,這樣你就可以粘貼和嘗試。 2個字典,1 stockdata等爲bonddata有僞造數據,但你的想法。我還在下面添加了一些評論以重新實現我正在嘗試完成的內容。

謝謝!

namespace MapBBFieldsToEntityProperties 
{ 
    using System; 
    using System.Collections.Generic; 

    class Program 
    { 
     public class StockDataResult 
     { 
      public string Name { get; set; } 
      public decimal LastPrice { get; set; } 
      public DateTime SettlementDate { get; set; } 
      public decimal EPS { get; set; } 

     } 

     public class BondDataResult 
     { 
      public string Name { get; set; } 
      public string Issuer { get; set; } 
      public decimal Duration { get; set; } 
      public DateTime YieldToMaturity { get; set; } 
     } 


     static void Main(string[] args) 
     { 

      // Data Coming from Bloomberg. 
      // Dictionary Key is the Bloomberg Data Field Name. 
      // Dictionary Object is the Value returns and can be any .Net primitive Type 

      // Sample Data returned for a Stock Query to Bloomberg 
      Dictionary<string, object> dctBloombergStockData 
       = new Dictionary<string, object> 
          { 
           { "NAME", "IBM" }, 
           { "PX_LAST", 181.30f }, 
           { "SETTLE_DT", "11/25/2013" } // This is Datetime value 
          }; 

      // Sample Data returned for a Bond Query to Bloomberg 
      Dictionary<string, object> dctBloombergBondData = 
       new Dictionary<string, object> 
          { 
           { "NAME", "IBM" }, 
           { "ISSUE_ORG","IBM Corp" }, 
           { "DURATION", 4.430f }, 
           { "YLD_TO_M", 6.456f } 
          }; 

      // This is my Stock Entity 
      StockDataResult stockData = new StockDataResult(); 

      // This is my Bond Entity 
      BondDataResult bondData = new BondDataResult(); 

      // PROBLEM STATEMENT: 
      // 
      // Need to somehow Map the Data returned from Bloomberg into the 
      // Corresponding Strong-typed Entity for that Data Type. 
      // i.e. 
      // map dctBloombergStockData to stockData Entity instance as follows 
      // 
      // dctBloombergStockData."NAME" Key <--------> stockData.Name Property so that 
      // dctBloombergStockData["NAME"] value of "IBM" can be assigned to stockData.Name 
      // 
      // dctBloombergStockData."PX_LAST" Key <--------> stockData.LastPrice Property so that 
      // dctBloombergStockData["PX_LAST"] value 181.30f can be assigned to stockData.LastPrice value. 
      // .... 
      // .. you get the idea. 
      // Similarly, 
      // map dctBloombergBondData Data to bondData Entity instance as follows 
      // 
      // dctBloombergBondData."NAME" Key <--------> bondData.Name Property so that 
      // dctBloombergBondData["NAME"] value of "IBM" can be assigned to bondData.Name property's value 
      // 
      // dctBloombergBondData."ISSUE_ORG" Key <--------> bondData.Issuer Property so that 
      // dctBloombergBondData["ISSUE_ORG"] value 181.30f can be assigned to bondData.Issuer property's value. 
      // 
      // dctBloombergBondData."YLD_TO_M" Key <--------> bondData.YieldToMaturity Property so that 
      // dctBloombergBondData["YLD_TO_M"] value 181.30f can be assigned to bondData.YieldToMaturity property's value.         
     } 
    } 
} 

回答

3

我相信相當多的改進是可能的,但是這是指定的映射,並使用該地圖的一種方式。

class Program 
{ 
    public class Mapper<TEntity> where TEntity : class 
    { 
     private readonly Dictionary<string, Action<TEntity, object>> _propertyMappers = new Dictionary<string, Action<TEntity, object>>(); 
     private Func<TEntity> _entityFactory; 

     public Mapper<TEntity> ConstructUsing(Func<TEntity> entityFactory) 
     { 
      _entityFactory = entityFactory; 
      return this; 
     } 

     public Mapper<TEntity> Map<TProperty>(Expression<Func<TEntity, TProperty>> memberExpression, string bloombergFieldName, Expression<Func<object, TProperty>> converter) 
     { 
      var converterInput = Expression.Parameter(typeof(object), "converterInput"); 
      var invokeConverter = Expression.Invoke(converter, converterInput); 
      var assign = Expression.Assign(memberExpression.Body, invokeConverter); 
      var mapAction = Expression.Lambda<Action<TEntity, object>>(
       assign, memberExpression.Parameters[0], converterInput).Compile(); 
      _propertyMappers[bloombergFieldName] = mapAction; 
      return this; 
     } 

     public TEntity MapFrom(Dictionary<string, object> bloombergDict) 
     { 
      var instance = _entityFactory(); 
      foreach (var entry in bloombergDict) 
      { 
       _propertyMappers[entry.Key](instance, entry.Value); 
      } 
      return instance; 
     } 
    } 

    public class StockDataResult 
    { 
     public string Name { get; set; } 
     public decimal LastPrice { get; set; } 
     public DateTime SettlementDate { get; set; } 
     public decimal EPS { get; set; } 
    } 

    public static void Main(params string[] args) 
    { 
     var mapper = new Mapper<StockDataResult>() 
      .ConstructUsing(() => new StockDataResult()) 
      .Map(x => x.Name, "NAME", p => (string)p) 
      .Map(x => x.LastPrice, "PX_LAST", p => Convert.ToDecimal((float)p)) 
      .Map(x => x.SettlementDate, "SETTLE_DT", p => DateTime.ParseExact((string)p, "MM/dd/yyyy", null)); 


     var dctBloombergStockData = new Dictionary<string, object> 
     { 
      { "NAME", "IBM" }, 
      { "PX_LAST", 181.30f }, 
      { "SETTLE_DT", "11/25/2013" } // This is Datetime value 
     }; 
     var myStockResult = mapper.MapFrom(dctBloombergStockData); 

     Console.WriteLine(myStockResult.Name); 
     Console.WriteLine(myStockResult.LastPrice); 
     Console.WriteLine(myStockResult.SettlementDate); 
    } 
} 
+0

這是一些不錯的代碼。我對它做了一些修改。 – Max

+0

感謝Alex&Max。我將在週末測試你的兩個答案,並讓你知道! – Shiva

+0

@Alex這個偉大的工程!謝謝。我也用BondData測試過它。 1個問題,如果BloombergDataDictionary中的字段沒有映射到實體,我得到一個錯誤。所以我改變了MapFrom代碼,如下檢查密鑰。這是解決這個問題的正確方法嗎? foreach(bloombergDict中的var條目) if(_propertyMappers.ContainsKey(entry.Key)) _propertyMappers [entry.Key](instance,entry.Value); } – Shiva

0

如何:

stockData.Name = dctBloombergStockData["NAME"]; 
stockData.LastPrice = dctBloombergStockData["PX_LAST"] 
//and so on... 
+0

這是行不通的。我忘記提及彭博字典字段會有所不同,具體取決於我告訴彭博社。即1個數據結果可以具有「名稱」和「PX_LAST」,而一些其他請求可能具有「PX_LAST」和「SETTLE_DT」。因此我需要一個通用的方法/映射器。 – Shiva

+0

爲什麼不創建一些條件語句並根據字典中的鍵設置對象屬性呢?數據中有多少種不同的可能性可以回來? – elevine

1

正如你所說你的自我,你需要一個映射表。你可以在你的類型中創建一個靜態只讀字典,將從彭博返回的每個關鍵字映射到強類型類中的屬性。

這裏是我會怎麼做。 PS:我用linqpad進行測試。PPS:您可以根據需要添加儘可能多的映射器到字典中。您還需要fast-member才能運行此代碼。

void Main() 
{ 
    var dctBloombergStockData = new Dictionary<string, object> 
     { 
      { "NAME", "IBM" }, 
      { "PX_LAST", 181.30f }, 
      { "SETTLE_DT", "11/25/2013" } // This is Datetime value 
     }; 
    StockDataResult.FromBloombergData(dctBloombergStockData); 
} 

// Define other methods and classes here 
interface IMapper 
{ 
    string PropertyName { get; } 
    object Parse(object source); 
} 

class Mapper<T, TResult> : IMapper 
{ 
    private Func<T, TResult> _parser; 
    public Mapper(string propertyName, Func<T, TResult> parser) 
    { 
     PropertyName = propertyName; 
     _parser = parser; 
    } 

    public string PropertyName { get; private set; } 

    public TResult Parse(T source) 
    { 
     source.Dump(); 
     return _parser(source); 
    } 

    object IMapper.Parse(object source) 
    { 
     source.Dump(); 
     return Parse((T)source); 
    } 
} 

public class StockDataResult 
{ 
    private static TypeAccessor Accessor = TypeAccessor.Create(typeof(StockDataResult)); 

    private static readonly Dictionary<string, IMapper> Mappers = new Dictionary<string, IMapper>(StringComparer.CurrentCultureIgnoreCase){ 
      { "NAME", new Mapper<string, string>("Name", a => a) }, 
      { "PX_LAST", new Mapper<float, decimal>("LastPrice", a => Convert.ToDecimal(a)) }, 
      { "SETTLE_DT", new Mapper<string, DateTime>("SettlementDate", a => DateTime.ParseExact(a, "MM/dd/yyyy", null)) } 
     }; 

    protected StockDataResult() 
    { } 

    public string Name { get; set; } 
    public float LastPrice { get; set; } 
    public DateTime SettlementDate { get; set; } 
    public decimal EPS { get; set; } 

    public static StockDataResult FromBloombergData(Dictionary<string, object> state) 
    { 
     var result = new StockDataResult(); 
     IMapper mapper; 
     foreach (var entry in state) 
     { 
      if(!Mappers.TryGetValue(entry.Key, out mapper)) 
      { continue; } 
      Accessor[result, mapper.PropertyName.Dump()] = mapper.Parse(entry.Value); 
     } 
     return result; 
    } 
} 
+0

我加了一個谷歌代碼對FastMember彙編的參考,也試過了Nuget包。在構建項目時仍然在.Dump()語句中出現此錯誤。我錯過了什麼? 「'T'不包含'Dump'的定義,並且沒有可以找到'T'類型的第一個參數的擴展方法'Dump'」。 – Shiva

+0

我認爲你的代碼也可以工作。在我的代碼庫中使用FastMember可能需要大量文件才能在我的客戶端獲得批准,因此這可能不是理想的解決方案。不過,我也喜歡你的解決方案,所以我提高了它。感謝您編寫代碼。 – Shiva

+0

Dump()方法是linqpad的一部分。我使用linqpad做概念證明。您需要將其刪除,因爲它在vs.net中不可用。 – Max

相關問題