File: System\ServiceModel\Activities\Dispatcher\ControlOperationInvoker.cs
Project: ndp\cdf\src\NetFx40\System.ServiceModel.Activities\System.ServiceModel.Activities.csproj (System.ServiceModel.Activities)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.ServiceModel.Activities.Dispatcher
{
    using System.Activities;
    using System.Activities.DynamicUpdate;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime;
    using System.Runtime.DurableInstancing;
    using System.ServiceModel.Activities;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.Transactions;
    using System.Xml.Linq;
 
    //This will be the top most invoker for all endpoint operations for durable services.
    //It operates in 3 modes.
    //1) IWorkflowInstanceManagement endpoint operation(completely implemented by this type)
    //2) Application endpoint(Modelled Receive/DurableService) Custom invoker inherited from this type overriding OnBegin/End ServiceOperation.
    //3) Durable Standard Endpoint(LRCS/Interop/Delay) eventual dispatch call will be delegated to innerInvoker with DurableInstanceContext as service object.
 
    //This class is single point of interaction with DurableInstanceManagement and ensures appropriate DurableInstance for the request, based on the mode of operation
    //message will be dispatched to various part of DurableInstance.
    class ControlOperationInvoker : IManualConcurrencyOperationInvoker
    {
        protected static readonly object[] emptyObjectArray = new object[0];
 
        readonly DurableInstanceManager instanceManager;
        readonly string operationName;
        readonly bool isOneWay;
        protected readonly ServiceEndpoint endpoint;
        readonly int inputParameterCount;
 
        readonly IOperationInvoker innerInvoker;
        readonly bool isControlOperation;
        readonly CorrelationKeyCalculator keyCalculator;
 
        readonly WorkflowServiceHost host;
        readonly BufferedReceiveManager bufferedReceiveManager;
        readonly TimeSpan persistTimeout;
 
        public ControlOperationInvoker(OperationDescription description, ServiceEndpoint endpoint,
            CorrelationKeyCalculator correlationKeyCalculator, ServiceHostBase host)
            : this(description, endpoint, correlationKeyCalculator, null, host)
        {
        }
 
        public ControlOperationInvoker(OperationDescription description, ServiceEndpoint endpoint,
            CorrelationKeyCalculator correlationKeyCalculator, IOperationInvoker innerInvoker, ServiceHostBase host)
        {
            Fx.Assert(host is WorkflowServiceHost, "ControlOperationInvoker must be used with a WorkflowServiceHost");
 
            this.host = (WorkflowServiceHost)host;
            this.instanceManager = this.host.DurableInstanceManager;
            this.operationName = description.Name;
            this.isOneWay = description.IsOneWay;
            this.endpoint = endpoint;
            this.innerInvoker = innerInvoker;
            this.keyCalculator = correlationKeyCalculator;
            this.persistTimeout = this.host.PersistTimeout;
 
            if (description.DeclaringContract == WorkflowControlEndpoint.WorkflowControlServiceContract ||
                description.DeclaringContract == WorkflowControlEndpoint.WorkflowControlServiceBaseContract)
            {
                //Mode1: This invoker belongs to IWorkflowInstanceManagement operation.
                this.isControlOperation = true;
                switch (this.operationName)
                {
                    case XD2.WorkflowInstanceManagementService.Cancel:
                    case XD2.WorkflowInstanceManagementService.TransactedCancel:
                    case XD2.WorkflowInstanceManagementService.Run:
                    case XD2.WorkflowInstanceManagementService.TransactedRun:
                    case XD2.WorkflowInstanceManagementService.Unsuspend:
                    case XD2.WorkflowInstanceManagementService.TransactedUnsuspend:
                        this.inputParameterCount = 1;
                        break;
                    case XD2.WorkflowInstanceManagementService.Abandon:
                    case XD2.WorkflowInstanceManagementService.Suspend:
                    case XD2.WorkflowInstanceManagementService.TransactedSuspend:
                    case XD2.WorkflowInstanceManagementService.Terminate:
                    case XD2.WorkflowInstanceManagementService.TransactedTerminate:
                    case XD2.WorkflowInstanceManagementService.Update:
                    case XD2.WorkflowInstanceManagementService.TransactedUpdate:
                        this.inputParameterCount = 2;
                        break;
                    default:
                        throw Fx.AssertAndThrow("Unreachable code");
                }
            }
            else if (endpoint is WorkflowHostingEndpoint)
            {
                this.CanCreateInstance = true;
            }
            else
            {
                this.bufferedReceiveManager = this.host.Extensions.Find<BufferedReceiveManager>();
            }
        }
 
        public bool IsSynchronous { get { return false; } }
 
        protected bool CanCreateInstance { get; set; }
        protected string StaticBookmarkName { get; set; }
 
        protected string OperationName
        {
            get { return this.operationName; }
        }
 
        public BufferedReceiveManager BufferedReceiveManager
        {
            get { return this.bufferedReceiveManager; }
        }
 
        public DurableInstanceManager InstanceManager
        {
            get { return this.instanceManager; }
        }
 
        public virtual object[] AllocateInputs()
        {
            if (this.innerInvoker != null) //Mode 3: Delegate call to innerInvoker.
            {
                return this.innerInvoker.AllocateInputs();
            }
            else if (this.isControlOperation) //Mode 1
            {
                if (this.inputParameterCount == 0)
                {
                    return emptyObjectArray;
                }
                else
                {
                    return new object[this.inputParameterCount];
                }
            }
            //Mode 2: Derived invoker should ensure appropriate in parameter count based on its contract.
            throw Fx.AssertAndThrow("Derived invoker should have handled this case");
        }
 
        // We own the formatter only if the message is oneway and is used 
        // to detemine if the message needs to be disposed or not.
        bool IManualConcurrencyOperationInvoker.OwnsFormatter
        {
            get { return this.isOneWay; }
        }
 
        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            return Invoke(instance, inputs, null, out outputs);
        }
 
        public object Invoke(object instance, object[] inputs, IInvokeReceivedNotification notification, out object[] outputs)
        {
            throw FxTrace.Exception.AsError(new NotImplementedException());
        }
 
        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            return InvokeBegin(instance, inputs, null, callback, state);
        }
 
        public IAsyncResult InvokeBegin(object instance, object[] inputs, IInvokeReceivedNotification notification, AsyncCallback callback, object state)
        {
            if (inputs == null)
            {
                throw FxTrace.Exception.ArgumentNull("inputs");
            }
 
            //Fetch Instance and Dispatch.
            return new ControlOperationAsyncResult(this, inputs, notification, TimeSpan.MaxValue, callback, state);
        }
 
        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            return ControlOperationAsyncResult.End(out outputs, result);
        }
 
        //This is the dispatch call for Non-IWorkflowInstanceManagement Operations.
        protected virtual IAsyncResult OnBeginServiceOperation(WorkflowServiceInstance durableInstance,
            OperationContext operationContext, object[] inputs, Transaction currentTransaction, IInvokeReceivedNotification notification,
            TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new ServiceOperationAsyncResult(this.innerInvoker, durableInstance, inputs, operationContext, currentTransaction, notification,
                callback, state);
        }
        protected virtual object OnEndServiceOperation(WorkflowServiceInstance durableInstance, out object[] outputs, IAsyncResult result)
        {
            return ServiceOperationAsyncResult.End(out outputs, result);
        }
 
 
        // pass OperationContext.Current if you don't already have an OperationContext.
        protected void GetInstanceKeys(OperationContext operationContext, out InstanceKey instanceKey, out ICollection<InstanceKey> additionalKeys)
        {
            CorrelationMessageProperty correlationMessageProperty = null;
            InstanceKey localInstanceKey;
            ICollection<InstanceKey> localAdditionalKeys;
 
            instanceKey = InstanceKey.InvalidKey;
            additionalKeys = new ReadOnlyCollection<InstanceKey>(new InstanceKey[] { });
 
            if (!CorrelationMessageProperty.TryGet(operationContext.IncomingMessageProperties,
                out correlationMessageProperty))
            {
                if (this.keyCalculator != null)
                {
                    MessageBuffer requestMessageBuffer;
                    bool success;
 
                    if (operationContext.IncomingMessageProperties.TryGetValue(ChannelHandler.MessageBufferPropertyName, out requestMessageBuffer))
                    {
                        success = this.keyCalculator.CalculateKeys(requestMessageBuffer, operationContext.IncomingMessage, out localInstanceKey, out localAdditionalKeys);
                    }
                    else
                    {
                        // Message is not preserved.(DispatchRuntime.PreserveMessage is false.)
                        // this could be a case where we only have context queries, in this case we don't preserve the message
                        success = this.keyCalculator.CalculateKeys(operationContext.IncomingMessage, out localInstanceKey, out localAdditionalKeys);
                    }
                    if (success)
                    {
                        if (localInstanceKey != null)
                        {
                            instanceKey = localInstanceKey;
                        }
                        if (localAdditionalKeys != null)
                        {
                            additionalKeys = localAdditionalKeys;
                        }
 
                        correlationMessageProperty = new CorrelationMessageProperty(instanceKey, additionalKeys);
 
                        operationContext.IncomingMessageProperties.Add(CorrelationMessageProperty.Name, correlationMessageProperty);
                    }
                }
            }
            else
            {
                instanceKey = correlationMessageProperty.CorrelationKey;
                additionalKeys = correlationMessageProperty.AdditionalKeys;
            }
 
            //If InstanceKey is still not resolved do the activation operation validation.
            if (instanceKey == null || !instanceKey.IsValid)
            {
                if (!this.CanCreateInstance)
                {
                    this.host.RaiseUnknownMessageReceived(operationContext.IncomingMessage);
 
                    throw FxTrace.Exception.AsError(
                        new FaultException(
                        new DurableDispatcherAddressingFault()));
                }
            }
        }
 
 
        class ControlOperationAsyncResult : AsyncResult
        {
            static AsyncCompletion handleEndGetInstance = new AsyncCompletion(HandleEndGetInstance);
            static AsyncCompletion handleEndOperation = new AsyncCompletion(HandleEndOperation);
            static AsyncCompletion handleEndAbandonReceiveContext;
            static ReadOnlyCollection<InstanceKey> emptyKeyCollection = new ReadOnlyCollection<InstanceKey>(new InstanceKey[] { });
            static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(Finally);
 
            object[] inputs;
            TimeoutHelper timeoutHelper;
 
            Guid instanceId;
            WorkflowIdentityKey updatedIdentity;
            InstanceKey instanceKey = InstanceKey.InvalidKey;
            ICollection<InstanceKey> additionalKeys = emptyKeyCollection;
 
            WorkflowServiceInstance workflowServiceInstance;
            Transaction transaction;
            ReceiveContext receiveContext;
            Exception operationException;
 
            object returnValue;
            OperationContext operationContext;
 
            ControlOperationInvoker invoker;
            IInvokeReceivedNotification notification;
 
            object[] outputs = emptyObjectArray;
 
            WorkflowGetInstanceContext getInstanceContext;
 
            public ControlOperationAsyncResult(ControlOperationInvoker invoker, object[] inputs, IInvokeReceivedNotification notification, TimeSpan timeout,
                AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.invoker = invoker;
                this.inputs = inputs;
                this.timeoutHelper = new TimeoutHelper(timeout);
                this.transaction = Transaction.Current;
                this.operationContext = OperationContext.Current;
                this.OnCompleting = onCompleting;
 
                bool completeSelf = false;
                bool success = false;
                try
                {
                    if (notification != null)
                    {
                        if (this.operationContext.SessionId == null)
                        {
                            // Datagram messages are completely concurrent to loadOrCreate and instance operations. Same as WCF's ConcurrencyMode.Single
                            notification.NotifyInvokeReceived();
                        }
                        else
                        {
                            // For session, we will notify after we enter into WorkflowServiceInstance pending queue.
                            // This achieves synchronization and ordered messages within a session and concurrent across distinct sessions.
                            this.notification = notification;
                        }
                    }
 
                    if (invoker.BufferedReceiveManager != null)
                    {
                        if (!ReceiveContext.TryGet(this.operationContext.IncomingMessageProperties, out this.receiveContext))
                        {
                            Fx.Assert("ReceiveContext expected when BufferedReceives are enabled");
                        }
                    }
 
                    completeSelf = this.Process();
                    success = true;
                }
                finally
                {
                    // in the success cases, OnCompleting has us covered
                    if (!success)
                    {
                        Finally(this, null);
                    }
                }
 
                if (completeSelf)
                {
                    this.Complete(true);
                }
            }
 
            static void Finally(AsyncResult result, Exception completionException)
            {
                ControlOperationAsyncResult thisPtr = (ControlOperationAsyncResult)result;
                if (thisPtr.workflowServiceInstance != null)
                {
                    thisPtr.workflowServiceInstance.ReleaseReference();
                    thisPtr.workflowServiceInstance = null;
                }
                if (completionException != null)
                {
                    thisPtr.invoker.host.FaultServiceHostIfNecessary(completionException);
                }
            }
 
            public static object End(out object[] outputs, IAsyncResult result)
            {
                ControlOperationAsyncResult thisPtr = AsyncResult.End<ControlOperationAsyncResult>(result);
                outputs = thisPtr.outputs;
                return thisPtr.returnValue;
            }
 
            bool Process()
            {
                EnsureInstanceIdAndIdentity();
 
                this.getInstanceContext = new WorkflowGetInstanceContext
                {
                    WorkflowHostingEndpoint = this.invoker.endpoint as WorkflowHostingEndpoint,
                    CanCreateInstance = this.invoker.CanCreateInstance,
                    Inputs = this.inputs,
                    OperationContext = this.operationContext,
                };
 
                IAsyncResult result;
 
                bool shouldAbandon = true;
                try
                {
                    try
                    {
                        if (!this.invoker.isControlOperation && this.instanceKey != null && this.instanceKey.IsValid)
                        {
                            result = this.invoker.instanceManager.BeginGetInstance(this.instanceKey, this.additionalKeys, this.getInstanceContext,
                                this.invoker.persistTimeout, this.PrepareAsyncCompletion(handleEndGetInstance), this);
                        }
                        else
                        {
                            result = this.invoker.instanceManager.BeginGetInstance(this.instanceId, this.getInstanceContext, this.updatedIdentity,
                                this.invoker.persistTimeout, this.PrepareAsyncCompletion(handleEndGetInstance), this);
                        }
                        shouldAbandon = false;
                    }
                    catch (InstanceLockedException exception)
                    {
                        RedirectionException redirectionException;
                        if (TryCreateRedirectionException(exception, out redirectionException))
                        {
                            throw FxTrace.Exception.AsError(redirectionException);
                        }
                        throw FxTrace.Exception.AsError(CreateFaultException(exception));
                    }
                    catch (OperationCanceledException exception)
                    {
                        BufferReceiveHelper(ref shouldAbandon, true);
                        throw FxTrace.Exception.AsError(new RetryException(null, exception));
                    }
                    catch (InstancePersistenceException exception)
                    {
                        BufferReceiveHelper(ref shouldAbandon, false);
 
                        if (exception is InstanceKeyNotReadyException)
                        {
                            this.invoker.host.RaiseUnknownMessageReceived(this.operationContext.IncomingMessage);
                        }
 
                        this.invoker.host.FaultServiceHostIfNecessary(exception);
 
                        throw FxTrace.Exception.AsError(CreateFaultException(exception));
                    }
                    catch (InstanceUpdateException)
                    {
                        throw FxTrace.Exception.AsError(new FaultException(OperationExecutionFault.CreateUpdateFailedFault(
                            SR.WorkflowInstanceUpdateFailed(this.instanceId, this.updatedIdentity.Identity))));
                    }
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                    {
                        throw;
                    }
                    if (!shouldAbandon || !ShouldAbandonReceiveContext())
                    {
                        throw;
                    }
                    return AbandonReceiveContext(exception);
                }
 
                if (result.CompletedSynchronously)
                {
                    return HandleEndGetInstance(result);
                }
                return false;
            }
 
            void BufferReceiveHelper(ref bool shouldAbandon, bool retry)
            {
                if (this.invoker.BufferedReceiveManager != null)
                {
                    Fx.Assert(this.receiveContext != null, "receiveContext must not be null!");
                    bool bufferSuccess = this.invoker.BufferedReceiveManager.BufferReceive(
                        this.operationContext, this.receiveContext, this.invoker.StaticBookmarkName, BufferedReceiveState.WaitingOnInstance, retry);
                    if (bufferSuccess)
                    {
                        if (TD.BufferOutOfOrderMessageNoInstanceIsEnabled())
                        {
                            TD.BufferOutOfOrderMessageNoInstance(this.invoker.StaticBookmarkName);
                        }
 
                        shouldAbandon = false;
                    }
                }
            }
 
            static bool HandleEndGetInstance(IAsyncResult result)
            {
                ControlOperationAsyncResult thisPtr = (ControlOperationAsyncResult)result.AsyncState;
 
                bool shouldAbandon = true;
                try
                {
                    try
                    {
                        thisPtr.workflowServiceInstance = thisPtr.invoker.instanceManager.EndGetInstance(result);
                        shouldAbandon = false;
                    }
                    catch (InstanceLockedException exception)
                    {
                        RedirectionException redirectionException;
                        if (thisPtr.TryCreateRedirectionException(exception, out redirectionException))
                        {
                            throw FxTrace.Exception.AsError(redirectionException);
                        }
                        throw FxTrace.Exception.AsError(CreateFaultException(exception));
                    }
                    catch (OperationCanceledException exception)
                    {
                        thisPtr.BufferReceiveHelper(ref shouldAbandon, true);
                        throw FxTrace.Exception.AsError(new RetryException(null, exception));
                    }
                    catch (InstancePersistenceException exception)
                    {
                        thisPtr.BufferReceiveHelper(ref shouldAbandon, false);
 
                        if (exception is InstanceKeyNotReadyException)
                        {
                            thisPtr.invoker.host.RaiseUnknownMessageReceived(thisPtr.operationContext.IncomingMessage);
                        }
 
                        thisPtr.invoker.host.FaultServiceHostIfNecessary(exception);
 
                        throw FxTrace.Exception.AsError(CreateFaultException(exception));
                    }
                    catch (InstanceUpdateException)
                    {
                        throw FxTrace.Exception.AsError(new FaultException(OperationExecutionFault.CreateUpdateFailedFault(
                            SR.WorkflowInstanceUpdateFailed(thisPtr.instanceId, thisPtr.updatedIdentity.Identity))));
                    }
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                    {
                        throw;
                    }
                    if (!shouldAbandon || !thisPtr.ShouldAbandonReceiveContext())
                    {
                        throw;
                    }
                    return thisPtr.AbandonReceiveContext(exception);
                }
 
                // When creating a new instance for a normal, keyless app message, create a key for use by context exchange.
                if (!thisPtr.instanceKey.IsValid && thisPtr.instanceId == Guid.Empty)
                {
                    ContextMessageProperty outgoingContextMessageProperty = null;
 
                    if (!ContextMessageProperty.TryGet(thisPtr.operationContext.OutgoingMessageProperties, out outgoingContextMessageProperty))
                    {
                        outgoingContextMessageProperty = new ContextMessageProperty();
                        outgoingContextMessageProperty.Context.Add(ContextMessageProperty.InstanceIdKey, Guid.NewGuid().ToString());
                        outgoingContextMessageProperty.AddOrReplaceInMessageProperties(thisPtr.operationContext.OutgoingMessageProperties);
                    }
                    else
                    {
                        outgoingContextMessageProperty.Context[ContextMessageProperty.InstanceIdKey] = Guid.NewGuid().ToString();
                    }
                }
                return thisPtr.PerformOperation();
            }
 
            bool PerformOperation()
            {
                IAsyncResult result = null;
                bool completed = false;
 
                if (this.invoker.isControlOperation)
                {
                    //Mode 1: Dispatch directly to WorkflowServiceInstance method.
                    switch (this.invoker.operationName)
                    {
                        case XD2.WorkflowInstanceManagementService.Suspend:
                        case XD2.WorkflowInstanceManagementService.TransactedSuspend:
                            result = this.workflowServiceInstance.BeginSuspend(false, (string)this.inputs[1] ?? SR.DefaultSuspendReason,
                                this.transaction, this.timeoutHelper.RemainingTime(),
                                this.PrepareAsyncCompletion(handleEndOperation), this);
                            break;
                        case XD2.WorkflowInstanceManagementService.Unsuspend:
                        case XD2.WorkflowInstanceManagementService.TransactedUnsuspend:
                            result = this.workflowServiceInstance.BeginUnsuspend(this.transaction, this.timeoutHelper.RemainingTime(),
                                this.PrepareAsyncCompletion(handleEndOperation), this);
                            break;
                        case XD2.WorkflowInstanceManagementService.Terminate:
                        case XD2.WorkflowInstanceManagementService.TransactedTerminate:
                            result = this.workflowServiceInstance.BeginTerminate((string)this.inputs[1] ?? SR.DefaultTerminationReason,
                                this.transaction, this.timeoutHelper.RemainingTime(),
                                this.PrepareAsyncCompletion(handleEndOperation), this);
                            break;
                        case XD2.WorkflowInstanceManagementService.Run:
                        case XD2.WorkflowInstanceManagementService.TransactedRun:
                            result = this.workflowServiceInstance.BeginRun(this.transaction, this.timeoutHelper.RemainingTime(),
                                this.PrepareAsyncCompletion(handleEndOperation), this);
                            break;
                        case XD2.WorkflowInstanceManagementService.Cancel:
                        case XD2.WorkflowInstanceManagementService.TransactedCancel:
                            result = this.workflowServiceInstance.BeginCancel(this.transaction,
                                this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(handleEndOperation), this);
                            break;
                        case XD2.WorkflowInstanceManagementService.Abandon:
                            string reason = (string)this.inputs[1];
                            result = this.workflowServiceInstance.BeginAbandon(
                                new WorkflowApplicationAbortedException(!String.IsNullOrEmpty(reason) ? reason : SR.DefaultAbortReason),
                                this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(handleEndOperation), this);
                            break;
                        case XD2.WorkflowInstanceManagementService.Update:
                        case XD2.WorkflowInstanceManagementService.TransactedUpdate:
                            WorkflowIdentity identity = (WorkflowIdentity)this.inputs[1];
                            if (!object.Equals(identity, this.workflowServiceInstance.DefinitionIdentity))
                            {
                                throw FxTrace.Exception.AsError(new FaultException(
                                    OperationExecutionFault.CreateUpdateFailedFault(SR.CannotUpdateLoadedInstance(this.workflowServiceInstance.Id))));
                            }
                            if (this.workflowServiceInstance.IsActive)
                            {
                                result = this.workflowServiceInstance.BeginRun(this.transaction, this.invoker.operationName, this.timeoutHelper.RemainingTime(),
                                    this.PrepareAsyncCompletion(handleEndOperation), this);
                            }
                            else
                            {
                                result = new CompletedAsyncResult(this.PrepareAsyncCompletion(handleEndOperation), this);
                            }
                            break;
                        default:
                            throw Fx.AssertAndThrow("Unreachable code");
 
                    }
                    if (this.notification != null)
                    {
                        this.notification.NotifyInvokeReceived();
                    }
                }
                else if (this.getInstanceContext.WorkflowCreationContext != null)
                {
                    result = BeginRunAndGetResponse(timeoutHelper, this.PrepareAsyncCompletion(handleEndOperation), this);
                    if (this.notification != null)
                    {
                        this.notification.NotifyInvokeReceived();
                    }
                }
                else
                {
                    try
                    {
                        //User Endpoint operation.
                        result = this.invoker.OnBeginServiceOperation(this.workflowServiceInstance, this.operationContext,
                            this.inputs, this.transaction, this.notification, this.timeoutHelper.RemainingTime(),
                            this.PrepareAsyncCompletion(handleEndOperation), this);
                    }
                    catch (FaultException)
                    {
                        throw; // ReceiveContext was handled appropriately by WorkflowOperationContext
                    }
                    catch (Exception exception)
                    {
                        if (Fx.IsFatal(exception))
                        {
                            throw;
                        }
                        if (!ShouldAbandonReceiveContext())
                        {
                            throw;
                        }
                        return AbandonReceiveContext(exception);
                    }
                }
 
                if (result != null && result.CompletedSynchronously)
                {
                    completed = HandleEndOperation(result);
                }
 
                return completed;
            }
 
            static bool HandleEndOperation(IAsyncResult result)
            {
                ControlOperationAsyncResult thisPtr = (ControlOperationAsyncResult)result.AsyncState;
 
                if (thisPtr.invoker.isControlOperation)
                {
                    switch (thisPtr.invoker.operationName)
                    {
                        case XD2.WorkflowInstanceManagementService.Suspend:
                        case XD2.WorkflowInstanceManagementService.TransactedSuspend:
                            thisPtr.workflowServiceInstance.EndSuspend(result);
                            break;
                        case XD2.WorkflowInstanceManagementService.Unsuspend:
                        case XD2.WorkflowInstanceManagementService.TransactedUnsuspend:
                            thisPtr.workflowServiceInstance.EndUnsuspend(result);
                            break;
                        case XD2.WorkflowInstanceManagementService.Terminate:
                        case XD2.WorkflowInstanceManagementService.TransactedTerminate:
                            thisPtr.workflowServiceInstance.EndTerminate(result);
                            break;
                        case XD2.WorkflowInstanceManagementService.Run:
                        case XD2.WorkflowInstanceManagementService.TransactedRun:
                            thisPtr.workflowServiceInstance.EndRun(result);
                            break;
                        case XD2.WorkflowInstanceManagementService.Cancel:
                        case XD2.WorkflowInstanceManagementService.TransactedCancel:
                            thisPtr.workflowServiceInstance.EndCancel(result);
                            break;
                        case XD2.WorkflowInstanceManagementService.Abandon:
                            thisPtr.workflowServiceInstance.EndAbandon(result);
                            break;
                        case XD2.WorkflowInstanceManagementService.Update:
                        case XD2.WorkflowInstanceManagementService.TransactedUpdate:
                            if (result is CompletedAsyncResult)
                            {
                                CompletedAsyncResult.End(result);
                            }
                            else
                            {
                                thisPtr.workflowServiceInstance.EndRun(result);
                            }
                            break;
                        default:
                            throw Fx.AssertAndThrow("Unreachable code");
                    }
                }
                else if (thisPtr.getInstanceContext.WorkflowCreationContext != null)
                {
                    thisPtr.returnValue = thisPtr.EndRunAndGetResponse(result, out thisPtr.outputs);
                }
                else
                {
                    //User operation
                    try
                    {
                        thisPtr.returnValue = thisPtr.invoker.OnEndServiceOperation(thisPtr.workflowServiceInstance, out thisPtr.outputs, result);
                    }
                    catch (FaultException)
                    {
                        throw; // ReceiveContext was handled appropriately by WorkflowOperationContext
                    }
                    catch (Exception exception)
                    {
                        if (Fx.IsFatal(exception))
                        {
                            throw;
                        }
                        if (!thisPtr.ShouldAbandonReceiveContext())
                        {
                            throw;
                        }
                        return thisPtr.AbandonReceiveContext(exception);
                    }
                }
 
                return true;
            }
 
            bool ShouldAbandonReceiveContext()
            {
                return this.receiveContext != null && this.receiveContext.State != ReceiveContextState.Faulted;
            }
 
            bool AbandonReceiveContext(Exception operationException)
            {
                Fx.Assert(ShouldAbandonReceiveContext(), "ShouldAbandonReceiveContext() is false!");
                if (handleEndAbandonReceiveContext == null)
                {
                    handleEndAbandonReceiveContext = new AsyncCompletion(HandleEndAbandonReceiveContext);
                }
 
                Fx.Assert(operationException != null, "operationException must not be null!");
                Fx.Assert(this.operationException == null, "AbandonReceiveContext must not be called twice!");
                this.operationException = operationException;
                IAsyncResult result = this.receiveContext.BeginAbandon(TimeSpan.MaxValue, this.PrepareAsyncCompletion(handleEndAbandonReceiveContext), this);
                return SyncContinue(result);
            }
 
            static bool HandleEndAbandonReceiveContext(IAsyncResult result)
            {
                ControlOperationAsyncResult thisPtr = (ControlOperationAsyncResult)result.AsyncState;
                thisPtr.receiveContext.EndAbandon(result);
                throw FxTrace.Exception.AsError(thisPtr.operationException);
            }
 
            void EnsureInstanceIdAndIdentity()
            {
                if (this.invoker.isControlOperation)
                {
                    switch (this.invoker.operationName)
                    {
                        case XD2.WorkflowInstanceManagementService.Abandon:
                        case XD2.WorkflowInstanceManagementService.Cancel:
                        case XD2.WorkflowInstanceManagementService.TransactedCancel:
                        case XD2.WorkflowInstanceManagementService.Run:
                        case XD2.WorkflowInstanceManagementService.TransactedRun:
                        case XD2.WorkflowInstanceManagementService.Suspend:
                        case XD2.WorkflowInstanceManagementService.TransactedSuspend:
                        case XD2.WorkflowInstanceManagementService.Terminate:
                        case XD2.WorkflowInstanceManagementService.TransactedTerminate:
                        case XD2.WorkflowInstanceManagementService.Unsuspend:
                        case XD2.WorkflowInstanceManagementService.TransactedUnsuspend:
                            this.instanceId = GetInstanceIdForControlOperation(this.inputs);
                            break;
                        case XD2.WorkflowInstanceManagementService.Update:
                        case XD2.WorkflowInstanceManagementService.TransactedUpdate:
                            this.instanceId = GetInstanceIdForControlOperation(this.inputs);
                            this.updatedIdentity = new WorkflowIdentityKey(GetIdentityForControlOperation(this.inputs));
                            break;
                        default:
                            throw Fx.AssertAndThrow("Unreachable code");
                    }
                }
                else if (this.invoker.endpoint is WorkflowHostingEndpoint)
                {
                    WorkflowHostingEndpoint hostingEndpoint = (WorkflowHostingEndpoint)this.invoker.endpoint;
                    this.instanceId = hostingEndpoint.OnGetInstanceId(inputs, this.operationContext);
                    if (this.instanceId == Guid.Empty)
                    {
                        this.invoker.GetInstanceKeys(this.operationContext, out this.instanceKey, out this.additionalKeys);
                    }
                }
                else
                {
                    //User endpoint operation.
                    this.invoker.GetInstanceKeys(this.operationContext, out this.instanceKey, out this.additionalKeys);
                }
            }
 
            Guid GetInstanceIdForControlOperation(object[] args)
            {
                Fx.Assert(args != null && args.Length > 0, "Cannot get argument");
                object arg = args[0];
 
                if (arg != null && arg is Guid)
                {
                    return (Guid)arg;
                }
                else
                {
                    throw FxTrace.Exception.AsError(new InvalidOperationException(SR.FailedToGetInstanceIdForControlOperation));
                }
            }
 
            WorkflowIdentity GetIdentityForControlOperation(object[] args)
            {
                Fx.Assert(args != null && args.Length > 1, "Cannot get argument");
                object arg = args[1];
 
                if (arg == null || arg is WorkflowIdentity)
                {
                    return (WorkflowIdentity)arg;
                }
                else
                {
                    throw FxTrace.Exception.AsError(new InvalidOperationException(SR.FailedToGetWorkflowIdentityForControlOperation));
                }
            }
 
            bool TryCreateRedirectionException(InstanceLockedException exception, out RedirectionException redirectionException)
            {
                Uri redirectVia = null;
 
                object redirectViaObject;
                string endpointName = this.invoker.endpoint != null ? this.invoker.endpoint.Name : null;
                XName endpointXName = endpointName == null ? null : WorkflowServiceNamespace.EndpointsPath.GetName(endpointName);
                if (endpointXName != null && exception.SerializableInstanceOwnerMetadata != null &&
                    exception.SerializableInstanceOwnerMetadata.TryGetValue(endpointXName, out redirectViaObject))
                {
                    redirectVia = redirectViaObject as Uri;
                }
 
                if (redirectVia == null)
                {
                    redirectionException = null;
                    return false;
                }
 
                redirectionException = new RedirectionException(RedirectionType.Resource, RedirectionDuration.Permanent,
                    RedirectionScope.Session, new RedirectionLocation(redirectVia));
                return true;
            }
 
            static FaultException CreateFaultException(InstancePersistenceException exception)
            {
                return new FaultException(OperationExecutionFault.CreateInstanceNotFoundFault(exception.Message));
            }
 
            IAsyncResult BeginRunAndGetResponse(TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
            {
                return RunAndGetResponseAsyncResult.Create(this, timeoutHelper, callback, state);
            }
 
            object EndRunAndGetResponse(IAsyncResult result, out object[] outputs)
            {
                return RunAndGetResponseAsyncResult.End(result, out outputs);
            }
 
            class RunAndGetResponseAsyncResult : AsyncResult
            {
                static AsyncCompletion handleEndRun = new AsyncCompletion(HandleEndRun);
                static AsyncCompletion handleEndSuspend = new AsyncCompletion(HandleEndSuspend);
                static AsyncCompletion handleEndGetResponse = new AsyncCompletion(HandleEndGetResponse);
 
                ControlOperationAsyncResult control;
                TimeoutHelper timeoutHelper;
                object returnValue;
                object[] outputs;
 
                RunAndGetResponseAsyncResult(ControlOperationAsyncResult control, TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.control = control;
                    this.timeoutHelper = timeoutHelper;
 
                    bool completeSelf = true;
 
                    if (control.getInstanceContext.WorkflowCreationContext.CreateOnly)
                    {
                        completeSelf = Suspend();
                    }
                    else
                    {
                        completeSelf = Run();
                    }
 
                    if (completeSelf)
                    {
                        Complete(true);
                    }
                }
 
                public static RunAndGetResponseAsyncResult Create(ControlOperationAsyncResult control, TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
                {
                    return new RunAndGetResponseAsyncResult(control, timeoutHelper, callback, state);
                }
 
                public static object End(IAsyncResult result, out object[] outputs)
                {
                    RunAndGetResponseAsyncResult thisPtr = AsyncResult.End<RunAndGetResponseAsyncResult>(result);
                    outputs = thisPtr.outputs;
                    return thisPtr.returnValue;
                }
 
                bool Run()
                {
                    IAsyncResult result = this.control.workflowServiceInstance.BeginRun(this.control.transaction, this.timeoutHelper.RemainingTime(),
                        PrepareAsyncCompletion(handleEndRun), this);
                    return SyncContinue(result);
                }
 
                static bool HandleEndRun(IAsyncResult result)
                {
                    RunAndGetResponseAsyncResult thisPtr = (RunAndGetResponseAsyncResult)result.AsyncState;
                    thisPtr.control.workflowServiceInstance.EndRun(result);
                    return thisPtr.GetResponse();
                }
 
                bool Suspend()
                {
                    IAsyncResult result = this.control.workflowServiceInstance.BeginSuspend(false, SR.DefaultCreateOnlyReason,
                        this.control.transaction, this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndSuspend), this);
                    return SyncContinue(result);
                }
 
                static bool HandleEndSuspend(IAsyncResult result)
                {
                    RunAndGetResponseAsyncResult thisPtr = (RunAndGetResponseAsyncResult)result.AsyncState;
                    thisPtr.control.workflowServiceInstance.EndSuspend(result);
                    return thisPtr.GetResponse();
                }
 
                bool GetResponse()
                {
                    IAsyncResult result = this.control.getInstanceContext.WorkflowHostingResponseContext.BeginGetResponse(this.timeoutHelper.RemainingTime(),
                        PrepareAsyncCompletion(handleEndGetResponse), this);
                    return SyncContinue(result);
                }
 
                static bool HandleEndGetResponse(IAsyncResult result)
                {
                    RunAndGetResponseAsyncResult thisPtr = (RunAndGetResponseAsyncResult)result.AsyncState;
                    thisPtr.returnValue = thisPtr.control.getInstanceContext.WorkflowHostingResponseContext.EndGetResponse(result, out thisPtr.outputs);
                    return true;
                }
            }
        }
 
        //AsyncResult implementation for User endpoint operation dispatch.
        class ServiceOperationAsyncResult : TransactedAsyncResult
        {
            static AsyncCompletion handleEndInvoke = new AsyncCompletion(HandleEndInvoke);
            IOperationInvoker innerInvoker;
            WorkflowServiceInstance durableInstance;
            object[] inputs;
            OperationContext operationContext;
            object returnValue;
            object[] outputs;
            Transaction currentTransaction;
            IInvokeReceivedNotification notification;
 
            public ServiceOperationAsyncResult(IOperationInvoker innerInvoker, WorkflowServiceInstance durableInstance,
                object[] inputs, OperationContext operationContext, Transaction currentTransaction, IInvokeReceivedNotification notification,
                AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.durableInstance = durableInstance;
                this.operationContext = operationContext;
                this.inputs = inputs;
                this.innerInvoker = innerInvoker;
                this.currentTransaction = currentTransaction;
                this.notification = notification;
 
                if (innerInvoker == null)
                {
                    //Mode2: Derived invoker should have handled this call.
                    throw Fx.AssertAndThrow("Cannot reach this path without innerInvoker");
                }
 
                if (this.innerInvoker.IsSynchronous)
                {
                    TransactionScope scope = TransactionHelper.CreateTransactionScope(this.currentTransaction);
                    try
                    {
                        using (new OperationContextScopeHelper(this.operationContext))
                        {
                            IManualConcurrencyOperationInvoker manualInvoker = this.innerInvoker as IManualConcurrencyOperationInvoker;
                            if (manualInvoker != null)
                            {
                                this.returnValue = manualInvoker.Invoke(this.durableInstance, this.inputs, this.notification, out this.outputs);
                            }
                            else
                            {
                                this.returnValue = this.innerInvoker.Invoke(this.durableInstance, this.inputs, out this.outputs);
                            }
                        }
                    }
                    finally
                    {
                        TransactionHelper.CompleteTransactionScope(ref scope);
                    }
                    this.Complete(true);
                }
                else
                {
                    IAsyncResult result;
                    using (PrepareTransactionalCall(this.currentTransaction))
                    {
                        using (new OperationContextScopeHelper(this.operationContext))
                        {
                            IManualConcurrencyOperationInvoker manualInvoker = this.innerInvoker as IManualConcurrencyOperationInvoker;
                            if (manualInvoker != null)
                            {
                                result = manualInvoker.InvokeBegin(this.durableInstance, this.inputs, this.notification, this.PrepareAsyncCompletion(handleEndInvoke), this);
                            }
                            else
                            {
                                result = this.innerInvoker.InvokeBegin(this.durableInstance, this.inputs, this.PrepareAsyncCompletion(handleEndInvoke), this);
                            }
                        }
                    }
                    if (SyncContinue(result))
                    {
                        this.Complete(true);
                    }
                }
            }
 
            public static object End(out object[] outputs, IAsyncResult result)
            {
                ServiceOperationAsyncResult thisPtr = AsyncResult.End<ServiceOperationAsyncResult>(result);
                outputs = thisPtr.outputs;
                return thisPtr.returnValue;
            }
 
            static bool HandleEndInvoke(IAsyncResult result)
            {
                ServiceOperationAsyncResult thisPtr = (ServiceOperationAsyncResult)result.AsyncState;
 
                TransactionScope scope = TransactionHelper.CreateTransactionScope(thisPtr.currentTransaction);
                try
                {
                    using (new OperationContextScopeHelper(thisPtr.operationContext))
                    {
                        thisPtr.returnValue = thisPtr.innerInvoker.InvokeEnd(thisPtr.durableInstance, out thisPtr.outputs, result);
                        return true;
                    }
                }
                finally
                {
                    TransactionHelper.CompleteTransactionScope(ref scope);
                }
            }
 
            class OperationContextScopeHelper : IDisposable
            {
                OperationContext current;
 
                public OperationContextScopeHelper(OperationContext newContext)
                {
                    this.current = OperationContext.Current;
                    OperationContext.Current = newContext;
                }
 
                void IDisposable.Dispose()
                {
                    OperationContext.Current = this.current;
                }
            }
        }
    }
}