Object Relational Mapping via Reflection
Same thing 10-100 times faster.Additionally, IndexMap can be built only once per DataTable.using System;using System.Collections.Generic;using System.Data;using System.Linq;using System.Linq.Expressions;using System.Reflection;namespace ReflectiveReader{ public static...
Same thing 10-100 times faster.
Additionally,
IndexMap
can be built only once per DataTable
.
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ReflectiveReader
{
public static class DataAccessExtensions
{
//Getting Convert.ChangeType method's MethodInfo and store in static field to access later.
private static readonly MethodInfo ChangeType =
typeof(Convert).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == "ChangeType")
.Select(m => new { Method = m, Parameters = m.GetParameters() })
.Where(p => p.Parameters.Length == 2)
.Where(p => p.Parameters[0].ParameterType == typeof(Object) && p.Parameters[1].ParameterType == typeof(Type))
.Select(p => p.Method)
.Single();
/// <summary>
/// Static class. Container for precompiled property-accessing delegates.
/// </summary>
/// <typeparam name="TReflectedType">Type, wich property-accessing delegates will be constructed on access to PropertySetters static property.</typeparam>
private static class DataAccessExtensionReflectedType<TReflectedType> where TReflectedType : class
{
private static Dictionary<string, Action<TReflectedType, object>> _setters = null;
private static readonly object SyncRoot = new object();
//Property implemented using singletone pattern. Simplifies debugging. (Comparing with .cctor initialization)
public static IDictionary<string, Action<TReflectedType, object>> PropertySetters
{
get
{
if (_setters == null)
{
lock (SyncRoot)
{
if (_setters == null)
{//compile setters on first access
_setters = CompileSetters();
}
}
}
return _setters;
}
}
/// <summary>
/// Compiles delegates for setting properties of TReflectedType.
/// </summary>
/// <returns>Dictionary, contatning propertyname - delegate mapping.</returns>
private static Dictionary<string, Action<TReflectedType, object>> CompileSetters()
{
var setters = new Dictionary<string, Action<TReflectedType, object>>();
var type = typeof(TReflectedType);
var objectType = typeof(object);
//for each property in TRefelectedType
foreach (var property in type.GetProperties())
{
//that can be written
if (property.CanWrite)
{
var instance = Expression.Parameter(type, "instance");
var value = Expression.Parameter(objectType, "value");
//build lambda expression
var expression = Expression.Lambda(typeof(Action<TReflectedType, object>),
Expression.IfThen(
Expression.IsFalse(
Expression.TypeIs(value, typeof(DBNull))),
Expression.Assign(
Expression.Property(instance, property),
Expression.Convert(
Expression.Call(null, ChangeType, value, Expression.Constant(property.PropertyType))
,property.PropertyType)
)
)
, instance , value);
//compile it into delegate
var @delegate = expression.Compile();
//store delegate into dictionary and associate with property's name
setters.Add(property.Name, (Action<TReflectedType, object>)@delegate);
}
}
return setters;
}
}
/// <summary>
/// Builds Dictionary, associating column id in given DataTable with corresponding property-accessing delegate.
/// </summary>
/// <typeparam name="TInstanceType">Type of instance to write values to.</typeparam>
/// <param name="table">DataTable to read values from.</param>
/// <returns>Comumn id to property accessing delegate mapping.</returns>
public static IDictionary<int, Action<TInstanceType, object>> BuildIndexMap<TInstanceType>(this DataTable table) where TInstanceType : class
{
var indexMap = new Dictionary<int, Action<TInstanceType, object>>();
//for each compiled property-accessing delegate and property name pair
foreach (var pair in DataAccessExtensionReflectedType<TInstanceType>.PropertySetters)
{
//get corresponding column index by property name
var index = table.Columns.IndexOf(pair.Key);
//if found
if (index >= 0)
{
//add index and delegate to result dictionary
indexMap.Add(index, pair.Value);
}
}
return indexMap;
}
/// <summary>
/// Sets properties of specified instance from given DataRow using given ColumnIndex to delegate mapping.
/// </summary>
/// <typeparam name="TInstanceType">Type of instance to set values.</typeparam>
/// <param name="instance">Instance to set values.</param>
/// <param name="indexMap">ColumnIndex to property-accessing delegate mapping.</param>
/// <param name="row">Row to read values.</param>
public static void SetPropertiesFrom<TInstanceType>(this TInstanceType instance, IDictionary<int, Action<TInstanceType, object>> indexMap, DataRow row) where TInstanceType : class
{
foreach (var pair in indexMap)
{
pair.Value(instance, row[pair.Key]);
}
}
/// <summary>
/// Sets properties of specified instance from given DataRow. Builds ColumnIndex to property-accessing delegate mapping and uses it.
/// </summary>
public static void SetPropertiesFrom<TInstanceType>(this TInstanceType instance, DataRow row) where TInstanceType : class
{
SetPropertiesFrom(instance, BuildIndexMap<TInstanceType>(row.Table), row);
}
}
}