e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
614 lines
24 KiB
C#
614 lines
24 KiB
C#
namespace System.Workflow.Activities
|
|
{
|
|
#region Imports
|
|
|
|
using System;
|
|
using System.Text;
|
|
using System.Reflection;
|
|
using System.Collections;
|
|
using System.CodeDom;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Drawing.Design;
|
|
using System.Drawing;
|
|
using System.Diagnostics;
|
|
using System.Workflow.ComponentModel;
|
|
using System.Workflow.ComponentModel.Design;
|
|
using System.Workflow.Runtime;
|
|
using System.Runtime.Serialization;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.Design.Serialization;
|
|
using System.Xml.Serialization;
|
|
using System.Workflow.ComponentModel.Compiler;
|
|
|
|
#endregion
|
|
|
|
[SRDescription(SR.StateActivityDescription)]
|
|
[ToolboxItem(typeof(ActivityToolboxItem))]
|
|
[Designer(typeof(StateDesigner), typeof(IDesigner))]
|
|
[ToolboxBitmap(typeof(StateActivity), "Resources.StateActivity.png")]
|
|
[ActivityValidator(typeof(StateActivityValidator))]
|
|
[SRCategory(SR.Standard)]
|
|
[System.Runtime.InteropServices.ComVisible(false)]
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public class StateActivity : CompositeActivity
|
|
{
|
|
#region Fields
|
|
|
|
public const string StateChangeTrackingDataKey = "StateActivity.StateChange";
|
|
internal static DependencyProperty StateMachineExecutionStateProperty = DependencyProperty.Register(StateMachineExecutionState.StateMachineExecutionStateKey, typeof(StateMachineExecutionState), typeof(StateActivity), new PropertyMetadata());
|
|
|
|
#endregion Fields
|
|
|
|
#region Constructor
|
|
|
|
public StateActivity()
|
|
{
|
|
}
|
|
|
|
public StateActivity(string name)
|
|
: base(name)
|
|
{
|
|
}
|
|
|
|
#endregion Constructor
|
|
|
|
#region Methods
|
|
|
|
protected override void OnClosed(IServiceProvider provider)
|
|
{
|
|
base.RemoveProperty(StateActivity.StateMachineExecutionStateProperty);
|
|
}
|
|
|
|
#region Public Methods
|
|
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
internal Activity GetDynamicActivity(Activity childActivity)
|
|
{
|
|
if (childActivity == null)
|
|
throw new ArgumentNullException("childActivity");
|
|
if (!this.EnabledActivities.Contains(childActivity))
|
|
throw new ArgumentException(SR.GetString(SR.Error_StateChildNotFound), "childActivity");
|
|
else
|
|
{
|
|
Activity[] dynamicChildActivity = this.GetDynamicActivities(childActivity);
|
|
|
|
if (dynamicChildActivity.Length != 0)
|
|
return dynamicChildActivity[0];
|
|
else
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public Activity GetDynamicActivity(String childActivityName)
|
|
{
|
|
if (childActivityName == null)
|
|
throw new ArgumentNullException("childActivityName");
|
|
|
|
Activity childActivity = null;
|
|
|
|
for (int i = 0; i < this.EnabledActivities.Count; ++i)
|
|
{
|
|
if (this.EnabledActivities[i].QualifiedName.Equals(childActivityName))
|
|
{
|
|
childActivity = this.EnabledActivities[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (childActivity != null)
|
|
return GetDynamicActivity(childActivity);
|
|
|
|
throw new ArgumentException(SR.GetString(SR.Error_StateChildNotFound), "childActivityName");
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
protected override void Initialize(IServiceProvider provider)
|
|
{
|
|
base.Initialize(provider);
|
|
|
|
ActivityExecutionContext context = (ActivityExecutionContext)provider;
|
|
|
|
StateActivity rootState = StateMachineHelpers.GetRootState(this);
|
|
if (!StateMachineHelpers.IsStateMachine(rootState))
|
|
throw new InvalidOperationException(SR.GetError_StateActivityMustBeContainedInAStateMachine());
|
|
|
|
string initialStateName = StateMachineHelpers.GetInitialStateName(this);
|
|
if (String.IsNullOrEmpty(initialStateName))
|
|
throw new InvalidOperationException(SR.GetError_CannotExecuteStateMachineWithoutInitialState());
|
|
|
|
//
|
|
|
|
|
|
if (this.QualifiedName != initialStateName)
|
|
StateMachineSubscriptionManager.DisableStateWorkflowQueues(context, this);
|
|
}
|
|
|
|
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
|
|
if (StateMachineHelpers.IsRootState(this))
|
|
{
|
|
ExecuteRootState(executionContext);
|
|
}
|
|
else
|
|
{
|
|
if (StateMachineHelpers.IsLeafState(this))
|
|
{
|
|
ExecuteLeafState(executionContext);
|
|
}
|
|
else
|
|
{
|
|
ExecuteState(executionContext);
|
|
}
|
|
}
|
|
|
|
return this.ExecutionStatus;
|
|
}
|
|
|
|
private void ExecuteRootState(ActivityExecutionContext context)
|
|
{
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
StateMachineExecutionState executionState = new StateMachineExecutionState(this.WorkflowInstanceId);
|
|
executionState.SchedulerBusy = false;
|
|
state.SetValue(StateActivity.StateMachineExecutionStateProperty, executionState);
|
|
executionState.SubscriptionManager.CreateSetStateEventQueue(context);
|
|
|
|
string initialStateName = StateMachineHelpers.GetInitialStateName(state);
|
|
executionState.CalculateStateTransition(this, initialStateName);
|
|
executionState.ProcessActions(context);
|
|
}
|
|
|
|
private static void ExecuteState(ActivityExecutionContext context)
|
|
{
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
executionState.SchedulerBusy = false;
|
|
executionState.ProcessActions(context);
|
|
}
|
|
|
|
private static void ExecuteLeafState(ActivityExecutionContext context)
|
|
{
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
executionState.SchedulerBusy = false;
|
|
|
|
executionState.CurrentStateName = state.QualifiedName;
|
|
|
|
StateInitializationActivity stateInitialization = GetStateInitialization(context);
|
|
if (stateInitialization != null)
|
|
{
|
|
ExecuteStateInitialization(context, stateInitialization);
|
|
}
|
|
else
|
|
{
|
|
EnteringLeafState(context);
|
|
}
|
|
}
|
|
|
|
private static void EnteringLeafState(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
Debug.Assert(StateMachineHelpers.IsLeafState(state));
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
executionState.SubscriptionManager.SubscribeToSetStateEvent(context);
|
|
|
|
string completedStateName = StateMachineHelpers.GetCompletedStateName(state);
|
|
if (StateMachineHelpers.IsCompletedState(state))
|
|
{
|
|
// make sure that we track that we entered the completed state
|
|
EnteringStateAction enteringState = new EnteringStateAction(state.QualifiedName);
|
|
executionState.EnqueueAction(enteringState);
|
|
executionState.ProcessActions(context);
|
|
|
|
// this is the final state, so we start completing this tree
|
|
executionState.Completed = true;
|
|
LeavingState(context);
|
|
}
|
|
else
|
|
{
|
|
if (String.IsNullOrEmpty(executionState.NextStateName))
|
|
{
|
|
executionState.SubscriptionManager.ReevaluateSubscriptions(context);
|
|
EnteringStateAction enteringState = new EnteringStateAction(state.QualifiedName);
|
|
executionState.EnqueueAction(enteringState);
|
|
executionState.LockQueue();
|
|
}
|
|
else
|
|
{
|
|
// The StateInitialization requested a state transtion
|
|
EnteringStateAction enteringState = new EnteringStateAction(state.QualifiedName);
|
|
executionState.EnqueueAction(enteringState);
|
|
executionState.ProcessTransitionRequest(context);
|
|
}
|
|
executionState.ProcessActions(context);
|
|
}
|
|
}
|
|
|
|
internal static void LeavingState(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
if (StateMachineHelpers.IsLeafState(state))
|
|
{
|
|
StateFinalizationActivity stateFinalization = GetStateFinalization(context);
|
|
if (stateFinalization == null)
|
|
Complete(context);
|
|
else
|
|
ExecuteStateFinalization(context, stateFinalization);
|
|
}
|
|
else
|
|
Complete(context);
|
|
}
|
|
|
|
private static void CleanUp(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
|
|
if (state.ExecutionStatus == ActivityExecutionStatus.Faulting)
|
|
return; // if we're faulting, then we're already in a bad state, so we don't try to unsubscribe
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
StateMachineSubscriptionManager subscriptionManager = executionState.SubscriptionManager;
|
|
subscriptionManager.UnsubscribeState(context);
|
|
|
|
if (StateMachineHelpers.IsRootState(state))
|
|
subscriptionManager.DeleteSetStateEventQueue(context);
|
|
else if (StateMachineHelpers.IsLeafState(state))
|
|
subscriptionManager.UnsubscribeToSetStateEvent(context);
|
|
}
|
|
|
|
protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
|
|
CleanUp(executionContext);
|
|
|
|
Debug.Assert(executionContext.Activity == this);
|
|
|
|
bool canCloseNow = true;
|
|
ActivityExecutionContextManager contextManager = executionContext.ExecutionContextManager;
|
|
|
|
foreach (ActivityExecutionContext existingContext in contextManager.ExecutionContexts)
|
|
{
|
|
if (existingContext.Activity.Parent == this)
|
|
{
|
|
canCloseNow = false;
|
|
|
|
if (existingContext.Activity.ExecutionStatus == ActivityExecutionStatus.Executing)
|
|
existingContext.CancelActivity(existingContext.Activity);
|
|
}
|
|
}
|
|
return canCloseNow ? ActivityExecutionStatus.Closed : this.ExecutionStatus;
|
|
}
|
|
|
|
private static void Complete(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
if (StateMachineHelpers.IsLeafState(state))
|
|
{
|
|
executionState.PreviousStateName = state.Name;
|
|
}
|
|
|
|
CleanUp(context);
|
|
executionState.SchedulerBusy = true;
|
|
context.CloseActivity();
|
|
}
|
|
|
|
private static void ExecuteChild(ActivityExecutionContext context, Activity childActivity)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
if (childActivity == null)
|
|
throw new ArgumentNullException("childActivity");
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
Debug.Assert(!executionState.SchedulerBusy);
|
|
executionState.SchedulerBusy = true;
|
|
ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
|
|
ActivityExecutionContext childContext = contextManager.CreateExecutionContext(childActivity);
|
|
childContext.Activity.Closed += state.HandleChildActivityClosed;
|
|
childContext.ExecuteActivity(childContext.Activity);
|
|
}
|
|
|
|
private static void CleanupChildAtClosure(ActivityExecutionContext context, Activity childActivity)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
if (childActivity == null)
|
|
throw new ArgumentNullException("childActivity");
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
|
|
childActivity.Closed -= state.HandleChildActivityClosed;
|
|
|
|
ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
|
|
ActivityExecutionContext childContext = contextManager.GetExecutionContext(childActivity);
|
|
contextManager.CompleteExecutionContext(childContext);
|
|
}
|
|
|
|
internal void RaiseProcessActionEvent(ActivityExecutionContext context)
|
|
{
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
Debug.Assert(!executionState.SchedulerBusy);
|
|
executionState.SchedulerBusy = true;
|
|
base.Invoke<EventArgs>(this.HandleProcessActionEvent, new EventArgs());
|
|
}
|
|
|
|
private void HandleProcessActionEvent(object sender,
|
|
EventArgs eventArgs)
|
|
{
|
|
ActivityExecutionContext context = sender as ActivityExecutionContext;
|
|
if (context == null)
|
|
throw new ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender");
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
executionState.SchedulerBusy = false;
|
|
executionState.ProcessActions(context);
|
|
}
|
|
|
|
#region HandleStatusChange
|
|
|
|
private void HandleChildActivityClosed(object sender, ActivityExecutionStatusChangedEventArgs eventArgs)
|
|
{
|
|
ActivityExecutionContext context = sender as ActivityExecutionContext;
|
|
if (context == null)
|
|
throw new ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender");
|
|
if (eventArgs == null)
|
|
throw new ArgumentNullException("eventArgs");
|
|
|
|
Activity completedChildActivity = eventArgs.Activity;
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
executionState.SchedulerBusy = false;
|
|
|
|
CleanupChildAtClosure(context, completedChildActivity);
|
|
switch (state.ExecutionStatus)
|
|
{
|
|
case ActivityExecutionStatus.Canceling:
|
|
case ActivityExecutionStatus.Faulting:
|
|
context.CloseActivity();
|
|
return;
|
|
|
|
case ActivityExecutionStatus.Executing:
|
|
if (completedChildActivity is EventDrivenActivity)
|
|
{
|
|
HandleEventDrivenCompleted(context);
|
|
return;
|
|
}
|
|
|
|
StateInitializationActivity stateInitialization = completedChildActivity as StateInitializationActivity;
|
|
if (stateInitialization != null)
|
|
{
|
|
HandleStateInitializationCompleted(context, stateInitialization);
|
|
return;
|
|
}
|
|
|
|
if (completedChildActivity is StateFinalizationActivity)
|
|
{
|
|
HandleStateFinalizationCompleted(context);
|
|
return;
|
|
}
|
|
|
|
if (completedChildActivity is StateActivity)
|
|
{
|
|
HandleSubStateCompleted(context);
|
|
return;
|
|
}
|
|
|
|
InvalidChildActivity(state);
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException(SR.GetInvalidActivityStatus(context.Activity));
|
|
}
|
|
}
|
|
|
|
private static void InvalidChildActivity(StateActivity state)
|
|
{
|
|
if (StateMachineHelpers.IsLeafState(state))
|
|
throw new InvalidOperationException(SR.GetError_InvalidLeafStateChild());
|
|
else
|
|
throw new InvalidOperationException(SR.GetError_InvalidCompositeStateChild());
|
|
}
|
|
|
|
internal static void ExecuteEventDriven(ActivityExecutionContext context, EventDrivenActivity eventDriven)
|
|
{
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
Debug.Assert(!executionState.HasEnqueuedActions);
|
|
ExecuteChild(context, eventDriven);
|
|
}
|
|
|
|
private static void HandleEventDrivenCompleted(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
if (String.IsNullOrEmpty(executionState.NextStateName))
|
|
{
|
|
executionState.SubscriptionManager.ReevaluateSubscriptions(context);
|
|
executionState.LockQueue();
|
|
}
|
|
else
|
|
executionState.ProcessTransitionRequest(context);
|
|
executionState.ProcessActions(context);
|
|
}
|
|
|
|
private static void ExecuteStateInitialization(ActivityExecutionContext context, StateInitializationActivity stateInitialization)
|
|
{
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
Debug.Assert(!executionState.HasEnqueuedActions);
|
|
ExecuteChild(context, stateInitialization);
|
|
}
|
|
|
|
private static void HandleStateInitializationCompleted(ActivityExecutionContext context, StateInitializationActivity stateInitialization)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
if (stateInitialization == null)
|
|
throw new ArgumentNullException("stateInitialization");
|
|
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
|
|
if (!String.IsNullOrEmpty(executionState.NextStateName) && executionState.NextStateName.Equals(state.QualifiedName))
|
|
throw new InvalidOperationException(SR.GetInvalidSetStateInStateInitialization());
|
|
|
|
EnteringLeafState(context);
|
|
}
|
|
|
|
private static void ExecuteStateFinalization(ActivityExecutionContext context, StateFinalizationActivity stateFinalization)
|
|
{
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
ExecuteChild(context, stateFinalization);
|
|
}
|
|
|
|
private static void HandleStateFinalizationCompleted(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
Complete(context);
|
|
}
|
|
|
|
internal static void ExecuteState(ActivityExecutionContext context, StateActivity state)
|
|
{
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
ExecuteChild(context, state);
|
|
}
|
|
|
|
private static void HandleSubStateCompleted(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
|
|
StateMachineExecutionState executionState = GetExecutionState(context);
|
|
if (executionState.Completed)
|
|
{
|
|
// We're closing the state machine
|
|
LeavingState(context);
|
|
}
|
|
else
|
|
{
|
|
executionState.ProcessActions(context);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper methods
|
|
|
|
private static StateInitializationActivity GetStateInitialization(ActivityExecutionContext context)
|
|
{
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
Debug.Assert(StateMachineHelpers.IsLeafState(state),
|
|
"GetStateInitialization: StateInitialization is only allowed in a leaf node state");
|
|
return GetHandlerActivity<StateInitializationActivity>(context);
|
|
}
|
|
|
|
private static StateFinalizationActivity GetStateFinalization(ActivityExecutionContext context)
|
|
{
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
Debug.Assert(StateMachineHelpers.IsLeafState(state),
|
|
"GetStateFinalization: StateFinalization is only allowed in a leaf node state");
|
|
return GetHandlerActivity<StateFinalizationActivity>(context);
|
|
}
|
|
|
|
private static T GetHandlerActivity<T>(ActivityExecutionContext context) where T : class
|
|
{
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
foreach (Activity activity in state.EnabledActivities)
|
|
{
|
|
T handler = activity as T;
|
|
if (handler != null)
|
|
{
|
|
return handler;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static StateMachineExecutionState GetExecutionState(ActivityExecutionContext context)
|
|
{
|
|
if (context == null)
|
|
throw new ArgumentNullException("context");
|
|
|
|
StateActivity state = (StateActivity)context.Activity;
|
|
StateMachineExecutionState executionState = GetExecutionState(state);
|
|
return executionState;
|
|
}
|
|
|
|
|
|
private static StateMachineExecutionState GetExecutionState(StateActivity state)
|
|
{
|
|
if (state == null)
|
|
throw new ArgumentNullException("state");
|
|
|
|
StateActivity rootState = StateMachineHelpers.GetRootState(state);
|
|
StateMachineExecutionState executionState = StateMachineExecutionState.Get(rootState);
|
|
return executionState;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion Methods
|
|
|
|
#region Dynamic Update Functions
|
|
|
|
protected override void OnActivityChangeAdd(ActivityExecutionContext executionContext, Activity addedActivity)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
if (addedActivity == null)
|
|
throw new ArgumentNullException("addedActivity");
|
|
|
|
if (!addedActivity.Enabled)
|
|
return;
|
|
|
|
if (executionContext.Activity.ExecutionStatus != ActivityExecutionStatus.Executing)
|
|
return; // activity is not executing
|
|
|
|
EventDrivenActivity eventDriven = addedActivity as EventDrivenActivity;
|
|
if (eventDriven == null)
|
|
return;
|
|
|
|
// Activity we added is an EventDrivenActivity
|
|
|
|
// First we disable the queue
|
|
StateMachineSubscriptionManager.ChangeEventDrivenQueueState(executionContext, eventDriven, false);
|
|
StateActivity rootState = StateMachineHelpers.GetRootState(executionContext.Activity as StateActivity);
|
|
StateMachineExecutionState executionState = StateMachineExecutionState.Get(rootState);
|
|
StateActivity currentState = StateMachineHelpers.GetCurrentState(executionContext);
|
|
if (currentState == null)
|
|
return; // Dynamic update happened before we entered the initial state
|
|
|
|
StateMachineSubscriptionManager subscriptionManager = executionState.SubscriptionManager;
|
|
subscriptionManager.ReevaluateSubscriptions(executionContext);
|
|
executionState.LockQueue();
|
|
executionState.ProcessActions(executionContext);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|