Изменение сущностей в событии OnFlushDirty в NHibernate

183
25 августа 2018, 21:20

Есть сущность "настройки", в которой хранится путь к данным. Есть сущности, которые пользуются настройками, чтобы строить свой путь к данным.

Хочется на изменении настроек валидировать пути остальных сущностей и пересохранять их при необходимости. Как это пытаюсь сделать:

  1. Перекрыл Interceptor и ловлю событие OnFlushDirty, которое позволяет отловить изменение нужного свойства:

    var folderIndex = propertyNames.ToList().IndexOf(nameof(Folder));
    if (folderIndex > -1 && previousState != null)
    {
      var previous = previousState[folderIndex] as string;
      var current = currentState[folderIndex] as string;
      if (previous != current)
      {
        var someEntities = session.Query<ISomeEntity>().Where(m => m.Setting == this).ToList();
        foreach (var someEntit in someEntities)
        {
          someEntity.RefreshFolder();
        }
      }
    }
    

Расчёт был на то, что раз изменения внутри сессии (уже в коммите транзакции), то изменения подцепятся автоматически. Не помогло. Если после someEntity.RefreshFolder(); добавить session.SaveOrUpdate(someEntity) то чуда не происходит и сущности тоже не сохраняются.

  1. Снаружи вызов настроек обёрнут в транзакцию:

    using (var tranc = session.OpenTransaction())
    {
      try
      {
        session.SaveOrUpdate(setting);
        tranc.Commit();
      }
      catch (System.Exception)
      {
        tranc.Rollback();
        throw;
      }
    }
    
  2. В SomeEntity есть ссылка на Setting, как видно в п1, и если я попытаюсь сделать отдельную транзакцию, то оно уходит в SO, т.к. для сохранения сущности становится необходима сохраненная настройка, а она снова вызовет сохранение сущности.

  3. У всех сессий включен session.FlushMode = FlushMode.Commit, чтобы случайные изменения в сессии (которые например потом были провалидированы исключением) не пытались сохраняться в базу.

В целом, хочется чтобы операция смены папки в настройках была транзакционной, потому что если нет - то можно просто после завершения транзакции делать те же операции не связывая их друг с другом, но тогда гарантировать валидность данных уже нельзя.

Answer 1

Судя по всему, после вызова tranc.Commit(); добавлять объекты для коммита уже нельзя. От этого и пляшем.

Основная причина использования OnFlushDirty - трекер изменений в сущности. Эту информацию можно достать и вручную:

  var impl = session.GetSessionImplementation();
  var key = impl.PersistenceContext.GetEntry(entity);
  if (key == null)
  {
    var name = impl.GuessEntityName(entity);
    var persister = impl.GetEntityPersister(name, entity);
    return new ChangeTrackerArgs(persister.GetPropertyValues(entity, EntityMode.Poco), null, persister.PropertyNames);
  }
  var current = key.Persister.GetPropertyValues(entity, EntityMode.Poco);
  return new ChangeTrackerArgs(current, key.LoadedState, key.Persister.PropertyNames);

Таким образом, каждый session.SaveOrUpdate(entity) превращается в

  var state = GetChangeTrackerArgs(entity)
  entity.BeforeSave(state);
  session.SaveOrUpdate(entity);

Т.е. вызов сохранения любой сущности может теперь в BeforeSave сделать аналогичный вызов для любой другой сущности и работать будет для любой вложенности.

Interceptor с его OnFlushDirty всё ещё нужен, т.к. кроме явно вызванных SaveOrUpdate есть ещё неявные изменения, которые поймал хибер. Поэтому, там аналогичный вызов entity.BeforeSave(new ChangeTrackerArgs(currentState, previousState, propertyNames)); Этот вызов уже не сумеет добавить в транзакцию сущности, но зато всё равно провалидирует сущность перед сохранением, если она вдруг попадёт в сохранение неявно.

В дополнение к вышесказанному, хоть это и не касается вопроса. Трекер изменений в хибере работает на сессиях, поэтому для нормальной работы ему нужно получение сущности в конкретной сессии, а потом и её изменение. Если сущность получена в одной сессии, а в другой пытается сохраниться - трекер будет пуст, т.е. все мучения выше - не сильно полезны.

READ ALSO
C# цепочка функций

C# цепочка функций

Есть 2 метода расширения :

184
Вырезать текст из предложения

Вырезать текст из предложения

Есть большое предложение, из которого нужно вырезать такую часть:

162
Реализация сокета socket.io + node.js + php

Реализация сокета socket.io + node.js + php

Я очень плох в сокетах и JS, поэтому прошу понимания)) Вдохновленный этой статьей, решил попробовать написать подобное у себяУ меня есть PHP скрипт,...

235
Контроллер PHP Laravel работа с JSON

Контроллер PHP Laravel работа с JSON

у меня есть JSON массив который хранится в базе данныхКак реализовать алгоритм изменения/добавления/удаления данных из этого массива в контроллере...

169