e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
321 lines
12 KiB
C#
321 lines
12 KiB
C#
#pragma warning disable 1634, 1691
|
|
namespace System.Workflow.Activities
|
|
{
|
|
using System;
|
|
using System.Xml.Serialization;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Collections;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Design;
|
|
using System.Reflection;
|
|
using System.Workflow.Activities;
|
|
using System.Workflow.ComponentModel;
|
|
using System.Workflow.ComponentModel.Design;
|
|
using System.ComponentModel.Design.Serialization;
|
|
using System.Workflow.ComponentModel.Compiler;
|
|
using System.Security.Principal;
|
|
using System.Workflow.Runtime.Tracking;
|
|
using System.Diagnostics;
|
|
using System.Workflow.Runtime;
|
|
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public sealed class StateMachineWorkflowInstance
|
|
{
|
|
private Guid _instanceId;
|
|
private WorkflowInstance _workflowInstance;
|
|
private SqlTrackingQuery _sqlTrackingQuery;
|
|
private SqlTrackingService _sqlTrackingService;
|
|
private SqlTrackingWorkflowInstance _sqlTrackingWorkflowInstance;
|
|
private StateMachineWorkflowActivity _stateMachineWorkflow;
|
|
private WorkflowRuntime _runtime;
|
|
|
|
internal const string StateHistoryPropertyName = "StateHistory";
|
|
|
|
public StateMachineWorkflowInstance(WorkflowRuntime runtime, Guid instanceId)
|
|
{
|
|
if (runtime == null)
|
|
throw new ArgumentNullException("runtime");
|
|
if (instanceId == Guid.Empty)
|
|
throw new ArgumentNullException("instanceId");
|
|
_runtime = runtime;
|
|
_instanceId = instanceId;
|
|
_workflowInstance = runtime.GetWorkflow(instanceId);
|
|
_stateMachineWorkflow = _workflowInstance.GetWorkflowDefinition() as StateMachineWorkflowActivity;
|
|
if (_stateMachineWorkflow == null)
|
|
throw new ArgumentException(SR.GetStateMachineWorkflowRequired(), "instanceId");
|
|
}
|
|
|
|
public StateMachineWorkflowActivity StateMachineWorkflow
|
|
{
|
|
get
|
|
{
|
|
// we always get a new definition, in case a
|
|
// dynamic updated happened. The exception handling here
|
|
// is because after the workflow completes, we can no longer
|
|
// retrieve the workflow definition. In this case, we
|
|
// return the last retrieved definition
|
|
try
|
|
{
|
|
_stateMachineWorkflow = (StateMachineWorkflowActivity)this.WorkflowInstance.GetWorkflowDefinition();
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
}
|
|
|
|
return _stateMachineWorkflow;
|
|
}
|
|
}
|
|
|
|
public Guid InstanceId
|
|
{
|
|
get
|
|
{
|
|
return _instanceId;
|
|
}
|
|
}
|
|
|
|
public WorkflowInstance WorkflowInstance
|
|
{
|
|
get
|
|
{
|
|
return _workflowInstance;
|
|
}
|
|
}
|
|
|
|
public StateActivity CurrentState
|
|
{
|
|
get
|
|
{
|
|
return GetCurrentState();
|
|
}
|
|
}
|
|
|
|
public string CurrentStateName
|
|
{
|
|
get
|
|
{
|
|
StateActivity currentState = this.CurrentState;
|
|
if (currentState == null)
|
|
return null;
|
|
return currentState.QualifiedName;
|
|
}
|
|
}
|
|
|
|
private static ReadOnlyCollection<StateActivity> GetLeafStates(StateActivity parentState)
|
|
{
|
|
if (parentState == null)
|
|
throw new ArgumentNullException("parentState");
|
|
|
|
List<StateActivity> leafStates = new List<StateActivity>();
|
|
Queue<StateActivity> states = new Queue<StateActivity>();
|
|
states.Enqueue(parentState);
|
|
while (states.Count > 0)
|
|
{
|
|
StateActivity parent = states.Dequeue();
|
|
foreach (Activity childActivity in parent.EnabledActivities)
|
|
{
|
|
StateActivity childState = childActivity as StateActivity;
|
|
if (childState != null)
|
|
{
|
|
if (StateMachineHelpers.IsLeafState(childState))
|
|
leafStates.Add(childState);
|
|
else
|
|
states.Enqueue(childState);
|
|
}
|
|
}
|
|
}
|
|
return leafStates.AsReadOnly();
|
|
}
|
|
|
|
|
|
public ReadOnlyCollection<StateActivity> States
|
|
{
|
|
get
|
|
{
|
|
StateMachineWorkflowActivity stateMachineWorkflow = this.StateMachineWorkflow;
|
|
|
|
#pragma warning disable 56503
|
|
|
|
if (stateMachineWorkflow == null)
|
|
throw new InvalidOperationException();
|
|
|
|
return GetLeafStates(stateMachineWorkflow);
|
|
|
|
#pragma warning restore 56503
|
|
}
|
|
}
|
|
|
|
public ReadOnlyCollection<string> PossibleStateTransitions
|
|
{
|
|
get
|
|
{
|
|
return GetPossibleStateTransitions();
|
|
}
|
|
}
|
|
|
|
public ReadOnlyCollection<string> StateHistory
|
|
{
|
|
get
|
|
{
|
|
return GetStateHistory();
|
|
}
|
|
}
|
|
|
|
public void EnqueueItem(IComparable queueName, object item)
|
|
{
|
|
EnqueueItem(queueName, item, null, null);
|
|
}
|
|
|
|
public void EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, object workItem)
|
|
{
|
|
this.WorkflowInstance.EnqueueItemOnIdle(queueName, item, pendingWork, workItem);
|
|
}
|
|
|
|
public void SetState(StateActivity targetState)
|
|
{
|
|
if (targetState == null)
|
|
throw new ArgumentNullException("targetState");
|
|
SetState(targetState.QualifiedName);
|
|
}
|
|
|
|
public void SetState(string targetStateName)
|
|
{
|
|
if (targetStateName == null)
|
|
throw new ArgumentNullException("targetStateName");
|
|
StateActivity targetState = FindActivityByQualifiedName(targetStateName) as StateActivity;
|
|
if (targetState == null)
|
|
throw new ArgumentOutOfRangeException("targetStateName");
|
|
SetStateEventArgs eventArgs = new SetStateEventArgs(targetStateName);
|
|
this.WorkflowInstance.EnqueueItemOnIdle(System.Workflow.Activities.StateMachineWorkflowActivity.SetStateQueueName, eventArgs, null, null);
|
|
}
|
|
|
|
internal Activity FindActivityByQualifiedName(string id)
|
|
{
|
|
return StateMachineHelpers.FindActivityByName(this.StateMachineWorkflow, id);
|
|
}
|
|
|
|
private StateActivity GetCurrentState()
|
|
{
|
|
ReadOnlyCollection<WorkflowQueueInfo> workflowQueuedInfos = this.WorkflowInstance.GetWorkflowQueueData();
|
|
foreach (WorkflowQueueInfo queueInfo in workflowQueuedInfos)
|
|
{
|
|
if (queueInfo.QueueName.Equals(StateMachineWorkflowActivity.SetStateQueueName))
|
|
{
|
|
if (queueInfo.SubscribedActivityNames.Count == 0)
|
|
return null;
|
|
Debug.Assert(queueInfo.SubscribedActivityNames.Count == 1);
|
|
StateMachineWorkflowActivity stateMachineWorkflow = this.StateMachineWorkflow;
|
|
StateActivity currentState = StateMachineHelpers.FindStateByName(stateMachineWorkflow, queueInfo.SubscribedActivityNames[0]);
|
|
return currentState;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
private ReadOnlyCollection<string> GetPossibleStateTransitions()
|
|
{
|
|
List<string> targetStates = new List<string>();
|
|
ReadOnlyCollection<WorkflowQueueInfo> workflowQueuedInfos = this.WorkflowInstance.GetWorkflowQueueData();
|
|
StateMachineWorkflowActivity stateMachineWorkflow = this.StateMachineWorkflow;
|
|
foreach (WorkflowQueueInfo queueInfo in workflowQueuedInfos)
|
|
{
|
|
foreach (string subscribedActivityName in queueInfo.SubscribedActivityNames)
|
|
{
|
|
Activity subscribedActivity = StateMachineHelpers.FindActivityByName(stateMachineWorkflow, subscribedActivityName);
|
|
IEventActivity eventActivity = subscribedActivity as IEventActivity;
|
|
if (eventActivity == null)
|
|
continue;
|
|
|
|
EventDrivenActivity eventDriven = StateMachineHelpers.GetParentEventDriven(eventActivity);
|
|
Debug.Assert(eventDriven != null);
|
|
Queue<Activity> activities = new Queue<Activity>();
|
|
activities.Enqueue(eventDriven);
|
|
while (activities.Count > 0)
|
|
{
|
|
Activity activity = activities.Dequeue();
|
|
SetStateActivity setState = activity as SetStateActivity;
|
|
if (setState != null)
|
|
{
|
|
targetStates.Add(setState.TargetStateName);
|
|
}
|
|
else
|
|
{
|
|
CompositeActivity compositeActivity = activity as CompositeActivity;
|
|
if (compositeActivity != null)
|
|
{
|
|
foreach (Activity childActivity in compositeActivity.EnabledActivities)
|
|
{
|
|
activities.Enqueue(childActivity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return targetStates.AsReadOnly();
|
|
}
|
|
|
|
private ReadOnlyCollection<string> GetStateHistory()
|
|
{
|
|
if (_sqlTrackingService == null)
|
|
{
|
|
_sqlTrackingService = _runtime.GetService<SqlTrackingService>();
|
|
if (_sqlTrackingService == null)
|
|
throw new InvalidOperationException(SR.GetSqlTrackingServiceRequired());
|
|
}
|
|
|
|
if (_sqlTrackingQuery == null)
|
|
_sqlTrackingQuery = new SqlTrackingQuery(_sqlTrackingService.ConnectionString);
|
|
|
|
StateMachineWorkflowActivity stateMachineWorkflow;
|
|
Stack<string> stateHistory = new Stack<string>();
|
|
|
|
try
|
|
{
|
|
stateMachineWorkflow = this.StateMachineWorkflow;
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
return new ReadOnlyCollection<string>(stateHistory.ToArray());
|
|
}
|
|
|
|
if (_sqlTrackingWorkflowInstance == null)
|
|
{
|
|
bool result = _sqlTrackingQuery.TryGetWorkflow(_instanceId, out _sqlTrackingWorkflowInstance);
|
|
if (!result)
|
|
{
|
|
// Workflow has not started yet, so we just return an
|
|
// empty collection
|
|
return new ReadOnlyCollection<string>(stateHistory.ToArray());
|
|
}
|
|
}
|
|
|
|
_sqlTrackingWorkflowInstance.Refresh();
|
|
IList<UserTrackingRecord> events = _sqlTrackingWorkflowInstance.UserEvents;
|
|
foreach (UserTrackingRecord record in events)
|
|
{
|
|
if (record.UserDataKey != StateActivity.StateChangeTrackingDataKey)
|
|
continue;
|
|
|
|
string stateQualifiedName = record.UserData as string;
|
|
if (stateQualifiedName == null)
|
|
throw new InvalidOperationException(SR.GetInvalidUserDataInStateChangeTrackingRecord());
|
|
|
|
StateActivity state = StateMachineHelpers.FindStateByName(stateMachineWorkflow, record.QualifiedName);
|
|
if (state == null)
|
|
throw new InvalidOperationException(SR.GetInvalidUserDataInStateChangeTrackingRecord());
|
|
|
|
if (StateMachineHelpers.IsLeafState(state))
|
|
stateHistory.Push(stateQualifiedName);
|
|
}
|
|
|
|
ReadOnlyCollection<string> history = new ReadOnlyCollection<string>(stateHistory.ToArray());
|
|
return history;
|
|
}
|
|
}
|
|
}
|