Skip to content

Instantly share code, notes, and snippets.

@radleta
Created July 1, 2011 20:04
Show Gist options
  • Save radleta/1059288 to your computer and use it in GitHub Desktop.
Save radleta/1059288 to your computer and use it in GitHub Desktop.
Helper Classes to Copy Properties
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
namespace Helpers
{
/// <summary>
/// Used by the <see cref="PropertyHelper.CopyProperties"/> method to
/// know which properties to skip.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class IgnorePropertiesAttribute : Attribute
{
/// <summary>
/// Initialize a new instance of the class.
/// </summary>
/// <param name="ignoredPropertyNames"></param>
public IgnorePropertiesAttribute(params string[] ignoredPropertyNames)
{
this.IgnoredPropertyNames = new List<string>(ignoredPropertyNames);
}
/// <summary>
/// The list of names of the properties to ignore.
/// </summary>
public List<string> IgnoredPropertyNames { get; private set; }
}
/// <summary>
/// Helper methods for properties.
/// </summary>
public static class PropertyHelper
{
/// <summary>
/// Copies all the properties from the source to the destination. If they do not have
/// matching properties, you must use the <see cref="IgnoredPropertyNamesAttribute"/>
/// to destinate which properties should be skipped.
/// </summary>
/// <typeparam name="SOURCE_TYPE">The source type.</typeparam>
/// <typeparam name="DESTINATION_TYPE">The destination type.</typeparam>
/// <param name="source">The source instance to copy the data FROM.</param>
/// <param name="destination">The destination instance to copy the data TO.</param>
public static void CopyProperties(object source, object destination)
{
if (source == null)
{
throw new System.ArgumentNullException("source");
}
if (destination == null)
{
throw new System.ArgumentNullException("destination");
}
Type sourceType = source.GetType();
Type destType = destination.GetType();
var ignoredProperties = new List<string>();
// get ignored properties on the source type
var ignorePropertiesAttrs = sourceType.GetCustomAttributes(typeof(IgnorePropertiesAttribute), true);
if (ignorePropertiesAttrs.Length > 0 && ignorePropertiesAttrs[0] is IgnorePropertiesAttribute)
ignoredProperties.AddRange((ignorePropertiesAttrs[0] as IgnorePropertiesAttribute).IgnoredPropertyNames);
// get ignored properties from the destination type
ignorePropertiesAttrs = destType.GetCustomAttributes(typeof(IgnorePropertiesAttribute), true);
if (ignorePropertiesAttrs.Length > 0 && ignorePropertiesAttrs[0] is IgnorePropertiesAttribute)
ignoredProperties.AddRange((ignorePropertiesAttrs[0] as IgnorePropertiesAttribute).IgnoredPropertyNames);
var sourceProperties = new List<PropertyInfo>(sourceType.GetProperties());
sourceProperties = sourceProperties.FindAll(prop => !ignoredProperties.Contains(prop.Name) && prop.CanRead);
var destProperties = new List<PropertyInfo>(destType.GetProperties());
destProperties = destProperties.FindAll(prop => !ignoredProperties.Contains(prop.Name) && prop.CanWrite);
var destPropertyDictionary = destProperties.ToDictionary<PropertyInfo, string>(prop => prop.Name);
// validate the source properties exist on destination before we start modifying the destination
var missingPropertiesText = new System.Text.StringBuilder();
foreach (var sourceProperty in sourceProperties)
{
if (!destPropertyDictionary.ContainsKey(sourceProperty.Name))
{
if (missingPropertiesText.Length > 0) missingPropertiesText.Append(", ");
missingPropertiesText.Append(sourceProperty.Name);
}
}
// blow up if they are missing
if (missingPropertiesText.Length>0)
throw new System.ArgumentOutOfRangeException("destination", destType.FullName, string.Format("Destination type does not have all the expected properties. Either the properties must exist or you must use the IgnorePropertiesAttribute and skip the missing properties. This is to be explicit and ensure the data is copied. Missing Properties = {0}", missingPropertiesText));
// copy all properties from the source to the destination
foreach (var sourceProperty in sourceProperties)
{
var destProperty = destPropertyDictionary[sourceProperty.Name];
object sourceValue = sourceProperty.GetValue(source, null);
destProperty.SetValue(destination, sourceValue, null);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment