Copying specific properties using Reflection

Hello again everyone. Happy Monday! Today, I will be going over yet another solution that I decided to tackle within the project I am currently working on that I thought was useful enough to share on my blog. I hope everyone enjoys it.

Have you ever wanted to copy fields from one object to the other of the same type, but you didn’t want “everything” to copy? You could almost say it is a merge, but one that you control what is merged over. Well, I had such a requirement because the current scenario had circumstances where the object that we have in memory has changed during an edit on the page. Now, if any of you have used MVC in the past or are using it currently, you may know that the object that is originally bound to the View is rebound when submission occurs and the form submission is sent back to the Action/ Controller. When this occurs, if there is data that was in there from the original object in memory and no hidden fields were created for these fields, those fields will be empty or defaulted.

This would cause you to want to possibly copy data of the defaulted properties from the object in memory prior to sending to the database. There are many ways to do this, one way is to use a one for one copy where you copy each property in the method itself, but what if this happens all over your application and you want this function to be centralized. You have to make the code more generic so it takes in a parameter that lists the properties you wish to have copied.

The first attempt to come up with a solution looked similar to this:

 private TModel Copy<TModel>(TModel itemToUpdate, IEnumerable<string> fieldsToCopy) where TModel : class
        {
                var itemFromCache = cache.Items.SingleOrDefault(x => x.GUID == itemToUpdate.GUID);

                     foreach (var field in fieldsToCopy)
                    {
                        itemFromCache.GetType().GetProperty(field).SetValue(itemFromCache, itemToUpdate.GetType().GetProperty(field).GetValue(itemToUpdate, null), null);
                    }
                }
            return itemFromCache;
        }

The majority of the code is self explainitory. First, we take the cache object (which in our case is a public cache of memory) and find the item we wish to update by GUID.
Then using reflection and the second parameter, we loop through each field and update the proper field and return the updated object back.
This was very close, with one problem. The second parameter that holds the fields you wish to copy is a list of strings. This works just fine,
but we want this to be very generic and updatable. using strings, you will not get intellisense nor will you be able to easily refactor if a property changes name.
One way to fix this is using the params keyword for .NET. This keyword allows you to pass in an array of some object into it. This object can be generic and the array can be as big as you wish. Perfect!

This is what it looks like with the new revision that I actually implemented.

 public TModel Copy<TModel>(TModel itemToUpdate, params Expression<Func<TObject, object>>[ ] propertiesToCopywhere TModel : class
        {
                var itemFromCache = cache.Items.SingleOrDefault(x => x.GUID == itemToUpdate.GUID);

 if (!fromObject.GetType().Equals(itemFromCache.GetType()))
            {
                throw new InvalidOperationException("The Object being copied must be of the same type as the target object");
            }

            // While grabing the property string values, we need to make sure it is not a UnaryExpression (boolean, DateTime etc..)
            // Found solution here: http://blogs.lessthandot.com/index.php/DesktopDev/MSTech/when-is-a-lambdaexpression-a
            var propertyNames = propertiesToCopy.Select(expression => (MemberExpression)(expression.Body is UnaryExpression ? ((UnaryExpression)expression.Body).Operand : expression.Body))
                                                    .Select(body => body.Member.Name).ToList();

                     foreach (var field in propertyNames 
                    {
                        itemFromCache.GetType().GetProperty(field).SetValue(itemFromCache, itemToUpdate.GetType().GetProperty(field).GetValue(itemToUpdate, null), null);
                    }
                }
            return itemFromCache;
        }

 

You will notice that I added a check to make sure the object being passed in is of the same type as the cached object. I have been bitten by that too many times
not to have it in there. I also changed out the string list parameter for the params object. You will also notice that the
array I am using is our favorite Expression<Func<T,U>>. This will allow us to pass in anonymous types in as parameters and they will be both strongly
typed and allow multiple properties (the array).
The use of a few lambda expressions in the method will allow us to parse the properties out and use the results as the property names.
You will notice in the lambda expression that parses the properties (propertyNames), that we are checking for both Member and Unary Expressions.
UnaryExpressios are objects like DateTime, Boolean etc.). I placed a comment with the link I found that recommended this above the expression
so that you can read more about it.
Now for the syntax of using this method.

 var data = Copy(updatedModel, property => property.Prop1, property => property.Prop2, property.Prop3);

Hope everyone enjoyed, and please feel free to comment or ask questions:

Happy Coding Smile

Advertisements

About Gregg Coleman

I am Senior-level Software Engineer working primarily these days with .NET. I have a good working knowledge of ASP.NET MVC, Web Forms, WCF web services and Windows Services. I spend much of my time in the Web Services (SOAP and REST) world in my current job designing and implementing various SOA architectures. I have been in the software engineering industry for about 6 years now and will not now nor ever consider myself an "expert" in programming because there is always so much to learn. My favorite thing about designing software is there are always new emerging technologies and something to learn every day! My current job has me spending much of my job on the bleeding edge of technologies and changing gears all the time, so I'm never bored and always challenged. On my spare time I enjoy weight training, reading and venturing to new places near by. Of course programing and learning new technologies are another hobby of mine.
This entry was posted in .NET, .NET 4.5, ASP.NET, Classes, Data Mapping, Generics, LINQ, Programming, Programming Paradims, Uncategorized and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s