Recently I had to use ExpandoObject in my application, So I want to know how could I use my old Mapper to also map from Dynamic ExpandoOnjects, because some how it does not map the fields, and properties from an Expando.
Code:
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);
}
}
}
}
}
}
Usage:
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] : How could I use my mapper (presented above) to map from the dynamic ExpandoObject.
- [P] : The problem is that it works perfectly in normal
<object, object>
mapping; However when provided with<ExpandoObject, object>
it does not map anything.