3- If the property is formated like this public string Property;
the get properties does not get it.
Oh, that's not a property, that's a field. If you want consider fields as well.
static Mapper()
{
PropertyMap = typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.ToDictionary(p => p.Name.ToLower(), p => p);
FieldMap = typeof(T).GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance)
.ToDictionary(f => f.Name.ToLower(), f => f);
}
2- When i try to map int to a Nullable simply it will throw a type mismatch because i can't find a way to detect and cast it properly.
Why check for Nullable
type, let reflection figure it out. If value is valid, it will be assigned.
public static void Map(ExpandoObject source, T destination)
{
if (source == null)
throw new ArgumentNullException("source");
if (destination == null)
throw new ArgumentNullException("destination");
foreach (var kv in source)
{
PropertyInfo p;
if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
{
p.SetValue(destination, kv.Value, null);
}
else
{
FieldInfo f;
if (FieldMap.TryGetValue(kv.Key.ToLower(), out f))
{
f.SetValue(destination, kv.Value);
}
}
}
}
1 - It does not recursively map the inner objects of the ExpandoObject as supposed.
Seems to work for your InnerClass
at least.
Class c = new Class();
dynamic o = new ExpandoObject();
o.Name = "Carl";
o.Level = 7;
o.Inner = new InnerClass
{
Name = "Inner Carl",
Level = 10
};
o.Property = "my Property value"; // dont forget to set this
Mapper<Class>.Map(o, c);
EDIT: based on your comments, I've create two overloaded methods MergeProperty
. You can write similarly overloaded methods for fields.
public static void MergeProperty(PropertyInfo pi, ExpandoObject source, object target)
{
Type propType = pi.PropertyType;
// dont recurse for value type, Nullable<T> and strings
if (propType.IsValueType || propType == typeof(string))
{
var sourceVal = source.First(kvp => kvp.Key == pi.Name).Value;
if(sourceVal != null)
pi.SetValue(target, sourceVal, null);
}
else // recursively map inner class properties
{
var props = propType.GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
foreach (var p in props)
{
var sourcePropValue = source.First(kvp => kvp.Key == pi.Name).Value;
var targetPropValue = pi.GetValue(target, null);
if (sourcePropValue != null)
{
if (targetPropValue == null) // replace
{
pi.SetValue(target, source.First(kvp => kvp.Key == pi.Name).Value, null);
}
else
{
MergeProperty(p, sourcePropValue, targetPropValue);
}
}
}
}
}
public static void MergeProperty(PropertyInfo pi, object source, object target)
{
Type propType = pi.PropertyType;
PropertyInfo sourcePi = source.GetType().GetProperty(pi.Name);
// dont recurse for value type, Nullable<T> and strings
if (propType.IsValueType || propType == typeof(string))
{
var sourceVal = sourcePi.GetValue(source, null);
if(sourceVal != null)
pi.SetValue(target, sourceVal, null);
}
else // recursively map inner class properties
{
var props = propType.GetProperties(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
foreach (var p in props)
{
var sourcePropValue = sourcePi.GetValue(source, null);
var targetPropValue = pi.GetValue(target, null);
if (sourcePropValue != null)
{
if (targetPropValue == null) // replace
{
pi.SetValue(target, sourcePi.GetValue(source, null), null);
}
else
{
MergeProperty(p, sourcePropValue, targetPropValue);
}
}
}
}
}
You can use the methods this way:
public static void Map(ExpandoObject source, T destination)
{
if (source == null)
throw new ArgumentNullException("source");
if (destination == null)
throw new ArgumentNullException("destination");
foreach (var kv in source)
{
PropertyInfo p;
if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
{
MergeProperty(p, source, destination);
}
else
{
// do similar merge for fields
}
}
}