File: System\ServiceModel\Activities\WorkflowServiceHost.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.Activities;
    using System.Activities.Hosting;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Configuration;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.IO;
    using System.Runtime;
    using System.Runtime.DurableInstancing;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Activities.Configuration;
    using System.ServiceModel.Activities.Description;
    using System.ServiceModel.Activities.Dispatcher;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Activities.Diagnostics;
    using System.Xml;
    using System.Xml.Linq;
 
    [Fx.Tag.XamlVisible(false)]
    public class WorkflowServiceHost : ServiceHostBase
    {
        static readonly XName mexContractXName = XName.Get(ServiceMetadataBehavior.MexContractName, ServiceMetadataBehavior.MexContractNamespace);
        static readonly Type mexBehaviorType = typeof(ServiceMetadataBehavior);
        static readonly TimeSpan defaultPersistTimeout = TimeSpan.FromSeconds(30);
        static readonly TimeSpan defaultTrackTimeout = TimeSpan.FromSeconds(30);
        static readonly Type baseActivityType = typeof(Activity);
        static readonly Type correlationQueryBehaviorType = typeof(CorrelationQueryBehavior);
        static readonly Type bufferedReceiveServiceBehaviorType = typeof(BufferedReceiveServiceBehavior);
 
        WorkflowServiceHostExtensions workflowExtensions;
        DurableInstanceManager durableInstanceManager;
 
        WorkflowDefinitionProvider workflowDefinitionProvider;
    
        Activity activity;
        WorkflowService serviceDefinition;
        IDictionary<XName, ContractDescription> inferredContracts;
        IDictionary<XName, Collection<CorrelationQuery>> correlationQueries;
 
        WorkflowUnhandledExceptionAction unhandledExceptionAction;
        TimeSpan idleTimeToPersist;
        TimeSpan idleTimeToUnload;
 
        WorkflowServiceHostPerformanceCounters workflowServiceHostPerformanceCounters; 
 
        [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors, 
            Justification = "Based on prior are from WCF3: By design, don't want to complicate ServiceHost state model")]
        public WorkflowServiceHost(object serviceImplementation, params Uri[] baseAddresses)
            : base()
        {
            if (serviceImplementation == null)
            {
                throw FxTrace.Exception.ArgumentNull("serviceImplementation");
            }
 
            if (serviceImplementation is WorkflowService)
            {
                InitializeFromConstructor((WorkflowService)serviceImplementation, baseAddresses);
            }
            else
            {
                Activity activity = serviceImplementation as Activity;
                if (activity == null)
                {
                    throw FxTrace.Exception.Argument("serviceImplementation", SR.InvalidServiceImplementation);
                }
                InitializeFromConstructor(activity, baseAddresses);
            }
        }
 
 
        [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors, 
            Justification = "Based on prior are from WCF3: By design, don't want to complicate ServiceHost state model")]
        public WorkflowServiceHost(Activity activity, params Uri[] baseAddresses)
            : base()
        {
            if (activity == null)
            {
                throw FxTrace.Exception.ArgumentNull("activity");
            }
 
            InitializeFromConstructor(activity, baseAddresses);
        }
 
        [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors,
            Justification = "Based on prior art from WCF 3.0: By design, don't want to complicate ServiceHost state model")]
        public WorkflowServiceHost(WorkflowService serviceDefinition, params Uri[] baseAddresses)
            : base()
        {
            if (serviceDefinition == null)
            {
                throw FxTrace.Exception.ArgumentNull("serviceDefinition");
            }
 
            InitializeFromConstructor(serviceDefinition, baseAddresses);
      
        }
 
        [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors,
            Justification = "Based on prior art from WCF 3.0: By design, don't want to complicate ServiceHost state model")]
        protected WorkflowServiceHost()
        {
            InitializeFromConstructor((WorkflowService)null);
        }
 
        public Activity Activity
        {
            get
            {
                return this.activity;
            }
        }
 
        public WorkflowInstanceExtensionManager WorkflowExtensions
        {
            get
            {
                return this.workflowExtensions;
            }
        }
 
        public DurableInstancingOptions DurableInstancingOptions
        {
            get
            {
                return this.durableInstanceManager.DurableInstancingOptions;
            }
        }
 
        public ICollection<WorkflowService> SupportedVersions
        {
            get
            {
                return this.workflowDefinitionProvider.SupportedVersions;
            }
        }
 
        internal XName ServiceName
        {
            get;
            set;
        }
 
        internal TimeSpan PersistTimeout
        {
            get;
            set;
        }
 
        internal TimeSpan TrackTimeout
        {
            get;
            set;
        }
 
        // 
        internal TimeSpan FilterResumeTimeout
        {
            get;
            set;
        }
 
        internal DurableInstanceManager DurableInstanceManager
        {
            get
            {
                return this.durableInstanceManager;
            }
        }
 
        internal bool IsLoadTransactionRequired
        {
            get;
            private set;
        }
 
        // set by WorkflowUnhandledExceptionBehavior.ApplyDispatchBehavior, used by WorkflowServiceInstance.UnhandledExceptionPolicy
        internal WorkflowUnhandledExceptionAction UnhandledExceptionAction
        {
            get { return this.unhandledExceptionAction; }
            set 
            {
                Fx.Assert(WorkflowUnhandledExceptionActionHelper.IsDefined(value), "Undefined WorkflowUnhandledExceptionAction");
                this.unhandledExceptionAction = value; 
            }
        }
 
        // set by WorkflowIdleBehavior.ApplyDispatchBehavior, used by WorkflowServiceInstance.UnloadInstancePolicy
        internal TimeSpan IdleTimeToPersist
        {
            get { return this.idleTimeToPersist; }
            set 
            {
                Fx.Assert(value >= TimeSpan.Zero, "IdleTimeToPersist cannot be less than zero");
                this.idleTimeToPersist = value; 
            }
        }
        internal TimeSpan IdleTimeToUnload
        {
            get { return this.idleTimeToUnload; }
            set 
            {
                Fx.Assert(value >= TimeSpan.Zero, "IdleTimeToUnload cannot be less than zero");
                this.idleTimeToUnload = value; 
            }
        }
 
        internal bool IsConfigurable
        {
            get
            {
                lock (this.ThisLock)
                {
                    return this.State == CommunicationState.Created || this.State == CommunicationState.Opening;
                }
            }
        }
 
        internal WorkflowServiceHostPerformanceCounters WorkflowServiceHostPerformanceCounters
        {
            get
            {
                return this.workflowServiceHostPerformanceCounters;
            }
        }
        
        internal bool OverrideSiteName
        {
            get;
            set;
        }
 
        void InitializeFromConstructor(Activity activity, params Uri[] baseAddresses)
        {
            WorkflowService serviceDefinition = new WorkflowService 
            {
                Body = activity
            };
 
            InitializeFromConstructor(serviceDefinition, baseAddresses);
        }
 
        void InitializeFromConstructor(WorkflowService serviceDefinition, params Uri[] baseAddresses)
        {
            // first initialize some values to their defaults
            this.idleTimeToPersist = WorkflowIdleBehavior.defaultTimeToPersist;
            this.idleTimeToUnload = WorkflowIdleBehavior.defaultTimeToUnload;
            this.unhandledExceptionAction = WorkflowUnhandledExceptionBehavior.defaultAction;
            this.workflowExtensions = new WorkflowServiceHostExtensions();
 
            // If the AppSettings.DefaultAutomaticInstanceKeyDisassociation is specified and is true, create a DisassociateInstanceKeysExtension, set its
            // AutomaticDisassociationEnabled property to true, and add it to the extensions collection so that System.Activities.BookmarkScopeHandle will
            // unregister its BookmarkScope, which will cause key disassociation. KB2669774.
            if (AppSettings.DefaultAutomaticInstanceKeyDisassociation)
            {
                DisassociateInstanceKeysExtension extension = new DisassociateInstanceKeysExtension();
                extension.AutomaticDisassociationEnabled = true;
                this.workflowExtensions.Add(extension);
            }
 
            if (TD.CreateWorkflowServiceHostStartIsEnabled())
            {
                TD.CreateWorkflowServiceHostStart();
            }
            if (serviceDefinition != null)
            {
                this.workflowDefinitionProvider = new WorkflowDefinitionProvider(serviceDefinition, this);
                InitializeDescription(serviceDefinition, new UriSchemeKeyedCollection(baseAddresses));
            }
            this.durableInstanceManager = new DurableInstanceManager(this);            
 
            if (TD.CreateWorkflowServiceHostStopIsEnabled())
            {
                TD.CreateWorkflowServiceHostStop();
            }
 
            this.workflowServiceHostPerformanceCounters = new WorkflowServiceHostPerformanceCounters(this);
        }
 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")]
        public ServiceEndpoint AddServiceEndpoint(XName serviceContractName, Binding binding, string address,
            Uri listenUri = null, string behaviorConfigurationName = null)
        {
            return AddServiceEndpoint(serviceContractName, binding, new Uri(address, UriKind.RelativeOrAbsolute), listenUri, behaviorConfigurationName);
        }
 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")]
        public ServiceEndpoint AddServiceEndpoint(XName serviceContractName, Binding binding, Uri address,
            Uri listenUri = null, string behaviorConfigurationName = null)
        {
            if (binding == null)
            {
                throw FxTrace.Exception.ArgumentNull("binding");
            }
            if (address == null)
            {
                throw FxTrace.Exception.ArgumentNull("address");
            }
 
            Uri via = this.MakeAbsoluteUri(address, binding);
            return AddServiceEndpointCore(serviceContractName, binding, new EndpointAddress(via), listenUri, behaviorConfigurationName);
        }
 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")]
        ServiceEndpoint AddServiceEndpointCore(XName serviceContractName, Binding binding, EndpointAddress address,
            Uri listenUri = null, string behaviorConfigurationName = null)
        {
            if (serviceContractName == null)
            {
                throw FxTrace.Exception.ArgumentNull("serviceContractName");
            }
            if (this.inferredContracts == null)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(
                    SR.ContractNotFoundInAddServiceEndpoint(serviceContractName.LocalName, serviceContractName.NamespaceName)));
            }
 
            ServiceEndpoint serviceEndpoint;
            ContractDescription description;
            
            ContractInferenceHelper.ProvideDefaultNamespace(ref serviceContractName);
 
            if (this.inferredContracts.TryGetValue(serviceContractName, out description))
            {
                serviceEndpoint = new ServiceEndpoint(description, binding, address);
 
                if (!string.IsNullOrEmpty(behaviorConfigurationName))
                {
                    ConfigLoader.LoadChannelBehaviors(behaviorConfigurationName, null, serviceEndpoint.Behaviors);
                }
            }
            else if (serviceContractName == mexContractXName)  // Special case for mex endpoint
            {
                if (!this.Description.Behaviors.Contains(mexBehaviorType))
                {
                    throw FxTrace.Exception.AsError(new InvalidOperationException(
                        SR.ServiceMetadataBehaviorNotFoundForServiceMetadataEndpoint(this.Description.Name)));
                }
 
                serviceEndpoint = new ServiceMetadataEndpoint(binding, address);
            }
            else
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(
                    SR.ContractNotFoundInAddServiceEndpoint(serviceContractName.LocalName, serviceContractName.NamespaceName)));
            }
 
            if (listenUri != null)
            {
                listenUri = base.MakeAbsoluteUri(listenUri, binding);
                serviceEndpoint.ListenUri = listenUri;
            }
 
            base.Description.Endpoints.Add(serviceEndpoint);
 
            if (TD.ServiceEndpointAddedIsEnabled())
            {
                TD.ServiceEndpointAdded(address.Uri.ToString(), binding.GetType().ToString(), serviceEndpoint.Contract.Name);
            }
 
            return serviceEndpoint;
        }
 
        // Duplicate public AddServiceEndpoint methods from the base class
        // This is to ensure that base class methods with string are not hidden by derived class methods with XName
        public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address)
        {
            return base.AddServiceEndpoint(implementedContract, binding, address);
        }
 
        public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address)
        {
            return base.AddServiceEndpoint(implementedContract, binding, address);
        }
 
        public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri)
        {
            return base.AddServiceEndpoint(implementedContract, binding, address, listenUri);
        }
 
        public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri)
        {
            return base.AddServiceEndpoint(implementedContract, binding, address, listenUri);
        }
 
        public override void AddServiceEndpoint(ServiceEndpoint endpoint)
        {
            if (!endpoint.IsSystemEndpoint)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseAddServiceEndpointOverloadForWorkflowServices));
            }
            
            base.AddServiceEndpoint(endpoint);
        }        
 
        internal override void AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> defaultEndpoints)
        {
            if (this.inferredContracts != null)
            {
                foreach (XName contractName in this.inferredContracts.Keys)
                {
                    ServiceEndpoint endpoint = AddServiceEndpoint(contractName, defaultBinding, String.Empty);
                    ConfigLoader.LoadDefaultEndpointBehaviors(endpoint);
                    AddCorrelationQueryBehaviorToServiceEndpoint(endpoint);
                    defaultEndpoints.Add(endpoint);
                }
            }
        }
 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, MessageId = "0#", Justification = "This is defined by the ServiceHost base class")]
        protected override ServiceDescription CreateDescription(out IDictionary<string, ContractDescription> implementedContracts)
        {
            Fx.AssertAndThrow(this.serviceDefinition != null, "serviceDefinition is null");
 
            this.activity = this.serviceDefinition.Body;
 
            Dictionary<string, ContractDescription> result = new Dictionary<string, ContractDescription>();
 
            // Note: We do not check whether this.inferredContracts == null || this.inferredContracts.Count == 0,
            // because we need to support hosting workflow with zero contract.
            this.inferredContracts = this.serviceDefinition.GetContractDescriptions();
 
            if (this.inferredContracts != null)
            {
                foreach (ContractDescription contract in this.inferredContracts.Values)
                {
                    if (!string.IsNullOrEmpty(contract.ConfigurationName))
                    {
                        if (result.ContainsKey(contract.ConfigurationName))
                        {
                            throw FxTrace.Exception.AsError(new InvalidOperationException(SR.DifferentContractsSameConfigName));
                        }
                        result.Add(contract.ConfigurationName, contract);
                    }
                }
            }
 
            implementedContracts = result;
 
            // Currently, only WorkflowService has CorrelationQueries property
            this.correlationQueries = this.serviceDefinition.CorrelationQueries;
            ServiceDescription serviceDescription = this.serviceDefinition.GetEmptyServiceDescription();
            serviceDescription.Behaviors.Add(new WorkflowServiceBehavior(this.workflowDefinitionProvider));
            return serviceDescription;
        }
 
        void InitializeDescription(WorkflowService serviceDefinition, UriSchemeKeyedCollection baseAddresses)
        {
            Fx.Assert(serviceDefinition != null, "caller must verify");
 
            this.serviceDefinition = serviceDefinition;
            base.InitializeDescription(baseAddresses);
 
            foreach (Endpoint endpoint in serviceDefinition.Endpoints)
            {
                if (endpoint.Binding == null)
                {
                    string endpointName = ContractValidationHelper.GetErrorMessageEndpointName(endpoint.Name);
                    string contractName = ContractValidationHelper.GetErrorMessageEndpointServiceContractName(endpoint.ServiceContractName);
                    throw FxTrace.Exception.AsError(new InvalidOperationException(SR.MissingBindingInEndpoint(endpointName, contractName)));
                }
 
                ServiceEndpoint serviceEndpoint = AddServiceEndpointCore(endpoint.ServiceContractName, endpoint.Binding,
                        endpoint.GetAddress(this), endpoint.ListenUri, endpoint.BehaviorConfigurationName);
 
                if (!string.IsNullOrEmpty(endpoint.Name))
                {
                    serviceEndpoint.Name = endpoint.Name;
                }
                serviceEndpoint.UnresolvedAddress = endpoint.AddressUri;
                serviceEndpoint.UnresolvedListenUri = endpoint.ListenUri;
            }
 
            this.PersistTimeout = defaultPersistTimeout;
            this.TrackTimeout = defaultTrackTimeout;
            this.FilterResumeTimeout = TimeSpan.FromSeconds(AppSettings.FilterResumeTimeoutInSeconds);
        }
 
        protected override void InitializeRuntime()
        {
            if (base.Description != null)
            {
                FixupEndpoints();
                this.SetScopeName();
                if (this.DurableInstancingOptions.ScopeName == null)
                {
                    this.DurableInstancingOptions.ScopeName = XNamespace.Get(this.Description.Namespace).GetName(this.Description.Name);
                }
            }
 
            base.InitializeRuntime();
 
            this.WorkflowServiceHostPerformanceCounters.InitializePerformanceCounters();
            
            this.ServiceName = XNamespace.Get(this.Description.Namespace).GetName(this.Description.Name);
 
            // add a host-wide SendChannelCache (with default settings) if one doesn't exist
            this.workflowExtensions.EnsureChannelCache();
 
            // add a host-wide (free-threaded) CorrelationExtension based on our ServiceName
            this.WorkflowExtensions.Add(new CorrelationExtension(this.DurableInstancingOptions.ScopeName));
 
            this.WorkflowExtensions.MakeReadOnly();
 
            // now calculate if IsLoadTransactionRequired
            this.IsLoadTransactionRequired = WorkflowServiceInstance.IsLoadTransactionRequired(this);
 
            if (this.serviceDefinition != null)
            {
                ValidateBufferedReceiveProperty();
                this.serviceDefinition.ResetServiceDescription();
            }
        }
 
        internal override void AfterInitializeRuntime(TimeSpan timeout)
        {
            this.durableInstanceManager.Open(timeout);
        }
 
        internal override IAsyncResult BeginAfterInitializeRuntime(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.durableInstanceManager.BeginOpen(timeout, callback, state);
        }
 
        internal override void EndAfterInitializeRuntime(IAsyncResult result)
        {
            this.durableInstanceManager.EndOpen(result);
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
            base.OnClose(timeoutHelper.RemainingTime());
 
            this.durableInstanceManager.Close(timeoutHelper.RemainingTime());
 
            this.workflowServiceHostPerformanceCounters.Dispose();
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new CloseAsyncResult(this, timeout, callback, state);
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            CloseAsyncResult.End(result);
        }
 
        protected override void OnAbort()
        {
            base.OnAbort();
 
            this.durableInstanceManager.Abort();
 
            this.workflowServiceHostPerformanceCounters.Dispose();
        }
 
        internal void FaultServiceHostIfNecessary(Exception exception)
        {
            if (exception is InstancePersistenceException && !(exception is InstancePersistenceCommandException))
            {
                this.Fault(exception);
            }
        }
 
        IAsyncResult BeginHostClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return base.OnBeginClose(timeout, callback, state);
        }
        void EndHostClose(IAsyncResult result)
        {
            base.OnEndClose(result);
        }
 
        void AddCorrelationQueryBehaviorToServiceEndpoint(ServiceEndpoint serviceEndpoint)
        {
            Fx.Assert(serviceEndpoint != null, "Argument cannot be null!");
            Fx.Assert(serviceEndpoint.Contract != null, "ServiceEndpoint must have a contract!");
            Fx.Assert(this.serviceDefinition != null, "Missing WorkflowService!");
            Fx.Assert(!serviceEndpoint.Behaviors.Contains(correlationQueryBehaviorType),
                "ServiceEndpoint should not have CorrelationQueryBehavior before this point!");
 
            XName endpointContractName = XName.Get(serviceEndpoint.Contract.Name, serviceEndpoint.Contract.Namespace);
 
            Collection<CorrelationQuery> queries;
            if (this.correlationQueries != null && this.correlationQueries.TryGetValue(endpointContractName, out queries))
            {
                // Filter out duplicate CorrelationQueries in the collection.
                // Currently, we only do reference comparison and Where message filter comparison.
                Collection<CorrelationQuery> uniqueQueries = new Collection<CorrelationQuery>();
                foreach (CorrelationQuery correlationQuery in queries)
                {
                    if (!uniqueQueries.Contains(correlationQuery))
                    {
                        uniqueQueries.Add(correlationQuery);
                    }
                    else
                    {
                        if (TD.DuplicateCorrelationQueryIsEnabled())
                        {
                            TD.DuplicateCorrelationQuery(correlationQuery.Where.ToString());
                        }
                    }
                }
                serviceEndpoint.Behaviors.Add(new CorrelationQueryBehavior(uniqueQueries) { ServiceContractName = endpointContractName });
            }
            else if (CorrelationQueryBehavior.BindingHasDefaultQueries(serviceEndpoint.Binding))
            {
                if (!serviceEndpoint.Behaviors.Contains(typeof(CorrelationQueryBehavior)))
                {
                    serviceEndpoint.Behaviors.Add(new CorrelationQueryBehavior(new Collection<CorrelationQuery>()) { ServiceContractName = endpointContractName });
                }
            }
        }
 
        void FixupEndpoints()
        {
            Fx.Assert(this.Description != null, "ServiceDescription cannot be null");
 
            Dictionary<Type, ContractDescription> contractDescriptionDictionary = new Dictionary<Type, ContractDescription>();
            foreach (ServiceEndpoint serviceEndpoint in this.Description.Endpoints)
            {
                if (this.serviceDefinition.AllowBufferedReceive)
                {
                    // All application-level endpoints need to support ReceiveContext
                    SetupReceiveContextEnabledAttribute(serviceEndpoint);
                }
 
                // Need to add CorrelationQueryBehavior here so that endpoints added from config are included.
                // It is possible that some endpoints already have CorrelationQueryBehavior from
                // the AddDefaultEndpoints code path. We should skip them.
                if (!serviceEndpoint.Behaviors.Contains(correlationQueryBehaviorType))
                {
                    AddCorrelationQueryBehaviorToServiceEndpoint(serviceEndpoint);
                }
 
                // Need to ensure that any WorkflowHostingEndpoints using the same contract type actually use the
                // same contractDescription instance since this is required by WCF.
                if (serviceEndpoint is WorkflowHostingEndpoint)
                {
                    ContractDescription contract;
                    if (contractDescriptionDictionary.TryGetValue(serviceEndpoint.Contract.ContractType, out contract))
                    {
                        serviceEndpoint.Contract = contract;
                    }
                    else
                    {
                        contractDescriptionDictionary[serviceEndpoint.Contract.ContractType] = serviceEndpoint.Contract;
                    }
                }
            }
 
            if (this.serviceDefinition.AllowBufferedReceive && !this.Description.Behaviors.Contains(bufferedReceiveServiceBehaviorType))
            {
                this.Description.Behaviors.Add(new BufferedReceiveServiceBehavior());
            }
        }
 
        void SetScopeName()
        {
            VirtualPathExtension virtualPathExtension = this.Extensions.Find<VirtualPathExtension>();
            if (virtualPathExtension != null)
            {
                // Web Hosted scenario            
                WorkflowHostingOptionsSection hostingOptions = (WorkflowHostingOptionsSection)ConfigurationManager.GetSection(ConfigurationStrings.WorkflowHostingOptionsSectionPath);
                if (hostingOptions != null && hostingOptions.OverrideSiteName)
                {
                    this.OverrideSiteName = hostingOptions.OverrideSiteName;
 
                    string fullVirtualPath = virtualPathExtension.VirtualPath.Substring(1);
                    fullVirtualPath = ("/" == virtualPathExtension.ApplicationVirtualPath) ? fullVirtualPath : virtualPathExtension.ApplicationVirtualPath + fullVirtualPath;
 
                    int index = fullVirtualPath.LastIndexOf("/", StringComparison.OrdinalIgnoreCase); 
                    string virtualDirectoryPath = fullVirtualPath.Substring(0, index + 1);
 
                    this.DurableInstancingOptions.ScopeName = XName.Get(XmlConvert.EncodeLocalName(Path.GetFileName(virtualPathExtension.VirtualPath)),
                        string.Format(CultureInfo.InvariantCulture, "/{0}{1}", this.Description.Name, virtualDirectoryPath));
                }
            }
        }
        
        void SetupReceiveContextEnabledAttribute(ServiceEndpoint serviceEndpoint)
        {
            if (BufferedReceiveServiceBehavior.IsWorkflowEndpoint(serviceEndpoint))
            {
                foreach (OperationDescription operation in serviceEndpoint.Contract.Operations)
                {
                    ReceiveContextEnabledAttribute behavior = operation.Behaviors.Find<ReceiveContextEnabledAttribute>();
                    if (behavior == null)
                    {
                        operation.Behaviors.Add(new ReceiveContextEnabledAttribute() { ManualControl = true });
                    }
                    else
                    {
                        behavior.ManualControl = true;
                    }
                }
            }
        }
 
        void ValidateBufferedReceiveProperty()
        {
            // Validate that the AttachedProperty is indeed being used when the behavior is also used
            bool hasBehavior = this.Description.Behaviors.Contains(bufferedReceiveServiceBehaviorType);
            if (hasBehavior && !this.serviceDefinition.AllowBufferedReceive)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.BufferedReceiveBehaviorUsedWithoutProperty));
            }
        }
 
        // specialized WorkflowInstanceExtensionManager that can default in a SendMessageChannelCache
        class WorkflowServiceHostExtensions : WorkflowInstanceExtensionManager
        {
            static Type SendReceiveExtensionType = typeof(SendReceiveExtension);
 
            bool hasChannelCache;
 
            public WorkflowServiceHostExtensions()
                : base()
            {
            }
 
            public override void Add<T>(Func<T> extensionCreationFunction)
            {
                ThrowIfNotSupported(typeof(T));
 
                if (TypeHelper.AreTypesCompatible(typeof(T), typeof(SendMessageChannelCache)))
                {
                    this.hasChannelCache = true;
                }
                base.Add<T>(extensionCreationFunction);
            }
 
            public override void Add(object singletonExtension)
            {
                ThrowIfNotSupported(singletonExtension.GetType());
 
                if (singletonExtension is SendMessageChannelCache)
                {
                    this.hasChannelCache = true;
                }
                base.Add(singletonExtension);
            }
 
            public void EnsureChannelCache()
            {
                if (!this.hasChannelCache)
                {
                    Add(new SendMessageChannelCache());
                    this.hasChannelCache = true;
                }
            }
 
            void ThrowIfNotSupported(Type type)
            {
                if (TypeHelper.AreTypesCompatible(type, SendReceiveExtensionType))
                {
                    throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ExtensionTypeNotSupported(SendReceiveExtensionType.FullName)));
                }
            }
        }
 
        class CloseAsyncResult : AsyncResult
        {
            static AsyncCompletion handleDurableInstanceManagerEndClose = new AsyncCompletion(HandleDurableInstanceManagerEndClose);
            static AsyncCompletion handleEndHostClose = new AsyncCompletion(HandleEndHostClose);
 
            TimeoutHelper timeoutHelper;
            WorkflowServiceHost host;
 
            public CloseAsyncResult(WorkflowServiceHost host, TimeSpan timeout, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.timeoutHelper = new TimeoutHelper(timeout);
                this.host = host;
 
                if (CloseHost())
                {
                    Complete(true);
                }
            }
 
            bool CloseDurableInstanceManager()
            {
                IAsyncResult result = this.host.durableInstanceManager.BeginClose(
                    this.timeoutHelper.RemainingTime(), base.PrepareAsyncCompletion(handleDurableInstanceManagerEndClose), this);
                return SyncContinue(result);
            }
 
            bool CloseHost()
            {
                IAsyncResult result = this.host.BeginHostClose(
                    this.timeoutHelper.RemainingTime(), base.PrepareAsyncCompletion(handleEndHostClose), this);
                return SyncContinue(result);
            }
 
            static bool HandleDurableInstanceManagerEndClose(IAsyncResult result)
            {
                CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState;
 
                thisPtr.host.durableInstanceManager.EndClose(result);
                
                thisPtr.host.WorkflowServiceHostPerformanceCounters.Dispose();
                return true;
            }
 
            static bool HandleEndHostClose(IAsyncResult result)
            {
                CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState;
 
                thisPtr.host.EndHostClose(result);
                return thisPtr.CloseDurableInstanceManager();
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<CloseAsyncResult>(result);
            }
        }
    }
}