【问题标题】:Mapping Dynamic ExpandoObject To Object将动态 ExpandoObject 映射到对象
【发布时间】:2013-10-25 04:11:40
【问题描述】:

最近我不得不在我的应用程序中使用 ExpandoObject,所以我想知道如何使用我的旧 Mapper 也从 Dynamic ExpandoOnjects 映射,因为它不映射字段和 Expando 的属性。

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public class Mapper
{
    private static readonly Dictionary<KeyValuePair<Type, Type>, object> Maps = new Dictionary<KeyValuePair<Type, Type>, object>();

    private static PropertyInfo[] _fromProperties;
    private static PropertyInfo[] _toProperties;
    private static FieldInfo[] _fromFields;
    private static FieldInfo[] _toFields;

    // Rules...
    private static readonly Func<PropertyInfo, PropertyInfo, bool> MatchingProps = (t1, t2) => t1.Name == t2.Name && t1.PropertyType.Name == t2.PropertyType.Name;
    private static readonly Func<FieldInfo, FieldInfo, bool> MatchingFields = (t1, t2) => t1.Name == t2.Name && t1.FieldType.Name == t2.FieldType.Name;
    private static readonly Func<PropertyInfo, FieldInfo, bool> MatchingPropertyToField = (t1, t2) => t1.Name == t2.Name && t1.PropertyType.Name == t2.FieldType.Name;
    private static readonly Func<FieldInfo, PropertyInfo, bool> MatchingFieldToProperty = (t1, t2) => t1.Name == t2.Name && t1.FieldType.Name == t2.PropertyType.Name;

    public static void AddMap<TFrom, TTo>(Action<TFrom, TTo> map = null)
            where TFrom : class
            where TTo : class { Maps.Add(new KeyValuePair<Type, Type>(typeof(TFrom), typeof(TTo)), map); }

    public static void Map<TFromType, TOType>(TFromType @from, TOType to)
    {
        var key = new KeyValuePair<Type, Type>(typeof(TFromType), typeof(TOType));
        var map = (Action<TFromType, TOType>)Maps[key];

        bool hasMapping = Maps.Any(x => x.Key.Equals(key));

        if (!hasMapping)
            throw new Exception(string.Format("No map defined for {0} => {1}", typeof(TFromType).Name, typeof(TOType).Name));

        Type tFrom = typeof(TFromType);
        Type tTo = typeof(TOType);

        _fromProperties = tFrom.GetProperties();
        _fromFields = tFrom.GetFields();
        _toProperties = tTo.GetProperties();
        _toFields = tTo.GetFields();

        SyncProperties(@from, to);
        SyncFields(@from, to);

        if (!Equals(map, null))
            map(@from, to);
    }

    private static void SyncProperties<TFromType, TOType>(TFromType objFrom, TOType objTo)
    {
        PropertyInfo[] fromProperties = _fromProperties;
        PropertyInfo[] toProperties = _toProperties;
        FieldInfo[] toFields = _toFields;

        if (fromProperties != null && fromProperties.Any()) {
            foreach (PropertyInfo fromProperty in fromProperties) {
                if (toProperties.Any(x => x.Name == fromProperty.Name)) {
                    PropertyInfo destinationProperty = toProperties.FirstOrDefault(x => x.Name == fromProperty.Name);

                    if (MatchingProps(fromProperty, destinationProperty)) {
                        object val = fromProperty.GetValue(objFrom, null);
                        if (Equals(val, null)) continue;
                        if (!Equals(destinationProperty, null)) destinationProperty.SetValue(objTo, Convert.ChangeType(val, fromProperty.PropertyType), null);
                    }
                }

                if (toFields.Any(x => x.Name == fromProperty.Name)) {
                    FieldInfo destinationField = toFields.FirstOrDefault(x => x.Name == fromProperty.Name);

                    if (MatchingPropertyToField(fromProperty, destinationField)) {
                        object val = fromProperty.GetValue(objFrom, null);
                        if (Equals(val, null)) continue;
                        if (!Equals(destinationField, null)) destinationField.SetValue(objTo, val);
                    }
                }
            }
        }
    }

    private static void SyncFields<TFromType, TOType>(TFromType objFrom, TOType objTo)
    {
        FieldInfo[] fromFields = _fromFields;
        FieldInfo[] toFields = _toFields;
        PropertyInfo[] toProperties = _toProperties;

        if (fromFields != null && fromFields.Any()) {
            foreach (FieldInfo fromField in fromFields) {
                if (toFields.Any(x => x.Name == fromField.Name)) {
                    FieldInfo destinationField = toFields.FirstOrDefault(x => x.Name == fromField.Name);

                    if (MatchingFields(fromField, destinationField)) {
                        object val = fromField.GetValue(objFrom);
                        if (Equals(val, null)) continue;
                        if (!Equals(destinationField, null)) destinationField.SetValue(objTo, val);
                    }
                }

                if (toProperties.Any(x => x.Name == fromField.Name)) {
                    PropertyInfo destinationProperty = toProperties.FirstOrDefault(x => x.Name == fromField.Name);

                    if (MatchingFieldToProperty(fromField, destinationProperty)) {
                        object val = fromField.GetValue(objFrom);
                        if (Equals(val, null)) continue;
                        if (!Equals(destinationProperty, null)) destinationProperty.SetValue(objTo, val, null);
                    }
                }
            }
        }
    }
}

