2

最近、アプリケーションで ExpandoObject を使用する必要がありました。そのため、古いマッパーを使用して動的 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 プロパティではないためです。ExpandoObjectwithキーワードを使用するとdynamic、任意のプロパティをオブジェクトに与えることができ、これはDLR実行時に によって処理されます。の動的インスタンスでは、通常の静的型のメソッドGetProperties()を使用することはできません。GetFields()ExpandoObject

Mapper消費するように拡張するExpandObjectには、それを特別なケースと見なす必要があります。

ここで私の答えを見てください。

編集:反射ExpandoObjectは難しくありません。PropertyInfoただし、セットまたはセットを取得することはできませんFieldInfo。あなたはただ得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プロパティの名前と値を取得するには、 and.Valueの代わりに.PropertyType.Nameandを使用できます.GetValue()

すべてのコードでこの特殊化を考慮する必要があります。

于 2013-10-24T19:20:49.040 に答える