65.9K
CodeProject 正在变化。 阅读更多。
Home

在顺序 WWF 中重试活动执行

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2013 年 3 月 23 日

CPOL

5分钟阅读

viewsIcon

13033

downloadIcon

1

关于 WWF 的知识分享文章。

引言

本文是写给那些已经知道如何在 WWF (顺序工作流) 中进行开发的人。在这里,我将提供一个解决方案,关于如何在顺序工作流中执行相同的活动,以应对不确定的失败。我之所以找到这个解决方案,是因为我在项目中长期以来都遇到这个问题,并且每次都需要手动完成工作流的编排。

背景

如果您的工作流中有许多活动集,它们调用外部/内部 Web 服务/WCF,并且由于这些 Web 服务/WCF 的内部服务器错误,抛出了 FaultException (WebException),导致您的工作流活动因错误而失败/终止。最终,您的整个工作流都会因此而终止。因为这就是您开发/设计工作流的方式,即如果活动中出现任何问题,就停止在那里。但有些特定情况是,您不希望整个工作流停止,并且可能需要再次执行该活动集,直到达到一定的次数,看看它是否能正常工作(重试向 Web 服务发送相同的请求),如果它工作正常,那么它应该在不终止工作流的情况下执行工作流中的其余活动。

理解客户活动的生命周期

活动在其生命周期中有六个状态。这些状态是:**已初始化、执行中、取消中、已关闭、补偿中和故障中**。

在**已初始化**状态期间,已经为活动创建了 ActivityExecutionContext,并且已经执行了该活动特定的其他初始化详细信息。当活动进入**执行中**状态时,活动的主要功能就会被执行。一个活动会被父活动显式地置于**取消中**状态,或者因为在该活动执行过程中抛出了异常。**已关闭**状态是活动的最后也是最终状态。唯一的例外是,如果一个活动成功完成,但根据业务逻辑必须经历补偿状态。然后,活动将从已关闭状态过渡到补偿状态,并在补偿逻辑完成后再次返回到已关闭状态。

下面的流程图显示了活动如何在各种活动状态之间转换。

所以,基本上,在正常路径下,活动会经历**已初始化、执行中**和**已关闭**。

研究的要点 

我们知道,如果在活动的**执行中**、**取消中**或**补偿中**状态期间抛出了异常,它将过渡到**故障中**状态。

 Note: An activity cannot move from the Closed state to the Executing state.  

由于我们知道一旦活动关闭就无法再移动到执行中状态,但我们可以在活动关闭之前重新执行它。这意味着,如果由于任何原因活动进入了故障中状态,就有可能将其移回执行中状态。

现在,我们知道这里可能有一个新的转换。下图用绿色线条显示了新的状态转换。

我们知道,活动的所有状态都可以通过其预定义的方法进行控制。我们可以在自定义活动中重写这些方法。下面是我将在我的研究中使用的两个主要重写方法。

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)

如何将活动状态从故障中状态恢复到执行中状态?

我们可以通过在活动内部处理故障来实现这一点。通过重写 HandleFault 方法来处理活动状态,我们可以知道 executionContext 是什么,抛出了哪种类型的异常,并且为了重新执行活动,我们可以调用 Execute 方法,并将 executionContext 传递给它。但是,在这样做时必须非常小心,并且要确保正确地处理重试逻辑,否则可能会导致死锁。

使用代码

在这里,我将通过我的示例 POC 项目向您展示我是如何实现的,您也可以使用相同的方法来实现。对您而言唯一不同的是您的重试逻辑/要求。我们总是需要确保重试逻辑不会陷入无限循环。

下面是我的项目解决方案。

 

在这个项目中,我有以下顺序工作流,包含三个活动。

 

在 CallWebServices 活动中,我试图模拟该活动正在调用 Web 服务,并且可能会出现服务器错误,这将抛出服务器错误故障异常,导致整个工作流因其抛出的异常而终止。

现在,为了解决整个工作流终止的问题,并从那里再次重试执行相同的活动,以便如果下次重试成功,并且从 Web 服务获得成功响应,工作流将继续完成编排。

Replaying => I am trying to achieve moving the activity state from Faulting to Executing  

为了实现这个问题的解决方案,我添加了一个 BaseActivity,它将被我的所有工作流活动继承,如下所示。

public partial class Activity1: MyBaseActivity 

我考虑编写基类活动,是因为根据我们解决方案的要求,我们需要每个活动都具备此重试功能。

我还向我的基类活动添加了一个依赖属性来设置/限制重试次数。因此,任何继承我的基类活动的活动都应该设置此属性。在下面的图片中,我为 CallWebServices 活动设置了 AllowedReplayCount 为 2,这意味着该活动最多可以重试自身 2 次。

  

以下是将依赖属性添加到我的基类活动的 [代码]

public static DependencyProperty AllowedReplayCountProperty = 
  DependencyProperty.Register("AllowedReplayCount", typeof(Int32), typeof(MyBaseActivity));

[DescriptionAttribute("AllowedReplayCount")]
[CategoryAttribute("Replay Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public Int32 AllowedReplayCount
{
    get
    {
        return ((Int32)(base.GetValue(MyBaseActivity.AllowedReplayCountProperty)));
    }
    set
    {
        base.SetValue(MyBaseActivity.AllowedReplayCountProperty, value);
    }
}

在这个基类活动中,我通过重写 HandleFault 函数来处理由活动抛出的故障。该函数提供了两个重要数据:executionContextexception。利用这些数据,我应用了重新执行相同活动的逻辑。并且通过依赖属性,我将控制重试,确保它不会陷入无限循环。

protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
{
    if (exception is WebException && ReplayCount < AllowedReplayCount)
    {
        if (!exception.Message.Contains("404") || !exception.Message.Contains("409"))
        {
            Console.WriteLine("Faulted " + executionContext.Activity.Name);
            ReplayCount++;
            return Execute(executionContext);
        }
    }
    return base.HandleFault(executionContext, exception);
}  

到目前为止,我们已经看到了如何通过 HandleFault 函数重试相同的活动,但这还不够,我们还需要工作流在故障活动重试成功后执行其余活动。我们需要这个,因为这是工作流的基本功能;如果任何活动发生故障,它会终止整个工作流。所以我们必须阻止工作流从执行中状态转移到故障中状态。

为了实现这种情况,我在我的基类活动中添加了一个 faultHandlerActivity。

 

为了支持重试,我为 faultHandlerActivity 设置了 FaultType 为 System.Net.WebException。我还添加了 codeActivity

private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
    CodeActivity senderActity = sender as CodeActivity;
    FaultHandlerActivity faultException = senderActity.Parent as FaultHandlerActivity;
    if (faultException.Fault is WebException)
    {
        if (ReplayCount > AllowedReplayCount)
        {
            throw faultException.Fault;
        }
    }
    else
    {
        throw faultException.Fault;
    }
}

我使用 codeAcitivity 来检查由活动抛出的故障是否符合重试条件。如果它不属于我的重试条件,那么我才允许它将故障抛出给工作流。

以上就是在一个工作流中实现此重试活动功能所需的一切。

© . All rights reserved.