I have had to do something like this myself, and of course I used reflection to do it. Here is the code I used, with an explanation below:
private static readonly ConcurrentDictionary<(Type, Type), (PropertyInfo, PropertyInfo)[]> PICache =
new ConcurrentDictionary<(Type, Type), (PropertyInfo, PropertyInfo)[]>();
public static E2 EntityCopy<E1, E2>(E1 from, E2 to)
{
var types = (fromType: typeof(E1), toType: typeof(E2));
if (PICache.TryGetValue(types, out var props))
{
foreach ((var p1, var p2) in props)
{
p2.SetValue(to, p1.GetValue(from));
}
}
else
{
PropertyInfo[] GetOrderedProps(Type t) => t.GetProperties().OrderBy(p => p.Name).ToArray();
var e1_props = GetOrderedProps(types.fromType);
var e2_props = GetOrderedProps(types.toType);
var props_new = new List<(PropertyInfo, PropertyInfo)>();
var i1 = 0; var i2 = 0;
while (i1 < e1_props.Count() && i2 < e2_props.Count())
{
var p1 = e1_props[i1]; var p2 = e2_props[i2];
if (p1.Name == p2.Name)
{
if (!p2.CustomAttributes.Any(a => a.AttributeType == typeof(KeyAttribute))
&& !(p2.PropertyType.Name.Contains("ICollection"))
&& p1.GetMethod != null
&& p2.SetMethod != null)
{
try
{
p2.SetValue(to, p1.GetValue(from));
props_new.Add((p1, p2));
}
catch (Exception ex)
{
Log(ex.Message);
}
}
i1++; i2++;
}
else if (string.Compare(p1.Name, p2.Name) < 0) { i1++; }
else { i2++; }
}
PICache.TryAdd(types, props_new.ToArray());
}
return to;
}
Here, PICache
is caching an ordered array of pairs of PropertyInfo
s. The idea is that the first time we see a pair of Type
s, we will go through the properties in a specific order (here I've ordered by name) and match the source property to the destination property. Once we've done this once the properties and their ordering are cached for perfomance.
You should be able to see some caveats here. I am not copying any ICollection
property, since I am using EF6 with lazy loading. If I were to copy these properties I could end up loading data from the database I don't want, which massively impacts performance. I am also not copying the Key, which you may want to do.