Есть сущность "настройки", в которой хранится путь к данным.
Есть сущности, которые пользуются настройками, чтобы строить свой путь к данным.
Хочется на изменении настроек валидировать пути остальных сущностей и пересохранять их при необходимости. Как это пытаюсь сделать:
Перекрыл 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
Расчёт был на то, что раз изменения внутри сессии (уже в коммите транзакции), то изменения подцепятся автоматически. Не помогло.
Если после someEntity.RefreshFolder(); добавить session.SaveOrUpdate(someEntity) то чуда не происходит и сущности тоже не сохраняются.
Снаружи вызов настроек обёрнут в транзакцию:
using (var tranc = session.OpenTransaction())
{
try
{
session.SaveOrUpdate(setting);
tranc.Commit();
}
catch (System.Exception)
{
tranc.Rollback();
throw;
}
}
В SomeEntity есть ссылка на Setting, как видно в п1, и если я попытаюсь сделать отдельную транзакцию, то оно уходит в SO, т.к. для сохранения сущности становится необходима сохраненная настройка, а она снова вызовет сохранение сущности.
У всех сессий включен session.FlushMode = FlushMode.Commit, чтобы случайные изменения в сессии (которые например потом были провалидированы исключением) не пытались сохраняться в базу.
В целом, хочется чтобы операция смены папки в настройках была транзакционной, потому что если нет - то можно просто после завершения транзакции делать те же операции не связывая их друг с другом, но тогда гарантировать валидность данных уже нельзя.
Ответ
Судя по всему, после вызова 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));
Этот вызов уже не сумеет добавить в транзакцию сущности, но зато всё равно провалидирует сущность перед сохранением, если она вдруг попадёт в сохранение неявно.
В дополнение к вышесказанному, хоть это и не касается вопроса. Трекер изменений в хибере работает на сессиях, поэтому для нормальной работы ему нужно получение сущности в конкретной сессии, а потом и её изменение. Если сущность получена в одной сессии, а в другой пытается сохраниться - трекер будет пуст, т.е. все мучения выше - не сильно полезны.
Комментариев нет:
Отправить комментарий