初投稿(ブログすらない)なのであまり上手く書けませんが、間違い等があればご指摘頂きたいです…
プロパティをGetValueとかするリフレクションが遅かったので、その改善策を考えてみました。
private void Update(object model,List<PropertyInfo> query)
{
if (model == null)
return;
query.ForEach(i =>
{
PropertyInfo prop = model.GetType().GetProperty(i.Name);
if (prop != null && prop.PropertyType == i.PropertyType && prop.CanRead)
{
var val = prop.GetValue(model, null);
i.SetValue(view, val, null);
}
});
}
こんな感じでEntityとViewModelの詰め替えとかによく使ってました。
AutoMapperとかもあるけど…なんだか好きになれないので…
ただ100万回foreachでぶん回すとかすると遅いのでExpression Treeでマッパーを作ってみました。(Webアプリ100万回ぶん回す処理とかはないかな)
正直なところExpression Treeを完全に理解できてないので拙いコードになってる気がします。
/// <summary>
/// Propertyマッパー
/// </summary>
public class PropertyMapper
{
/// <summary>
/// デフォルトコンストラクタ
/// </summary>
private PropertyMapper()
{
}
/// <summary>
/// 型:TModel の CanRead なプロパティの値を 型:TEntity の対応する CanWrite なプロパティの値へ設定します。
/// 設定可能なプロパティは PropertyName と PropertyType が同一でなければなりません。
/// </summary>
/// <typeparam name="TModel">設定元タイプ</typeparam>
/// <typeparam name="TEntity">設定先タイプ</typeparam>
/// <param name="items">IEnmerableを実装した TModel</param>
/// <returns>IEnumerableを実装した TEntity</returns>
public static IEnumerable<TEntity> Mapper<TModel, TEntity>(IEnumerable<TModel> items)
where TModel : class, new()
where TEntity : class, new()
{
// 戻り値
var entities = new List<TEntity>();
// Getアクセッサ デリゲートキャッシュ
var getterDelegateItems = new Dictionary<int, Func<TModel, object>>();
// Setアクセッサ デリゲートキャッシュ
var setterDelegateItems = new Dictionary<int, Action<TEntity, object>>();
// Getアクセッサ デリゲート
Func<TModel, object> getter = null;
// Setアクセッサ デリゲート
Action<TEntity, object> setter = null;
// 設定対象プロパティリスト
var entityTargetProps = new List<PropertyInfo>();
// TEntityプロパティリスト ディクショナリ(パフォーマンス対策)
var entityProps = typeof(TEntity).GetProperties().Where(a => a.CanWrite).ToDictionary(a => a.Name);
// TModel プロパティリスト
var modelProps = typeof(TModel).GetProperties().Where(a => a.CanRead);
// 設定対象プロパティを抽出
PropertyInfo entityProp = null;
foreach (var modelProp in modelProps)
{
// TEntityプロパティリストに TModelプロパティと同一の名前で存在する、且つ、型が完全一致する場合
if (entityProps.TryGetValue(modelProp.Name, out entityProp) && entityProp.PropertyType == modelProp.PropertyType)
{
// 設定対象プロパティに追加
entityTargetProps.Add(entityProp);
}
}
// プロパティ設定
foreach (var item in items)
{
// 設定先TEntity
var entity = new TEntity();
// 設定対象プロパティ
foreach (var setProp in entityTargetProps)
{
// キャッシュされていないかどうかを判定し、未キャッシュの場合は生成
if (!getterDelegateItems.TryGetValue(setProp.MetadataToken, out getter))
{
// Getアクセッサ デリゲートを生成
getter = CreateGetDelegate<TModel>(setProp.Name);
// 生成したGetデリゲートをキャッシュ
getterDelegateItems.Add(setProp.MetadataToken, getter);
// Setアクセッサ デリゲートを生成
setter = CreateSetDelegate<TEntity>(setProp.Name);
// 生成したSetデリゲートをキャッシュ
setterDelegateItems.Add(setProp.MetadataToken, setter);
}
setter = setterDelegateItems[setProp.MetadataToken];
// プロパティ値を取得
var val = getter(item);
// プロパティ値を設定
setter(entity, val);
}
entities.Add(entity);
}
return entities;
}
/// <summary>
/// Setアクセッサ デリゲートを生成します
/// </summary>
/// <typeparam name="TModel">プロパティ値が設定されるオブジェクト型</typeparam>
/// <param name="memberName">設定するプロパティ名</param>
/// <returns>Setアクセッサ デリゲート</returns>
private static Action<TModel, object> CreateSetDelegate<TModel>(string memberName)
{
var target = Expression.Parameter(typeof(TModel), "target");
var value = Expression.Parameter(typeof(object), "value");
var left = Expression.PropertyOrField(target, memberName);
var right = Expression.Convert(value, left.Type);
var lambda = Expression.Lambda<Action<TModel, object>>(
Expression.Assign(left, right),
target,
value);
return lambda.Compile();
}
/// <summary>
/// Getアクセッサ デリゲートを生成します
/// </summary>
/// <typeparam name="TModel">プロパティ値が返されるオブジェクト型</typeparam>
/// <param name="propertyName">取得するプロパティ名</param>
/// <returns>Getアクセッサ デリゲート</returns>
private static Func<TModel, object> CreateGetDelegate<TModel>(string propertyName)
{
var target = Expression.Parameter(typeof(TModel), "target");
var lambda = Expression.Lambda<Func<TModel, object>>(
Expression.Convert(
Expression.PropertyOrField(target, propertyName),
typeof(object)),
target);
return lambda.Compile();
}
}
Non Expression Tree | Expression Tree | |
---|---|---|
1回目 | 2820ms | 463ms |
2回目 | 2849ms | 470ms |
3回目 | 2924ms | 614ms |
4回目 | 3527ms | 516ms |
一応速くなってます。