e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
500 lines
20 KiB
C#
500 lines
20 KiB
C#
namespace System.Workflow.Activities
|
|
{
|
|
#region Imports
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.CodeDom;
|
|
using System.Drawing;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Workflow.ComponentModel;
|
|
using System.Workflow.ComponentModel.Design;
|
|
using System.Collections.Generic;
|
|
using System.Workflow.ComponentModel.Compiler;
|
|
using System.Workflow.Runtime;
|
|
using System.Workflow.Activities.Common;
|
|
|
|
#endregion
|
|
|
|
[ToolboxItem(false)]
|
|
[Designer(typeof(EventHandlersDesigner), typeof(IDesigner))]
|
|
[ToolboxBitmap(typeof(EventHandlersActivity), "Resources.events.png")]
|
|
[ActivityValidator(typeof(EventHandlersValidator))]
|
|
[SRCategory(SR.Standard)]
|
|
[AlternateFlowActivityAttribute]
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public sealed class EventHandlersActivity : CompositeActivity, IActivityEventListener<ActivityExecutionStatusChangedEventArgs>
|
|
{
|
|
public EventHandlersActivity()
|
|
{
|
|
}
|
|
|
|
public EventHandlersActivity(string name)
|
|
: base(name)
|
|
{
|
|
}
|
|
|
|
#region Runtime State Specific Dependency Property
|
|
static DependencyProperty ActivityStateProperty = DependencyProperty.Register("ActivityState", typeof(List<EventHandlerEventActivitySubscriber>), typeof(EventHandlersActivity));
|
|
static DependencyProperty IsScopeCompletedProperty = DependencyProperty.Register("IsScopeCompleted", typeof(bool), typeof(EventHandlersActivity), new PropertyMetadata(false));
|
|
|
|
private List<EventHandlerEventActivitySubscriber> ActivityState
|
|
{
|
|
get
|
|
{
|
|
return (List<EventHandlerEventActivitySubscriber>)base.GetValue(ActivityStateProperty);
|
|
}
|
|
set
|
|
{
|
|
if (value == null)
|
|
base.RemoveProperty(ActivityStateProperty);
|
|
else
|
|
base.SetValue(ActivityStateProperty, value);
|
|
}
|
|
}
|
|
|
|
private bool IsScopeCompleted
|
|
{
|
|
get
|
|
{
|
|
return (bool)base.GetValue(IsScopeCompletedProperty);
|
|
}
|
|
set
|
|
{
|
|
base.SetValue(IsScopeCompletedProperty, value);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
internal void UnsubscribeAndClose()
|
|
{
|
|
base.Invoke<EventArgs>(this.OnUnsubscribeAndClose, EventArgs.Empty);
|
|
}
|
|
|
|
#region Protected Methods
|
|
protected override void OnClosed(IServiceProvider provider)
|
|
{
|
|
base.RemoveProperty(EventHandlersActivity.ActivityStateProperty);
|
|
base.RemoveProperty(EventHandlersActivity.IsScopeCompletedProperty);
|
|
}
|
|
|
|
protected override void Initialize(IServiceProvider provider)
|
|
{
|
|
if (this.Parent == null)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_MustHaveParent));
|
|
|
|
base.Initialize(provider);
|
|
}
|
|
|
|
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
|
|
List<EventHandlerEventActivitySubscriber> eventActivitySubscribers = new List<EventHandlerEventActivitySubscriber>();
|
|
this.ActivityState = eventActivitySubscribers;
|
|
|
|
for (int i = 0; i < this.EnabledActivities.Count; ++i)
|
|
{
|
|
EventDrivenActivity childActivity = this.EnabledActivities[i] as EventDrivenActivity;
|
|
EventHandlerEventActivitySubscriber eventDrivenSubscriber = new EventHandlerEventActivitySubscriber(childActivity);
|
|
eventActivitySubscribers.Add(eventDrivenSubscriber);
|
|
childActivity.EventActivity.Subscribe(executionContext, eventDrivenSubscriber);
|
|
}
|
|
return ActivityExecutionStatus.Executing;
|
|
}
|
|
protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
|
|
if (this.ActivityState == null)
|
|
return ActivityExecutionStatus.Closed;
|
|
|
|
bool scopeCompleted = this.IsScopeCompleted;
|
|
bool canCloseNow = true;
|
|
|
|
for (int i = 0; i < this.EnabledActivities.Count; ++i)
|
|
{
|
|
EventDrivenActivity childActivity = this.EnabledActivities[i] as EventDrivenActivity;
|
|
EventHandlerEventActivitySubscriber eventActivitySubscriber = this.ActivityState[i] as EventHandlerEventActivitySubscriber;
|
|
|
|
eventActivitySubscriber.PendingExecutionCount = 0;
|
|
|
|
ActivityExecutionContextManager contextManager = executionContext.ExecutionContextManager;
|
|
ActivityExecutionContext childContext = contextManager.GetExecutionContext(childActivity);
|
|
|
|
if (childContext != null)
|
|
{
|
|
switch (childContext.Activity.ExecutionStatus)
|
|
{
|
|
case ActivityExecutionStatus.Canceling:
|
|
case ActivityExecutionStatus.Faulting:
|
|
canCloseNow = false;
|
|
break;
|
|
case ActivityExecutionStatus.Executing:
|
|
childContext.CancelActivity(childContext.Activity);
|
|
canCloseNow = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!scopeCompleted) //UnSubscribe from event.
|
|
{
|
|
childActivity.EventActivity.Unsubscribe(executionContext, eventActivitySubscriber);
|
|
}
|
|
}
|
|
|
|
if (canCloseNow)
|
|
{
|
|
this.ActivityState = null;
|
|
return ActivityExecutionStatus.Closed;
|
|
}
|
|
else
|
|
{
|
|
return this.ExecutionStatus;
|
|
}
|
|
}
|
|
protected override void OnActivityChangeAdd(ActivityExecutionContext executionContext, Activity addedActivity)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
|
|
if (addedActivity == null)
|
|
throw new ArgumentNullException("addedActivity");
|
|
|
|
EventDrivenActivity eda = addedActivity as EventDrivenActivity;
|
|
|
|
EventHandlersActivity activity = (EventHandlersActivity)executionContext.Activity as EventHandlersActivity;
|
|
EventHandlerEventActivitySubscriber eventActivitySubscriber = new EventHandlerEventActivitySubscriber(eda);
|
|
|
|
if (activity.ExecutionStatus == ActivityExecutionStatus.Executing && activity.ActivityState != null && !activity.IsScopeCompleted)
|
|
{
|
|
eda.EventActivity.Subscribe(executionContext, eventActivitySubscriber);
|
|
activity.ActivityState.Insert(activity.EnabledActivities.IndexOf(addedActivity), eventActivitySubscriber);
|
|
}
|
|
}
|
|
protected override void OnActivityChangeRemove(ActivityExecutionContext executionContext, Activity removedActivity)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
if (removedActivity == null)
|
|
throw new ArgumentNullException("removedActivity");
|
|
|
|
EventDrivenActivity eda = removedActivity as EventDrivenActivity;
|
|
|
|
// find out the status of the scope
|
|
EventHandlersActivity activity = (EventHandlersActivity)executionContext.Activity as EventHandlersActivity;
|
|
|
|
if (activity.ExecutionStatus == ActivityExecutionStatus.Executing && activity.ActivityState != null && !activity.IsScopeCompleted)
|
|
{
|
|
for (int i = 0; i < activity.ActivityState.Count; ++i)
|
|
{
|
|
EventHandlerEventActivitySubscriber eventSubscriber = activity.ActivityState[i];
|
|
if (eventSubscriber.eventDrivenActivity.QualifiedName.Equals(removedActivity.QualifiedName))
|
|
{
|
|
eda.EventActivity.Unsubscribe(executionContext, eventSubscriber);
|
|
activity.ActivityState.RemoveAt(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnWorkflowChangesCompleted(ActivityExecutionContext executionContext)
|
|
{
|
|
if (executionContext == null)
|
|
throw new ArgumentNullException("executionContext");
|
|
|
|
base.OnWorkflowChangesCompleted(executionContext);
|
|
|
|
if (this.ActivityState != null)
|
|
{
|
|
switch (this.ExecutionStatus)
|
|
{
|
|
case ActivityExecutionStatus.Executing:
|
|
if (this.IsScopeCompleted && AllHandlersAreQuiet(this, executionContext))
|
|
executionContext.CloseActivity();
|
|
break;
|
|
case ActivityExecutionStatus.Faulting:
|
|
case ActivityExecutionStatus.Canceling:
|
|
if (AllHandlersAreQuiet(this, executionContext))
|
|
executionContext.CloseActivity();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Private Impls
|
|
|
|
#region IActivityEventListener<ActivityExecutionStatusChangedEventArgs> Members
|
|
|
|
void IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent(object sender, ActivityExecutionStatusChangedEventArgs e)
|
|
{
|
|
if (sender == null)
|
|
throw new ArgumentNullException("sender");
|
|
if (e == null)
|
|
throw new ArgumentNullException("e");
|
|
|
|
ActivityExecutionContext context = sender as ActivityExecutionContext;
|
|
if (context == null)
|
|
throw new ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender");
|
|
|
|
EventDrivenActivity eda = e.Activity as EventDrivenActivity;
|
|
EventHandlersActivity eventHandlers = context.Activity as EventHandlersActivity;
|
|
|
|
e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);
|
|
|
|
ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
|
|
contextManager.CompleteExecutionContext(contextManager.GetExecutionContext(eda));
|
|
|
|
switch (eventHandlers.ExecutionStatus)
|
|
{
|
|
case ActivityExecutionStatus.Executing:
|
|
for (int i = 0; i < eventHandlers.EnabledActivities.Count; ++i)
|
|
{
|
|
if (eventHandlers.EnabledActivities[i].QualifiedName.Equals(eda.QualifiedName))
|
|
{
|
|
EventHandlerEventActivitySubscriber eventActivitySubscriber = eventHandlers.ActivityState[i];
|
|
|
|
if (eventActivitySubscriber.PendingExecutionCount > 0)
|
|
{
|
|
eventActivitySubscriber.PendingExecutionCount--;
|
|
eventActivitySubscriber.IsBlocked = false;
|
|
|
|
ActivityExecutionContext childContext = contextManager.CreateExecutionContext(eventHandlers.EnabledActivities[i]);
|
|
childContext.Activity.RegisterForStatusChange(Activity.ClosedEvent, this);
|
|
childContext.ExecuteActivity(childContext.Activity);
|
|
}
|
|
else
|
|
{
|
|
eventActivitySubscriber.IsBlocked = true;
|
|
if (eventHandlers.IsScopeCompleted && AllHandlersAreQuiet(eventHandlers, context))
|
|
context.CloseActivity();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ActivityExecutionStatus.Canceling:
|
|
case ActivityExecutionStatus.Faulting:
|
|
if (AllHandlersAreQuiet(eventHandlers, context))
|
|
context.CloseActivity();
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helpers
|
|
private bool AllHandlersAreQuiet(EventHandlersActivity handlers, ActivityExecutionContext context)
|
|
{
|
|
ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
|
|
|
|
for (int i = 0; i < handlers.EnabledActivities.Count; ++i)
|
|
{
|
|
EventDrivenActivity eventDriven = handlers.EnabledActivities[i] as EventDrivenActivity;
|
|
if (contextManager.GetExecutionContext(eventDriven) != null || (handlers.ActivityState != null && handlers.ActivityState[i].PendingExecutionCount > 0))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
private void OnUnsubscribeAndClose(object sender, EventArgs args)
|
|
{
|
|
if (sender == null)
|
|
throw new ArgumentNullException("sender");
|
|
if (args == null)
|
|
throw new ArgumentNullException("args");
|
|
|
|
ActivityExecutionContext context = (ActivityExecutionContext)sender;
|
|
if (context == null)
|
|
throw new ArgumentException("sender");
|
|
|
|
EventHandlersActivity handlers = context.Activity as EventHandlersActivity;
|
|
if (context.Activity.ExecutionStatus != ActivityExecutionStatus.Executing)
|
|
return;
|
|
|
|
Debug.Assert(!handlers.IsScopeCompleted, "Only notified of scope body completion once");
|
|
handlers.IsScopeCompleted = true;
|
|
|
|
ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
|
|
bool readyToClose = true;
|
|
for (int i = 0; i < handlers.EnabledActivities.Count; ++i)
|
|
{
|
|
EventDrivenActivity evtDriven = handlers.EnabledActivities[i] as EventDrivenActivity;
|
|
EventHandlerEventActivitySubscriber eventSubscriber = handlers.ActivityState[i];
|
|
evtDriven.EventActivity.Unsubscribe(context, eventSubscriber);
|
|
|
|
if (contextManager.GetExecutionContext(evtDriven) != null || handlers.ActivityState[i].PendingExecutionCount != 0)
|
|
readyToClose = false;
|
|
}
|
|
|
|
if (readyToClose)
|
|
{
|
|
handlers.ActivityState = null;
|
|
context.CloseActivity();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region EventSubscriber
|
|
[Serializable]
|
|
private sealed class EventHandlerEventActivitySubscriber : IActivityEventListener<QueueEventArgs>
|
|
{
|
|
bool isBlocked;
|
|
int numOfMsgs;
|
|
|
|
internal EventDrivenActivity eventDrivenActivity;
|
|
|
|
internal EventHandlerEventActivitySubscriber(EventDrivenActivity eventDriven)
|
|
{
|
|
isBlocked = true;
|
|
numOfMsgs = 0;
|
|
this.eventDrivenActivity = eventDriven;
|
|
}
|
|
|
|
internal bool IsBlocked
|
|
{
|
|
get
|
|
{
|
|
return isBlocked;
|
|
}
|
|
set
|
|
{
|
|
isBlocked = value;
|
|
}
|
|
}
|
|
|
|
internal int PendingExecutionCount
|
|
{
|
|
get
|
|
{
|
|
return numOfMsgs;
|
|
}
|
|
set
|
|
{
|
|
numOfMsgs = value;
|
|
}
|
|
}
|
|
|
|
void IActivityEventListener<QueueEventArgs>.OnEvent(object sender, QueueEventArgs e)
|
|
{
|
|
if (sender == null)
|
|
throw new ArgumentNullException("sender");
|
|
if (e == null)
|
|
throw new ArgumentNullException("e");
|
|
ActivityExecutionContext context = sender as ActivityExecutionContext;
|
|
|
|
if (context == null)
|
|
throw new ArgumentException("sender");
|
|
|
|
EventHandlersActivity handlers = context.Activity as EventHandlersActivity;
|
|
|
|
if (handlers.ExecutionStatus != ActivityExecutionStatus.Executing)
|
|
return;
|
|
|
|
if (!handlers.EnabledActivities.Contains(eventDrivenActivity))
|
|
return; //Activity is dynamically removed.
|
|
|
|
if (IsBlocked)
|
|
{
|
|
IsBlocked = false;
|
|
ActivityExecutionContextManager contextManager = context.ExecutionContextManager;
|
|
ActivityExecutionContext childContext = contextManager.CreateExecutionContext(eventDrivenActivity);
|
|
childContext.Activity.RegisterForStatusChange(Activity.ClosedEvent, handlers);
|
|
childContext.ExecuteActivity(childContext.Activity);
|
|
}
|
|
else
|
|
{
|
|
PendingExecutionCount++;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
[Browsable(false)]
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
private Activity GetDynamicActivity(Activity childActivity)
|
|
{
|
|
if (childActivity == null)
|
|
throw new ArgumentNullException("childActivity");
|
|
|
|
if (!this.EnabledActivities.Contains(childActivity))
|
|
throw new ArgumentException(SR.GetString(SR.Error_EventHandlersChildNotFound), "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_EventHandlersChildNotFound), "childActivityName");
|
|
}
|
|
}
|
|
|
|
internal sealed class EventHandlersValidator : CompositeActivityValidator
|
|
{
|
|
public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
|
|
{
|
|
ValidationErrorCollection validationErrors = base.Validate(manager, obj);
|
|
|
|
EventHandlersActivity eventHandlers = obj as EventHandlersActivity;
|
|
if (eventHandlers == null)
|
|
throw new ArgumentException(SR.GetString(SR.Error_UnexpectedArgumentType, typeof(EventHandlersActivity).FullName), "obj");
|
|
|
|
if (eventHandlers.Parent == null)
|
|
{
|
|
validationErrors.Add(new ValidationError(SR.GetString(SR.Error_MustHaveParent), ErrorNumbers.Error_EventHandlersDeclParentNotScope));
|
|
return validationErrors;
|
|
}
|
|
|
|
// Parent must support event handlers
|
|
if (!(eventHandlers.Parent is EventHandlingScopeActivity))
|
|
validationErrors.Add(new ValidationError(SR.GetString(SR.Error_EventHandlersDeclParentNotScope, eventHandlers.Parent.QualifiedName), ErrorNumbers.Error_EventHandlersDeclParentNotScope));
|
|
|
|
bool bNotAllEventHandler = false;
|
|
foreach (Activity activity in eventHandlers.EnabledActivities)
|
|
{
|
|
if (!(activity is EventDrivenActivity))
|
|
bNotAllEventHandler = true;
|
|
}
|
|
|
|
// validate that all child activities are event driven activities.
|
|
if (bNotAllEventHandler)
|
|
validationErrors.Add(new ValidationError(SR.GetString(SR.Error_ListenNotAllEventDriven), ErrorNumbers.Error_ListenNotAllEventDriven));
|
|
|
|
return validationErrors;
|
|
}
|
|
}
|
|
}
|