File: System\ServiceModel\Activities\CorrelationHandle.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
{
    using System;
    using System.Activities;
    using System.Collections.ObjectModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime;
    using System.Runtime.DurableInstancing;
    using System.Runtime.Serialization;
 
    [DataContract]
    public class CorrelationHandle : Handle
    {
        internal static readonly string StaticExecutionPropertyName = typeof(CorrelationHandle).FullName;
 
        static readonly Type requestReplyCorrelationInitializerType = typeof(RequestReplyCorrelationInitializer);
 
        // CorrelationHandles to support Context/Durable Duplex
        // For processing the CallBackContextMessageProperty
        static readonly Type callbackCorrelationInitializerType = typeof(CallbackCorrelationInitializer);  
 
        // This is for passing the Context information that we get in the reply message from the Server in the initial handshake 
        // to the next Sendmessage activity from the client to the server
        static readonly Type contextCorrelationInitializerType = typeof(ContextCorrelationInitializer);
 
        //// To get to the same instance on the server side( between SendReply and following Receive) and on the client side( between Send and following send)
        //static readonly Type followingContextCorrelationInitializerType = typeof(FollowingContextCorrelationInitializer);
        
        CorrelationCallbackContext callbackContext;
        CorrelationContext context;
 
        InstanceKey instanceKey;
 
        // This is never null when it matters because the CorrelationHandle sets this during OnInitialize
        NoPersistHandle noPersistHandle;
 
        // This is never null when it matters because the CorrelationHandle sets this during OnInitialize
        BookmarkScopeHandle bookmarkScopeHandle;
 
        public CorrelationHandle()
            : base()
        {
        }
 
        [DataMember(Name = "noPersistHandle")]
        internal NoPersistHandle SerializedNoPersistHandle
        {
            get { return this.noPersistHandle; }
            set { this.noPersistHandle = value; }
        }
 
        [DataMember(Name = "bookmarkScopeHandle")]
        internal BookmarkScopeHandle SerializedBookmarkScopeHandle
        {
            get { return this.bookmarkScopeHandle; }
            set { this.bookmarkScopeHandle = value; }
        }
 
        [DataMember(EmitDefaultValue = false)]
        internal Guid E2ETraceId
        {
            get;
            set;
        }
 
        // Used for durable correlation purposes
        internal InstanceKey InstanceKey
        {
            get
            {
                return this.instanceKey;
            }
            private set
            {
                this.instanceKey = value;
            }
        }
 
        
        // As a convenience, we let the same correlation handle that is used for durable
        // correlations be leveraged for a single outstanding transient (e.g. Request-Reply)
        // correlation. This is primarily used in the ambient correlation case, and is 
        // done this way since we cannot have two Execution Properties (i.e. activityContext.Properties)
        // with the same type at a given scope without a patch to the WF Runtime
        internal InstanceKey TransientInstanceKey
        {
            get;
            set;
        }
 
        [DataMember(Name = "InstanceKey", EmitDefaultValue = false)]
        [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "Called from Serialization")]
        internal SerializableInstanceKey SerializableInstanceKey
        {
            get
            {
                if (this.InstanceKey != null)
                {
                    return new SerializableInstanceKey(this.InstanceKey);
                }
                return null;
            }
 
            set
            {
                this.InstanceKey = value.ToInstanceKey();
            }
        }
 
        internal CorrelationRequestContext RequestContext
        {
            get;
            private set;
        }
 
        internal CorrelationResponseContext ResponseContext
        {
            get;
            private set;
        }
 
        [DataMember(EmitDefaultValue = false)]
        internal CorrelationCallbackContext CallbackContext
        {
            get
            {
                return this.callbackContext;
            }
 
            set
            {
                Fx.Assert(this.callbackContext == null || this.callbackContext == value, "cannot set two different callback contexts");
                this.callbackContext = value;
            }
        }
 
        [DataMember(EmitDefaultValue = false)]
        internal CorrelationContext Context
        {
            get
            {
                return this.context;
            }
 
            set
            {
                Fx.Assert(this.context == null || this.context == value, "cannot set two different callback contexts");
                this.context = value;
            }
        }
 
 
        [DataMember(EmitDefaultValue = false)]
        internal BookmarkScope Scope
        {
            get;
            set;
        }
 
        protected override void OnInitialize(HandleInitializationContext context)
        {
            this.noPersistHandle = context.CreateAndInitializeHandle<NoPersistHandle>();
            this.bookmarkScopeHandle = context.CreateAndInitializeHandle<BookmarkScopeHandle>();
        }
 
        protected override void OnUninitialize(HandleInitializationContext context)
        {
            SendReceiveExtension sendReceiveExtension = context.GetExtension<SendReceiveExtension>();
            if (sendReceiveExtension != null)
            {
                if (this.InstanceKey != null)
                {
                    sendReceiveExtension.OnUninitializeCorrelation(this.InstanceKey);
                }
                if (this.TransientInstanceKey != null)
                {
                    sendReceiveExtension.OnUninitializeCorrelation(this.TransientInstanceKey);
                }
            }
 
            context.UninitializeHandle(this.noPersistHandle);
            context.UninitializeHandle(this.bookmarkScopeHandle);
        }
 
        internal BookmarkScope EnsureBookmarkScope(NativeActivityContext executionContext)
        {
            if (this.Scope == null)
            {
                this.Scope = executionContext.DefaultBookmarkScope;
            }
            return this.Scope;
        }
 
        internal bool TryRegisterRequestContext(NativeActivityContext executionContext, CorrelationRequestContext requestContext)
        {
            Fx.Assert(requestContext != null, "requires a valid requestContext");
            if (this.noPersistHandle == null)
            {
                return false;
            }
            if (this.RequestContext == null)
            {
                this.noPersistHandle.Enter(executionContext);
                this.RequestContext = requestContext;
                return true;
            }
 
            return object.ReferenceEquals(this.RequestContext, requestContext);
        }
 
        internal bool TryRegisterResponseContext(NativeActivityContext executionContext, CorrelationResponseContext responseContext)
        {
            Fx.Assert(responseContext != null, "requires a valid responseContext");
            if (this.noPersistHandle == null)
            {
                return false;
            }
            if (this.ResponseContext == null)
            {
                this.noPersistHandle.Enter(executionContext);
                this.ResponseContext = responseContext;
                return true;
            }
 
            return object.ReferenceEquals(this.ResponseContext, responseContext);
        }
 
        internal bool TryAcquireRequestContext(NativeActivityContext executionContext, out CorrelationRequestContext requestContext)
        {
            if (this.RequestContext != null)
            {
                // We have a context, and we should disassociate it from the correlation handle
                this.noPersistHandle.Exit(executionContext);
                requestContext = this.RequestContext;
                this.RequestContext = null;
                return true;
            }
            else
            {
                requestContext = null;
                return false;
            }
        }
 
        internal bool TryAcquireResponseContext(NativeActivityContext executionContext, out CorrelationResponseContext responseContext)
        {
            if (this.ResponseContext != null)
            {
                // We have a context, and we should disassociate it from the correlation handle
                this.noPersistHandle.Exit(executionContext);
                responseContext = this.ResponseContext;
                this.ResponseContext = null;
                return true;
            }
            else
            {
                responseContext = null;
                return false;
            }
        }
 
        internal void InitializeBookmarkScope(NativeActivityContext context, InstanceKey instanceKey)
        {
            Fx.Assert(context != null, "executionContext cannot be null");
            Fx.Assert(instanceKey != null, "instanceKey cannot be null");
 
            if (context.GetExtension<SendReceiveExtension>() != null)
            {
                if (this.InstanceKey != null && this.InstanceKey.Value != instanceKey.Value)
                {
                    throw FxTrace.Exception.AsError(
                        new InvalidOperationException(SR.CorrelationHandleInUse(this.InstanceKey.Value, instanceKey.Value)));
                }
                this.InstanceKey = instanceKey;
            }
            else
            {
                if (this.Scope == null)
                {
                    this.bookmarkScopeHandle.CreateBookmarkScope(context, instanceKey.Value);
                    this.Scope = this.bookmarkScopeHandle.BookmarkScope;
                }
                else
                {
                    if (this.Scope.IsInitialized)
                    {
                        if (this.Scope.Id != instanceKey.Value)
                        {
                            throw FxTrace.Exception.AsError(
                                new InvalidOperationException(SR.CorrelationHandleInUse(this.Scope.Id, instanceKey.Value)));
                        }
                    }
                    else
                    {
                        this.Scope.Initialize(context, instanceKey.Value);
                    }
                }
            }
        }
 
        internal bool IsInitalized()
        {
            if (this.Scope != null || this.CallbackContext != null || this.Context != null || this.ResponseContext != null || this.RequestContext != null || (this.InstanceKey != null && this.InstanceKey.IsValid))
            {
                return true;
            }
 
            return false;
        }
 
        internal static CorrelationHandle GetAmbientCorrelation(NativeActivityContext context)
        {
            return context.Properties.Find(CorrelationHandle.StaticExecutionPropertyName) as CorrelationHandle;
        }
 
        internal static CorrelationHandle GetExplicitRequestReplyCorrelation(NativeActivityContext context, Collection<CorrelationInitializer> correlationInitializers)
        {
            return GetTypedCorrelationHandle(context, correlationInitializers, requestReplyCorrelationInitializerType);
        }
 
        internal static CorrelationHandle GetExplicitCallbackCorrelation(NativeActivityContext context, Collection<CorrelationInitializer> correlationInitializers)
        {
            return GetTypedCorrelationHandle(context, correlationInitializers, callbackCorrelationInitializerType);
        }
 
        internal static CorrelationHandle GetExplicitContextCorrelation(NativeActivityContext context, Collection<CorrelationInitializer> correlationInitializers)
        {
            return GetTypedCorrelationHandle(context, correlationInitializers, contextCorrelationInitializerType);
        }
 
        internal static CorrelationHandle GetTypedCorrelationHandle(NativeActivityContext context, Collection<CorrelationInitializer> correlationInitializers, Type correlationInitializerType)
        {
            CorrelationHandle typedCorrelationHandle = null;
 
            if (correlationInitializers != null && correlationInitializers.Count > 0)
            {
                foreach (CorrelationInitializer correlation in correlationInitializers)
                {
                    if (correlationInitializerType == correlation.GetType())
                    {
                        typedCorrelationHandle = correlation.CorrelationHandle.Get(context);
                        
                        // We return the first handle we find
                        break;
                    }
                }
            }
 
            return typedCorrelationHandle;
        }
    }
}