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
{
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();
private static class DataAccessExtensionReflectedType<TReflectedType> where TReflectedType : class
{
private static Dictionary<string, Action<TReflectedType, object>> _setters = null;
private static readonly object SyncRoot = new object();
public static IDictionary<string, Action<TReflectedType, object>> PropertySetters
{
get
{
if (_setters == null)
{
lock (SyncRoot)
{
if (_setters == null)
{
_setters = CompileSetters();
}
}
}
return _setters;
}
}
private static Dictionary<string, Action<TReflectedType, object>> CompileSetters()
{
var setters = new Dictionary<string, Action<TReflectedType, object>>();
var type = typeof(TReflectedType);
var objectType = typeof(object);
foreach (var property in type.GetProperties())
{
if (property.CanWrite)
{
var instance = Expression.Parameter(type, "instance");
var value = Expression.Parameter(objectType, "value");
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);
var @delegate = expression.Compile();
setters.Add(property.Name, (Action<TReflectedType, object>)@delegate);
}
}
return setters;
}
}
public static IDictionary<int, Action<TInstanceType, object>> BuildIndexMap<TInstanceType>(this DataTable table) where TInstanceType : class
{
var indexMap = new Dictionary<int, Action<TInstanceType, object>>();
foreach (var pair in DataAccessExtensionReflectedType<TInstanceType>.PropertySetters)
{
var index = table.Columns.IndexOf(pair.Key);
if (index >= 0)
{
indexMap.Add(index, pair.Value);
}
}
return indexMap;
}
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]);
}
}
public static void SetPropertiesFrom<TInstanceType>(this TInstanceType instance, DataRow row) where TInstanceType : class
{
SetPropertiesFrom(instance, BuildIndexMap<TInstanceType>(row.Table), row);
}
}
}