Click here to Skip to main content
Click here to Skip to main content
Alternative Tip

Tagged as

Go to top

Object Relational Mapping via Reflection

, 15 Jul 2011
Rate this:
Please Sign up or sign in to vote.
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);
        }
    }
}

License

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

Share

About the Author

Kelqualyn

Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
QuestionHandling Nullables PinmemberJörgen Andersson5-Nov-12 2:52 
AnswerRe: Handling Nullables PinmemberKelqualyn5-Nov-12 7:20 
GeneralRe: Yep. Using CodeDom compiler for such task probably will resu... PinmemberKelqualyn19-Jul-11 21:45 
GeneralRe: Thanks heaps for updating the code, I understand what you ar... PinmemberSimon Bridge19-Jul-11 17:56 
GeneralTry to convert step-by-step instead of direct conversion. Ev... PinmemberKelqualyn4-Aug-11 9:56 
GeneralReal nice! I tried to implement type conversion to Enum type... Pinmembersimmerzeel3-Aug-11 4:20 
GeneralRe: Look at next post :-) PinmemberKelqualyn4-Aug-11 20:57 
Generaldeleted PinmemberKelqualyn14-Jul-11 20:48 
GeneralI really wanted to do a comparative test, running 1000 insta... PinmemberSimon Bridge14-Jul-11 20:07 
GeneralRe: To compile this code you need to target .NET Framework 4. Bu... PinmemberKelqualyn15-Jul-11 6:26 
GeneralReason for my vote of 3 This does not even compile... which ... PinmemberSimon Bridge14-Jul-11 20:04 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 15 Jul 2011
Article Copyright 2011 by Kelqualyn
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid