Как можно сделать так, чтобы логика обработчика события, реализованная на событийном слое посредством класса-наследника BaseEntityEventListener, выполнялась в одной транзакции с операцией, вызвавшей этот обработчик события?
Как можно сделать так, чтобы логика обработчика события, реализованная на событийном слое посредством класса-наследника BaseEntityEventListener, выполнялась в одной транзакции с операцией, вызвавшей этот обработчик события?
Например, при добавлении некоторой корневой сущности я хочу одновременно создать несколько сущностей, связанных с корневой. Требуется, чтобы все эти действия выполнились атомарно. Создание одного без другого считается нарушением целостности.
В процессе выполнения логики обработчика события OnInserted, которая добавляет связанные сущности, могут возникнуть исключения, которые я не могу предвидеть, или которые мне накладно предугадывать в обработчике OnInserting для предотвращения создания самой корневой сущности.
Нравится
Возможно вопрос нужно сформулировать немного по другому. Есть ли механизм заключения в транзакцию стандартных действий, запускаемых кнопками "Сохранить" и "Удалить"? Чтобы и обработчики событий отработали в этой транзакции.
Михаил, сами по себе транзакции в серверном коде использовать можно. Например, в схеме 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#-кода. Затем, если нужно обеспечить универсальность применения, можно вынести логику в действие с параметрами, которое можно будет вызывать из БП без написания кода.