231 lines
8.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using System.ComponentModel;
using Csla.Properties;
namespace Csla.Data
{
/// <summary>
/// Map data from a source into a target object
/// by copying public property values.
/// </summary>
/// <remarks></remarks>
public static class DataMapper
{
#region Map from IDictionary
/// <summary>
/// Copies values from the source into the
/// properties of the target.
/// </summary>
/// <param name="source">A name/value dictionary containing the source values.</param>
/// <param name="target">An object with properties to be set from the dictionary.</param>
/// <remarks>
/// The key names in the dictionary must match the property names on the target
/// object. Target properties may not be readonly or indexed.
/// </remarks>
public static void Map(System.Collections.IDictionary source, object target)
{
Map(source, target, false);
}
/// <summary>
/// Copies values from the source into the
/// properties of the target.
/// </summary>
/// <param name="source">A name/value dictionary containing the source values.</param>
/// <param name="target">An object with properties to be set from the dictionary.</param>
/// <param name="ignoreList">A list of property names to ignore.
/// These properties will not be set on the target object.</param>
/// <remarks>
/// The key names in the dictionary must match the property names on the target
/// object. Target properties may not be readonly or indexed.
/// </remarks>
public static void Map(System.Collections.IDictionary source, object target, params string[] ignoreList)
{
Map(source, target, false, ignoreList);
}
/// <summary>
/// Copies values from the source into the
/// properties of the target.
/// </summary>
/// <param name="source">A name/value dictionary containing the source values.</param>
/// <param name="target">An object with properties to be set from the dictionary.</param>
/// <param name="ignoreList">A list of property names to ignore.
/// These properties will not be set on the target object.</param>
/// <param name="suppressExceptions">If <see langword="true" />, any exceptions will be supressed.</param>
/// <remarks>
/// The key names in the dictionary must match the property names on the target
/// object. Target properties may not be readonly or indexed.
/// </remarks>
public static void Map(
System.Collections.IDictionary source,
object target, bool suppressExceptions,
params string[] ignoreList)
{
List<string> ignore = new List<string>(ignoreList);
foreach (string propertyName in source.Keys)
{
if (!ignore.Contains(propertyName))
{
try
{
SetPropertyValue(target, propertyName, source[propertyName]);
}
catch (Exception ex)
{
if (!suppressExceptions)
throw new ArgumentException(
String.Format("{0} ({1})",
Resources.PropertyCopyFailed, propertyName), ex);
}
}
}
}
#endregion
#region Map from Object
/// <summary>
/// Copies values from the source into the
/// properties of the target.
/// </summary>
/// <param name="source">An object containing the source values.</param>
/// <param name="target">An object with properties to be set from the dictionary.</param>
/// <remarks>
/// The property names and types of the source object must match the property names and types
/// on the target object. Source properties may not be indexed.
/// Target properties may not be readonly or indexed.
/// </remarks>
public static void Map(object source, object target)
{
Map(source, target, false);
}
/// <summary>
/// Copies values from the source into the
/// properties of the target.
/// </summary>
/// <param name="source">An object containing the source values.</param>
/// <param name="target">An object with properties to be set from the dictionary.</param>
/// <param name="ignoreList">A list of property names to ignore.
/// These properties will not be set on the target object.</param>
/// <remarks>
/// The property names and types of the source object must match the property names and types
/// on the target object. Source properties may not be indexed.
/// Target properties may not be readonly or indexed.
/// </remarks>
public static void Map(object source, object target, params string[] ignoreList)
{
Map(source, target, false, ignoreList);
}
/// <summary>
/// Copies values from the source into the
/// properties of the target.
/// </summary>
/// <param name="source">An object containing the source values.</param>
/// <param name="target">An object with properties to be set from the dictionary.</param>
/// <param name="ignoreList">A list of property names to ignore.
/// These properties will not be set on the target object.</param>
/// <param name="suppressExceptions">If <see langword="true" />, any exceptions will be supressed.</param>
/// <remarks>
/// <para>
/// The property names and types of the source object must match the property names and types
/// on the target object. Source properties may not be indexed.
/// Target properties may not be readonly or indexed.
/// </para><para>
/// Properties to copy are determined based on the source object. Any properties
/// on the source object marked with the <see cref="BrowsableAttribute"/> equal
/// to false are ignored.
/// </para>
/// </remarks>
public static void Map(
object source, object target,
bool suppressExceptions,
params string[] ignoreList)
{
List<string> ignore = new List<string>(ignoreList);
PropertyInfo[] sourceProperties =
GetSourceProperties(source.GetType());
foreach (PropertyInfo sourceProperty in sourceProperties)
{
string propertyName = sourceProperty.Name;
if (!ignore.Contains(propertyName))
{
try
{
SetPropertyValue(
target, propertyName,
sourceProperty.GetValue(source, null));
}
catch (Exception ex)
{
if (!suppressExceptions)
throw new ArgumentException(
String.Format("{0} ({1})",
Resources.PropertyCopyFailed, propertyName), ex);
}
}
}
}
private static PropertyInfo[] GetSourceProperties(Type sourceType)
{
List<PropertyInfo> result = new List<PropertyInfo>();
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(sourceType);
foreach (PropertyDescriptor item in props)
if (item.IsBrowsable)
result.Add(sourceType.GetProperty(item.Name));
return result.ToArray();
}
#endregion
/// <summary>
/// Sets an object's property with the specified value,
/// coercing that value to the appropriate type if possible.
/// </summary>
/// <param name="target">Object containing the property to set.</param>
/// <param name="propertyName">Name of the property to set.</param>
/// <param name="value">Value to set into the property.</param>
public static void SetPropertyValue(
object target, string propertyName, object value)
{
PropertyInfo propertyInfo =
target.GetType().GetProperty(propertyName);
if (value == null)
propertyInfo.SetValue(target, value, null);
else
{
Type pType =
Utilities.GetPropertyType(propertyInfo.PropertyType);
Type vType =
Utilities.GetPropertyType(value.GetType());
if (pType.Equals(vType))
{
// types match, just copy value
propertyInfo.SetValue(target, value, null);
}
else
{
// types don't match, try to coerce
if (pType.Equals(typeof(Guid)))
propertyInfo.SetValue(
target, new Guid(value.ToString()), null);
else if (pType.IsEnum && vType.Equals(typeof(string)))
propertyInfo.SetValue(target, Enum.Parse(pType, value.ToString()), null);
else
propertyInfo.SetValue(
target, Convert.ChangeType(value, pType), null);
}
}
}
}
}