So I have the below (which is working correctly and all my unit tests are passing) but it's ugly and is violating SOLID, I will have to change the class every time I want to be able to map a different type (The multiple if
statements).
Code
/// <summary>
/// Maps the matching properties from one type to the other by name.
/// <para>If the name does not match then it will not be mapped.</para>
/// <para>This class was intended to be used in a class that derives from "IValueInjection" </para>
/// </summary>
/// <param name="target">The target object.</param>
/// <param name="source">The source object.</param>
/// <exception cref="System.InvalidCastException"> If there is no cast type then this will be thrown</exception>
public static void MapMatchingProperties(object target, object source)
{
var sourceProps = source.GetType().GetProperties();
var targetProps = target.GetType().GetProperties();
for (var i = 0; i < targetProps.Count(); i++)
{
var sourceProperty = sourceProps.FirstOrDefault(x => String.Equals(x.Name, targetProps[i].Name, StringComparison.InvariantCultureIgnoreCase));
if (sourceProperty != null)
{
var objVal = GetPropValue(source, sourceProperty.Name);
if(objVal == null ) continue;
var fullProperty = target.GetType().GetProperty(targetProps[i].Name);
var newType = fullProperty.PropertyType;
object castType = null;
if (newType == typeof(int))
{
castType = (int)objVal;
}
else if (newType == typeof(bool))
{
castType = (bool)objVal;
}
else if (newType == typeof(string))
{
castType = (string)objVal;
}
else if (newType == typeof(long))
{
castType = (long)objVal;
}
else if (newType == typeof(DateTime))
{
castType = objVal as DateTime? ?? new DateTime();
}
if (castType == null)
{
throw new InvalidCastException(string.Format("Could not find the correct cast type for property: {0}, type: {1}", targetProps[i].Name, fullProperty.PropertyType));
}
var val = castType;
SetPropValue(target, val, targetProps[i].Name);
}
}
}
private static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
private static void SetPropValue(object target, object value, string propName)
{
var prop = target.GetType().GetProperty(propName);
prop.SetValue(target, value);
}
As you can see what I am doing is finding the matching property by property name and then setting it.
The reason for this is basically to map JSON classes (classes with their property names are all lower case) to my own classes.
The main problem is the if {} else {}
statements since I will have to expand that when I want to map a new type.
An example of this class used in a mapping scenario:
protected override void Inject(LogResult source, object target)
{
MappingHelper.MapMatchingProperties(target, source);
var dateTime = target.GetType().GetProperty("Time", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (dateTime == null) return;
var convertedTime = source.Time.UnixTimeStampToDateTime(); // Converts to a valid DateTime
if (convertedTime == default(DateTime)) return;
dateTime.SetValue(target, convertedTime);
}
Log Result
public class LogResult
{
public int ID { get; set; } <-- This property name has different casing
public string Kind { get; set; }
public int Time { get; set; }
public string Text { get; set; }
}
View Model
public class LogViewModel
{
public int Id { get; set; }
public string Kind { get; set; }
public DateTime Time { get; set; }
public string Text { get; set; }
}