Это не самое простое решение и может быть утомительным, но я реализовал то, что вам нужно, в моем проекте, расширив граф объектов, обновив свойство, как я его нашел, и отследив, что у меня есть. посетил. Это дало большие воспоминания старшекурсникам по информатике. =) * * Тысяча одна
По сути, возьмите свой объект, получите его свойства и поместите их в стек. Для каждого свойства в стеке проверьте, является ли это свойство, которое вы ищете. Обрабатывать, если совпадают, игнорировать, если простой тип данных, добавлять в стек, если сложный объект.
Несколько вещей, которые помогли мне в моей реализации:
- Создайте интерфейс, содержащий искомое свойство, и сделайте все классы, у которых это свойство, реализует его. Это облегчает опрос ваших классов с отражением.
- Старайтесь быть умными с типами сложных свойств, которые вы бросаете в стек для прохождения. Я использовал интерфейс, который обозначает, что этот сложный тип может иметь свойство, которое необходимо обновить.
- Используйте что-то (я использовал HashSet), чтобы пометить объекты, которые вы видели, и помочь предотвратить круговые обращения.
- У меня есть эта функциональность как часть пользовательского метода Save () BaseContext (наследующего EF
ObjectContext
). Я называю это исправлением, а затем звоню base.SaveChanges()
.
Как я уже сказал, это не простой, простой вопрос, но мой код обрабатывает графы глубоких объектов и обновляет несколько экземпляров свойств. Если интересно, я могу добавить псевдокод.
EDIT / UPDATE
Как показывает код, меня интересует обновление текущих значений UserName и DateTime для любого числа объектов, которые могут появиться на графике.
Примечания:
- Интерфейс
IInsertedInfo
содержит свойства, которые нам нужно обновить. Поэтому, если объект реализует IInsertedInfo
, мы знаем, как обновить его свойства.
- Объект, реализующий
IRequiresCurrentUserDateTime
, - это объект, который где-то на своем графике имеет объект, реализующий IInsertedInfo
.
- Это делается в контексте сохранения с EF. Я опрашиваю
ObjectStateManager
для объектов, которые должны быть сохранены / обновлены.
Я не претендую на то, чтобы это было самым кратким решением, но оно прекрасно работает для меня. Кроме того, проверки для IRequiresCurrentUserDateTime
и других фильтров (некоторые из них для ясности не используются) используются исключительно для обеспечения управляемости пространства поиска и предотвращения охвата объектов .NET, типов, не относящихся к классам и т. Д.
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);
}
}
}
}
}