我可以采取哪些步骤使工作流活动适应异常

时间:2011-11-10 14:35:30

标签: workflow-foundation-4

我对Workflow Foundation开发很陌生,并且担心我在业务流程处理方面存在严重漏洞,因为他们没有正确处理自定义活动中的应用程序/数据库异常。

我很感激我可以采取一些措施来将这种弹性添加到我的自定义活动中,这样我就可以轻松使用设计器和其他工具来确保尽可能地创建脆弱的自定义活动并可能导致工作流清理问题。

2 个答案:

答案 0 :(得分:4)

以下是一些在不同执行阶段可供您用来处理异常的选项。


第一个选项(在活动/工作流程执行时间):

首先,在自定义活动中,您应该始终尝试在其执行中处理异常。有些活动可能不起作用,但整体工作流程可以继续,在这种情况下,将错误记录到持久性,甚至向用户显示某些东西没有按预期工作但事情将继续是好的选择

话虽如此,总会出现活动必须(甚至应该)抛出异常的情况,并且应该在工作流级别处理这些异常。类似于:如果此活动发生此异常,请执行此操作,否则,执行此操作

让我们假设您有一个自定义活动,它会向DB保留一些内容:

public sealed PersistIntegerToDb : CodeActivity
{
    public InArgument<int> ValueToPersist { get; set; }

    protected override void Execute(CodeActivityMetadata metadata)
    {
        try
        {
            // persist
        }
        catch(SqlException exception) 
        {
            // re throws the SqlException

            throw new SqlException("'ValueToPersist' wasn't persisted.", exception);
        }
    }
}

然后,在您的代码或设计师中,您可以使用TryCatch活动来捕获该错误并按照您希望的方式对待它:

var workflow = new TryCatch
{
    Try = new PersistIntegerToDb
    {
        ValueToPersist = 10
    },
    Catches =
    {
        new Catch<SqlException>
        {
            Action = new ActivityAction<SqlException>
            {
                Handler = new WriteLine 
                {
                     Text = "An error occurred and the value wasn't saved! Anyway workflow will continue..."
                }
            }
        }
    }
}

或者您可以使用TerminateWorkflow终止它。


第二个选项(在设计时):

好的,但你可以说客户端不知道他必须处理这些案件。在这种情况下,这是您可以考虑的可用性选项,而不是在设计器上提供PersistIntegerToDb,您可以通过IActivityTemplateFactory提供已被异常捕获包围的活动:

public sealed PersistIntegerToDbFactory : IActivityTemplateFactory
{
    public Activity Create(DependencyObject target)
    {
        return new TryCatch
        {
            Try = new PersistIntegerToDb
            {
                ValueToPersist = 10
            },
            Catches =
            {
                new Catch<SqlException>
                {
                }
            }
        };
    }
}

现在您只需添加PersistIntegerToDbFactory,就像它是常规活动一样:

new ToolboxItemWrapper(typeof(PersistIntegerToDbFactory), null, "Persist Integer");

第三个选项(在验证时):

执行前永远不要忘记validate工作流程!

var validationResults =
    ActivityValidationServices.Validate(workflow);

foreach(var error in validationResults.Errors)
{
    Console.WriteLine(string.Format(
        "Validation error '{0}', generated on activity '{1}' in the property named {2}",
        error.Message,
        error.Source.DisplayName,
        error.PropertyName));
}

第四个选项(在申请执行时):

您可以使用OnUnhandledException事件处理执行期间可能发生的所有未处理的异常:

var wfApp = new WorkflowApplication(activity);

wfApp.OnUnhandledException += 
    delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
    {
        if (e.UnhandledException is SqlException)
        {
            Console.WriteLine("Some data wasn't properly persited.");
        }
        else 
        {
            Console.WriteLine("Unknown error: " + e.UnhandledException.GetType());
            Console.WriteLine("With message: " + e.UnhandledException.Message);
        }

        Console.WriteLine("Ok, workflow will be abort");

        return UnhandledExceptionAction.Abort;
    };

请注意,在此阶段,您只能Abort, Cancel and Terminate工作流程,这就是您应该1)避免抛出异常或2)处理工作流程中的异常的原因。 OnUnhandledException是您优雅地结束工作流程执行的最后机会,即使是为了记录目的,也应始终对其进行处理。例如,DivideByZeroExceptions之类的东西可能会出现,几乎不可能在验证时预测和捕获。

答案 1 :(得分:2)

就自定义活动而言,您应该将它们视为任何其他代码。处理你可以做的错误,让你无法处理其余的泡沫。

在工作流级别,您可以使用TryCatch活动和工作流持久性来处理错误。特别坚持是人们经常忽视的东西。在工作流程中的适当步骤添加持久性活动,并将工作流程设置为中止未处理的错误。现在,您可以返回并重新加载上一个良好的工作流状态,然后重试导致未处理异常的操作。使用数据库之类的资源从故障中恢复的好方法,这些资源可能由于某种原因而无法使用,然后再回来。