Merging .net object graph
- by Tiju John
Hi guys, has anyone come across any scenario wherein you needed to merge one object with another object of same type, merging the complete object graph.
for e.g. If i have a person object and one person object is having first name and other the last name, some way to merge both the objects into a single object.
public class Person
{
public Int32 Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class MyClass
{
//both instances refer to the same person, probably coming from different sources
Person obj1 = new Person(); obj1.Id=1; obj1.FirstName = "Tiju";
Person obj2 = new Person(); ojb2.Id=1; obj2.LastName = "John";
//some way of merging both the object
obj1.MergeObject(obj2); //??
//obj1.Id // = 1
//obj1.FirstName // = "Tiju"
//obj1.LastName // = "John"
}
I had come across such type of requirement and I wrote an extension method to do the same.
public static class ExtensionMethods
{
private const string Key = "Id";
public static IList MergeList(this IList source, IList target)
{
Dictionary itemData = new Dictionary();
//fill the dictionary for existing list
string temp = null;
foreach (object item in source)
{
temp = GetKeyOfRecord(item);
if (!String.IsNullOrEmpty(temp))
itemData[temp] = item;
}
//if the same id exists, merge the object, otherwise add to the existing list.
foreach (object item in target)
{
temp = GetKeyOfRecord(item);
if (!String.IsNullOrEmpty(temp) && itemData.ContainsKey(temp))
itemData[temp].MergeObject(item);
else
source.Add(item);
}
return source;
}
private static string GetKeyOfRecord(object o)
{
string keyValue = null;
Type pointType = o.GetType();
if (pointType != null)
foreach (PropertyInfo propertyItem in pointType.GetProperties())
{
if (propertyItem.Name == Key)
{ keyValue = (string)propertyItem.GetValue(o, null); }
}
return keyValue;
}
public static object MergeObject(this object source, object target)
{
if (source != null && target != null)
{
Type typeSource = source.GetType();
Type typeTarget = target.GetType();
//if both types are same, try to merge
if (typeSource != null && typeTarget != null && typeSource.FullName == typeTarget.FullName)
if (typeSource.IsClass && !typeSource.Namespace.Equals("System", StringComparison.InvariantCulture))
{
PropertyInfo[] propertyList = typeSource.GetProperties();
for (int index = 0; index < propertyList.Length; index++)
{
Type tempPropertySourceValueType = null;
object tempPropertySourceValue = null;
Type tempPropertyTargetValueType = null;
object tempPropertyTargetValue = null;
//get rid of indexers
if (propertyList[index].GetIndexParameters().Length == 0)
{
tempPropertySourceValue = propertyList[index].GetValue(source, null);
tempPropertyTargetValue = propertyList[index].GetValue(target, null);
}
if (tempPropertySourceValue != null)
tempPropertySourceValueType = tempPropertySourceValue.GetType();
if (tempPropertyTargetValue != null)
tempPropertyTargetValueType = tempPropertyTargetValue.GetType();
//if the property is a list
IList ilistSource = tempPropertySourceValue as IList;
IList ilistTarget = tempPropertyTargetValue as IList;
if (ilistSource != null || ilistTarget != null)
{
if (ilistSource != null)
ilistSource.MergeList(ilistTarget);
else
propertyList[index].SetValue(source, ilistTarget, null);
}
//if the property is a Dto
else if (tempPropertySourceValue != null || tempPropertyTargetValue != null)
{
if (tempPropertySourceValue != null)
tempPropertySourceValue.MergeObject(tempPropertyTargetValue);
else
propertyList[index].SetValue(source, tempPropertyTargetValue, null);
}
}
}
}
return source;
}
}
However, this works when the source property is null, if target has it, it will copy that to source.
IT can still be improved to merge when inconsistencies are there e.g. if FirstName="Tiju" and FirstName="John"
Any commments appreciated.
Thanks
TJ