这不是最简单的解决方案,可能有点乏味,但我通过跨越对象图、在找到属性时更新属性以及跟踪我访问过的内容,实现了您在我的项目中所需的功能。它给了计算机科学本科课程一个很好的倒叙。=)
基本上,获取对象,获取其属性,并将其推送到堆栈上。对于堆栈上的每个属性,测试它是否是您要查找的属性。如果匹配则处理,如果是简单数据类型则忽略,如果是复杂对象则添加到堆栈。
有几件事对我的实现有帮助:
-
-
试着聪明地处理要遍历的堆栈上的复杂属性类型。我使用了一个接口,该接口指定此复杂类型可能具有需要更新的属性。
-
-
我将此功能作为BaseContext的一部分(继承EF的
ObjectContext
)自定义Save()方法。我叫这个修理工然后
base.SaveChanges()
正如我所说,这不是一个简单、直接的问题,但我的代码处理深度对象图并更新多个属性实例。如果有兴趣的话,我可以继续使用一些伪代码。
如代码所示,我对更新图形中可能出现的任意数量对象的当前用户名和日期时间值感兴趣。
笔记:
-
接口
IInsertedInfo
包含我们需要更新的属性。所以如果一个对象实现
,我们知道要更新它的属性。
-
实现的对象
IRequiresCurrentUserDateTime
是一个在其图中某处有一个实现
.
-
ObjectStateManager
对于需要保存/更新的对象。
我不认为这是最简洁的解决方案,但它对我很好。另外,检查
IRequiresCurrentUserDateTime
private void HandleInsertedUserNames(string userName)
{
// grab any entity that requires a current user value
var requiresUser = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified)
.Where(ose => ose.Entity is IRequiresCurrentUserDateTime
|| ose.Entity is IInsertedInfo)
.Select(ose => ose.Entity);
var now = DateTime.Now;
object current;
var seen = new HashSet<object>();
var stack = new Stack<object>();
// for each entity requiring a current user value...
foreach (var obj in requiresUser)
{
// traverse its object graph and update any objects that implement IRequiresCurrentUserDateTime
stack.Push(obj);
while (stack.Count > 0)
{
current = stack.Pop();
if (current != null && !seen.Contains(current))
{
// mark object as seen
seen.Add(current);
// if object implements IInsertedInfo, then set its property
if (current is IInsertedInfo)
{
(current as IInsertedInfo).UserName = userName;
(current as IInsertedInfo).DateTime = now;
// we can continue on to the next object in the stack if we've hit an IInsertedInfo
continue;
}
// REMOVED FILTERING TESTS I USED TO REDUCE SEARCH SPACE (e.g.: is modified?)
if (current is IRequiresCurrentUserDateTime)
{
// push any instance, public properties and push them to the stack
current.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
// HERE YOU CAN FILTER THE PROPERTY COLLECTION VIA WHERE CLAUSES (e.g.: only certain namespace, type, etc)
// select the actual value of the property
.Select(type => type.GetValue(current, null))
// further filter -- only values NOT already in the requiresUser
// list and those that implement IRequiresCurrentUserDateTime or IInsertedInfo
.Where(value => !requiresUser.Contains(value)
&& (value is IRequiresCurrentUserDateTime
|| value is IInsertedInfo))
.ToList().ForEach(stack.push);
}
}
}
}
}