PersistenceContext.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / System.ServiceModel.Activities / System / ServiceModel / Activities / Dispatcher / PersistenceContext.cs / 1407647 / PersistenceContext.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

namespace System.ServiceModel.Activities.Dispatcher 
{
    using System.Activities.DurableInstancing; 
    using System.Activities.Hosting; 
    using System.Collections;
    using System.Collections.ObjectModel; 
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime;
    using System.Runtime.DurableInstancing; 
    using System.ServiceModel.Channels;
    using System.ServiceModel.Activities.Description; 
    using System.Transactions; 
    using System.Xml.Linq;
 
    sealed class PersistenceContext : CommunicationObject
    {
        // Dictionary keyed by Transaction HashCode. The value is the enlistment for that transaction.
        internal static Dictionary Enlistments = new Dictionary(); 

        readonly PersistenceProviderDirectory directory; 
 
        readonly InstanceStore store;
        readonly InstanceHandle handle; 

        readonly HashSet keysToAssociate;
        readonly HashSet keysToDisassociate;
 
        static TimeSpan defaultOpenTimeout = TimeSpan.FromSeconds(90);
        static TimeSpan defaultCloseTimeout = TimeSpan.FromSeconds(90); 
 

        bool operationInProgress; 

        WorkflowServiceInstance workflowInstance;

        // The hash code of the transaction that has this particular context "locked". 
        // If the value of this property is 0, then no transaction is working on this context
        // and it is available to be "locked" by a transaction. Locking for transactions is done 
        // with QueueForTransactionLock. This method returns a TransactionWaitAsyncResult. If the 
        // lock was obtained by the call, the resulting AsyncResult will be marked as "Completed"
        // upon return from QueueForTransactionLock. If not, the caller should wait on the 
        // AsyncResult.AsyncWaitHandle before proceeding to update any of the fields of the context.
        int lockingTransaction;
        //We are keeping a reference to both the transaction object and the hash code to avoid calling the GetHashCode multiple times
        Transaction lockingTransactionObject; 

        // This is the queue of TransactionWaitAsyncResult objects that are waiting for the 
        // context to become "unlocked" with respect to a transaction. DequeueTransactionWaiter 
        // removes the first element from this queue and returns it. If there is no element on the
        // queue, null is returned indicating that there was no outstanding waiter. 
        Queue transactionWaiterQueue;

        // Used by PPD when there is no store.
        internal PersistenceContext(PersistenceProviderDirectory directory, 
            Guid instanceId, InstanceKey key, IEnumerable associatedKeys)
        { 
            Fx.Assert(directory != null, "Directory is null in PersistenceContext."); 
            Fx.Assert(instanceId != Guid.Empty, "Cannot provide an empty instance ID.");
 
            this.directory = directory;

            InstanceId = instanceId;
 
            AssociatedKeys = associatedKeys != null ? new HashSet(associatedKeys) :
                new HashSet(); 
            if (key != null && !AssociatedKeys.Contains(key)) 
            {
                AssociatedKeys.Add(key); 
            }

            this.keysToAssociate = new HashSet(AssociatedKeys);
            this.keysToDisassociate = new HashSet(); 

            this.lockingTransaction = 0; 
            this.Detaching = false; 
            this.transactionWaiterQueue = new Queue();
        } 

        // Used by PPD when there is a store.
        internal PersistenceContext(PersistenceProviderDirectory directory, InstanceStore store,
            InstanceHandle handle, Guid instanceId, IEnumerable associatedKeys, 
            bool newInstance, bool locked, InstanceView view)
            : this(directory, instanceId, null, associatedKeys) 
        { 
            Fx.Assert(store != null, "Null store passed to PersistenceContext.");
            Fx.Assert(handle != null, "Null handle passed to PersistenceContext."); 

            this.store = store;
            this.handle = handle;
 
            IsInitialized = !newInstance;
            IsLocked = locked; 
 
            if (view != null)
            { 
                ReadSuspendedInfo(view);
            }

            // If we were loaded or we locked the instance, the keys will have been [....]'d. 
            if (IsInitialized || IsLocked)
            { 
                RationalizeSavedKeys(false); 
            }
 
            if (IsInitialized)
            {
                Fx.Assert(view != null, "View must be specified on an initialized instance.");
                this.workflowInstance = this.directory.InitializeInstance(InstanceId, this, view.InstanceData, null); 
            }
        } 
 
        public Guid InstanceId { get; private set; }
 
        public bool IsLocked { get; private set; }
        public bool IsInitialized { get; private set; }
        public bool IsCompleted { get; private set; }
        public bool IsVisible { get; internal set; } 

        public bool IsSuspended { get; set; } 
        public string SuspendedReason { get; set; } 

        // Set to true when we detach from the PPD under a transaction. When the transaction completes, 
        // either commit or abort, we will finish the removal from the PPD.
        internal bool Detaching
        {
            get; set; 
        }
 
        public bool CanPersist 
        {
            get 
            {
                return (this.store != null);
            }
        } 

        public bool IsHandleValid 
        { 
            get
            { 
                return this.handle == null || this.handle.IsValid;
            }
        }
 
        internal Transaction LockingTransaction
        { 
            get 
            {
                lock (ThisLock) 
                {
                    ThrowIfDisposedOrNotOpen();
                    return this.lockingTransactionObject;
                } 
            }
        } 
 
        // Used only by PPD.
        internal bool IsPermanentlyRemoved { get; set; } 

        // If there's a directory, only it can write to this collection as long as the isntance is locked.  Otherwise,
        // only this class can.
        internal HashSet AssociatedKeys { get; private set; } 
        internal ReadOnlyCollection Bookmarks { get; set; }
 
        protected override TimeSpan DefaultCloseTimeout { get { return defaultCloseTimeout; } } 
        protected override TimeSpan DefaultOpenTimeout { get { return defaultOpenTimeout; } }
 
        // Remove key associations.  These are never immediately propagated to the store / cache.  Succeeds
        // if the keys don't exist or are associated with a different instance (in which case they are
        // not disassociated).
        public void DisassociateKeys(ICollection expiredKeys) 
        {
            ThrowIfDisposedOrNotOpen(); 
            Fx.Assert(expiredKeys != null, "'expiredKeys' parameter to DisassociateKeys cannot be null."); 

            try 
            {
                StartOperation();
                ThrowIfCompleted();
                ThrowIfNotVisible(); 
                Fx.Assert(!IsInitialized || IsLocked, "Should not be visible if initialized and not locked.");
 
                foreach (InstanceKey key in expiredKeys) 
                {
                    if (AssociatedKeys.Contains(key) && !this.keysToDisassociate.Contains(key)) 
                    {
                        this.keysToDisassociate.Add(key);
                        this.keysToAssociate.Remove(key);
                    } 
                    else
                    { 
                        Fx.Assert(!this.keysToAssociate.Contains(key), "Cannot be planning to associate this key."); 
                    }
                } 
            }
            finally
            {
                FinishOperation(); 
            }
        } 
 
        public IAsyncResult BeginSave(
            IDictionary instance, 
            SaveStatus saveStatus,
            TimeSpan timeout,
            AsyncCallback callback,
            object state) 
        {
            ThrowIfDisposedOrNotOpen(); 
            Fx.AssertAndThrow(instance != null, "'instance' parameter to BeginSave cannot be null."); 

            return new SaveAsyncResult(this, instance, saveStatus, timeout, callback, state); 
        }

        public void EndSave(IAsyncResult result)
        { 
            SaveAsyncResult.End(result);
        } 
 
        public IAsyncResult BeginRelease(TimeSpan timeout, AsyncCallback callback, object state)
        { 
            ThrowIfDisposedOrNotOpen();

            return new ReleaseAsyncResult(this, timeout, callback, state);
        } 

        public void EndRelease(IAsyncResult result) 
        { 
            ReleaseAsyncResult.End(result);
        } 

        public IAsyncResult BeginAssociateKeys(
            ICollection associatedKeys, TimeSpan timeout, AsyncCallback callback, object state)
        { 
            return BeginAssociateKeysHelper(associatedKeys, timeout, true, callback, state);
        } 
 
        internal IAsyncResult BeginAssociateInfrastructureKeys(
            ICollection associatedKeys, TimeSpan timeout, AsyncCallback callback, object state) 
        {
            return BeginAssociateKeysHelper(associatedKeys, timeout, true, callback, state);
        }
 
        IAsyncResult BeginAssociateKeysHelper(ICollection associatedKeys,
            TimeSpan timeout, bool applicationKeys, AsyncCallback callback, object state) 
        { 
            ThrowIfDisposedOrNotOpen();
            Fx.Assert(associatedKeys != null, "'associatedKeys' parameter to BeginAssociateKeys cannot be null."); 

            return new AssociateKeysAsyncResult(this, associatedKeys, timeout, applicationKeys, callback, state);
        }
 
        public void EndAssociateKeys(IAsyncResult result)
        { 
            AssociateKeysAsyncResult.End(result); 
        }
 
        internal void EndAssociateInfrastructureKeys(IAsyncResult result)
        {
            AssociateKeysAsyncResult.End(result);
        } 

        // UpdateSuspendMetadata and Unlock instance 
        public IAsyncResult BeginUpdateSuspendMetadata(Exception reason, TimeSpan timeout, AsyncCallback callback, object state) 
        {
            ThrowIfDisposedOrNotOpen(); 

            return new UpdateSuspendMetadataAsyncResult(this, reason, timeout, callback, state);
        }
 
        public void EndUpdateSuspendMetadata(IAsyncResult result)
        { 
            UpdateSuspendMetadataAsyncResult.End(result); 
        }
 
        public WorkflowServiceInstance GetInstance(WorkflowGetInstanceContext parameters)
        {
            if (this.workflowInstance == null && parameters != null)
            { 
                lock (ThisLock)
                { 
                    ThrowIfDisposedOrNotOpen(); 

                    if (this.workflowInstance == null) 
                    {
                        try
                        {
                            WorkflowServiceInstance result; 
                            if (parameters.WorkflowHostingEndpoint != null)
                            { 
                                WorkflowHostingResponseContext responseContext = new WorkflowHostingResponseContext(); 
                                WorkflowCreationContext creationContext = parameters.WorkflowHostingEndpoint.OnGetCreationContext(parameters.Inputs, parameters.OperationContext, InstanceId, responseContext);
                                if (creationContext == null) 
                                {
                                    throw FxTrace.Exception.AsError(WorkflowHostingEndpoint.CreateDispatchFaultException());
                                }
                                result = this.directory.InitializeInstance(InstanceId, this, null, creationContext); 

                                // Return args 
                                parameters.WorkflowCreationContext = creationContext; 
                                parameters.WorkflowHostingResponseContext = responseContext;
                            } 
                            else
                            {
                                result = this.directory.InitializeInstance(InstanceId, this, null, null);
                            } 
                            this.workflowInstance = result;
                        } 
                        finally 
                        {
                            if (this.workflowInstance == null) 
                            {
                                Fault();
                            }
                        } 
                    }
                } 
            } 
            return this.workflowInstance;
        } 

