Исключение выходит из рабочего процесса, несмотря на активность TryCatch
У меня есть рабочий процесс внутри службы Windows, который представляет собой цикл, который периодически выполняет работу. Работа выполняется внутри TryCatch
деятельность. Try
собственность TransactionScope
действие, которое оборачивает некоторые пользовательские действия, которые читают и обновляют базу данных. Когда транзакция завершится неудачно, я ожидаю, что любое исключение, которое вызвало это, будет обнаружено TryCatch
, Однако мой рабочий процесс прерывается. У меня есть следующий рабочий процесс:
var wf = new While(true)
{
Body = new Sequence
{
Activities =
{
new TryCatch
{
Try = new TransactionScope
{
IsolationLevel = IsolationLevel.ReadCommitted,
Body = new Sequence
{
Activities = { ..custom database activities.. }
},
AbortInstanceOnTransactionFailure = false
},
Catches =
{
new Catch<Exception>
{
Action = new ActivityAction<Exception>
{
Argument = exception,
Handler = ..log error..
}
}
}
},
new Delay { Duration = new InArgument<TimeSpan>(duration) }
}
},
}
В моем случае, возможно, что база данных иногда недоступна, поэтому очевидно, что транзакция не будет зафиксирована. В этом случае происходит то, что рабочий процесс прерывается со следующим исключением:
System.OperationCanceledException: ошибка обработки текущего рабочего элемента привела к прерыванию рабочего процесса.
Внутреннее исключение:
System.Transactions.TransactionException: операция недопустима для состояния транзакции.
Это имеет смысл, потому что я только что выключил базу данных. Однако, почему это исключение не обрабатывается моим TryCatch
деятельность?
РЕДАКТИРОВАТЬ 1: Некоторая дополнительная информация. Я запускаю рабочий процесс, используя WorkflowApplication
учебный класс. Чтобы лучше увидеть, что происходит, я указал свойства Aborted
а также OnUnhandledException
, Когда возникает исключение, оно переходит непосредственно к Aborted
а также OnUnhandledException
пропускается (хотя это явно необработанное исключение).
РЕДАКТИРОВАТЬ 2: я включил журнал отладки, и это обеспечивает некоторую дополнительную информацию. "Пользовательские действия с базой данных" успешно завершены. Первая запись в журнале событий, которая указывает, что что-то не так, - это подробное сообщение уровня: транзакция во время выполнения завершилась с состоянием "Прервано". Затем я вижу информационное сообщение: WorkflowInstance Id: 'dbd1ba5c-2d8a-428c-970d-21215d7e06d9' E2E Activity (не уверен, что это значит). И информационное сообщение после этого : "Операция" System.Activities.Statements.TransactionScope ", DisplayName:" Транзакция для запуска немедленно проверяет ", InstanceId:" 389 "завершено в состоянии" Ошибка ".
После этого сообщения я вижу, что каждый родитель (включая TryCatch
активность) завершается в состоянии "Неисправность", заканчивая прерыванием моего рабочего процесса.
РЕДАКТИРОВАТЬ 3: Для ясности, все работает, как ожидалось, когда происходит исключение в любой из "пользовательских операций с базой данных". Обнаружено исключение, и рабочий процесс продолжается. Это происходит только тогда, когда транзакция не может быть зафиксирована в конце TransactionScope
, Смотрите следующую трассировку стека, которая регистрируется из Aborted
Перезвоните:
at System.Transactions.TransactionStateInDoubt.Rollback(InternalTransaction tx, Exception e)
at System.Transactions.Transaction.Rollback(Exception e)
at System.Activities.Runtime.ActivityExecutor.CompleteTransactionWorkItem.HandleException(Exception exception)
Если вы следите за звонками из TransactionScope.OnCompletion(...)
в конце концов вы прибудете на ActivityExecutor
класс из стека трассировки.
2 ответа
Транзакции совершаются асинхронно и по факту. Вы не можете реагировать на сбой транзакции для фиксации из-за проблемы на уровне менеджера ресурсов.
Как вы указали, вы можете иметь дело с исключениями, возникающими в вашей деятельности. Если вы посмотрите на записи отслеживания вашего рабочего процесса, я предполагаю, что вы увидите, что действие TryCatch закрыто до прерывания транзакции.
Много лет назад, когда я был руководителем программы в команде COM+, я изучал эту проблему, потому что часто люди хотят, чтобы транзакционный компонент (или рабочий процесс) мог реагировать на прерывание транзакции.
Асинхронный характер разрешения транзакции означает, что вы просто не можете реагировать на него в самом компоненте. Решение состоит в том, чтобы реагировать на вызывающего абонента, который затем может предпринять некоторые действия.
Предполагается, что после отмены транзакции ничто из состояния, заданного в транзакции, не может быть безопасно использовано - все будет отброшено, поскольку транзакция отменяется.
Поэтому просто добавлю к ответу Рона. Единственный вариант здесь - добавить SqlWorkflowInstanceStore и удалить действие Persist непосредственно перед TransactionScope. Когда транзакция прерывается, весь рабочий процесс будет прерван, но сохраненное в прошлом состояние все еще будет в базе данных постоянства, и рабочий процесс можно будет перезапустить из этого ранее сохраненного состояния и выполнить транзакцию снова.