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