        protected override void OnAbort()
        {
            if (this.handle != null) 
            {
                this.handle.Free(); 
            } 
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new CloseAsyncResult(this, callback, state);
        } 

        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) 
        { 
            return new CompletedAsyncResult(callback, state);
        } 

        protected override void OnClose(TimeSpan timeout)
        {
            try 
            {
                StartOperation(); 
 
                if (this.store != null)
                { 
                    this.handle.Free();
                }
            }
            finally 
            {
                FinishOperation(); 
            } 
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            CloseAsyncResult.End(result);
        } 

        protected override void OnEndOpen(IAsyncResult result) 
        { 
            CompletedAsyncResult.End(result);
        } 

        // PersistenceProviderDirectory calls Open in an async path.  Do not introduce blocking work to this method
        // without changing PersistenceProviderDirectory to call BeginOpen instead.
        protected override void OnOpen(TimeSpan timeout) 
        {
        } 
 
        protected override void OnClosing()
        { 
            base.OnClosing();
            this.directory.RemoveInstance(this, true);
        }
 
        protected override void OnFaulted()
        { 
            base.OnFaulted(); 
            this.directory.RemoveInstance(this, true);
        } 

        void RationalizeSavedKeys(bool updateDirectory)
        {
            if (updateDirectory) 
            {
                this.directory.RemoveAssociations(this, this.keysToDisassociate); 
            } 
            else
            { 
                foreach (InstanceKey key in this.keysToDisassociate)
                {
                    AssociatedKeys.Remove(key);
                } 
            }
 
            this.keysToAssociate.Clear(); 
            this.keysToDisassociate.Clear();
        } 

        void ReadSuspendedInfo(InstanceView view)
        {
            string suspendedReason = null; 
            if (TryGetValue(view.InstanceMetadata, WorkflowServiceNamespace.SuspendReason, out suspendedReason))
            { 
                IsSuspended = true; 
                SuspendedReason = suspendedReason;
            } 
            else
            {
                IsSuspended = false;
                SuspendedReason = null; 
            }
        } 
 
        void StartOperation()
        { 
            Fx.AssertAndThrow(!this.operationInProgress, "PersistenceContext doesn't support multiple operations.");
            this.operationInProgress = true;
        }
 
        void FinishOperation()
        { 
            this.operationInProgress = false; 
        }
 
        void OnFinishOperationHelper(Exception exception, bool ownsThrottle)
        {
            try
            { 
                if (exception is OperationCanceledException)
                { 
                    throw FxTrace.Exception.AsError(new CommunicationObjectAbortedException(SR.HandleFreedInDirectory, exception)); 
                }
                else if (exception is TimeoutException) 
                {
                    Fault();
                }
            } 
            finally
            { 
                if (ownsThrottle) 
                {
                    this.directory.ReleaseThrottle(); 
                }
                FinishOperation();
            }
        } 

        void ThrowIfCompleted() 
        { 
            Fx.AssertAndThrow(!IsCompleted, "PersistenceContext operation invalid: instance already completed.");
        } 

        void ThrowIfNotVisible()
        {
            // Be charitable to racy aborts. 
            if (!IsVisible)
            { 
                lock (ThisLock) 
                {
                    Fx.AssertAndThrow(State != CommunicationState.Opened, 
                        "PersistenceContext operation invalid: instance must be visible.");
                }
            }
        } 

        internal static bool TryGetValue(IDictionary data, XName key, out T value) 
        { 
            InstanceValue instanceValue;
            value = default(T); 
            if (data.TryGetValue(key, out instanceValue) && !instanceValue.IsDeletedValue)
            {
                if (instanceValue.Value is T)
                { 
                    value = (T)instanceValue.Value;
                    return true; 
                } 
                else if (instanceValue.Value == null && !(value is ValueType))
                { 
                    // need to check for null assignments to value types
                    return true;
                }
                else 
                {
                    if (instanceValue.Value == null) 
                    { 
                        throw FxTrace.Exception.AsError(new InstancePersistenceException(SRCore.NullAssignedToValueType(typeof(T))));
                    } 
                    else
                    {
                        throw FxTrace.Exception.AsError(new InstancePersistenceException(SRCore.IncorrectValueType(typeof(T), instanceValue.Value.GetType())));
                    } 
                }
            } 
            else 
            {
                return false; 
            }
        }

        internal TransactionWaitAsyncResult BeginEnlist(TimeSpan timeout, AsyncCallback callback, object state) 
        {
            ThrowIfDisposedOrNotOpen(); 
            // The transaction to enlist on is in Transaction.Current. The actual enlistment, if needed, will be made in 
            // TransactionWaitAsyncResult when it is notified that it has the transaction lock.
            return new TransactionWaitAsyncResult(Transaction.Current, this, timeout, callback, state); 
        }

        [SuppressMessage(FxCop.Category.ReliabilityBasic, FxCop.Rule.CommunicationObjectThrowIf,
            Justification = "We are intentionally re-validating the state of the instance after having acquired the transaction lock")] 
        internal void EndEnlist(IAsyncResult result)
        { 
            TransactionWaitAsyncResult.End(result); 
            // The PersistenceContext may have been aborted while we were waiting for the transaction lock.
            ThrowIfDisposedOrNotOpen(); 
        }


        // Returns true if the call was able to obtain the transaction lock; false if we had 
        // to queue the request for the lock.
        internal bool QueueForTransactionLock(Transaction requestingTransaction, TransactionWaitAsyncResult txWaitAsyncResult) 
        { 
            lock (ThisLock)
            { 
                // If the transaction "lock" is not already held, give it to this requester.
                if (0 == this.lockingTransaction)
                {
                    // It's possible that this particular request is not transacted. 
                    if (null != requestingTransaction)
                    { 
                        this.lockingTransaction = requestingTransaction.GetHashCode(); 
                        this.lockingTransactionObject = requestingTransaction.Clone();
                    } 
                    // No queuing because we weren't already locked by a transaction.
                    return true;
                }
                else if ((null != requestingTransaction) && (this.lockingTransaction == requestingTransaction.GetHashCode())) 
                {
                    // Same transaction as the locking transaction - no queuing. 
                    return true; 
                }
                else 
                {
                    // Some other transaction has the lock, so add the AsyncResult to the queue.
                    this.transactionWaiterQueue.Enqueue(txWaitAsyncResult);
                    return false; 
                }
            } 
        } 

        // Dequeue and schedule the top element on queue of waiting TransactionWaitAsyncResult objects. 
        // Before returning this also makes the transaction represented by the dequeued TransactionWaitAsyncResult
        // the owner of the transaction "lock" for this context.
        internal void ScheduleNextTransactionWaiter()
        { 
            TransactionWaitAsyncResult dequeuedWaiter = null;
            bool detachThis = false; 
 
            lock (ThisLock)
            { 
                // Only try Dequeue if we have entries on the queue.
                bool atLeastOneSuccessfullyCompleted = false;
                if (0 < this.transactionWaiterQueue.Count)
                { 
                    while ((0 < this.transactionWaiterQueue.Count) && !atLeastOneSuccessfullyCompleted)
                    { 
                        dequeuedWaiter = this.transactionWaiterQueue.Dequeue(); 

                        // It's possible that the waiter didn't have a transaction. 
                        // If that is the case, we don't have a transaction to "lock" the context.
                        if (null != dequeuedWaiter.Transaction)
                        {
                            this.lockingTransactionObject = dequeuedWaiter.Transaction; 
                            this.lockingTransaction = lockingTransactionObject.GetHashCode();
                        } 
                        else 
                        {
                            this.lockingTransaction = 0; 
                            this.lockingTransactionObject = null;
                        }

                        atLeastOneSuccessfullyCompleted = dequeuedWaiter.Complete() || atLeastOneSuccessfullyCompleted; 

                        if (this.Detaching) 
                        { 
                            detachThis = true;
                            this.Detaching = false; 
                        }
 						
                        // If we are doing a permanent detach, we must have received an OnClosing or
                        // OnFaulted while the PersistenceContext was locked for a transaction. In that 
                        // case, we want to wake up ALL waiters.
                        if (this.IsPermanentlyRemoved) 
                        { 
                            this.lockingTransaction = 0;
                            this.lockingTransactionObject = null; 
                            while (0 < this.transactionWaiterQueue.Count)
                            {
                                dequeuedWaiter = this.transactionWaiterQueue.Dequeue();
                                atLeastOneSuccessfullyCompleted = dequeuedWaiter.Complete() || atLeastOneSuccessfullyCompleted; 
                            }
                        } 
 
                        // Now we need to look for any adjacent waiters in the queue that are
                        // waiting for the same transaction. If we still have entries on the queue, 
                        // we must have a waiterToComplete. Note that if we were doing a permanent detach,
                        // there won't be any waiters left in the queue at this point.
                        while (0 < this.transactionWaiterQueue.Count)
                        { 
                            TransactionWaitAsyncResult nextWaiter = this.transactionWaiterQueue.Peek();
                            if (0 == this.lockingTransaction) 
                            { 
                                // We dequeue this waiter because we shouldn't block transactional waiters
                                // behind non-transactional waiters because there is nothing to wake up the 
                                // transactional waiters in that case. Also set this.LockingTransaction
                                // to that of the next waiter.
                                if (null != nextWaiter.Transaction)
                                { 
                                    this.lockingTransactionObject = nextWaiter.Transaction;
                                    this.lockingTransaction = this.lockingTransactionObject.GetHashCode(); 
                                } 
                            }
                            else if (null != nextWaiter.Transaction) 
                            {
                                // Stop looking if the new lockingTransaction is different than
                                // the nextWaiter's transaction.
                                if (this.lockingTransaction != nextWaiter.Transaction.GetHashCode()) 
                                {
                                    break;  // out of the inner-while 
                                } 
                            }
                            else 
                            {
                                // The nextWaiter is non-transational, so it doesn't match the current
                                // lock holder, so we are done.
                                break;  // out of the inner-while 
                            }
 
                            dequeuedWaiter = this.transactionWaiterQueue.Dequeue(); 
                            atLeastOneSuccessfullyCompleted = dequeuedWaiter.Complete() || atLeastOneSuccessfullyCompleted;
                        } 
                    }
                }
                if (!atLeastOneSuccessfullyCompleted)
                { 
                    // There are no more waiters, so the context is no longer "locked" by a transaction.
                    this.lockingTransaction = 0; 
                    this.lockingTransactionObject = null; 
                }
            } 

            // If we are detaching and it is NOT permanently removed, finish the detach by calling RemoveInstance non-transactionally.
            // It will be marked as permanently removed in OnClosing and OnFaulted and it will have already been removed, so we don't
            // want to try to remove it again. 
            if (detachThis)
            { 
                this.directory.RemoveInstance(this, false); 
            }
        } 

        bool ScheduleDetach()
        {
            lock (ThisLock) 
            {
                if (this.lockingTransaction != 0) 
                { 
                    Detaching = true;
                    return true; 
                }
            }
            return false;
        } 

