Вопрос

Как можно сделать так, чтобы логика обработчика события, реализованная на событийном слое посредством класса-наследника BaseEntityEventListener, выполнялась в одной транзакции с операцией, вызвавшей этот обработчик события?

Как можно сделать так, чтобы логика обработчика события, реализованная на событийном слое посредством класса-наследника BaseEntityEventListener, выполнялась в одной транзакции с операцией, вызвавшей этот обработчик события?

Например, при добавлении некоторой корневой сущности я хочу одновременно создать несколько сущностей, связанных с корневой. Требуется, чтобы все эти действия выполнились атомарно. Создание одного без другого считается нарушением целостности.

В процессе выполнения логики обработчика события OnInserted, которая добавляет связанные сущности, могут возникнуть исключения, которые я не могу предвидеть, или которые мне накладно предугадывать в обработчике OnInserting для предотвращения создания самой корневой сущности.

Нравится

6 комментариев

Возможно вопрос нужно сформулировать немного по другому. Есть ли механизм заключения в транзакцию стандартных действий, запускаемых кнопками "Сохранить" и "Удалить"? Чтобы и обработчики событий отработали в этой транзакции.

Михаил, сами по себе транзакции в серверном коде использовать можно. Например, в схеме EntityArchiver:

private void HistorizeEntities(bool hasCascade) {
	using (_dbExecutor) {
		try {
			_dbExecutor.StartTransaction();
			InsertArchivingData(_entitySet.Entities);
			if (hasCascade) {
				DeleteReferencedEntityData(_entitySet.Entities);
			} else {
				DeleteAllEntitiesData(_entitySet.Entities);
			}
			_dbExecutor.CommitTransaction();
		} catch (Exception exception) {
			_dbExecutor.RollbackTransaction();
			throw exception;
		}
	}
}

 Но для вызова внутри транзакции логики событий встроенного БП навряд ли получится это применить.

Как обходные варианты можно рассмотреть либо написание хранимки, делающей в одной транзакции всю работу по добавлению в разные таблицы и вызов её вместо стандартной логики сохранения. Либо не хранимки, а функции на C# с использованием транзакций как в примере выше. В обоих случаях нужно не забыть убедиться в работоспособности всей стандартной логики заполнения полей, нумерации, заполнения прав и др. деталей.

Либо же можно реализовать в базе триггер на событии вставки в основную таблицу и в нём создавать записи на детали с нужными значениями. Для гарантии атомарности можно даже сделать его instead of insert, добавляя там в одной транзакции в обе таблицы.

 

Думаю, что поддержка транзакций в такой серьезной системе должна осуществляться штатными средствами. Например, в мастере раздела на странице редактирования можно было бы вывести флажок "Выполнять действия в транзакции". И лучше, чтобы этот флажок был установлен по умолчанию. Даже лучше, чтобы эти стандартные действия (создание, редактирование, удаление и др.) выполнялись в транзакции вообще без каких либо настроек.

Михаил, уже зафиксирована идея «Возможность сохранять несколько entity в одной транзакции». В то же время, поскольку на объекте может быть куча дополнительной логики на событии сохранения: во встроенном БП, событийном слое, отдельных БП, кейсах, оперирующая с записями в других разделах, обернуть это всё в транзакцию может быть невозможно. Плюс бизнес-процессы являются интерпретируемыми и даже без таймеров и действий пользователя отрабатывают не мгновенно.

Зверев Александр,

MS Dynamics все эти ограничения не мешают, к слову. (Не сдержался, извините. Сколько работаю с Creatio, как вспоминаю про транзакции и разваливающиеся данные - хочется нецензурно ругаться.) То, что трудоёмко - выделяется в асинхронные операции, то, что необходимо сделать исключительно совместно - в синхронные. отдельно пре-валидация выполняется вне транзакции, но может "срубить" основную операцию. Произошла ошибка в обработчике пост-операции - откатывается вся операция.

Александр, если для конкретных операций обеспечение консистентности данных критично, лучше вынести их в отдельную хранимку, где и вызывать их внутри транзакции. Хранимку можно запускать из C#-кода. Затем, если нужно обеспечить универсальность применения, можно вынести логику в действие с параметрами, которое можно будет вызывать из БП без написания кода.

Показать все комментарии