エンティティのナビゲーションプロパティが何か設定されていると都合が悪い場面があるとか何とかで、いっそすべて null クリアしてしまえ、って感じの拡張。
ようこそExpression 沼へ。
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
var buzz = new Buzz();
buzz.ClearReference();
Console.WriteLine("process end.");
Console.ReadLine();
}
}
static class ClearReferencesExtension
{
static Dictionary<Type, ClearReferenceActions> ClearEntityReferences = new Dictionary<Type, ClearReferenceActions>();
public static void ClearReference<TEntity>(this TEntity entity)
{
if (!ClearEntityReferences.ContainsKey(typeof(TEntity)))
{
CreateClearReference<TEntity>();
}
var clearActions = ClearEntityReferences[typeof(TEntity)];
foreach(var action in clearActions.Actions)
{
action.Invoke(entity);
}
}
static void CreateClearReference<TEntity>()
{
var clear = new ClearReferenceActions();
var entityType = typeof(TEntity);
var parameter = Expression.Parameter(typeof(object), "$entity");
var properties = typeof(Buzz).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
foreach (var property in properties)
{
if (property.PropertyType.IsValueType)
{
continue;
}
if (property.PropertyType == typeof(string))
{
continue;
}
var nullable = property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
if (nullable)
{
var baseType = Nullable.GetUnderlyingType(property.PropertyType);
if (baseType.IsValueType)
{
continue;
}
if (baseType == typeof(string))
{
continue;
}
}
var propertyAccess = Expression.Property(Expression.Convert(parameter, entityType), property);
var assign = Expression.Assign(propertyAccess, Expression.Constant(null, property.PropertyType));
Expression<Action<object>> lambda = Expression.Lambda<Action<object>>(assign, parameter);
var action = lambda.Compile();
clear.Actions.Add(action);
}
ClearEntityReferences.Add(entityType, clear);
}
class ClearReferenceActions
{
public List<Action<object>> Actions = new List<Action<object>>();
}
}
注意
EF がマッピングしそうなプロパティを除外しようとしているけど、 DateTimeOffset とかHieralkeyId とかその他ValueObject的なものは一切考慮してないのでそういうプロパティを使っている場合はクリア対象にするしないを注意深く設定する必要がある。