        void PopulateActivationMetadata(SaveWorkflowCommand saveCommand) 
        { 
            if (!IsInitialized)
            { 
                Fx.Assert(this.directory.InstanceMetadataChanges != null, "We should always be non-null here.");
                foreach (KeyValuePair pair in this.directory.InstanceMetadataChanges)
                {
                    saveCommand.InstanceMetadataChanges.Add(pair.Key, pair.Value); 
                }
            } 
        } 

        class CloseAsyncResult : AsyncResult 
        {
            PersistenceContext persistenceContext;

            public CloseAsyncResult(PersistenceContext persistenceContext, AsyncCallback callback, object state) 
                : base(callback, state)
            { 
                this.persistenceContext = persistenceContext; 
                OnCompleting = new Action(OnFinishOperation);
 
                bool success = false;
                bool completeSelf = false;
                try
                { 
                    this.persistenceContext.StartOperation();
 
                    if (this.persistenceContext.store != null) 
                    {
                        Fx.Assert(this.persistenceContext.handle != null, "WorkflowInstance failed to call SetHandle - from OnBeginClose."); 
                        this.persistenceContext.handle.Free();
                    }
                    completeSelf = true;
                    success = true; 
                }
                finally 
                { 
                    if (!success)
                    { 
                        this.persistenceContext.FinishOperation();
                    }
                }
 
                if (completeSelf)
                { 
                    base.Complete(true); 
                }
            } 

            public static void End(IAsyncResult result)
            {
                AsyncResult.End(result); 
            }
 
            void OnFinishOperation(AsyncResult result, Exception exception) 
            {
                this.persistenceContext.FinishOperation(); 
            }
        }

        class SaveAsyncResult : AsyncResult 
        {
            static readonly AsyncCompletion handleEndExecute = new AsyncCompletion(HandleEndExecute); 
            static readonly AsyncCompletion handleEndEnlist = new AsyncCompletion(HandleEndEnlist); 

            readonly PersistenceContext persistenceContext; 
            readonly SaveStatus saveStatus;
            readonly TimeoutHelper timeoutHelper;
            readonly DependentTransaction transaction;
 
            public SaveAsyncResult(PersistenceContext persistenceContext, IDictionary instance, SaveStatus saveStatus, TimeSpan timeout,
                AsyncCallback callback, object state) 
                : base(callback, state) 
            {
                this.persistenceContext = persistenceContext; 
                OnCompleting = new Action(OnFinishOperation);

                this.saveStatus = saveStatus;
 
                bool success = false;
                try 
                { 
                    this.persistenceContext.StartOperation();
 
                    this.persistenceContext.ThrowIfCompleted();
                    this.persistenceContext.ThrowIfNotVisible();
                    Fx.Assert(!this.persistenceContext.IsInitialized || this.persistenceContext.IsLocked,
                        "Should not be visible if initialized and not locked."); 

                    this.timeoutHelper = new TimeoutHelper(timeout); 
 
                    Transaction currentTransaction = Transaction.Current;
                    if (currentTransaction != null) 
                    {
                        this.transaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
                    }
 
                    if (this.persistenceContext.store != null)
                    { 
                        SaveWorkflowCommand saveCommand = new SaveWorkflowCommand(); 
                        foreach (KeyValuePair value in instance)
                        { 
                            saveCommand.InstanceData.Add(value);
                        }
                        this.persistenceContext.PopulateActivationMetadata(saveCommand);
                        if (this.persistenceContext.IsSuspended) 
                        {
                            saveCommand.InstanceMetadataChanges.Add(WorkflowServiceNamespace.SuspendReason, new InstanceValue(this.persistenceContext.SuspendedReason)); 
                        } 
                        else
                        { 
                            saveCommand.InstanceMetadataChanges.Add(WorkflowServiceNamespace.SuspendReason, InstanceValue.DeletedValue);
                            saveCommand.InstanceMetadataChanges.Add(WorkflowServiceNamespace.SuspendException, InstanceValue.DeletedValue);
                        }
                        foreach (InstanceKey key in this.persistenceContext.keysToAssociate) 
                        {
                            saveCommand.InstanceKeysToAssociate.Add(key.Value, key.Metadata); 
                        } 
                        foreach (InstanceKey key in this.persistenceContext.keysToDisassociate)
                        { 
                            saveCommand.InstanceKeysToFree.Add(key.Value);
                        }

                        if (this.saveStatus == SaveStatus.Completed) 
                        {
                            saveCommand.CompleteInstance = true; 
                            saveCommand.UnlockInstance = true; 
                        }
                        else 
                        {
                            saveCommand.UnlockInstance = this.saveStatus == SaveStatus.Unlocked;
                        }
 
                        IAsyncResult result = this.persistenceContext.store.BeginExecute(
                            this.persistenceContext.handle, 
                            saveCommand, 
                            this.timeoutHelper.RemainingTime(),
                            PrepareAsyncCompletion(SaveAsyncResult.handleEndExecute), 
                            this);
                        if (SyncContinue(result))
                        {
                            Complete(true); 
                        }
                    } 
                    else 
                    {
                        if (this.saveStatus == SaveStatus.Completed) 
                        {
                            this.persistenceContext.IsCompleted = true;
                            this.persistenceContext.IsLocked = false;
                        } 
                        else
                        { 
                            this.persistenceContext.IsLocked = this.saveStatus != SaveStatus.Unlocked; 
                        }
                        if (AfterSave()) 
                        {
                            Complete(true);
                        }
                    } 
                    success = true;
                } 
                catch (OperationCanceledException exception) 
                {
                    throw FxTrace.Exception.AsError(new CommunicationObjectAbortedException(SR.HandleFreedInDirectory, exception)); 
                }
                catch (TimeoutException)
                {
                    this.persistenceContext.Fault(); 
                    throw;
                } 
                finally 
                {
                    if (!success) 
                    {
                        try
                        {
                            if (this.transaction != null) 
                            {
                                this.transaction.Complete(); 
                            } 
                        }
                        finally 
                        {
                            this.persistenceContext.FinishOperation();
                        }
                    } 
                }
            } 
 
