DataRowからエンティティにデータをマップするとか

データストアからエンティティを取り出す際に所謂ORMを使うわけだが、そのためのマッパー準備とか。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection;

    public class ObjectBinder
    {
        static Dictionary<string, Dictionary<string, IFieldBinder>> classBinders;

        static ObjectBinder()
        {
            classBinders = new Dictionary<string, Dictionary<string, IFieldBinder>>();
        }

        public ObjectBinder()
        {
        }
        public T toEntity<T>(DataRow row)
            where T : class, new()
        {
            var entity = new T();
            Dictionary<string, IFieldBinder> classBinder = getBinder(typeof(T));
            DataColumnCollection columns = row.Table.Columns;
            foreach (string fieldName in classBinder.Keys)
            {
                if (columns.Contains(fieldName))
                {
                    IFieldBinder binder = classBinder[fieldName] as IFieldBinder;
                    binder.setValue (entity, row[fieldName]);
                }
            }

            return entity;
        }
        public Tuple toTuple(object entity)
        {
            Dictionary<string, IFieldBinder> classBinder = getBinder(entity.GetType());

            Tuple tuple = new Tuple();

            foreach (string fieldName in classBinder.Keys)
            {
                IFieldBinder binder = classBinder[fieldName] as IFieldBinder;
                tuple[fieldName] = binder.getValue(entity);
            }

            return tuple;
        }
        public object getValue(object entity, string fieldName)
        {
            Dictionary<string, IFieldBinder> classBinder = getBinder(entity.GetType());

            IFieldBinder binder = classBinder[fieldName] as IFieldBinder;
            object value = binder.getValue(entity);
            return value;
        }
        Dictionary<string, IFieldBinder> getBinder(Type t)
        {
            if (classBinders.ContainsKey(t.FullName))
            {
                return classBinders[t.FullName];
            }
            Dictionary<string, IFieldBinder> classBinder = createClassBinder(t);
            classBinders.Add(t.FullName, classBinder);
            return classBinder;
        }
        Dictionary<string, IFieldBinder> createClassBinder(Type t)
        {
            Dictionary<string, IFieldBinder> classBinder = new Dictionary<string, IFieldBinder>();
            PropertyInfo[] properties = t.GetProperties();
            foreach (PropertyInfo property in properties)
            {
                Type binderType = typeof(FieldBinder<,>).MakeGenericType(t, property.PropertyType);

                IFieldBinder binder = Activator.CreateInstance(binderType, property) as IFieldBinder;

                classBinder.Add(property.Name, binder);


            }
            return classBinder;
        }

    }
    interface IFieldBinder
    {
        void setValue(object entity, object value);
        object getValue(object entity);
    }
    class FieldBinder<TEntity, TProperty> : IFieldBinder
    {
        public delegate void GenericSetterDelegate<T1, T2>(T1 entity, T2 value);
        public delegate T2 GenericGetterDelegate<T1, T2>(T1 entity);
        GenericSetterDelegate<TEntity, TProperty> setterDelegate;
        GenericGetterDelegate<TEntity, TProperty> getterDelegate;
        public FieldBinder(PropertyInfo property)
        {
            MethodInfo setter = property.GetSetMethod();
            MethodInfo getter = property.GetGetMethod();
            setterDelegate = Delegate.CreateDelegate(typeof(GenericSetterDelegate<TEntity, TProperty>), setter) as GenericSetterDelegate<TEntity, TProperty>;
            getterDelegate = Delegate.CreateDelegate(typeof(GenericGetterDelegate<TEntity, TProperty>), getter) as GenericGetterDelegate<TEntity, TProperty>;

        }
        public void setValue(object entity, object value)
        {
            this.setValue((TEntity)entity, (TProperty)value);
        }
        public void setValue(TEntity entity, TProperty value)
        {
            setterDelegate(entity, value);
        }
        public object getValue(object entity)
        {
            return getValue((TEntity)entity);
        }
        public TProperty getValue(TEntity entity)
        {
            return getterDelegate(entity);
        }
    }
    public class Tuple
    {
        Dictionary<object, object> keyValues;
        public Tuple()
        {
            keyValues = new Dictionary<object,object>();
        }
        public object this[object key]
        {
            get
            {
                return keyValues[key];
            }
            set
            {
                keyValues[key] = value;
            }
        }
    }

FieldBinderとIFieldBinderを分けているのは,ObjectBinder内でFieldBinderインスタンスを作っても呼び出せないからsetValue(object,object)とgetValue(object)をインターフェースにするため。
直接プロパティをセット/ゲットする時間の4倍から5倍はかかるが、PropertyInfoをキャッシュする場合の10分の1程度の時間で済むのでかなり高速。クラスのメタデータを使う動的ORMなら多分これで十分。

Tupleクラスは別に生のDictionaryを使っても良いけどシリアライズとかメソッドチェーンとかやりたいので別にクラスを作った。entity = datasource.get(key);みたいな事をやる場合、こんな風に出来る予定。

var datasource = new DataSoruce<Entity>();
var entity = datasource.get((new Tuple()).set("id",1).set("ordinal_position",10));

あまり使いたくはないが、複合キーを使いたいってリクエストはやっぱりあるので。

MethodInfoを直接使わずにDelegateを作るやり方はこれを参考にした。
http://d.hatena.ne.jp/crimsonwoods/20071206/1196923555

なお、このクラスを「フォームおぶじぇくと」の中で使うのは設計ミスですよお客さん。