Как перемещаться по дочерним объектам Entity Framework в универсальном методе? - PullRequest
0 голосов
/ 12 ноября 2010

У меня есть общий метод в моем хранилище, который обновляет свойство, общее для всех объектов в моей модели edmx:

    private void SetUpdateParams(TEntity entity)
    {
        PropertyInfo prop = typeof(TEntity).GetProperty("CommonProperty");

        prop.SetValue(entity, "Some Value", null);
    }

Это свойство вызывается методами добавления, обновления и удаления. Пример:

    public void Delete(TEntity entity)
    {
        SetUpdateParams(entity);
        _objectSet.DeleteObject(entity);
        txDB.SaveChanges();
    }

Это все работает невероятно хорошо, пока я не попытаюсь включить детей в сценарий каскадного удаления. Поскольку sprocs, которые я должен использовать, требует, чтобы это определенное свойство было установлено, я теперь должен выполнить рекурсивную связь и установить это свойство для всех загруженных потомков в ObjectSet. Проблема в том, что я никак не могу понять, как это сделать. Кто-нибудь делал что-то подобное раньше?

1 Ответ

1 голос
/ 16 ноября 2010

Это не самое простое решение и может быть утомительным, но я реализовал то, что вам нужно, в моем проекте, расширив граф объектов, обновив свойство, как я его нашел, и отследив, что у меня есть. посетил. Это дало большие воспоминания старшекурсникам по информатике. =) * * Тысяча одна

По сути, возьмите свой объект, получите его свойства и поместите их в стек. Для каждого свойства в стеке проверьте, является ли это свойство, которое вы ищете. Обрабатывать, если совпадают, игнорировать, если простой тип данных, добавлять в стек, если сложный объект.

Несколько вещей, которые помогли мне в моей реализации:

  • Создайте интерфейс, содержащий искомое свойство, и сделайте все классы, у которых это свойство, реализует его. Это облегчает опрос ваших классов с отражением.
  • Старайтесь быть умными с типами сложных свойств, которые вы бросаете в стек для прохождения. Я использовал интерфейс, который обозначает, что этот сложный тип может иметь свойство, которое необходимо обновить.
  • Используйте что-то (я использовал 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);
                }
            }
        }
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...