            public static void End(IAsyncResult result)
            { 
                AsyncResult.End(result);
            }

            static bool HandleEndExecute(IAsyncResult result) 
            {
                SaveAsyncResult thisPtr = (SaveAsyncResult)result.AsyncState; 
                thisPtr.persistenceContext.store.EndExecute(result); 
                thisPtr.persistenceContext.IsCompleted = thisPtr.saveStatus == SaveStatus.Completed;
                thisPtr.persistenceContext.IsLocked = thisPtr.saveStatus == SaveStatus.Locked; 
                return thisPtr.AfterSave();
            }

            bool AfterSave() 
            {
                this.persistenceContext.IsInitialized = true; 
 
                if (this.saveStatus != SaveStatus.Locked)
                { 
                    IAsyncResult result;
                    using (PrepareTransactionalCall(this.transaction))
                    {
                        result = this.persistenceContext.BeginEnlist(this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(SaveAsyncResult.handleEndEnlist), this); 
                    }
                    return SyncContinue(result); 
                } 

                return AfterEnlist(); 
            }

            bool AfterEnlist()
            { 
                this.persistenceContext.RationalizeSavedKeys(this.saveStatus == SaveStatus.Locked);
                return true; 
            } 

            static bool HandleEndEnlist(IAsyncResult result) 
            {
                SaveAsyncResult thisPtr = (SaveAsyncResult)result.AsyncState;
                thisPtr.persistenceContext.EndEnlist(result);
 
                if (!thisPtr.persistenceContext.ScheduleDetach())
                { 
                    thisPtr.persistenceContext.directory.RemoveInstance(thisPtr.persistenceContext); 
                }
                return thisPtr.AfterEnlist(); 
            }

            void OnFinishOperation(AsyncResult result, Exception exception)
            { 
                try
                { 
                    this.persistenceContext.OnFinishOperationHelper(exception, false); 
                }
                finally 
                {
                    if (this.transaction != null)
                    {
                        this.transaction.Complete(); 
                    }
                } 
            } 
        }
 
        class ReleaseAsyncResult : AsyncResult
        {
            static readonly AsyncCompletion handleEndExecute = new AsyncCompletion(HandleEndExecute);
            static readonly AsyncCompletion handleEndEnlist = new AsyncCompletion(HandleEndEnlist); 

            readonly PersistenceContext persistenceContext; 
            readonly TimeoutHelper timeoutHelper; 
            readonly DependentTransaction transaction;
 
            public ReleaseAsyncResult(PersistenceContext persistenceContext, TimeSpan timeout, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.persistenceContext = persistenceContext; 
                OnCompleting = new Action(OnFinishOperation);
 
                bool success = false; 
                try
                { 
                    this.persistenceContext.StartOperation();

                    this.timeoutHelper = new TimeoutHelper(timeout);
 
                    Transaction currentTransaction = Transaction.Current;
                    if (currentTransaction != null) 
                    { 
                        this.transaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
                    } 

                    if (this.persistenceContext.IsVisible)
                    {
                        if (this.persistenceContext.store != null && this.persistenceContext.IsLocked) 
                        {
                            SaveWorkflowCommand saveCommand = new SaveWorkflowCommand() { UnlockInstance = true }; 
                            this.persistenceContext.PopulateActivationMetadata(saveCommand); 
                            IAsyncResult result = this.persistenceContext.store.BeginExecute(
                                this.persistenceContext.handle, 
                                saveCommand,
                                this.timeoutHelper.RemainingTime(),
                                PrepareAsyncCompletion(ReleaseAsyncResult.handleEndExecute),
                                this); 
                            if (SyncContinue(result))
                            { 
                                Complete(true); 
                            }
                        } 
                        else
                        {
                            if (AfterUnlock())
                            { 
                                Complete(true);
                            } 
                        } 
                    }
                    else 
                    {
                        // If we're not visible because we were aborted in a ----, the caller needs to know.
                        lock (this.persistenceContext.ThisLock)
                        { 
                            this.persistenceContext.ThrowIfDisposedOrNotOpen();
                        } 
                        Complete(true); 
                    }
                    success = true; 
                }
                catch (OperationCanceledException exception)
                {
                    throw FxTrace.Exception.AsError(new CommunicationObjectAbortedException(SR.HandleFreedInDirectory, exception)); 
                }
                catch (TimeoutException) 
                { 
                    this.persistenceContext.Fault();
                    throw; 
                }
                finally
                {
                    if (!success) 
                    {
                        try 
                        { 
                            if (this.transaction != null)
                            { 
                                this.transaction.Complete();
                            }
                        }
                        finally 
                        {
                            this.persistenceContext.FinishOperation(); 
                        } 
                    }
                } 
            }

            public static void End(IAsyncResult result)
            { 
                AsyncResult.End(result);
            } 
 
            static bool HandleEndExecute(IAsyncResult result)
            { 
                ReleaseAsyncResult thisPtr = (ReleaseAsyncResult)result.AsyncState;
                thisPtr.persistenceContext.store.EndExecute(result);
                return thisPtr.AfterUnlock();
            } 

