当对象属性和字典键名不同时,如何将对象/类属性映射到字典?

时间:2013-11-23 01:34:04

标签: c# dictionary mapping automapper bloomberg

我正在调用彭博服务器API(针对股市数据)并将数据恢复到Dictionary<string, object>,其中字典的Key是彭博方面的Field Name,并且该对象包含Bloomberg的数据值,可以是stringdecimalDateTimeboolean等。

获得Bloomberg数据后,我需要使用返回的值填充强类型实体/类。根据我在对bloomberg的请求中发送的字段名称,返回的字典可以具有不同的键值。我遇到的问题是, bloomberg字段名称和我的.net实体的属性名称不匹配,所以我不确定我是否可以使用AutoMapper或类似的库进行此映射

我也尝试使用Tuple<string,string,object>,其中第一个元组项是bloomberg字段名,第二个元组项是我的实体的属性名,第三个元组项是从bloomberg返回的数据值。这也没有成功(到目前为止),所以我想知道是否有一种简单的直接方式来维护这个bloombergfield&lt; - &gt; EntityProperty映射并使用相应字段的Bloomberg数据值填充实体的值。 Generic(即使用C#Generics)解决方案会更好!

我已粘贴下面的示例控制台应用代码,因此您可以将其粘贴并试用。 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 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:1)

正如你所说的那样,你需要一张映射表。您可以在类型中创建一个静态只读字典,它将从Bloomberg返回的每个键字段映射到强类型类中的属性。

我将如何做到这一点。 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;
    }
}

答案 2 :(得分:-1)

怎么样:

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