我在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'EME活动(不确定这意味着什么)。之后的信息消息是:活动'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
类。
答案 0 :(得分:5)
事务以异步方式提交事务。由于资源管理器级别存在问题,您无法对提交事务的失败作出反应。
正如您所指出的,您可以处理活动中发生的异常。如果您查看工作流程的跟踪记录,我猜您会在事务中止之前看到TryCatch活动已关闭。
许多年前,当我担任COM +团队的项目经理时,我研究了这个问题,因为通常人们想要一个事务组件(或工作流),就像在这种情况下能够对事务中止做出反应。
事务解析的异步性质意味着您无法在组件本身中对其做出反应。解决方案是在调用者中做出反应,然后可以采取一些行动。
设计假设是,一旦事务中止,就不能安全地使用事务中的状态 - 它将全部被丢弃,因为事务被中止。
答案 1 :(得分:2)
只是为了补充罗恩的答案。这里唯一的选择是添加SqlWorkflowInstanceStore并在TransactionScope之前删除一个Persist活动。当事务中止时,整个工作流将中止,但过去保存的状态仍将在持久性数据库中,并且可以从此先前保存的状态重新启动工作流并再次执行事务。