2

最近我不得不在我的应用程序中使用 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>贴图中完美运行;但是,当提供它时,<ExpandoObject, object> 它不会映射任何东西。
4

1 回答 1

1

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

为了延长您Mapper的消费范围,ExpandObject您必须将其视为一种特殊情况。

在这里查看我的答案,它可能会对您有所帮助。

编辑:反思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();
}

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

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

于 2013-10-24T19:20:49.040 回答