Merging .net object graph

Posted by Tiju John on Stack Overflow See other posts from Stack Overflow or by Tiju John
Published on 2010-06-16T09:08:54Z Indexed on 2010/06/16 9:12 UTC
Read the original article Hit count: 297

Filed under:
|
|
|

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

© Stack Overflow or respective owner

Related posts about reflection

Related posts about object