Click here to Skip to main content
Click here to Skip to main content

Object Relational Mapping via Reflection

By , 15 Jul 2011
 
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);
        }
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Kelqualyn
Russian Federation Russian Federation
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHandling NullablesmemberJörgen Andersson5 Nov '12 - 2:52 
AnswerRe: Handling NullablesmemberKelqualyn5 Nov '12 - 7:20 
GeneralRe: Yep. Using CodeDom compiler for such task probably will resu...memberKelqualyn19 Jul '11 - 21:45 
GeneralRe: Thanks heaps for updating the code, I understand what you ar...memberSimon Bridge19 Jul '11 - 17:56 
GeneralTry to convert step-by-step instead of direct conversion. Ev...memberKelqualyn4 Aug '11 - 9:56 
GeneralReal nice! I tried to implement type conversion to Enum type...membersimmerzeel3 Aug '11 - 4:20 
GeneralRe: Look at next post :-)memberKelqualyn4 Aug '11 - 20:57 
GeneraldeletedmemberKelqualyn14 Jul '11 - 20:48 
GeneralI really wanted to do a comparative test, running 1000 insta...memberSimon Bridge14 Jul '11 - 20:07 
GeneralRe: To compile this code you need to target .NET Framework 4. Bu...memberKelqualyn15 Jul '11 - 6:26 
GeneralReason for my vote of 3 This does not even compile... which ...memberSimon Bridge14 Jul '11 - 20:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 15 Jul 2011
Article Copyright 2011 by Kelqualyn
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid