//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.Activities { using System; using System.Activities.DynamicUpdate; using System.Activities.Hosting; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime; using System.Runtime.DurableInstancing; using System.Threading; using System.Xml.Linq; // Wrapper over instance data retrieved from the Instance Store but not yet loaded into a WorkflowApplication. // Once this instance is loaded into a WFApp using WFApp.Load(), this object is stale and trying to abort or reload it wil throw. // Free-threaded: needs to be resillient to simultaneous loads/aborts on multiple threads public class WorkflowApplicationInstance { private int state; internal WorkflowApplicationInstance( WorkflowApplication.PersistenceManagerBase persistenceManager, IDictionary values, WorkflowIdentity definitionIdentity) { this.PersistenceManager = persistenceManager; this.Values = values; this.DefinitionIdentity = definitionIdentity; this.state = (int)State.Initialized; } private enum State { Initialized, Loaded, Aborted } public WorkflowIdentity DefinitionIdentity { get; private set; } public InstanceStore InstanceStore { get { return this.PersistenceManager.InstanceStore; } } public Guid InstanceId { get { return this.PersistenceManager.InstanceId; } } internal WorkflowApplication.PersistenceManagerBase PersistenceManager { get; private set; } internal IDictionary Values { get; private set; } public void Abandon() { this.Abandon(ActivityDefaults.DeleteTimeout); } public void Abandon(TimeSpan timeout) { TimeoutHelper.ThrowIfNegativeArgument(timeout); this.MarkAsAbandoned(); WorkflowApplication.DiscardInstance(this.PersistenceManager, timeout); } public IAsyncResult BeginAbandon(AsyncCallback callback, object state) { return this.BeginAbandon(ActivityDefaults.DeleteTimeout, callback, state); } public IAsyncResult BeginAbandon(TimeSpan timeout, AsyncCallback callback, object state) { TimeoutHelper.ThrowIfNegativeArgument(timeout); this.MarkAsAbandoned(); return WorkflowApplication.BeginDiscardInstance(this.PersistenceManager, timeout, callback, state); } public void EndAbandon(IAsyncResult asyncResult) { WorkflowApplication.EndDiscardInstance(asyncResult); } [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, Justification = "Approved Design. Returning a bool makes the intent much clearer than something that just returns a list.")] public bool CanApplyUpdate(DynamicUpdateMap updateMap, out IList activitiesBlockingUpdate) { if (updateMap == null) { throw FxTrace.Exception.ArgumentNull("updateMap"); } activitiesBlockingUpdate = WorkflowApplication.GetActivitiesBlockingUpdate(this, updateMap); return activitiesBlockingUpdate == null || activitiesBlockingUpdate.Count == 0; } internal void MarkAsLoaded() { int oldState = Interlocked.CompareExchange(ref this.state, (int)State.Loaded, (int)State.Initialized); this.ThrowIfLoadedOrAbandoned((State)oldState); } private void MarkAsAbandoned() { int oldState = Interlocked.CompareExchange(ref this.state, (int)State.Aborted, (int)State.Initialized); this.ThrowIfLoadedOrAbandoned((State)oldState); } private void ThrowIfLoadedOrAbandoned(State oldState) { if (oldState == State.Loaded) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationInstanceLoaded)); } if (oldState == State.Aborted) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationInstanceAbandoned)); } } } }