            bool AfterUnlock() 
            { 
                this.persistenceContext.IsLocked = false;
 
                IAsyncResult result;
                using (PrepareTransactionalCall(this.transaction))
                {
                    result = this.persistenceContext.BeginEnlist(this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(ReleaseAsyncResult.handleEndEnlist), this); 
                }
                return SyncContinue(result); 
            } 

            static bool HandleEndEnlist(IAsyncResult result) 
            {
                ReleaseAsyncResult thisPtr = (ReleaseAsyncResult)result.AsyncState;
                thisPtr.persistenceContext.EndEnlist(result);
 
                if (!thisPtr.persistenceContext.ScheduleDetach())
                { 
                    thisPtr.persistenceContext.directory.RemoveInstance(thisPtr.persistenceContext); 
                }
 
                foreach (InstanceKey key in thisPtr.persistenceContext.keysToAssociate)
                {
                    thisPtr.persistenceContext.AssociatedKeys.Remove(key);
                } 
                thisPtr.persistenceContext.keysToAssociate.Clear();
                thisPtr.persistenceContext.keysToDisassociate.Clear(); 
 
                return true;
            } 

            void OnFinishOperation(AsyncResult result, Exception exception)
            {
                try 
                {
                    this.persistenceContext.OnFinishOperationHelper(exception, false); 
                } 
                finally
                { 
                    if (this.transaction != null)
                    {
                        this.transaction.Complete();
                    } 
                }
            } 
        } 

        class AssociateKeysAsyncResult : AsyncResult 
        {
            static readonly AsyncCompletion handleEndExecute = new AsyncCompletion(HandleEndExecute);
            static readonly AsyncCompletion handleEndEnlist = new AsyncCompletion(HandleEndEnlist);
 
            readonly PersistenceContext persistenceContext;
            readonly bool applicationKeys; 
            readonly ICollection keysToAssociate; 
            readonly TimeoutHelper timeoutHelper;
            readonly DependentTransaction transaction; 

            public AssociateKeysAsyncResult(PersistenceContext persistenceContext, ICollection associatedKeys, TimeSpan timeout,
                bool applicationKeys, AsyncCallback callback, object state)
                : base(callback, state) 
            {
                this.persistenceContext = persistenceContext; 
                this.applicationKeys = applicationKeys; 
                this.keysToAssociate = associatedKeys;
                this.timeoutHelper = new TimeoutHelper(timeout); 

                OnCompleting = new Action(OnFinishOperation);

                bool success = false; 
                try
                { 
                    this.persistenceContext.StartOperation(); 

                    this.persistenceContext.ThrowIfCompleted(); 
                    this.persistenceContext.ThrowIfNotVisible();
                    Fx.Assert(!this.persistenceContext.IsInitialized || this.persistenceContext.IsLocked,
                        "Should not be visible if initialized and not locked.");
 
                    Transaction currentTransaction = Transaction.Current;
                    if (currentTransaction != null) 
                    { 
                        this.transaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
                    } 

                    // We need to get the transaction lock and enlist on the transaction, if there is one.
                    IAsyncResult enlistResult = persistenceContext.BeginEnlist(this.timeoutHelper.RemainingTime(),
                                      this.PrepareAsyncCompletion(handleEndEnlist), this); 
                    if (SyncContinue(enlistResult))
                    { 
                        Complete(true); 
                    }
                    success = true; 
                }
                catch (InstancePersistenceException)
                {
                    this.persistenceContext.Fault(); 
                    throw;
                } 
                catch (OperationCanceledException exception) 
                {
                    throw FxTrace.Exception.AsError(new CommunicationObjectAbortedException(SR.HandleFreedInDirectory, exception)); 
                }
                catch (TimeoutException)
                {
                    this.persistenceContext.Fault(); 
                    throw;
                } 
                finally 
                {
                    if (!success) 
                    {
                        try
                        {
                            // We need to complete our dependent clone because OnFinishOperation will not 
                            // get called in this case.
                            if (this.transaction != null) 
                            { 
                                this.transaction.Complete();
                            } 
                        }
                        finally
                        {
                            this.persistenceContext.FinishOperation(); 
                        }
                    } 
                } 
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End(result);
            } 

            static bool HandleEndExecute(IAsyncResult result) 
            { 
                AssociateKeysAsyncResult thisPtr = (AssociateKeysAsyncResult)result.AsyncState;
                thisPtr.persistenceContext.store.EndExecute(result); 
                return thisPtr.AfterUpdate();
            }

