e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
908 lines
43 KiB
C#
908 lines
43 KiB
C#
#pragma warning disable 1634, 1691
|
|
namespace System.Workflow.ComponentModel
|
|
{
|
|
#region Imports
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.ComponentModel.Design.Serialization;
|
|
using System.Reflection;
|
|
using System.Workflow.ComponentModel.Compiler;
|
|
using System.Workflow.ComponentModel.Serialization;
|
|
using System.Workflow.ComponentModel.Design;
|
|
using System.Xml;
|
|
using System.IO;
|
|
|
|
#endregion
|
|
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public sealed class WorkflowChanges
|
|
{
|
|
#region Data members
|
|
public static readonly DependencyProperty ConditionProperty = DependencyProperty.RegisterAttached("Condition", typeof(ActivityCondition), typeof(WorkflowChanges), new PropertyMetadata(DependencyPropertyOptions.Metadata));
|
|
internal static DependencyProperty WorkflowChangeActionsProperty = DependencyProperty.RegisterAttached("WorkflowChangeActions", typeof(IList), typeof(WorkflowChanges), new PropertyMetadata(DependencyPropertyOptions.NonSerialized));
|
|
internal static DependencyProperty WorkflowChangeVersionProperty = DependencyProperty.RegisterAttached("WorkflowChangeVersion", typeof(Guid), typeof(WorkflowChanges), new PropertyMetadata(Guid.Empty, DependencyPropertyOptions.NonSerialized));
|
|
|
|
private Activity originalRootActivity = null;
|
|
private Activity clonedRootActivity = null;
|
|
|
|
private List<WorkflowChangeAction> modelChangeActions = new List<WorkflowChangeAction>();
|
|
private bool saved = false;
|
|
|
|
#endregion
|
|
|
|
#region Constuctor & Destructor
|
|
|
|
public WorkflowChanges(Activity rootActivity)
|
|
{
|
|
if (rootActivity == null)
|
|
throw new ArgumentNullException("rootActivity");
|
|
if (!(rootActivity is CompositeActivity) || rootActivity.Parent != null)
|
|
throw new ArgumentException(SR.GetString(SR.Error_RootActivityTypeInvalid2), "rootActivity");
|
|
#pragma warning suppress 56506
|
|
if (rootActivity.DesignMode)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_NoRuntimeAvailable));
|
|
|
|
// get the original activity
|
|
this.originalRootActivity = (Activity)((Activity)rootActivity).GetValue(Activity.WorkflowDefinitionProperty);
|
|
if (this.originalRootActivity == null)
|
|
this.originalRootActivity = rootActivity;
|
|
|
|
// Work around: for dynamic update create a clone, without calling initialize for runtime
|
|
this.clonedRootActivity = (Activity)CloneRootActivity(originalRootActivity);
|
|
|
|
// make the tree readonly
|
|
ApplyDynamicUpdateMode((Activity)this.clonedRootActivity);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public members
|
|
// WhenConditionProperty Get and Set Accessors
|
|
public static object GetCondition(object dependencyObject)
|
|
{
|
|
if (dependencyObject == null)
|
|
throw new ArgumentNullException("dependencyObject");
|
|
if (!(dependencyObject is DependencyObject))
|
|
throw new ArgumentException(SR.GetString(SR.Error_UnexpectedArgumentType, typeof(DependencyObject).FullName), "dependencyObject");
|
|
|
|
return (dependencyObject as DependencyObject).GetValue(ConditionProperty);
|
|
}
|
|
|
|
public static void SetCondition(object dependencyObject, object value)
|
|
{
|
|
if (dependencyObject == null)
|
|
throw new ArgumentNullException("dependencyObject");
|
|
if (!(dependencyObject is DependencyObject))
|
|
throw new ArgumentException(SR.GetString(SR.Error_UnexpectedArgumentType, typeof(DependencyObject).FullName), "dependencyObject");
|
|
|
|
(dependencyObject as DependencyObject).SetValue(ConditionProperty, value);
|
|
}
|
|
|
|
public CompositeActivity TransientWorkflow
|
|
{
|
|
get
|
|
{
|
|
return this.clonedRootActivity as CompositeActivity;
|
|
}
|
|
}
|
|
|
|
public ValidationErrorCollection Validate()
|
|
{
|
|
TypeProvider typeProvider = CreateTypeProvider(this.originalRootActivity);
|
|
|
|
// create service provider
|
|
ServiceContainer serviceContainer = new ServiceContainer();
|
|
serviceContainer.AddService(typeof(ITypeProvider), typeProvider);
|
|
|
|
ValidationManager validationManager = new ValidationManager(serviceContainer);
|
|
ValidationErrorCollection errors;
|
|
using (WorkflowCompilationContext.CreateScope(validationManager))
|
|
{
|
|
errors = ValidationHelpers.ValidateObject(validationManager, this.clonedRootActivity);
|
|
}
|
|
return XomlCompilerHelper.MorphIntoFriendlyValidationErrors(errors);
|
|
}
|
|
|
|
private void Save()
|
|
{
|
|
ValidationErrorCollection errors = Validate();
|
|
if (errors.HasErrors)
|
|
throw new WorkflowValidationFailedException(SR.GetString(SR.Error_CompilerValidationFailed), errors);
|
|
|
|
//work around !!!for conditions we do diff
|
|
object originalConditions = ((Activity)this.originalRootActivity).GetValue(ConditionTypeConverter.DeclarativeConditionDynamicProp);
|
|
object changedConditions = ((Activity)this.clonedRootActivity).GetValue(ConditionTypeConverter.DeclarativeConditionDynamicProp);
|
|
if (null != originalConditions)
|
|
this.modelChangeActions.AddRange(((IWorkflowChangeDiff)originalConditions).Diff(originalConditions, changedConditions));
|
|
else if (null != changedConditions)
|
|
this.modelChangeActions.AddRange(((IWorkflowChangeDiff)changedConditions).Diff(originalConditions, changedConditions));
|
|
|
|
// diff the process model
|
|
this.modelChangeActions.AddRange(DiffTrees(this.originalRootActivity as CompositeActivity, this.clonedRootActivity as CompositeActivity));
|
|
|
|
// always call it after diff tree, otherwise it turns on the Locked.
|
|
ReleaseDynamicUpdateMode((Activity)this.clonedRootActivity);
|
|
|
|
// cache the change actions into the new workflow definition
|
|
ArrayList workflowChanges = (ArrayList)((Activity)this.clonedRootActivity).GetValue(WorkflowChanges.WorkflowChangeActionsProperty);
|
|
if (workflowChanges == null)
|
|
{
|
|
workflowChanges = new ArrayList();
|
|
((Activity)this.clonedRootActivity).SetValue(WorkflowChanges.WorkflowChangeActionsProperty, workflowChanges);
|
|
}
|
|
|
|
workflowChanges.AddRange(this.modelChangeActions);
|
|
((Activity)this.clonedRootActivity).SetValue(WorkflowChanges.WorkflowChangeVersionProperty, Guid.NewGuid());
|
|
this.saved = true;
|
|
|
|
// now initialize for runtime
|
|
((IDependencyObjectAccessor)this.clonedRootActivity).InitializeDefinitionForRuntime(null);
|
|
}
|
|
|
|
internal void ApplyTo(Activity activity)
|
|
{
|
|
if (activity == null)
|
|
throw new ArgumentNullException("activity");
|
|
|
|
if (activity.Parent != null)
|
|
throw new ArgumentException(SR.GetString(SR.Error_RootActivityTypeInvalid), "activity");
|
|
|
|
if (activity.RootActivity == null)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_MissingRootActivity));
|
|
|
|
if (activity.WorkflowCoreRuntime == null)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_NoRuntimeAvailable));
|
|
|
|
if (this.saved)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_TransactionAlreadyApplied));
|
|
|
|
if (!CompareWorkflowDefinition((Activity)this.originalRootActivity, (Activity)activity.RootActivity.GetValue(Activity.WorkflowDefinitionProperty)))
|
|
throw new ArgumentException(SR.GetString(SR.Error_WorkflowDefinitionModified), "activity");
|
|
|
|
this.Save();
|
|
|
|
// go up in the chain and then apply changes
|
|
IWorkflowCoreRuntime workflowCoreRuntime = activity.WorkflowCoreRuntime;
|
|
if (workflowCoreRuntime.CurrentAtomicActivity != null)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_InsideAtomicScope));
|
|
bool suspended = workflowCoreRuntime.SuspendInstance(SR.GetString(SR.SuspendReason_WorkflowChange));
|
|
try
|
|
{
|
|
// collect all context Activities
|
|
List<Activity> contextActivities = new List<Activity>();
|
|
Queue<Activity> contextActivitiesQueue = new Queue<Activity>();
|
|
contextActivitiesQueue.Enqueue(workflowCoreRuntime.RootActivity);
|
|
while (contextActivitiesQueue.Count > 0)
|
|
{
|
|
Activity contextActivity = contextActivitiesQueue.Dequeue();
|
|
contextActivities.Add(contextActivity);
|
|
|
|
// enqueue child context Activities
|
|
IList<Activity> nestedContextActivities = (IList<Activity>)contextActivity.GetValue(Activity.ActiveExecutionContextsProperty);
|
|
if (nestedContextActivities != null)
|
|
{
|
|
foreach (Activity nestedContextActivity in nestedContextActivities)
|
|
contextActivitiesQueue.Enqueue(nestedContextActivity);
|
|
}
|
|
}
|
|
|
|
// run instance level validations
|
|
ValidationErrorCollection validationErrors = new ValidationErrorCollection();
|
|
foreach (WorkflowChangeAction changeAction in this.modelChangeActions)
|
|
{
|
|
if (changeAction is ActivityChangeAction)
|
|
{
|
|
foreach (Activity contextActivity in contextActivities)
|
|
{
|
|
// WinOE Bug 16903: Ask the contextActivity itself whether or not it can be removed.
|
|
// An activity can not be removed if it's in the executing mode.
|
|
if (changeAction is RemovedActivityAction &&
|
|
contextActivity.DottedPath == ((RemovedActivityAction)changeAction).OriginalRemovedActivity.DottedPath)
|
|
validationErrors.AddRange(changeAction.ValidateChanges(contextActivity));
|
|
|
|
// Ask the parent context activity whether or not this child activity can be added or removed.
|
|
// The call to TraverseDottedPathFromRoot here should return the parent context activity for this change action.
|
|
if (contextActivity.TraverseDottedPathFromRoot(((ActivityChangeAction)changeAction).OwnerActivityDottedPath) != null)
|
|
validationErrors.AddRange(changeAction.ValidateChanges(contextActivity));
|
|
}
|
|
}
|
|
}
|
|
|
|
// if errors then return
|
|
if (validationErrors.HasErrors)
|
|
throw new WorkflowValidationFailedException(SR.GetString(SR.Error_RuntimeValidationFailed), validationErrors);
|
|
|
|
// verify if workflow can be changed
|
|
VerifyWorkflowCanBeChanged(workflowCoreRuntime);
|
|
|
|
// inform workflow runtime
|
|
workflowCoreRuntime.OnBeforeDynamicChange(this.modelChangeActions);
|
|
|
|
// set the new Workflow Definition
|
|
workflowCoreRuntime.RootActivity.SetValue(Activity.WorkflowDefinitionProperty, this.clonedRootActivity);
|
|
|
|
// apply changes to all context Activities
|
|
foreach (Activity contextActivity in contextActivities)
|
|
{
|
|
// apply change to state reader
|
|
foreach (WorkflowChangeAction changeAction in this.modelChangeActions)
|
|
{
|
|
if (changeAction is ActivityChangeAction)
|
|
{
|
|
if (contextActivity.TraverseDottedPathFromRoot(((ActivityChangeAction)changeAction).OwnerActivityDottedPath) != null)
|
|
{
|
|
bool result = changeAction.ApplyTo(contextActivity);
|
|
Debug.Assert(result, "ApplyTo failed");
|
|
}
|
|
}
|
|
}
|
|
// fixup meta properties and notify changes
|
|
// if the context activity is the one that's being removed, we do not fixup the meta properties.
|
|
Activity clonedActivity = ((Activity)this.clonedRootActivity).GetActivityByName(contextActivity.QualifiedName);
|
|
if (clonedActivity != null)
|
|
contextActivity.FixUpMetaProperties(clonedActivity);
|
|
NotifyChangesToChildExecutors(workflowCoreRuntime, contextActivity, this.modelChangeActions);
|
|
NotifyChangesCompletedToChildExecutors(workflowCoreRuntime, contextActivity);
|
|
}
|
|
|
|
// inform workflow runtime
|
|
workflowCoreRuntime.OnAfterDynamicChange(true, this.modelChangeActions);
|
|
}
|
|
catch
|
|
{
|
|
workflowCoreRuntime.OnAfterDynamicChange(false, this.modelChangeActions);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
if (suspended)
|
|
workflowCoreRuntime.Resume();
|
|
}
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Internal Helpers
|
|
private void OnActivityListChanged(object sender, ActivityCollectionChangeEventArgs e)
|
|
{
|
|
if (e.RemovedItems != null)
|
|
{
|
|
foreach (Activity removedActivity in e.RemovedItems)
|
|
{
|
|
if (removedActivity.Readonly)
|
|
ReleaseDynamicUpdateMode(removedActivity);
|
|
}
|
|
}
|
|
}
|
|
private void ApplyDynamicUpdateMode(Activity seedActivity)
|
|
{
|
|
Queue<Activity> queue = new Queue<Activity>();
|
|
queue.Enqueue(seedActivity);
|
|
while (queue.Count > 0)
|
|
{
|
|
Activity activity = queue.Dequeue();
|
|
activity.Readonly = true;
|
|
activity.DynamicUpdateMode = true;
|
|
foreach (DependencyProperty dependencyProperty in activity.MetaDependencyProperties)
|
|
{
|
|
if (activity.IsBindingSet(dependencyProperty))
|
|
{
|
|
ActivityBind activityBind = activity.GetBinding(dependencyProperty);
|
|
if (activityBind != null)
|
|
activityBind.DynamicUpdateMode = true;
|
|
}
|
|
}
|
|
|
|
if (activity is CompositeActivity)
|
|
{
|
|
CompositeActivity compositeActivity = activity as CompositeActivity;
|
|
compositeActivity.Activities.ListChanged += new EventHandler<ActivityCollectionChangeEventArgs>(this.OnActivityListChanged);
|
|
foreach (Activity activity2 in ((CompositeActivity)activity).Activities)
|
|
queue.Enqueue(activity2);
|
|
}
|
|
}
|
|
}
|
|
private void ReleaseDynamicUpdateMode(Activity seedActivity)
|
|
{
|
|
Queue<Activity> queue = new Queue<Activity>();
|
|
queue.Enqueue(seedActivity);
|
|
while (queue.Count > 0)
|
|
{
|
|
Activity activity = queue.Dequeue() as Activity;
|
|
activity.Readonly = false;
|
|
activity.DynamicUpdateMode = false;
|
|
foreach (DependencyProperty dependencyProperty in activity.MetaDependencyProperties)
|
|
{
|
|
if (activity.IsBindingSet(dependencyProperty))
|
|
{
|
|
ActivityBind activityBind = activity.GetBinding(dependencyProperty);
|
|
if (activityBind != null)
|
|
activityBind.DynamicUpdateMode = false;
|
|
}
|
|
}
|
|
if (activity is CompositeActivity)
|
|
{
|
|
CompositeActivity compositeActivity = activity as CompositeActivity;
|
|
compositeActivity.Activities.ListChanged -= new EventHandler<ActivityCollectionChangeEventArgs>(this.OnActivityListChanged);
|
|
foreach (Activity activity2 in ((CompositeActivity)activity).Activities)
|
|
queue.Enqueue(activity2);
|
|
}
|
|
}
|
|
}
|
|
private void VerifyWorkflowCanBeChanged(IWorkflowCoreRuntime workflowCoreRuntime)
|
|
{
|
|
// check if the update is allowed on this root-activity.
|
|
ActivityCondition dynamicUpdateCondition = ((Activity)workflowCoreRuntime.RootActivity).GetValue(WorkflowChanges.ConditionProperty) as ActivityCondition;
|
|
if (dynamicUpdateCondition != null)
|
|
{
|
|
using (workflowCoreRuntime.SetCurrentActivity(workflowCoreRuntime.RootActivity))
|
|
{
|
|
if (!dynamicUpdateCondition.Evaluate(workflowCoreRuntime.RootActivity, workflowCoreRuntime))
|
|
throw new InvalidOperationException(SR.GetString(CultureInfo.CurrentCulture, SR.Error_DynamicUpdateEvaluation, new object[] { workflowCoreRuntime.InstanceID.ToString() }));
|
|
}
|
|
}
|
|
}
|
|
private void NotifyChangesCompletedToChildExecutors(IWorkflowCoreRuntime workflowCoreRuntime, Activity contextActivity)
|
|
{
|
|
Queue compositeActivities = new Queue();
|
|
compositeActivities.Enqueue(contextActivity);
|
|
while (compositeActivities.Count > 0)
|
|
{
|
|
CompositeActivity compositeActivity = compositeActivities.Dequeue() as CompositeActivity;
|
|
if (compositeActivity == null || !WorkflowChanges.IsActivityExecutable(compositeActivity))
|
|
continue;
|
|
|
|
ISupportWorkflowChanges compositeActivityExecutor = ActivityExecutors.GetActivityExecutor(compositeActivity) as ISupportWorkflowChanges;
|
|
if (compositeActivityExecutor != null)
|
|
{
|
|
using (workflowCoreRuntime.SetCurrentActivity(compositeActivity))
|
|
{
|
|
using (ActivityExecutionContext executionContext = new ActivityExecutionContext(compositeActivity))
|
|
compositeActivityExecutor.OnWorkflowChangesCompleted(executionContext);
|
|
}
|
|
}
|
|
foreach (Activity activity in compositeActivity.Activities)
|
|
{
|
|
if (activity is CompositeActivity)
|
|
compositeActivities.Enqueue(activity);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
internal static bool IsActivityExecutable(Activity activity)
|
|
{
|
|
if (!activity.Enabled)
|
|
return false;
|
|
if (activity.Parent != null)
|
|
return IsActivityExecutable(activity.Parent);
|
|
return activity.Enabled;
|
|
}
|
|
|
|
private void NotifyChangesToChildExecutors(IWorkflowCoreRuntime workflowCoreRuntime, Activity contextActivity, IList<WorkflowChangeAction> changeActions)
|
|
{
|
|
foreach (WorkflowChangeAction action in changeActions)
|
|
{
|
|
if (!(action is ActivityChangeAction))
|
|
continue;
|
|
|
|
CompositeActivity ownerActivity = contextActivity.TraverseDottedPathFromRoot(((ActivityChangeAction)action).OwnerActivityDottedPath) as CompositeActivity;
|
|
if (ownerActivity == null || !WorkflowChanges.IsActivityExecutable(ownerActivity))
|
|
continue;
|
|
|
|
ISupportWorkflowChanges compositeActivityExecutor = ActivityExecutors.GetActivityExecutor(ownerActivity) as ISupportWorkflowChanges;
|
|
if (compositeActivityExecutor == null)
|
|
throw new ApplicationException(SR.GetString(SR.Error_WorkflowChangesNotSupported, ownerActivity.GetType().FullName));
|
|
|
|
using (workflowCoreRuntime.SetCurrentActivity(ownerActivity))
|
|
{
|
|
using (ActivityExecutionContext executionContext = new ActivityExecutionContext(ownerActivity))
|
|
{
|
|
if (action is AddedActivityAction)
|
|
{
|
|
Activity addedActivity = ownerActivity.Activities[((AddedActivityAction)action).Index];
|
|
if (WorkflowChanges.IsActivityExecutable(addedActivity))
|
|
{
|
|
addedActivity.OnActivityExecutionContextLoad(executionContext.Activity.RootActivity.WorkflowCoreRuntime);
|
|
executionContext.InitializeActivity(addedActivity);
|
|
compositeActivityExecutor.OnActivityAdded(executionContext, addedActivity);
|
|
}
|
|
}
|
|
else if (action is RemovedActivityAction)
|
|
{
|
|
RemovedActivityAction removedActivityAction = (RemovedActivityAction)action;
|
|
if (WorkflowChanges.IsActivityExecutable(removedActivityAction.OriginalRemovedActivity))
|
|
{
|
|
compositeActivityExecutor.OnActivityRemoved(executionContext, removedActivityAction.OriginalRemovedActivity);
|
|
if (removedActivityAction.OriginalRemovedActivity.ExecutionResult != ActivityExecutionResult.Uninitialized)
|
|
{
|
|
removedActivityAction.OriginalRemovedActivity.Uninitialize(executionContext.Activity.RootActivity.WorkflowCoreRuntime);
|
|
removedActivityAction.OriginalRemovedActivity.SetValue(Activity.ExecutionResultProperty, ActivityExecutionResult.Uninitialized);
|
|
}
|
|
removedActivityAction.OriginalRemovedActivity.OnActivityExecutionContextUnload(executionContext.Activity.RootActivity.WorkflowCoreRuntime);
|
|
removedActivityAction.OriginalRemovedActivity.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Static helpers
|
|
|
|
private static bool CompareWorkflowDefinition(Activity originalWorkflowDefinition, Activity currentWorkflowDefinition)
|
|
{
|
|
if (originalWorkflowDefinition == currentWorkflowDefinition)
|
|
return true;
|
|
|
|
if (originalWorkflowDefinition.GetType() != currentWorkflowDefinition.GetType())
|
|
return false;
|
|
|
|
Guid originalChangeVersion = (Guid)originalWorkflowDefinition.GetValue(WorkflowChanges.WorkflowChangeVersionProperty);
|
|
Guid currentChangeVersion = (Guid)currentWorkflowDefinition.GetValue(WorkflowChanges.WorkflowChangeVersionProperty);
|
|
return (originalChangeVersion == currentChangeVersion);
|
|
}
|
|
|
|
private static List<WorkflowChangeAction> DiffTrees(CompositeActivity originalCompositeActivity, CompositeActivity clonedCompositeActivity)
|
|
{
|
|
List<WorkflowChangeAction> listChanges = new List<WorkflowChangeAction>();
|
|
IEnumerator<Activity> clonedActivitiesEnum = clonedCompositeActivity.Activities.GetEnumerator();
|
|
IEnumerator<Activity> originalActivitiesEnum = originalCompositeActivity.Activities.GetEnumerator();
|
|
int currentRemoveIndex = 0;
|
|
while (originalActivitiesEnum.MoveNext())
|
|
{
|
|
bool foundMatching = false;
|
|
Activity originalActivity = originalActivitiesEnum.Current;
|
|
while (clonedActivitiesEnum.MoveNext())
|
|
{
|
|
Activity clonedActivity = clonedActivitiesEnum.Current;
|
|
if (clonedActivity.Readonly)
|
|
{
|
|
if (originalActivity.DottedPath == clonedActivity.CachedDottedPath)
|
|
{
|
|
currentRemoveIndex++;
|
|
foundMatching = true;
|
|
if (originalActivity is CompositeActivity)
|
|
listChanges.AddRange(DiffTrees(originalActivity as CompositeActivity, clonedActivity as CompositeActivity));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
listChanges.Add(new RemovedActivityAction(currentRemoveIndex, originalActivity, clonedCompositeActivity));
|
|
while (originalActivitiesEnum.MoveNext())
|
|
{
|
|
originalActivity = originalActivitiesEnum.Current;
|
|
if (originalActivity.DottedPath == clonedActivity.CachedDottedPath)
|
|
{
|
|
currentRemoveIndex++;
|
|
foundMatching = true;
|
|
if (originalActivity is CompositeActivity)
|
|
listChanges.AddRange(DiffTrees(originalActivity as CompositeActivity, clonedActivity as CompositeActivity));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
listChanges.Add(new RemovedActivityAction(currentRemoveIndex, originalActivity, clonedCompositeActivity));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
listChanges.Add(new AddedActivityAction(clonedCompositeActivity, clonedActivity));
|
|
currentRemoveIndex++;
|
|
}
|
|
}
|
|
if (!foundMatching)
|
|
{
|
|
listChanges.Add(new RemovedActivityAction(currentRemoveIndex, originalActivity, clonedCompositeActivity));
|
|
}
|
|
}
|
|
while (clonedActivitiesEnum.MoveNext())
|
|
listChanges.Add(new AddedActivityAction(clonedCompositeActivity, clonedActivitiesEnum.Current));
|
|
return listChanges;
|
|
}
|
|
|
|
private static Activity CloneRootActivity(Activity originalRootActivity)
|
|
{
|
|
// create new definition root
|
|
string xomlText = originalRootActivity.GetValue(Activity.WorkflowXamlMarkupProperty) as string;
|
|
string rulesText = null;
|
|
Activity clonedRootActivity = null;
|
|
IServiceProvider serviceProvider = originalRootActivity.GetValue(Activity.WorkflowRuntimeProperty) as IServiceProvider;
|
|
Debug.Assert(serviceProvider != null);
|
|
if (!string.IsNullOrEmpty(xomlText))
|
|
{
|
|
rulesText = originalRootActivity.GetValue(Activity.WorkflowRulesMarkupProperty) as string;
|
|
clonedRootActivity = Activity.OnResolveActivityDefinition(null, xomlText, rulesText, true, false, serviceProvider);
|
|
}
|
|
else
|
|
clonedRootActivity = Activity.OnResolveActivityDefinition(originalRootActivity.GetType(), null, null, true, false, serviceProvider);
|
|
|
|
if (clonedRootActivity == null)
|
|
throw new NullReferenceException(SR.GetString(SR.Error_InvalidRootForWorkflowChanges));
|
|
|
|
// deserialize change history and apply it to new definition tree
|
|
ArrayList workflowChanges = (ArrayList)((Activity)originalRootActivity).GetValue(WorkflowChanges.WorkflowChangeActionsProperty);
|
|
if (workflowChanges != null)
|
|
{
|
|
workflowChanges = CloneWorkflowChangeActions(workflowChanges, originalRootActivity);
|
|
if (workflowChanges != null)
|
|
{
|
|
// apply changes to the shared schedule Defn to get the instance specific copy
|
|
foreach (WorkflowChangeAction action in workflowChanges)
|
|
{
|
|
bool result = action.ApplyTo((Activity)clonedRootActivity);
|
|
Debug.Assert(result, "ApplyTo Failed");
|
|
}
|
|
((Activity)clonedRootActivity).SetValue(WorkflowChanges.WorkflowChangeActionsProperty, workflowChanges);
|
|
}
|
|
}
|
|
return clonedRootActivity;
|
|
}
|
|
|
|
private static ArrayList CloneWorkflowChangeActions(ArrayList workflowChanges, Activity rootActivity)
|
|
{
|
|
if (workflowChanges == null)
|
|
throw new ArgumentNullException("workflowChanges");
|
|
|
|
if (rootActivity == null)
|
|
throw new ArgumentNullException("rootActivity");
|
|
|
|
string dynamicUpdateHistory = null;
|
|
TypeProvider typeProvider = CreateTypeProvider(rootActivity);
|
|
ServiceContainer serviceContainer = new ServiceContainer();
|
|
serviceContainer.AddService(typeof(ITypeProvider), typeProvider);
|
|
DesignerSerializationManager manager = new DesignerSerializationManager(serviceContainer);
|
|
WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer();
|
|
|
|
ArrayList clonedWorkflowChanges = null;
|
|
// serialize dynamic updates
|
|
using (manager.CreateSession())
|
|
{
|
|
using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture))
|
|
{
|
|
using (XmlWriter xmlWriter = Helpers.CreateXmlWriter(sw))
|
|
{
|
|
WorkflowMarkupSerializationManager xomlSerializationManager = new WorkflowMarkupSerializationManager(manager);
|
|
xomlSerializer.Serialize(xomlSerializationManager, xmlWriter, workflowChanges);
|
|
dynamicUpdateHistory = sw.ToString();
|
|
}
|
|
}
|
|
|
|
// deserialize those
|
|
using (StringReader sr = new StringReader(dynamicUpdateHistory))
|
|
{
|
|
using (XmlReader xmlReader = XmlReader.Create(sr))
|
|
{
|
|
WorkflowMarkupSerializationManager xomlSerializationManager = new WorkflowMarkupSerializationManager(manager);
|
|
clonedWorkflowChanges = xomlSerializer.Deserialize(xomlSerializationManager, xmlReader) as ArrayList;
|
|
}
|
|
}
|
|
}
|
|
return clonedWorkflowChanges;
|
|
}
|
|
|
|
internal static TypeProvider CreateTypeProvider(Activity rootActivity)
|
|
{
|
|
TypeProvider typeProvider = new TypeProvider(null);
|
|
|
|
Type companionType = rootActivity.GetType();
|
|
typeProvider.SetLocalAssembly(companionType.Assembly);
|
|
typeProvider.AddAssembly(companionType.Assembly);
|
|
|
|
foreach (AssemblyName assemblyName in companionType.Assembly.GetReferencedAssemblies())
|
|
{
|
|
Assembly referencedAssembly = null;
|
|
try
|
|
{
|
|
referencedAssembly = Assembly.Load(assemblyName);
|
|
if (referencedAssembly != null)
|
|
typeProvider.AddAssembly(referencedAssembly);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
if (referencedAssembly == null && assemblyName.CodeBase != null)
|
|
typeProvider.AddAssemblyReference(assemblyName.CodeBase);
|
|
}
|
|
return typeProvider;
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
#region WorkflowChangeAction classes
|
|
|
|
[DesignerSerializer(typeof(WorkflowMarkupSerializer), typeof(WorkflowMarkupSerializer))]
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public abstract class WorkflowChangeAction
|
|
{
|
|
protected internal abstract bool ApplyTo(Activity rootActivity);
|
|
protected internal abstract ValidationErrorCollection ValidateChanges(Activity activity);
|
|
}
|
|
|
|
[DesignerSerializer(typeof(ActivityChangeActionMarkupSerializer), typeof(WorkflowMarkupSerializer))]
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public abstract class ActivityChangeAction : WorkflowChangeAction
|
|
{
|
|
private string ownerActivityDottedPath = string.Empty;
|
|
|
|
protected ActivityChangeAction()
|
|
{
|
|
}
|
|
|
|
protected ActivityChangeAction(CompositeActivity compositeActivity)
|
|
{
|
|
if (compositeActivity == null)
|
|
throw new ArgumentNullException("compositeActivity");
|
|
|
|
this.ownerActivityDottedPath = compositeActivity.DottedPath;
|
|
}
|
|
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
|
public string OwnerActivityDottedPath
|
|
{
|
|
get
|
|
{
|
|
return this.ownerActivityDottedPath;
|
|
}
|
|
internal set
|
|
{
|
|
this.ownerActivityDottedPath = value;
|
|
}
|
|
}
|
|
|
|
protected internal override ValidationErrorCollection ValidateChanges(Activity contextActivity)
|
|
{
|
|
if (contextActivity == null)
|
|
throw new ArgumentNullException("contextActivity");
|
|
|
|
ValidationErrorCollection errors = new ValidationErrorCollection();
|
|
|
|
CompositeActivity ownerActivity = contextActivity.TraverseDottedPathFromRoot(this.OwnerActivityDottedPath) as CompositeActivity;
|
|
if (ownerActivity != null && WorkflowChanges.IsActivityExecutable(ownerActivity))
|
|
{
|
|
foreach (Validator validator in ComponentDispenser.CreateComponents(ownerActivity.GetType(), typeof(ActivityValidatorAttribute)))
|
|
{
|
|
ValidationError error = validator.ValidateActivityChange(ownerActivity, this);
|
|
if (error != null)
|
|
errors.Add(error);
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
}
|
|
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public sealed class AddedActivityAction : ActivityChangeAction
|
|
{
|
|
private int index = 0;
|
|
private Activity addedActivity = null;
|
|
|
|
public AddedActivityAction()
|
|
{
|
|
}
|
|
|
|
public AddedActivityAction(CompositeActivity compositeActivity, Activity activityAdded)
|
|
: base(compositeActivity)
|
|
{
|
|
if (compositeActivity == null)
|
|
throw new ArgumentNullException("compositeActivity");
|
|
|
|
if (activityAdded == null)
|
|
throw new ArgumentNullException("activityAdded");
|
|
|
|
this.index = (compositeActivity.Activities != null) ? compositeActivity.Activities.IndexOf(activityAdded) : -1;
|
|
this.addedActivity = activityAdded;
|
|
}
|
|
public int Index
|
|
{
|
|
get
|
|
{
|
|
return this.index;
|
|
}
|
|
internal set
|
|
{
|
|
this.index = value;
|
|
}
|
|
}
|
|
|
|
public Activity AddedActivity
|
|
{
|
|
get
|
|
{
|
|
return this.addedActivity;
|
|
}
|
|
internal set
|
|
{
|
|
this.addedActivity = value;
|
|
}
|
|
}
|
|
|
|
protected internal override bool ApplyTo(Activity rootActivity)
|
|
{
|
|
if (rootActivity == null)
|
|
throw new ArgumentNullException("rootActivity");
|
|
if (!(rootActivity is CompositeActivity))
|
|
throw new ArgumentException(SR.GetString(SR.Error_RootActivityTypeInvalid), "rootActivity");
|
|
|
|
CompositeActivity ownerActivity = rootActivity.TraverseDottedPathFromRoot(this.OwnerActivityDottedPath) as CompositeActivity;
|
|
if (ownerActivity == null)
|
|
return false;
|
|
|
|
// !!!work around:
|
|
ownerActivity.DynamicUpdateMode = true;
|
|
CompositeActivity addedActivityOwner = this.addedActivity.Parent;
|
|
try
|
|
{
|
|
this.addedActivity.SetParent(ownerActivity);
|
|
Activity clonedAddedActivity = this.addedActivity;
|
|
if (!this.addedActivity.DesignMode)
|
|
clonedAddedActivity = this.addedActivity.Clone();
|
|
// We need to serialize and deserialize in order to clone during design mode
|
|
else
|
|
{
|
|
TypeProvider typeProvider = WorkflowChanges.CreateTypeProvider(rootActivity);
|
|
ServiceContainer serviceContainer = new ServiceContainer();
|
|
serviceContainer.AddService(typeof(ITypeProvider), typeProvider);
|
|
DesignerSerializationManager manager = new DesignerSerializationManager(serviceContainer);
|
|
WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer();
|
|
string addedActivityText = string.Empty;
|
|
// serialize dynamic updates
|
|
using (manager.CreateSession())
|
|
{
|
|
using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture))
|
|
{
|
|
using (XmlWriter xmlWriter = Helpers.CreateXmlWriter(sw))
|
|
{
|
|
WorkflowMarkupSerializationManager xomlSerializationManager = new WorkflowMarkupSerializationManager(manager);
|
|
xomlSerializer.Serialize(xomlSerializationManager, xmlWriter, this.addedActivity);
|
|
addedActivityText = sw.ToString();
|
|
}
|
|
}
|
|
|
|
// deserialize those
|
|
using (StringReader sr = new StringReader(addedActivityText))
|
|
{
|
|
using (XmlReader xmlReader = XmlReader.Create(sr))
|
|
{
|
|
WorkflowMarkupSerializationManager xomlSerializationManager = new WorkflowMarkupSerializationManager(manager);
|
|
clonedAddedActivity = xomlSerializer.Deserialize(xomlSerializationManager, xmlReader) as Activity;
|
|
}
|
|
}
|
|
}
|
|
if (clonedAddedActivity == null)
|
|
throw new InvalidOperationException(SR.GetString(SR.Error_ApplyDynamicChangeFailed));
|
|
}
|
|
if (ownerActivity.WorkflowCoreRuntime != null)
|
|
((IDependencyObjectAccessor)clonedAddedActivity).InitializeInstanceForRuntime(ownerActivity.WorkflowCoreRuntime);
|
|
|
|
clonedAddedActivity.SetParent(null);
|
|
ownerActivity.Activities.Insert(this.index, clonedAddedActivity);
|
|
}
|
|
finally
|
|
{
|
|
this.addedActivity.SetParent(addedActivityOwner);
|
|
ownerActivity.DynamicUpdateMode = false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public sealed class RemovedActivityAction : ActivityChangeAction
|
|
{
|
|
private int removedActivityIndex = -1;
|
|
private Activity originalRemovedActivity = null;
|
|
|
|
public RemovedActivityAction()
|
|
{
|
|
}
|
|
public RemovedActivityAction(int removedActivityIndex, Activity originalActivity, CompositeActivity clonedParentActivity)
|
|
: base(clonedParentActivity)
|
|
{
|
|
if (originalActivity == null)
|
|
throw new ArgumentNullException("originalActivity");
|
|
if (clonedParentActivity == null)
|
|
throw new ArgumentNullException("clonedParentActivity");
|
|
|
|
this.originalRemovedActivity = originalActivity;
|
|
this.removedActivityIndex = removedActivityIndex;
|
|
}
|
|
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
|
|
public int RemovedActivityIndex
|
|
{
|
|
get
|
|
{
|
|
return this.removedActivityIndex;
|
|
}
|
|
internal set
|
|
{
|
|
this.removedActivityIndex = value;
|
|
}
|
|
}
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public Activity OriginalRemovedActivity
|
|
{
|
|
get
|
|
{
|
|
return this.originalRemovedActivity;
|
|
}
|
|
internal set
|
|
{
|
|
this.originalRemovedActivity = value;
|
|
}
|
|
}
|
|
protected internal override ValidationErrorCollection ValidateChanges(Activity contextActivity)
|
|
{
|
|
ValidationErrorCollection errors = base.ValidateChanges(contextActivity);
|
|
Activity removedActivityInContext = contextActivity.TraverseDottedPathFromRoot(this.originalRemovedActivity.DottedPath);
|
|
if (WorkflowChanges.IsActivityExecutable(removedActivityInContext) && removedActivityInContext.ExecutionStatus == ActivityExecutionStatus.Executing)
|
|
errors.Add(new ValidationError(SR.GetString(SR.Error_RemoveExecutingActivity, this.originalRemovedActivity.QualifiedName), ErrorNumbers.Error_RemoveExecutingActivity));
|
|
return errors;
|
|
}
|
|
protected internal override bool ApplyTo(Activity rootActivity)
|
|
{
|
|
if (rootActivity == null)
|
|
throw new ArgumentNullException("rootActivity");
|
|
if (!(rootActivity is CompositeActivity))
|
|
throw new ArgumentException(SR.GetString(SR.Error_RootActivityTypeInvalid), "rootActivity");
|
|
|
|
|
|
CompositeActivity ownerActivity = rootActivity.TraverseDottedPathFromRoot(this.OwnerActivityDottedPath) as CompositeActivity;
|
|
if (ownerActivity == null)
|
|
return false;
|
|
|
|
if (this.removedActivityIndex >= ownerActivity.Activities.Count)
|
|
return false;
|
|
|
|
// !!!work around:
|
|
ownerActivity.DynamicUpdateMode = true;
|
|
try
|
|
{
|
|
this.originalRemovedActivity = ownerActivity.Activities[this.removedActivityIndex];
|
|
ownerActivity.Activities.RemoveAt(this.removedActivityIndex);
|
|
}
|
|
finally
|
|
{
|
|
ownerActivity.DynamicUpdateMode = false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Class ActivityChangeActionMarkupSerializer
|
|
internal sealed class ActivityChangeActionMarkupSerializer : WorkflowMarkupSerializer
|
|
{
|
|
protected internal override PropertyInfo[] GetProperties(WorkflowMarkupSerializationManager serializationManager, object obj)
|
|
{
|
|
List<PropertyInfo> properties = new List<PropertyInfo>(base.GetProperties(serializationManager, obj));
|
|
|
|
//Collect the internal properties, we do this so that activity change action apis don't need to expose unnecessary setters
|
|
foreach (PropertyInfo property in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
|
|
{
|
|
DesignerSerializationVisibility visibility = Helpers.GetSerializationVisibility(property);
|
|
if (visibility != DesignerSerializationVisibility.Hidden && property.GetSetMethod() == null && property.GetSetMethod(true) != null)
|
|
properties.Add(property);
|
|
}
|
|
|
|
return properties.ToArray();
|
|
}
|
|
}
|
|
#endregion
|
|
}
|