//------------------------------------------------------------ // 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 inferredContracts; IDictionary> 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 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 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 implementedContracts) { Fx.AssertAndThrow(this.serviceDefinition != null, "serviceDefinition is null"); this.activity = this.serviceDefinition.Body; Dictionary result = new Dictionary(); // 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 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 uniqueQueries = new Collection(); 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()) { ServiceContractName = endpointContractName }); } } } void FixupEndpoints() { Fx.Assert(this.Description != null, "ServiceDescription cannot be null"); Dictionary contractDescriptionDictionary = new Dictionary(); 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(); 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(); 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(Func extensionCreationFunction) { ThrowIfNotSupported(typeof(T)); if (TypeHelper.AreTypesCompatible(typeof(T), typeof(SendMessageChannelCache))) { this.hasChannelCache = true; } base.Add(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(result); } } } }