2013-11-23 3 views
0

내가 (주식 시장 데이터에 대한) 블룸버그 서버 API를 호출하고 다시 사전에 Key는 블룸버그의 측면에 Field Name하는 Dictionary<string, object>의 데이터를 받고, 그리고 개체가 블룸버그의 데이터 값을 포함하고 string, decimal이 될 수 있어요 DateTime, boolean개체 속성과 사전 키 이름이 다른 경우 개체/클래스 속성을 사전에 매핑하는 방법?

블룸버그 데이터를 얻은 후 반환 된 값으로 강력한 유형의 엔티티/클래스를 채워야합니다. 블룸버그에 대한 요청에서 보낸 필드 이름에 따라 반환 된 사전은 다른 키 값을 가질 수 있습니다. 내가 가지고있는 문제는 블룸버그 필드 이름과 .net 엔티티의 속성 이름이과 일치하지 않기 때문에 AutoMapper 또는 유사한 라이브러리를 사용하여이 매핑을 수행 할 수 있는지 확신 할 수 없습니다.

또한 첫 번째 튜플 항목이 블룸버그 필드 이름이고, 두 번째 튜플 항목이 내 엔터티의 속성 이름이고 세 번째 튜플 항목이 bloomberg에서 반환 된 데이터 값인 경우 Tuple<string,string,object>을 사용해 보았습니다. 그 중 하나 (지금까지) 밖으로 작동하지 않습니다, 그래서이 블룸버그 필드를 유지하는 간단한 직선 방법이 궁금하다 < -> EntityProperty 매핑 및 해당 필드에 블룸버그 데이터 값을 사용하여 엔터티의 값을 채 웁니다. 일반 (즉, C# Generics 사용) 솔루션이 더 좋을 것입니다!

아래 예제 콘솔 앱 코드를 붙여 넣어 붙여 넣기를 시도해 보았습니다. 2 개의 사전 (stockdatabonddata)은 가짜 데이터를 가지고 있지만 아이디어를 얻을 수 있습니다. 또한 아래에 설명을 추가하여 내가 수행하려고하는 것을 다시 반복합니다.

감사합니다.

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

자기가 말한 것처럼 매핑 테이블이 필요합니다. Bloomberg에서 반환 된 각 키 필드를 강력한 유형의 클래스의 속성에 매핑하는 정적 읽기 전용 사전을 입력 할 수 있습니다.

다음은 어떻게 수행할까요? 추 신 : 테스트를 위해 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

Google 코드에서 FastMember 어셈블리에 대한 참조를 추가했으며 Nuget 패키지를 사용해 보았습니다. 여전히 프로젝트를 빌드하는 동안 .Dump() 문에서이 오류가 발생합니다. 내가 뭘 놓치고 있니? " 'T'에는 '덤프'에 대한 정의가없고 'T'유형의 첫 번째 인수를 수락하는 확장 메소드 '덤프'를 찾을 수 없습니다." – Shiva

+0

나는 당신의 코드도 효과가 있다고 생각한다. 내 코드베이스에서 FastMember를 사용하면 내 고객 사이트에서 승인을 받기 위해 많은 서류 작업이 필요하므로 이상적인 솔루션이 아닐 수 있습니다. 그러나, 나는 또한 당신의 해결책을 좋아한다. 그래서 나는 그것을 upvoted했다. 코드를 작성해 주셔서 감사합니다. – Shiva

+0

Dump() 메서드는 linqpad의 일부입니다. 나는 개념 증명을하기 위해 linqpad를 사용한다. vnet에서 사용할 수 없으므로 제거해야합니다. – Max