            static bool HandleEndEnlist(IAsyncResult result) 
            {
                AssociateKeysAsyncResult thisPtr = (AssociateKeysAsyncResult)result.AsyncState; 
                bool returnValue = false; 

                if (!thisPtr.persistenceContext.directory.TryAddAssociations( 
                    thisPtr.persistenceContext,
                    thisPtr.keysToAssociate,
                    thisPtr.persistenceContext.keysToAssociate,
                    thisPtr.applicationKeys ? thisPtr.persistenceContext.keysToDisassociate : null)) 
                {
                    lock (thisPtr.persistenceContext.ThisLock) 
                    { 
                        thisPtr.persistenceContext.ThrowIfDisposedOrNotOpen();
                    } 
                    throw Fx.AssertAndThrow("Should only fail to add keys in a ---- with abort.");
                }

                if (thisPtr.persistenceContext.directory.ConsistencyScope == DurableConsistencyScope.Global) 
                {
                    // Only do a SetKeysToPersist or Save command if we have keys to associate or disassociate. 
                    // It's possible that we got invoked with a key that was already in the 
                    // AssociatedKeys collection.
                    if ((thisPtr.persistenceContext.keysToAssociate.Count != 0) || 
                        ((thisPtr.persistenceContext.keysToDisassociate.Count != 0) &&
                         (thisPtr.applicationKeys)))
                    {
                        if (thisPtr.persistenceContext.store != null) 
                        {
                            SaveWorkflowCommand saveCommand = new SaveWorkflowCommand(); 
                            foreach (InstanceKey key in thisPtr.persistenceContext.keysToAssociate) 
                            {
                                saveCommand.InstanceKeysToAssociate.Add(key.Value, key.Metadata); 
                            }
                            if (thisPtr.applicationKeys)
                            {
                                foreach (InstanceKey key in thisPtr.persistenceContext.keysToDisassociate) 
                                {
                                    saveCommand.InstanceKeysToFree.Add(key.Value); 
                                } 
                            }
                            IAsyncResult beginExecuteResult = null; 
                            using (thisPtr.PrepareTransactionalCall(thisPtr.transaction))
                            {
                                beginExecuteResult = thisPtr.persistenceContext.store.BeginExecute(
                                    thisPtr.persistenceContext.handle, 
                                    saveCommand,
                                    thisPtr.timeoutHelper.RemainingTime(), 
                                    thisPtr.PrepareAsyncCompletion(AssociateKeysAsyncResult.handleEndExecute), 
                                    thisPtr);
                            } 
                            returnValue = thisPtr.SyncContinue(beginExecuteResult);
                        }
                    }
                    else 
                    {
                        returnValue = thisPtr.AfterUpdate(); 
                    } 
                }
                else 
                {
                    returnValue = thisPtr.AfterUpdate();
                }
 
                return returnValue;
            } 
 
            bool AfterUpdate()
            { 
                if (this.applicationKeys)
                {
                    this.persistenceContext.RationalizeSavedKeys(true);
                } 
                else
                { 
                    this.persistenceContext.keysToAssociate.Clear(); 
                }
 
                return true;
            }

            void OnFinishOperation(AsyncResult result, Exception exception) 
            {
                if (exception is InstancePersistenceException) 
                { 
                    this.persistenceContext.Fault();
                } 
                try
                {
                    this.persistenceContext.OnFinishOperationHelper(exception, false);
                } 
                finally
                { 
                    // We are all done. If we have a savedTransaction, we need to complete it now. 
                    if (this.transaction != null)
                    { 
                        this.transaction.Complete();
                    }
                }
            } 
        }
 
        class UpdateSuspendMetadataAsyncResult : AsyncResult 
        {
            static readonly AsyncCompletion handleEndExecute = new AsyncCompletion(HandleEndExecute); 

            readonly PersistenceContext persistenceContext;
            readonly TimeoutHelper timeoutHelper;
            readonly DependentTransaction transaction; 

            public UpdateSuspendMetadataAsyncResult(PersistenceContext persistenceContext, Exception reason, TimeSpan timeout, AsyncCallback callback, object state) 
                : base(callback, state) 
            {
                this.persistenceContext = persistenceContext; 
                OnCompleting = new Action(OnFinishOperation);

                bool success = false;
                try 
                {
                    this.persistenceContext.StartOperation(); 
 
                    this.timeoutHelper = new TimeoutHelper(timeout);
 
                    Transaction currentTransaction = Transaction.Current;
                    if (currentTransaction != null)
                    {
                        this.transaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete); 
                    }
 
                    if (this.persistenceContext.store != null) 
                    {
                        SaveWorkflowCommand saveCommand = new SaveWorkflowCommand(); 
                        this.persistenceContext.PopulateActivationMetadata(saveCommand);
                        saveCommand.InstanceMetadataChanges[WorkflowServiceNamespace.SuspendReason] = new InstanceValue(reason.Message);
                        saveCommand.InstanceMetadataChanges[WorkflowServiceNamespace.SuspendException] = new InstanceValue(reason, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
                        saveCommand.UnlockInstance = true; 

                        IAsyncResult result = this.persistenceContext.store.BeginExecute( 
                            this.persistenceContext.handle, 
                            saveCommand,
                            this.timeoutHelper.RemainingTime(), 
                            PrepareAsyncCompletion(handleEndExecute),
                            this);
                        if (SyncContinue(result))
                        { 
                            Complete(true);
                        } 
                    } 
                    else
                    { 
                        Complete(true);
                    }
                    success = true;
                } 
                catch (OperationCanceledException exception)
                { 
                    throw FxTrace.Exception.AsError(new CommunicationObjectAbortedException(SR.HandleFreedInDirectory, exception)); 
                }
                catch (TimeoutException) 
                {
                    this.persistenceContext.Fault();
                    throw;
                } 
                finally
                { 
                    if (!success) 
                    {
                        try 
                        {
                            if (this.transaction != null)
                            {
                                this.transaction.Complete(); 
                            }
                        } 
                        finally 
                        {
                            this.persistenceContext.FinishOperation(); 
                        }
                    }
                }
            } 

            public static void End(IAsyncResult result) 
            { 
                AsyncResult.End(result);
            } 

            static bool HandleEndExecute(IAsyncResult result)
            {
                UpdateSuspendMetadataAsyncResult thisPtr = (UpdateSuspendMetadataAsyncResult)result.AsyncState; 
                thisPtr.persistenceContext.store.EndExecute(result);
                return true; 
            } 

            void OnFinishOperation(AsyncResult result, Exception exception) 
            {
                try
                {
                    this.persistenceContext.OnFinishOperationHelper(exception, false); 
                }
                finally 
                { 
                    if (this.transaction != null)
                    { 
                        this.transaction.Complete();
                    }
                }
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK