您的位置:首页 > 产品设计 > UI/UE

分析SequenceActivity和ConditionedActivityGroup来自定义一个可以回滚的Activity

2011-01-13 16:13 429 查看
理解AEC(ActivityExecutionContext)

当Workflow runtime执行一个Activity时,他会为这个Activity新建一个ActivityExecutionContext,它包含了执行Activity信息。更重要的一点是AEC是根据Activity临时变化的并且这个Activity是深copy,所以就很难得到执行这个Activity之前得AEC和Activity实例。当一个Activity执行多次的时候,他必须被copy多次,同时AEC也创建一个新的,代码:

ActivityExecutionContextchildContext=currentContext.ExecutionContextManager.CreateExecutionContext(childActivity);

它将会根据父AEC去为ChildActivity创建一个新的AEC,以维护自己。假设我们有一个自定义的Activity叫WorkflowRoot,我们去copy它的子Activity,会有以下结果:

RootContext

| WorkflowRoot (1)

| childActivity (1)

| grandChildActivity (1)

- childContext

childActivity (2)

grandChildActivity (2)

有2个问题:

childActivity(1) or childActivity(2) 相互有影响么?

一旦Clone了这个实例,这2个实例就 没有联系了。

childActivity(2)的父Activity是谁?

WorkflowRoot (1),所以新clone的Activity的父activity任然执行原Activity

分析SequenceActivityandConditionedActivityGroup

SequenceActivity将一个一个执行它的子activity,执行完最后一个Activity后这个SequenceActivity将会关闭。

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
if (executionContext == null)
{
throw new ArgumentNullException("executionContext");
}
if (base.EnabledActivities.Count == 0)
{
this.OnSequenceComplete(executionContext);
return ActivityExecutionStatus.Closed;
}
base.EnabledActivities[0].RegisterForStatusChange(Activity.ClosedEvent, this); //①

executionContext.ExecuteActivity(base.EnabledActivities[0]); //②
base.SetValue(ActiveChildQualifiedNameProperty, base.EnabledActivities[0].QualifiedName);
return ActivityExecutionStatus.Executing;
}
①注册了一个事件,当第一个Activity执行完毕后,将会触发OnEvent方法。②告诉我们要手工执行这个Activity

void IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent(object sender, ActivityExecutionStatusChangedEventArgs e)
{
if (sender == null)
{
throw new ArgumentNullException("sender");
}
if (e == null)
{
throw new ArgumentNullException("e");
}
ActivityExecutionContext executionContext = sender as ActivityExecutionContext;
if (executionContext == null)
{
throw new ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender");
}
e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);
SequenceActivity activity = executionContext.Activity as SequenceActivity;
if (activity == null)
{
throw new ArgumentException("sender");
}
if ((activity.ExecutionStatus == ActivityExecutionStatus.Canceling) || ((activity.ExecutionStatus == ActivityExecutionStatus.Faulting) && ((bool) base.GetValue(SequenceFaultingProperty))))
{
if (activity.ExecutionStatus == ActivityExecutionStatus.Faulting)
{
base.RemoveProperty(SequenceFaultingProperty);
}
base.RemoveProperty(ActiveChildQualifiedNameProperty);
executionContext.CloseActivity();
}
else if ((activity.ExecutionStatus == ActivityExecutionStatus.Executing) && !this.TryScheduleNextChild(executionContext))
{
this.OnSequenceComplete(executionContext);
executionContext.CloseActivity();
}
}
this.TryScheduleNextChild(executionContext)将执行下一个Activity

然而ConditionedActivityGroup就较为复杂。它的子节点可能被执行多次,所以当子节点的状态为pending/executing/idle时,它将维护一个状态集合,当他要执行或重新执行这个Activity时,执行以下方法:

private void ExecuteChild(ConditionedActivityGroup cag, Activity childActivity, ActivityExecutionContext context)
{
ActivityExecutionContext context2 = GetChildExecutionContext(context, childActivity, true);
cag.CAGState.ChildrenStats[childActivity.QualifiedName].State = CAGChildState.Excuting;
context2.Activity.RegisterForStatusChange(Activity.ClosedEvent, this);
context2.ExecuteActivity(context2.Activity);
}
我们可以通过分析系统中的Activity去学习自己定义Activity



自定义一个Rollback的Activity

自定义了一个NavigatorActivity,他的功能就是可以rollback或forward到一个Activity,当我从一个Activity到另一个activity,

创建了一个NavigateRequest类,用以存储源activity和目标activity,

然后把他用外部服务传入workflowRuntime的WorkflowQueueingService里,

然后在Excute()检查NavigateRequest是否存在,如果存在,将会停掉现在执行的Activity,将调度到目标Activity里。可以通过ConditionedActivityGroup去了解Activity执行的过程

整个的架构



定义NavigateRequest类:

/// <summary>
/// The parameter of a Rollback/Jump operation, which stores the source and target information.
/// </summary>
[Serializable]
public class NavigateRequest
{
private string source;
private string target;
private string responser;
private string remark;
private NavigateDirection? direction;

public string SourceActivity
{
get { return this.source; }
set { this.source = value; }
}
public string TargetActivity
{
get { return this.target; }
set { this.target = value; }
}
public string ResponsibleActivity
{
get { return this.responser; }
set { this.responser = value; }
}
public NavigateDirection? Direction
{
get { return this.direction; }
set { this.direction = value; }
}
public string Remark
{
get { return this.remark; }
set { this.remark = value; }
}

public NavigateRequest(string source, string target, string remark)
{
if (String.IsNullOrEmpty(source))
{
throw new ArgumentNullException("source");
}
if (String.IsNullOrEmpty(target))
{
throw new ArgumentNullException("target");
}
if (source == target)
{
throw new ArgumentException("Cannot navigate to itself.");
}
this.source = source;
this.target = target;
this.remark = remark;
}
public NavigateRequest(string source, string target)
: this(source, target, "")
{
}
public NavigateRequest(NavigateDirection direction, string source, string target)
: this(source, target)
{
this.direction = direction;
}

public NavigateRequest(NavigateDirection direction, string source, string target, string responser)
: this(direction, source, target)
{
this.responser = responser;
}
}

/// <summary>
/// The direction of navigate, rollback is backward navigation, while jump is forward.
/// </summary>
public enum NavigateDirection
{
Backward, Forward
}


注册rollbackactivity

/// <summary>
/// Register a rollback or jump request.
/// </summary>
/// <param name="provider"></param>
/// <param name="nav"></param>
protected static void RegisterNavigate(IServiceProvider provider, NavigateRequest nav)
{
WorkflowQueuingService qService = provider.GetService(typeof(WorkflowQueuingService)) as WorkflowQueuingService;
qService.GetWorkflowQueue(Environment.WorkflowRollbackQueue).Enqueue(nav);
}


根据检查是否要rollback,然后去schedule目标activity

/// <summary>Executes the activity.</summary>
/// <returns>The <see cref="T:System.Workflow.ComponentModel.ActivityExecutionStatus"></see> of the activity after executing the activity.</returns>
/// <param name="executionContext">The execution context of the activity.</param>
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
if (executionContext == null)
{
throw new ArgumentNullException("executionContext");
}
if (base.EnabledActivities.Count == 0)
{
this.OnSequenceComplete(executionContext);
return ActivityExecutionStatus.Closed;
}
//********************
NavigateRequest jump = NavigatorActivity.PeekNavigate(executionContext);
Activity firstActivity;
if (jump != null)
{
if (base.IsDirectChild(jump.TargetActivity))
{
firstActivity = base.GetActivityByName(jump.TargetActivity);
}
else
{
firstActivity = base.GetDirectChildActivity(jump.TargetActivity);
if (firstActivity is NavigatorActivity)
{
NavigateRequest nav = new NavigateRequest(NavigateDirection.Forward, jump.SourceActivity, jump.TargetActivity, firstActivity.QualifiedName);
NavigatorActivity.RegisterNavigate(executionContext, nav);
}
}
NavigatorActivity.ShiftNavigate(executionContext);
}
else
{
firstActivity = base.EnabledActivities[0];
}
Activity executingActivity = NavigatorActivity.GetRuntimeInitializedActivity(executionContext, firstActivity);
executingActivity.RegisterForStatusChange(Activity.ClosedEvent, this);
ExecuteChild(executingActivity, executionContext);
base.SetValue(AdvancedSequenceActivity.ActiveChildQualifiedNameProperty, executingActivity.QualifiedName);
//********************
return ActivityExecutionStatus.Executing;
}
protected override void OnActivityEvent(object sender, ActivityExecutionStatusChangedEventArgs e)
{
if (sender == null)
{
throw new ArgumentNullException("sender");
}
if (e == null)
{
throw new ArgumentNullException("e");
}
ActivityExecutionContext context1 = sender as ActivityExecutionContext;
if (context1 == null)
{
throw new ArgumentException("SenderMustBeActivityExecutionContext", "sender");
}
e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);

//************************
ActivityExecutionContext subContext = AdvancedSequenceActivity.GetChildExecutionContext(context1, e.Activity, false);
if (subContext != null)
{
context1.ExecutionContextManager.CompleteExecutionContext(subContext);
}
//***********************

AdvancedSequenceActivity activity1 = context1.Activity as AdvancedSequenceActivity;
if (activity1 == null)
{
throw new ArgumentException("sender");
}
if ((activity1.ExecutionStatus == ActivityExecutionStatus.Canceling) || ((activity1.ExecutionStatus == ActivityExecutionStatus.Faulting) && ((bool)base.GetValue(AdvancedSequenceActivity.SequenceFaultingProperty))))
{
if (activity1.ExecutionStatus == ActivityExecutionStatus.Faulting)
{
base.RemoveProperty(AdvancedSequenceActivity.SequenceFaultingProperty);
}
base.RemoveProperty(AdvancedSequenceActivity.ActiveChildQualifiedNameProperty);
//context1.CloseActivity();
this.Cleanup(context1);
}
else if ((activity1.ExecutionStatus == ActivityExecutionStatus.Executing) && !this.TryScheduleNextChild(context1))
{
this.OnSequenceComplete(context1);
//context1.CloseActivity();
this.Cleanup(context1);
}
}
/// <summary>
/// Called by OnActivityEvent method.
/// Schedules the execution of another child activity.
/// </summary>
/// <param name="executionContext"></param>
/// <returns></returns>
private bool TryScheduleNextChild(ActivityExecutionContext executionContext)
{
if (executionContext == null)
{
throw new ArgumentNullException("executionContext");
}
NavigatorActivity baseActivity = executionContext.Activity as NavigatorActivity;
if (baseActivity == null)
{
throw new InvalidOperationException("the sender must be NavigatorActivity.");
}
IList<Activity> list1 = baseActivity.EnabledActivities;
if (list1.Count == 0)
{
return false;
}
//**************************
string currentActivityName = (string)base.GetValue(AdvancedSequenceActivity.ActiveChildQualifiedNameProperty);
Activity nextActivity;
NavigateRequest rollback = NavigatorActivity.PeekNavigate(executionContext);
if (rollback != null)
{
//1. if a rollback has registered and it should be dealt by another activity, stop this immediately.
if (!NavigatorActivity.IsResponsible(rollback, base.QualifiedName))
{
return false;
}

//2. if a child activity has registered a rollback
if (base.IsDirectChild(rollback.TargetActivity))
{
//2.1. if the rollback target is inside this activity and is the direct child (not child's child), execute it directly.
nextActivity = base.GetActivityByName(rollback.TargetActivity);
}
else
{
nextActivity = base.GetDirectChildActivity(rollback.TargetActivity);
if (nextActivity != null)
{
//2.2. if the rollback target is inside this activity and is not direct child (may be a child's child), register a jump to it and execute its parent (its parent will make a jump execution to the target).
if (nextActivity is NavigatorActivity)
{
NavigateRequest nav = new NavigateRequest(NavigateDirection.Forward, rollback.SourceActivity, rollback.TargetActivity, nextActivity.QualifiedName);
NavigatorActivity.RegisterNavigate(executionContext, nav);
}
}
else
{
//2.3 if the rollback target is out of this activity, register the parent a rollback and stop this activity.
NavigatorActivity parent = base.Parent as NavigatorActivity;
if (parent != null)
{
NavigateRequest nav = new NavigateRequest(NavigateDirection.Backward, this.QualifiedName, rollback.TargetActivity, parent.QualifiedName);
NavigatorActivity.RegisterNavigate(executionContext, nav);
}
NavigatorActivity.ShiftNavigate(executionContext);
return false;
}
}
NavigatorActivity.ShiftNavigate(executionContext);
}
else
{
//3. no rollback found. an original execution will be performed.
int i;
for (i = 0; i < list1.Count; i++)
{
if (list1[i].QualifiedName == currentActivityName)
{
if (i == list1.Count - 1)
{
return false;
}
break;
}
}
nextActivity = list1[++i];
}

ExecuteChild(nextActivity, executionContext);
//*****************************
base.SetValue(AdvancedSequenceActivity.ActiveChildQualifiedNameProperty, nextActivity.QualifiedName);
return true;
}


参考文档:<http://blogs.msdn.com/b/advancedworkflow/archive/2006/03/21/557121.aspx>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