//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.Activities.Hosting { using System; using System.Text; using System.Activities.DynamicUpdate; using System.Activities.Runtime; using System.Activities.Tracking; using System.Activities.Validation; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime; using System.Runtime.DurableInstancing; using System.Threading; using System.Xml.Linq; [Fx.Tag.XamlVisible(false)] public abstract class WorkflowInstance { static readonly IDictionary EmptyMappedVariablesDictionary = new ReadOnlyDictionaryInternal(new Dictionary(0)); const int True = 1; const int False = 0; WorkflowInstanceControl controller; TrackingProvider trackingProvider; SynchronizationContext syncContext; LocationReferenceEnvironment hostEnvironment; ActivityExecutor executor; int isPerformingOperation; bool isInitialized; WorkflowInstanceExtensionCollection extensions; // Tracking for one-time actions per in-memory instance bool hasTrackedResumed; bool hasTrackedCompletion; bool isAborted; Exception abortedException; #if DEBUG StackTrace abortStack; #endif static WorkflowInstance() { try { using (TelemetryEventSource eventSource = new TelemetryEventSource()) { eventSource.V2Runtime(); } } catch { } } protected WorkflowInstance(Activity workflowDefinition) : this(workflowDefinition, null) { } protected WorkflowInstance(Activity workflowDefinition, WorkflowIdentity definitionIdentity) { if (workflowDefinition == null) { throw FxTrace.Exception.ArgumentNull("workflowDefinition"); } this.WorkflowDefinition = workflowDefinition; this.DefinitionIdentity = definitionIdentity; } public abstract Guid Id { get; } internal bool HasTrackingParticipant { get; private set; } internal bool HasTrackedStarted { get; private set; } internal bool HasPersistenceModule { get; private set; } public SynchronizationContext SynchronizationContext { get { return this.syncContext; } set { ThrowIfReadOnly(); this.syncContext = value; } } public LocationReferenceEnvironment HostEnvironment { get { return this.hostEnvironment; } set { ThrowIfReadOnly(); this.hostEnvironment = value; } } public Activity WorkflowDefinition { get; private set; } public WorkflowIdentity DefinitionIdentity { get; private set; } protected bool IsReadOnly { get { return this.isInitialized; } } protected internal abstract bool SupportsInstanceKeys { get; } // this is going away internal TrackingProvider TrackingProvider { get { Fx.Assert(HasTrackingParticipant, "we should only be called if we have a tracking participant"); return this.trackingProvider; } } protected WorkflowInstanceControl Controller { get { if (!this.isInitialized) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ControllerInvalidBeforeInitialize)); } return this.controller; } } // host-facing access to our cascading ExtensionManager resolution protected internal T GetExtension() where T : class { if (this.extensions != null) { return this.extensions.Find(); } else { return default(T); } } protected internal IEnumerable GetExtensions() where T : class { if (this.extensions != null) { return this.extensions.FindAll(); } else { return new T[0]; } } // locks down the given extensions manager and runs cache metadata on the workflow definition protected void RegisterExtensionManager(WorkflowInstanceExtensionManager extensionManager) { ValidateWorkflow(extensionManager); this.extensions = WorkflowInstanceExtensionManager.CreateInstanceExtensions(this.WorkflowDefinition, extensionManager); if (this.extensions != null) { this.HasPersistenceModule = this.extensions.HasPersistenceModule; } } // dispose the extensions that implement IDisposable protected void DisposeExtensions() { if (this.extensions != null) { this.extensions.Dispose(); this.extensions = null; } } protected static IList GetActivitiesBlockingUpdate(object deserializedRuntimeState, DynamicUpdateMap updateMap) { ActivityExecutor executor = deserializedRuntimeState as ActivityExecutor; if (executor == null) { throw FxTrace.Exception.Argument("deserializedRuntimeState", SR.InvalidRuntimeState); } if (updateMap == null) { throw FxTrace.Exception.ArgumentNull("updateMap"); } DynamicUpdateMap rootMap = updateMap; if (updateMap.IsForImplementation) { rootMap = updateMap.AsRootMap(); } IList result = executor.GetActivitiesBlockingUpdate(rootMap); if (result == null) { result = new List(); } return result; } // used for Create scenarios where you are providing root information protected void Initialize(IDictionary workflowArgumentValues, IList workflowExecutionProperties) { ThrowIfAborted(); ThrowIfReadOnly(); this.executor = new ActivityExecutor(this); EnsureDefinitionReady(); // workflowArgumentValues signals whether we are a new or loaded instance, so we can't pass in null. // workflowExecutionProperties is allowed to be null InitializeCore(workflowArgumentValues ?? ActivityUtilities.EmptyParameters, workflowExecutionProperties); } // used for Load scenarios where you are rehydrating a WorkflowInstance protected void Initialize(object deserializedRuntimeState) { Initialize(deserializedRuntimeState, null); } protected void Initialize(object deserializedRuntimeState, DynamicUpdateMap updateMap) { ThrowIfAborted(); ThrowIfReadOnly(); this.executor = deserializedRuntimeState as ActivityExecutor; if (this.executor == null) { throw FxTrace.Exception.Argument("deserializedRuntimeState", SR.InvalidRuntimeState); } this.executor.ThrowIfNonSerializable(); EnsureDefinitionReady(); WorkflowIdentity originalDefinitionIdentity = this.executor.WorkflowIdentity; bool success = false; Collection updateErrors = null; try { if (updateMap != null) { // check if map is for implementaiton, if (updateMap.IsForImplementation) { // if so, the definition root must be an activity // with no public/imported children and no public/imported delegates. if (DynamicUpdateMap.CanUseImplementationMapAsRoot(this.WorkflowDefinition)) { updateMap = updateMap.AsRootMap(); } else { throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidImplementationAsWorkflowRoot)); } } updateMap.ThrowIfInvalid(this.WorkflowDefinition); this.executor.WorkflowIdentity = this.DefinitionIdentity; this.executor.UpdateInstancePhase1(updateMap, this.WorkflowDefinition, ref updateErrors); ThrowIfDynamicUpdateErrorExists(updateErrors); } InitializeCore(null, null); if (updateMap != null) { this.executor.UpdateInstancePhase2(updateMap, ref updateErrors); ThrowIfDynamicUpdateErrorExists(updateErrors); // Track that dynamic update is successful if (this.Controller.TrackingEnabled) { this.Controller.Track(new WorkflowInstanceUpdatedRecord(this.Id, this.WorkflowDefinition.DisplayName, originalDefinitionIdentity, this.executor.WorkflowIdentity)); } } success = true; } catch (InstanceUpdateException updateException) { // Can't track through the controller because initialization failed if (this.HasTrackingParticipant && this.TrackingProvider.ShouldTrackWorkflowInstanceRecords) { IList blockingActivities = updateException.BlockingActivities; if (blockingActivities.Count == 0) { blockingActivities = new List { new ActivityBlockingUpdate(this.WorkflowDefinition, this.WorkflowDefinition.Id, updateException.Message) }.AsReadOnly(); } this.TrackingProvider.AddRecord(new WorkflowInstanceUpdatedRecord(this.Id, this.WorkflowDefinition.DisplayName, originalDefinitionIdentity, this.DefinitionIdentity, blockingActivities)); } throw; } finally { if (updateMap != null && !success) { executor.MakeNonSerializable(); } } } void ThrowIfDynamicUpdateErrorExists(Collection updateErrors) { if (updateErrors != null && updateErrors.Count > 0) { // update error found // exit early throw FxTrace.Exception.AsError(new InstanceUpdateException(updateErrors)); } } void ValidateWorkflow(WorkflowInstanceExtensionManager extensionManager) { if (!WorkflowDefinition.IsRuntimeReady) { LocationReferenceEnvironment localEnvironment = this.hostEnvironment; if (localEnvironment == null) { LocationReferenceEnvironment parentEnvironment = null; if (extensionManager != null && extensionManager.SymbolResolver != null) { parentEnvironment = extensionManager.SymbolResolver.AsLocationReferenceEnvironment(); } localEnvironment = new ActivityLocationReferenceEnvironment(parentEnvironment); } IList validationErrors = null; ActivityUtilities.CacheRootMetadata(WorkflowDefinition, localEnvironment, ProcessActivityTreeOptions.FullCachingOptions, null, ref validationErrors); ActivityValidationServices.ThrowIfViolationsExist(validationErrors); } } void EnsureDefinitionReady() { if (this.extensions != null) { this.extensions.Initialize(); if (this.extensions.HasTrackingParticipant) { this.HasTrackingParticipant = true; if (this.trackingProvider == null) { this.trackingProvider = new TrackingProvider(this.WorkflowDefinition); } else { // TrackingProvider could be non-null if an earlier initialization attempt failed. // This happens when WorkflowApplication calls Abort after a load failure. In this // case we want to preserve any pending tracking records (e.g. DU failure). this.trackingProvider.ClearParticipants(); } foreach (TrackingParticipant trackingParticipant in GetExtensions()) { this.trackingProvider.AddParticipant(trackingParticipant); } } } else { // need to ensure the workflow has been validated since the host isn't using extensions (and so didn't register anything) ValidateWorkflow(null); } } void InitializeCore(IDictionary workflowArgumentValues, IList workflowExecutionProperties) { Fx.Assert(this.WorkflowDefinition.IsRuntimeReady, "EnsureDefinitionReady should have been called"); Fx.Assert(this.executor != null, "at this point, we better have an executor"); // Do Argument validation for root activities WorkflowDefinition.HasBeenAssociatedWithAnInstance = true; if (workflowArgumentValues != null) { IDictionary actualInputs = workflowArgumentValues; if (object.ReferenceEquals(actualInputs, ActivityUtilities.EmptyParameters)) { actualInputs = null; } if (this.WorkflowDefinition.RuntimeArguments.Count > 0 || (actualInputs != null && actualInputs.Count > 0)) { ActivityValidationServices.ValidateRootInputs(this.WorkflowDefinition, actualInputs); } this.executor.ScheduleRootActivity(this.WorkflowDefinition, actualInputs, workflowExecutionProperties); } else { this.executor.OnDeserialized(this.WorkflowDefinition, this); } this.executor.Open(this.SynchronizationContext); this.controller = new WorkflowInstanceControl(this, this.executor); this.isInitialized = true; if (this.extensions != null && this.extensions.HasWorkflowInstanceExtensions) { WorkflowInstanceProxy proxy = new WorkflowInstanceProxy(this); for (int i = 0; i < this.extensions.WorkflowInstanceExtensions.Count; i++) { IWorkflowInstanceExtension extension = this.extensions.WorkflowInstanceExtensions[i]; extension.SetInstance(proxy); } } } protected void ThrowIfReadOnly() { if (this.isInitialized) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowInstanceIsReadOnly(this.Id))); } } protected internal abstract IAsyncResult OnBeginResumeBookmark(Bookmark bookmark, object value, TimeSpan timeout, AsyncCallback callback, object state); protected internal abstract BookmarkResumptionResult OnEndResumeBookmark(IAsyncResult result); protected internal abstract IAsyncResult OnBeginPersist(AsyncCallback callback, object state); protected internal abstract void OnEndPersist(IAsyncResult result); protected internal abstract void OnDisassociateKeys(ICollection keys); protected internal abstract IAsyncResult OnBeginAssociateKeys(ICollection keys, AsyncCallback callback, object state); protected internal abstract void OnEndAssociateKeys(IAsyncResult result); internal IAsyncResult BeginFlushTrackingRecordsInternal(AsyncCallback callback, object state) { return OnBeginFlushTrackingRecords(callback, state); } internal void EndFlushTrackingRecordsInternal(IAsyncResult result) { OnEndFlushTrackingRecords(result); } protected void FlushTrackingRecords(TimeSpan timeout) { if (this.HasTrackingParticipant) { this.TrackingProvider.FlushPendingRecords(timeout); } } protected IAsyncResult BeginFlushTrackingRecords(TimeSpan timeout, AsyncCallback callback, object state) { if (this.HasTrackingParticipant) { return this.TrackingProvider.BeginFlushPendingRecords(timeout, callback, state); } else { return new CompletedAsyncResult(callback, state); } } protected void EndFlushTrackingRecords(IAsyncResult result) { if (this.HasTrackingParticipant) { this.TrackingProvider.EndFlushPendingRecords(result); } else { CompletedAsyncResult.End(result); } } protected virtual IAsyncResult OnBeginFlushTrackingRecords(AsyncCallback callback, object state) { return this.Controller.BeginFlushTrackingRecords(ActivityDefaults.TrackingTimeout, callback, state); } protected virtual void OnEndFlushTrackingRecords(IAsyncResult result) { this.Controller.EndFlushTrackingRecords(result); } internal void NotifyPaused() { if (this.executor.State != ActivityInstanceState.Executing) { TrackCompletion(); } OnNotifyPaused(); } protected abstract void OnNotifyPaused(); internal void NotifyUnhandledException(Exception exception, Activity source, string sourceInstanceId) { if (this.controller.TrackingEnabled) { ActivityInfo faultSourceInfo = new ActivityInfo(source.DisplayName, source.Id, sourceInstanceId, source.GetType().FullName); this.controller.Track(new WorkflowInstanceUnhandledExceptionRecord(this.Id, this.WorkflowDefinition.DisplayName, faultSourceInfo, exception, this.DefinitionIdentity)); } OnNotifyUnhandledException(exception, source, sourceInstanceId); } protected abstract void OnNotifyUnhandledException(Exception exception, Activity source, string sourceInstanceId); protected internal abstract void OnRequestAbort(Exception reason); internal void OnDeserialized(bool hasTrackedStarted) { this.HasTrackedStarted = hasTrackedStarted; } void StartOperation(ref bool resetRequired) { StartReadOnlyOperation(ref resetRequired); // isRunning can only flip to true by an operation and therefore // we don't have to worry about this changing under us if (this.executor.IsRunning) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.RuntimeRunning)); } } void StartReadOnlyOperation(ref bool resetRequired) { bool wasPerformingOperation = false; try { } finally { wasPerformingOperation = Interlocked.CompareExchange(ref this.isPerformingOperation, True, False) == True; if (!wasPerformingOperation) { resetRequired = true; } } if (wasPerformingOperation) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.RuntimeOperationInProgress)); } } void FinishOperation(ref bool resetRequired) { if (resetRequired) { this.isPerformingOperation = False; } } internal void Abort(Exception reason) { if (!this.isAborted) { this.isAborted = true; if (reason != null) { this.abortedException = reason; } if (this.extensions != null) { this.extensions.Cancel(); } if (this.controller.TrackingEnabled) { // During abort we only track this one record if (reason != null) { string message = reason.Message; if (reason.InnerException != null) { message = SR.WorkflowAbortedReason(reason.Message, reason.InnerException.Message); } this.controller.Track(new WorkflowInstanceAbortedRecord(this.Id, this.WorkflowDefinition.DisplayName, message, this.DefinitionIdentity)); } } #if DEBUG if (!Fx.FastDebug) { if (reason != null) { reason.ToString(); } this.abortStack = new StackTrace(); } #endif } } void ValidatePrepareForSerialization() { ThrowIfAborted(); if (!this.Controller.IsPersistable) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.PrepareForSerializationRequiresPersistability)); } } void ValidateScheduleResumeBookmark() { ThrowIfAborted(); ThrowIfNotIdle(); } void ValidateGetBookmarks() { ThrowIfAborted(); } void ValidateGetMappedVariables() { ThrowIfAborted(); } void ValidatePauseWhenPersistable() { ThrowIfAborted(); if (this.Controller.IsPersistable) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.PauseWhenPersistableInvalidIfPersistable)); } } void Terminate(Exception reason) { // validate we're in an ok state ThrowIfAborted(); // terminate the runtime this.executor.Terminate(reason); // and track if necessary TrackCompletion(); } void TrackCompletion() { if (this.controller.TrackingEnabled && !this.hasTrackedCompletion) { ActivityInstanceState completionState = this.executor.State; if (completionState == ActivityInstanceState.Faulted) { Fx.Assert(this.executor.TerminationException != null, "must have a termination exception if we're faulted"); this.controller.Track(new WorkflowInstanceTerminatedRecord(this.Id, this.WorkflowDefinition.DisplayName, this.executor.TerminationException.Message, this.DefinitionIdentity)); } else if (completionState == ActivityInstanceState.Closed) { this.controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Completed, this.DefinitionIdentity)); } else { Fx.AssertAndThrow(completionState == ActivityInstanceState.Canceled, "Cannot be executing a workflow instance when WorkflowState was completed."); this.controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Canceled, this.DefinitionIdentity)); } this.hasTrackedCompletion = true; } } void TrackResumed() { // track if necessary if (!this.hasTrackedResumed) { if (this.Controller.TrackingEnabled) { if (!this.HasTrackedStarted) { this.TrackingProvider.AddRecord(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Started, this.DefinitionIdentity)); this.HasTrackedStarted = true; } else { this.TrackingProvider.AddRecord(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Resumed, this.DefinitionIdentity)); } } this.hasTrackedResumed = true; } } void Run() { // validate we're in an ok state ThrowIfAborted(); TrackResumed(); // and let the scheduler go this.executor.MarkSchedulerRunning(); } void ScheduleCancel() { // validate we're in an ok state ThrowIfAborted(); TrackResumed(); this.executor.CancelRootActivity(); } BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value) { // validate we're in an ok state ValidateScheduleResumeBookmark(); TrackResumed(); return this.executor.TryResumeHostBookmark(bookmark, value); } BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value, BookmarkScope scope) { // validate we're in an ok state ValidateScheduleResumeBookmark(); TrackResumed(); return this.executor.TryResumeBookmark(bookmark, value, scope); } void ThrowIfAborted() { if (this.isAborted || (this.executor != null && this.executor.IsAbortPending)) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowInstanceAborted(this.Id))); } } void ThrowIfNotIdle() { if (!this.executor.IsIdle) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.BookmarksOnlyResumableWhileIdle)); } } [SuppressMessage(FxCop.Category.Design, FxCop.Rule.NestedTypesShouldNotBeVisible, Justification = "these are effectively protected methods, but encapsulated in a struct to avoid naming conflicts")] protected struct WorkflowInstanceControl { ActivityExecutor executor; WorkflowInstance instance; internal WorkflowInstanceControl(WorkflowInstance instance, ActivityExecutor executor) { this.instance = instance; this.executor = executor; } public bool IsPersistable { get { return this.executor.IsPersistable; } } public bool HasPendingTrackingRecords { get { return this.instance.HasTrackingParticipant && this.instance.TrackingProvider.HasPendingRecords; } } public bool TrackingEnabled { get { return this.instance.HasTrackingParticipant && this.instance.TrackingProvider.ShouldTrackWorkflowInstanceRecords; } } public WorkflowInstanceState State { get { WorkflowInstanceState result; if (this.instance.isAborted) { result = WorkflowInstanceState.Aborted; } else if (!this.executor.IsIdle) { result = WorkflowInstanceState.Runnable; } else { if (this.executor.State == ActivityInstanceState.Executing) { result = WorkflowInstanceState.Idle; } else { result = WorkflowInstanceState.Complete; } } return result; } } public override bool Equals(object obj) { if (!(obj is WorkflowInstanceControl)) { return false; } WorkflowInstanceControl other = (WorkflowInstanceControl)obj; return other.instance == this.instance; } public override int GetHashCode() { return this.instance.GetHashCode(); } public static bool operator ==(WorkflowInstanceControl left, WorkflowInstanceControl right) { return left.Equals(right); } public static bool operator !=(WorkflowInstanceControl left, WorkflowInstanceControl right) { return !left.Equals(right); } public ReadOnlyCollection GetBookmarks() { bool resetRequired = false; try { this.instance.StartReadOnlyOperation(ref resetRequired); this.instance.ValidateGetBookmarks(); return this.executor.GetAllBookmarks(); } finally { this.instance.FinishOperation(ref resetRequired); } } public ReadOnlyCollection GetBookmarks(BookmarkScope scope) { bool resetRequired = false; try { this.instance.StartReadOnlyOperation(ref resetRequired); this.instance.ValidateGetBookmarks(); return this.executor.GetBookmarks(scope); } finally { this.instance.FinishOperation(ref resetRequired); } } public IDictionary GetMappedVariables() { bool resetRequired = false; try { this.instance.StartReadOnlyOperation(ref resetRequired); this.instance.ValidateGetMappedVariables(); IDictionary mappedLocations = this.instance.executor.GatherMappableVariables(); if (mappedLocations != null) { mappedLocations = new ReadOnlyDictionaryInternal(mappedLocations); } else { mappedLocations = WorkflowInstance.EmptyMappedVariablesDictionary; } return mappedLocations; } finally { this.instance.FinishOperation(ref resetRequired); } } public void Run() { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); this.instance.Run(); } finally { this.instance.FinishOperation(ref resetRequired); } this.executor.Run(); } public void RequestPause() { // No validations for this because we do not // require calls to Pause to be synchronized // by the caller this.executor.PauseScheduler(); } // Calls Pause when IsPersistable goes from false->true public void PauseWhenPersistable() { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); this.instance.ValidatePauseWhenPersistable(); this.executor.PauseWhenPersistable(); } finally { this.instance.FinishOperation(ref resetRequired); } } public void ScheduleCancel() { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); this.instance.ScheduleCancel(); } finally { this.instance.FinishOperation(ref resetRequired); } } public void Terminate(Exception reason) { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); this.instance.Terminate(reason); } finally { this.instance.FinishOperation(ref resetRequired); } } public BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value) { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); return this.instance.ScheduleBookmarkResumption(bookmark, value); } finally { this.instance.FinishOperation(ref resetRequired); } } public BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value, BookmarkScope scope) { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); return this.instance.ScheduleBookmarkResumption(bookmark, value, scope); } finally { this.instance.FinishOperation(ref resetRequired); } } public void Abort() { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); // No validations this.executor.Dispose(); this.instance.Abort(null); } finally { this.instance.FinishOperation(ref resetRequired); } } public void Abort(Exception reason) { bool resetRequired = false; try { this.instance.StartOperation(ref resetRequired); // No validations this.executor.Abort(reason); this.instance.Abort(reason); } finally { this.instance.FinishOperation(ref resetRequired); } } [SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters, Justification = "Only want to allow WorkflowInstanceRecord subclasses for WorkflowInstance-level tracking")] public void Track(WorkflowInstanceRecord instanceRecord) { if (this.instance.HasTrackingParticipant) { this.instance.TrackingProvider.AddRecord(instanceRecord); } } public void FlushTrackingRecords(TimeSpan timeout) { this.instance.FlushTrackingRecords(timeout); } public IAsyncResult BeginFlushTrackingRecords(TimeSpan timeout, AsyncCallback callback, object state) { return this.instance.BeginFlushTrackingRecords(timeout, callback, state); } public void EndFlushTrackingRecords(IAsyncResult result) { this.instance.EndFlushTrackingRecords(result); } public object PrepareForSerialization() { bool resetRequired = false; try { this.instance.StartReadOnlyOperation(ref resetRequired); this.instance.ValidatePrepareForSerialization(); return this.executor.PrepareForSerialization(); } finally { this.instance.FinishOperation(ref resetRequired); } } public ActivityInstanceState GetCompletionState() { return this.executor.State; } [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, Justification = "Arch approved design. Requires the out argument for extra information provided")] public ActivityInstanceState GetCompletionState(out Exception terminationException) { terminationException = this.executor.TerminationException; return this.executor.State; } [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, Justification = "Arch approved design. Requires the out argument for extra information provided")] public ActivityInstanceState GetCompletionState(out IDictionary outputs, out Exception terminationException) { outputs = this.executor.WorkflowOutputs; terminationException = this.executor.TerminationException; return this.executor.State; } public Exception GetAbortReason() { return this.instance.abortedException; } } } }