c# - 将动态 ExpandoObject 映射到对象

标签 c#

最近我不得不在我的应用程序中使用 ExpandoObject,所以我想知道如何使用我的旧映射器也从 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>测绘;但是,当提供 <ExpandoObject, object> 时 它没有映射任何东西。

最佳答案

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

延长您的Mapper消费ExpandObject您必须将其视为特殊情况。

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

编辑:反射(reflection) ExpandoObject并不难。但是,您不会获得一组 PropertyInfoFieldInfo从中。您只需得到KeyValuePair<string, object> 。因此,您可能必须添加这样的数组 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() .

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

关于c# - 将动态 ExpandoObject 映射到对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19574374/

相关文章:

c# - 在C#中查找没有完整路径的根目录

c# - 如何将 json 字符串序列化为表单发布数据

c# - 获取两个日期时间之间的(满)小时列表

c# - 从两个列表 C# 之间的比较创建一个列表

c# - 获取类字段列表

c# - 说服我转向 .net 3.5(从 2.0)

c# - 使 AForge VideoCaptureDevice 与 Unity 一起工作

C#- 在 'enclosing' 本地范围内使用变量?

c# - 设置属性是规则对 FluentValidation 有效

c# - FileStream.close() 不会为其他进程释放文件