用法

static void Main()
{
    dynamic o = new ExpandoObject();
    o.Name = "Pouce";
    o.Age = 42;
    o.Rank = new Rank
             {
                     Name = Ranks.Major
             };
    o.Guid = new Guid();

    Soldier soldier = new Soldier();
    Mapper.AddMap<ExpandoObject, Soldier>();
    Mapper.Map(o, soldier);

    Console.ReadLine();
}

public class Soldier
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Rank Rank { get; set; }
    public Guid Guid { get; set; }
}

public class Rank
{
    public Ranks Name { get; set; }
}

public enum Ranks
{
    Private,
    Specialist,
    Corporal,
    Sergeant,
    Captain,
    Major,
    Colonel,
    General
}
  • [Q]:如何使用我的映射器(如上所示)从 动态 ExpandoObject。
  • [P] : 问题是它在正常的<object, object> 映射中工作得很好;但是,当提供&lt;ExpandoObject, object&gt; 它不映射任何东西。

【问题讨论】:

标签: c#


【解决方案1】:

这是因为ExpandoObject 的属性不是真正的.NET 属性。通过使用ExpandoObjectdynamic 关键字,您可以为对象提供任意属性,这由DLR 在运行时处理。 您不能在 ExpandoObject 的动态实例上使用常规静态类型的方法 GetProperties()GetFields()

要将您的Mapper 扩展为使用ExpandObject,您必须将其视为一种特殊情况。

看我的回答here它可能对你有帮助。

编辑ExpandoObject 的反射并不难。但是,您不会从中获得一组PropertyInfoFieldInfo。你只会得到KeyValuePair&lt;string, object&gt;。因此,您可能需要添加一组这样的KeyValuePair 来存储信息。

在您的 Map() 方法中,您可以检查 ExpandoObject 作为特殊情况:

if (tFrom == typeof(ExpandoObject)) {
    _fromExpandoProperties = @from.Select(kvp => kvp).ToArray();
    // where _fromExpandoProperties is of type KeyValuePair<string, object>[]
} else {
    _fromProperties = tFrom.GetProperties();
}

要获取属性的名称和值,您可以使用.Key.Value 而不是.PropertyType.Name.GetValue()

您必须在所有代码中考虑这种专业化。

【讨论】:

  • 由于我对反射和映射相当陌生,我可以要求您在我的类上应用所需的编辑以便能够映射 Expando 吗?我知道你一定很忙,我不能只要求你这样做,但这肯定会对我有很大帮助。
猜你喜欢
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 2019-05-27
相关资源
最近更新 更多