1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 5 namespace System.ServiceModel.Activities 6 { 7 using System.Activities; 8 using System.Activities.Hosting; 9 using System.Collections.Generic; 10 using System.Collections.ObjectModel; 11 using System.Configuration; 12 using System.Diagnostics.CodeAnalysis; 13 using System.Globalization; 14 using System.IO; 15 using System.Runtime; 16 using System.Runtime.DurableInstancing; 17 using System.ServiceModel.Activation; 18 using System.ServiceModel.Activities.Configuration; 19 using System.ServiceModel.Activities.Description; 20 using System.ServiceModel.Activities.Dispatcher; 21 using System.ServiceModel.Channels; 22 using System.ServiceModel.Description; 23 using System.ServiceModel.Activities.Diagnostics; 24 using System.Xml; 25 using System.Xml.Linq; 26 27 [Fx.Tag.XamlVisible(false)] 28 public class WorkflowServiceHost : ServiceHostBase 29 { 30 static readonly XName mexContractXName = XName.Get(ServiceMetadataBehavior.MexContractName, ServiceMetadataBehavior.MexContractNamespace); 31 static readonly Type mexBehaviorType = typeof(ServiceMetadataBehavior); 32 static readonly TimeSpan defaultPersistTimeout = TimeSpan.FromSeconds(30); 33 static readonly TimeSpan defaultTrackTimeout = TimeSpan.FromSeconds(30); 34 static readonly Type baseActivityType = typeof(Activity); 35 static readonly Type correlationQueryBehaviorType = typeof(CorrelationQueryBehavior); 36 static readonly Type bufferedReceiveServiceBehaviorType = typeof(BufferedReceiveServiceBehavior); 37 38 WorkflowServiceHostExtensions workflowExtensions; 39 DurableInstanceManager durableInstanceManager; 40 41 WorkflowDefinitionProvider workflowDefinitionProvider; 42 43 Activity activity; 44 WorkflowService serviceDefinition; 45 IDictionary<XName, ContractDescription> inferredContracts; 46 IDictionary<XName, Collection<CorrelationQuery>> correlationQueries; 47 48 WorkflowUnhandledExceptionAction unhandledExceptionAction; 49 TimeSpan idleTimeToPersist; 50 TimeSpan idleTimeToUnload; 51 52 WorkflowServiceHostPerformanceCounters workflowServiceHostPerformanceCounters; 53 54 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors, 55 Justification = "Based on prior are from WCF3: By design, don't want to complicate ServiceHost state model")] WorkflowServiceHost(object serviceImplementation, params Uri[] baseAddresses)56 public WorkflowServiceHost(object serviceImplementation, params Uri[] baseAddresses) 57 : base() 58 { 59 if (serviceImplementation == null) 60 { 61 throw FxTrace.Exception.ArgumentNull("serviceImplementation"); 62 } 63 64 if (serviceImplementation is WorkflowService) 65 { 66 InitializeFromConstructor((WorkflowService)serviceImplementation, baseAddresses); 67 } 68 else 69 { 70 Activity activity = serviceImplementation as Activity; 71 if (activity == null) 72 { 73 throw FxTrace.Exception.Argument("serviceImplementation", SR.InvalidServiceImplementation); 74 } 75 InitializeFromConstructor(activity, baseAddresses); 76 } 77 } 78 79 80 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors, 81 Justification = "Based on prior are from WCF3: By design, don't want to complicate ServiceHost state model")] WorkflowServiceHost(Activity activity, params Uri[] baseAddresses)82 public WorkflowServiceHost(Activity activity, params Uri[] baseAddresses) 83 : base() 84 { 85 if (activity == null) 86 { 87 throw FxTrace.Exception.ArgumentNull("activity"); 88 } 89 90 InitializeFromConstructor(activity, baseAddresses); 91 } 92 93 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors, 94 Justification = "Based on prior art from WCF 3.0: By design, don't want to complicate ServiceHost state model")] WorkflowServiceHost(WorkflowService serviceDefinition, params Uri[] baseAddresses)95 public WorkflowServiceHost(WorkflowService serviceDefinition, params Uri[] baseAddresses) 96 : base() 97 { 98 if (serviceDefinition == null) 99 { 100 throw FxTrace.Exception.ArgumentNull("serviceDefinition"); 101 } 102 103 InitializeFromConstructor(serviceDefinition, baseAddresses); 104 105 } 106 107 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors, 108 Justification = "Based on prior art from WCF 3.0: By design, don't want to complicate ServiceHost state model")] WorkflowServiceHost()109 protected WorkflowServiceHost() 110 { 111 InitializeFromConstructor((WorkflowService)null); 112 } 113 114 public Activity Activity 115 { 116 get 117 { 118 return this.activity; 119 } 120 } 121 122 public WorkflowInstanceExtensionManager WorkflowExtensions 123 { 124 get 125 { 126 return this.workflowExtensions; 127 } 128 } 129 130 public DurableInstancingOptions DurableInstancingOptions 131 { 132 get 133 { 134 return this.durableInstanceManager.DurableInstancingOptions; 135 } 136 } 137 138 public ICollection<WorkflowService> SupportedVersions 139 { 140 get 141 { 142 return this.workflowDefinitionProvider.SupportedVersions; 143 } 144 } 145 146 internal XName ServiceName 147 { 148 get; 149 set; 150 } 151 152 internal TimeSpan PersistTimeout 153 { 154 get; 155 set; 156 } 157 158 internal TimeSpan TrackTimeout 159 { 160 get; 161 set; 162 } 163 164 // 165 internal TimeSpan FilterResumeTimeout 166 { 167 get; 168 set; 169 } 170 171 internal DurableInstanceManager DurableInstanceManager 172 { 173 get 174 { 175 return this.durableInstanceManager; 176 } 177 } 178 179 internal bool IsLoadTransactionRequired 180 { 181 get; 182 private set; 183 } 184 185 // set by WorkflowUnhandledExceptionBehavior.ApplyDispatchBehavior, used by WorkflowServiceInstance.UnhandledExceptionPolicy 186 internal WorkflowUnhandledExceptionAction UnhandledExceptionAction 187 { 188 get { return this.unhandledExceptionAction; } 189 set 190 { 191 Fx.Assert(WorkflowUnhandledExceptionActionHelper.IsDefined(value), "Undefined WorkflowUnhandledExceptionAction"); 192 this.unhandledExceptionAction = value; 193 } 194 } 195 196 // set by WorkflowIdleBehavior.ApplyDispatchBehavior, used by WorkflowServiceInstance.UnloadInstancePolicy 197 internal TimeSpan IdleTimeToPersist 198 { 199 get { return this.idleTimeToPersist; } 200 set 201 { 202 Fx.Assert(value >= TimeSpan.Zero, "IdleTimeToPersist cannot be less than zero"); 203 this.idleTimeToPersist = value; 204 } 205 } 206 internal TimeSpan IdleTimeToUnload 207 { 208 get { return this.idleTimeToUnload; } 209 set 210 { 211 Fx.Assert(value >= TimeSpan.Zero, "IdleTimeToUnload cannot be less than zero"); 212 this.idleTimeToUnload = value; 213 } 214 } 215 216 internal bool IsConfigurable 217 { 218 get 219 { 220 lock (this.ThisLock) 221 { 222 return this.State == CommunicationState.Created || this.State == CommunicationState.Opening; 223 } 224 } 225 } 226 227 internal WorkflowServiceHostPerformanceCounters WorkflowServiceHostPerformanceCounters 228 { 229 get 230 { 231 return this.workflowServiceHostPerformanceCounters; 232 } 233 } 234 235 internal bool OverrideSiteName 236 { 237 get; 238 set; 239 } 240 InitializeFromConstructor(Activity activity, params Uri[] baseAddresses)241 void InitializeFromConstructor(Activity activity, params Uri[] baseAddresses) 242 { 243 WorkflowService serviceDefinition = new WorkflowService 244 { 245 Body = activity 246 }; 247 248 InitializeFromConstructor(serviceDefinition, baseAddresses); 249 } 250 InitializeFromConstructor(WorkflowService serviceDefinition, params Uri[] baseAddresses)251 void InitializeFromConstructor(WorkflowService serviceDefinition, params Uri[] baseAddresses) 252 { 253 // first initialize some values to their defaults 254 this.idleTimeToPersist = WorkflowIdleBehavior.defaultTimeToPersist; 255 this.idleTimeToUnload = WorkflowIdleBehavior.defaultTimeToUnload; 256 this.unhandledExceptionAction = WorkflowUnhandledExceptionBehavior.defaultAction; 257 this.workflowExtensions = new WorkflowServiceHostExtensions(); 258 259 // If the AppSettings.DefaultAutomaticInstanceKeyDisassociation is specified and is true, create a DisassociateInstanceKeysExtension, set its 260 // AutomaticDisassociationEnabled property to true, and add it to the extensions collection so that System.Activities.BookmarkScopeHandle will 261 // unregister its BookmarkScope, which will cause key disassociation. KB2669774. 262 if (AppSettings.DefaultAutomaticInstanceKeyDisassociation) 263 { 264 DisassociateInstanceKeysExtension extension = new DisassociateInstanceKeysExtension(); 265 extension.AutomaticDisassociationEnabled = true; 266 this.workflowExtensions.Add(extension); 267 } 268 269 if (TD.CreateWorkflowServiceHostStartIsEnabled()) 270 { 271 TD.CreateWorkflowServiceHostStart(); 272 } 273 if (serviceDefinition != null) 274 { 275 this.workflowDefinitionProvider = new WorkflowDefinitionProvider(serviceDefinition, this); 276 InitializeDescription(serviceDefinition, new UriSchemeKeyedCollection(baseAddresses)); 277 } 278 this.durableInstanceManager = new DurableInstanceManager(this); 279 280 if (TD.CreateWorkflowServiceHostStopIsEnabled()) 281 { 282 TD.CreateWorkflowServiceHostStop(); 283 } 284 285 this.workflowServiceHostPerformanceCounters = new WorkflowServiceHostPerformanceCounters(this); 286 } 287 288 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")] AddServiceEndpoint(XName serviceContractName, Binding binding, string address, Uri listenUri = null, string behaviorConfigurationName = null)289 public ServiceEndpoint AddServiceEndpoint(XName serviceContractName, Binding binding, string address, 290 Uri listenUri = null, string behaviorConfigurationName = null) 291 { 292 return AddServiceEndpoint(serviceContractName, binding, new Uri(address, UriKind.RelativeOrAbsolute), listenUri, behaviorConfigurationName); 293 } 294 295 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")] AddServiceEndpoint(XName serviceContractName, Binding binding, Uri address, Uri listenUri = null, string behaviorConfigurationName = null)296 public ServiceEndpoint AddServiceEndpoint(XName serviceContractName, Binding binding, Uri address, 297 Uri listenUri = null, string behaviorConfigurationName = null) 298 { 299 if (binding == null) 300 { 301 throw FxTrace.Exception.ArgumentNull("binding"); 302 } 303 if (address == null) 304 { 305 throw FxTrace.Exception.ArgumentNull("address"); 306 } 307 308 Uri via = this.MakeAbsoluteUri(address, binding); 309 return AddServiceEndpointCore(serviceContractName, binding, new EndpointAddress(via), listenUri, behaviorConfigurationName); 310 } 311 312 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")] AddServiceEndpointCore(XName serviceContractName, Binding binding, EndpointAddress address, Uri listenUri = null, string behaviorConfigurationName = null)313 ServiceEndpoint AddServiceEndpointCore(XName serviceContractName, Binding binding, EndpointAddress address, 314 Uri listenUri = null, string behaviorConfigurationName = null) 315 { 316 if (serviceContractName == null) 317 { 318 throw FxTrace.Exception.ArgumentNull("serviceContractName"); 319 } 320 if (this.inferredContracts == null) 321 { 322 throw FxTrace.Exception.AsError(new InvalidOperationException( 323 SR.ContractNotFoundInAddServiceEndpoint(serviceContractName.LocalName, serviceContractName.NamespaceName))); 324 } 325 326 ServiceEndpoint serviceEndpoint; 327 ContractDescription description; 328 329 ContractInferenceHelper.ProvideDefaultNamespace(ref serviceContractName); 330 331 if (this.inferredContracts.TryGetValue(serviceContractName, out description)) 332 { 333 serviceEndpoint = new ServiceEndpoint(description, binding, address); 334 335 if (!string.IsNullOrEmpty(behaviorConfigurationName)) 336 { 337 ConfigLoader.LoadChannelBehaviors(behaviorConfigurationName, null, serviceEndpoint.Behaviors); 338 } 339 } 340 else if (serviceContractName == mexContractXName) // Special case for mex endpoint 341 { 342 if (!this.Description.Behaviors.Contains(mexBehaviorType)) 343 { 344 throw FxTrace.Exception.AsError(new InvalidOperationException( 345 SR.ServiceMetadataBehaviorNotFoundForServiceMetadataEndpoint(this.Description.Name))); 346 } 347 348 serviceEndpoint = new ServiceMetadataEndpoint(binding, address); 349 } 350 else 351 { 352 throw FxTrace.Exception.AsError(new InvalidOperationException( 353 SR.ContractNotFoundInAddServiceEndpoint(serviceContractName.LocalName, serviceContractName.NamespaceName))); 354 } 355 356 if (listenUri != null) 357 { 358 listenUri = base.MakeAbsoluteUri(listenUri, binding); 359 serviceEndpoint.ListenUri = listenUri; 360 } 361 362 base.Description.Endpoints.Add(serviceEndpoint); 363 364 if (TD.ServiceEndpointAddedIsEnabled()) 365 { 366 TD.ServiceEndpointAdded(address.Uri.ToString(), binding.GetType().ToString(), serviceEndpoint.Contract.Name); 367 } 368 369 return serviceEndpoint; 370 } 371 372 // Duplicate public AddServiceEndpoint methods from the base class 373 // This is to ensure that base class methods with string are not hidden by derived class methods with XName AddServiceEndpoint(string implementedContract, Binding binding, string address)374 public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address) 375 { 376 return base.AddServiceEndpoint(implementedContract, binding, address); 377 } 378 AddServiceEndpoint(string implementedContract, Binding binding, Uri address)379 public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address) 380 { 381 return base.AddServiceEndpoint(implementedContract, binding, address); 382 } 383 AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri)384 public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri) 385 { 386 return base.AddServiceEndpoint(implementedContract, binding, address, listenUri); 387 } 388 AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri)389 public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri) 390 { 391 return base.AddServiceEndpoint(implementedContract, binding, address, listenUri); 392 } 393 AddServiceEndpoint(ServiceEndpoint endpoint)394 public override void AddServiceEndpoint(ServiceEndpoint endpoint) 395 { 396 if (!endpoint.IsSystemEndpoint) 397 { 398 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseAddServiceEndpointOverloadForWorkflowServices)); 399 } 400 401 base.AddServiceEndpoint(endpoint); 402 } 403 AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> defaultEndpoints)404 internal override void AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> defaultEndpoints) 405 { 406 if (this.inferredContracts != null) 407 { 408 foreach (XName contractName in this.inferredContracts.Keys) 409 { 410 ServiceEndpoint endpoint = AddServiceEndpoint(contractName, defaultBinding, String.Empty); 411 ConfigLoader.LoadDefaultEndpointBehaviors(endpoint); 412 AddCorrelationQueryBehaviorToServiceEndpoint(endpoint); 413 defaultEndpoints.Add(endpoint); 414 } 415 } 416 } 417 418 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, MessageId = "0#", Justification = "This is defined by the ServiceHost base class")] CreateDescription(out IDictionary<string, ContractDescription> implementedContracts)419 protected override ServiceDescription CreateDescription(out IDictionary<string, ContractDescription> implementedContracts) 420 { 421 Fx.AssertAndThrow(this.serviceDefinition != null, "serviceDefinition is null"); 422 423 this.activity = this.serviceDefinition.Body; 424 425 Dictionary<string, ContractDescription> result = new Dictionary<string, ContractDescription>(); 426 427 // Note: We do not check whether this.inferredContracts == null || this.inferredContracts.Count == 0, 428 // because we need to support hosting workflow with zero contract. 429 this.inferredContracts = this.serviceDefinition.GetContractDescriptions(); 430 431 if (this.inferredContracts != null) 432 { 433 foreach (ContractDescription contract in this.inferredContracts.Values) 434 { 435 if (!string.IsNullOrEmpty(contract.ConfigurationName)) 436 { 437 if (result.ContainsKey(contract.ConfigurationName)) 438 { 439 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.DifferentContractsSameConfigName)); 440 } 441 result.Add(contract.ConfigurationName, contract); 442 } 443 } 444 } 445 446 implementedContracts = result; 447 448 // Currently, only WorkflowService has CorrelationQueries property 449 this.correlationQueries = this.serviceDefinition.CorrelationQueries; 450 ServiceDescription serviceDescription = this.serviceDefinition.GetEmptyServiceDescription(); 451 serviceDescription.Behaviors.Add(new WorkflowServiceBehavior(this.workflowDefinitionProvider)); 452 return serviceDescription; 453 } 454 InitializeDescription(WorkflowService serviceDefinition, UriSchemeKeyedCollection baseAddresses)455 void InitializeDescription(WorkflowService serviceDefinition, UriSchemeKeyedCollection baseAddresses) 456 { 457 Fx.Assert(serviceDefinition != null, "caller must verify"); 458 459 this.serviceDefinition = serviceDefinition; 460 base.InitializeDescription(baseAddresses); 461 462 foreach (Endpoint endpoint in serviceDefinition.Endpoints) 463 { 464 if (endpoint.Binding == null) 465 { 466 string endpointName = ContractValidationHelper.GetErrorMessageEndpointName(endpoint.Name); 467 string contractName = ContractValidationHelper.GetErrorMessageEndpointServiceContractName(endpoint.ServiceContractName); 468 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.MissingBindingInEndpoint(endpointName, contractName))); 469 } 470 471 ServiceEndpoint serviceEndpoint = AddServiceEndpointCore(endpoint.ServiceContractName, endpoint.Binding, 472 endpoint.GetAddress(this), endpoint.ListenUri, endpoint.BehaviorConfigurationName); 473 474 if (!string.IsNullOrEmpty(endpoint.Name)) 475 { 476 serviceEndpoint.Name = endpoint.Name; 477 } 478 serviceEndpoint.UnresolvedAddress = endpoint.AddressUri; 479 serviceEndpoint.UnresolvedListenUri = endpoint.ListenUri; 480 } 481 482 this.PersistTimeout = defaultPersistTimeout; 483 this.TrackTimeout = defaultTrackTimeout; 484 this.FilterResumeTimeout = TimeSpan.FromSeconds(AppSettings.FilterResumeTimeoutInSeconds); 485 } 486 InitializeRuntime()487 protected override void InitializeRuntime() 488 { 489 if (base.Description != null) 490 { 491 FixupEndpoints(); 492 this.SetScopeName(); 493 if (this.DurableInstancingOptions.ScopeName == null) 494 { 495 this.DurableInstancingOptions.ScopeName = XNamespace.Get(this.Description.Namespace).GetName(this.Description.Name); 496 } 497 } 498 499 base.InitializeRuntime(); 500 501 this.WorkflowServiceHostPerformanceCounters.InitializePerformanceCounters(); 502 503 this.ServiceName = XNamespace.Get(this.Description.Namespace).GetName(this.Description.Name); 504 505 // add a host-wide SendChannelCache (with default settings) if one doesn't exist 506 this.workflowExtensions.EnsureChannelCache(); 507 508 // add a host-wide (free-threaded) CorrelationExtension based on our ServiceName 509 this.WorkflowExtensions.Add(new CorrelationExtension(this.DurableInstancingOptions.ScopeName)); 510 511 this.WorkflowExtensions.MakeReadOnly(); 512 513 // now calculate if IsLoadTransactionRequired 514 this.IsLoadTransactionRequired = WorkflowServiceInstance.IsLoadTransactionRequired(this); 515 516 if (this.serviceDefinition != null) 517 { 518 ValidateBufferedReceiveProperty(); 519 this.serviceDefinition.ResetServiceDescription(); 520 } 521 } 522 AfterInitializeRuntime(TimeSpan timeout)523 internal override void AfterInitializeRuntime(TimeSpan timeout) 524 { 525 this.durableInstanceManager.Open(timeout); 526 } 527 BeginAfterInitializeRuntime(TimeSpan timeout, AsyncCallback callback, object state)528 internal override IAsyncResult BeginAfterInitializeRuntime(TimeSpan timeout, AsyncCallback callback, object state) 529 { 530 return this.durableInstanceManager.BeginOpen(timeout, callback, state); 531 } 532 EndAfterInitializeRuntime(IAsyncResult result)533 internal override void EndAfterInitializeRuntime(IAsyncResult result) 534 { 535 this.durableInstanceManager.EndOpen(result); 536 } 537 OnClose(TimeSpan timeout)538 protected override void OnClose(TimeSpan timeout) 539 { 540 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 541 542 base.OnClose(timeoutHelper.RemainingTime()); 543 544 this.durableInstanceManager.Close(timeoutHelper.RemainingTime()); 545 546 this.workflowServiceHostPerformanceCounters.Dispose(); 547 } 548 OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)549 protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) 550 { 551 return new CloseAsyncResult(this, timeout, callback, state); 552 } 553 OnEndClose(IAsyncResult result)554 protected override void OnEndClose(IAsyncResult result) 555 { 556 CloseAsyncResult.End(result); 557 } 558 OnAbort()559 protected override void OnAbort() 560 { 561 base.OnAbort(); 562 563 this.durableInstanceManager.Abort(); 564 565 this.workflowServiceHostPerformanceCounters.Dispose(); 566 } 567 FaultServiceHostIfNecessary(Exception exception)568 internal void FaultServiceHostIfNecessary(Exception exception) 569 { 570 if (exception is InstancePersistenceException && !(exception is InstancePersistenceCommandException)) 571 { 572 this.Fault(exception); 573 } 574 } 575 BeginHostClose(TimeSpan timeout, AsyncCallback callback, object state)576 IAsyncResult BeginHostClose(TimeSpan timeout, AsyncCallback callback, object state) 577 { 578 return base.OnBeginClose(timeout, callback, state); 579 } EndHostClose(IAsyncResult result)580 void EndHostClose(IAsyncResult result) 581 { 582 base.OnEndClose(result); 583 } 584 AddCorrelationQueryBehaviorToServiceEndpoint(ServiceEndpoint serviceEndpoint)585 void AddCorrelationQueryBehaviorToServiceEndpoint(ServiceEndpoint serviceEndpoint) 586 { 587 Fx.Assert(serviceEndpoint != null, "Argument cannot be null!"); 588 Fx.Assert(serviceEndpoint.Contract != null, "ServiceEndpoint must have a contract!"); 589 Fx.Assert(this.serviceDefinition != null, "Missing WorkflowService!"); 590 Fx.Assert(!serviceEndpoint.Behaviors.Contains(correlationQueryBehaviorType), 591 "ServiceEndpoint should not have CorrelationQueryBehavior before this point!"); 592 593 XName endpointContractName = XName.Get(serviceEndpoint.Contract.Name, serviceEndpoint.Contract.Namespace); 594 595 Collection<CorrelationQuery> queries; 596 if (this.correlationQueries != null && this.correlationQueries.TryGetValue(endpointContractName, out queries)) 597 { 598 // Filter out duplicate CorrelationQueries in the collection. 599 // Currently, we only do reference comparison and Where message filter comparison. 600 Collection<CorrelationQuery> uniqueQueries = new Collection<CorrelationQuery>(); 601 foreach (CorrelationQuery correlationQuery in queries) 602 { 603 if (!uniqueQueries.Contains(correlationQuery)) 604 { 605 uniqueQueries.Add(correlationQuery); 606 } 607 else 608 { 609 if (TD.DuplicateCorrelationQueryIsEnabled()) 610 { 611 TD.DuplicateCorrelationQuery(correlationQuery.Where.ToString()); 612 } 613 } 614 } 615 serviceEndpoint.Behaviors.Add(new CorrelationQueryBehavior(uniqueQueries) { ServiceContractName = endpointContractName }); 616 } 617 else if (CorrelationQueryBehavior.BindingHasDefaultQueries(serviceEndpoint.Binding)) 618 { 619 if (!serviceEndpoint.Behaviors.Contains(typeof(CorrelationQueryBehavior))) 620 { 621 serviceEndpoint.Behaviors.Add(new CorrelationQueryBehavior(new Collection<CorrelationQuery>()) { ServiceContractName = endpointContractName }); 622 } 623 } 624 } 625 FixupEndpoints()626 void FixupEndpoints() 627 { 628 Fx.Assert(this.Description != null, "ServiceDescription cannot be null"); 629 630 Dictionary<Type, ContractDescription> contractDescriptionDictionary = new Dictionary<Type, ContractDescription>(); 631 foreach (ServiceEndpoint serviceEndpoint in this.Description.Endpoints) 632 { 633 if (this.serviceDefinition.AllowBufferedReceive) 634 { 635 // All application-level endpoints need to support ReceiveContext 636 SetupReceiveContextEnabledAttribute(serviceEndpoint); 637 } 638 639 // Need to add CorrelationQueryBehavior here so that endpoints added from config are included. 640 // It is possible that some endpoints already have CorrelationQueryBehavior from 641 // the AddDefaultEndpoints code path. We should skip them. 642 if (!serviceEndpoint.Behaviors.Contains(correlationQueryBehaviorType)) 643 { 644 AddCorrelationQueryBehaviorToServiceEndpoint(serviceEndpoint); 645 } 646 647 // Need to ensure that any WorkflowHostingEndpoints using the same contract type actually use the 648 // same contractDescription instance since this is required by WCF. 649 if (serviceEndpoint is WorkflowHostingEndpoint) 650 { 651 ContractDescription contract; 652 if (contractDescriptionDictionary.TryGetValue(serviceEndpoint.Contract.ContractType, out contract)) 653 { 654 serviceEndpoint.Contract = contract; 655 } 656 else 657 { 658 contractDescriptionDictionary[serviceEndpoint.Contract.ContractType] = serviceEndpoint.Contract; 659 } 660 } 661 } 662 663 if (this.serviceDefinition.AllowBufferedReceive && !this.Description.Behaviors.Contains(bufferedReceiveServiceBehaviorType)) 664 { 665 this.Description.Behaviors.Add(new BufferedReceiveServiceBehavior()); 666 } 667 } 668 SetScopeName()669 void SetScopeName() 670 { 671 VirtualPathExtension virtualPathExtension = this.Extensions.Find<VirtualPathExtension>(); 672 if (virtualPathExtension != null) 673 { 674 // Web Hosted scenario 675 WorkflowHostingOptionsSection hostingOptions = (WorkflowHostingOptionsSection)ConfigurationManager.GetSection(ConfigurationStrings.WorkflowHostingOptionsSectionPath); 676 if (hostingOptions != null && hostingOptions.OverrideSiteName) 677 { 678 this.OverrideSiteName = hostingOptions.OverrideSiteName; 679 680 string fullVirtualPath = virtualPathExtension.VirtualPath.Substring(1); 681 fullVirtualPath = ("/" == virtualPathExtension.ApplicationVirtualPath) ? fullVirtualPath : virtualPathExtension.ApplicationVirtualPath + fullVirtualPath; 682 683 int index = fullVirtualPath.LastIndexOf("/", StringComparison.OrdinalIgnoreCase); 684 string virtualDirectoryPath = fullVirtualPath.Substring(0, index + 1); 685 686 this.DurableInstancingOptions.ScopeName = XName.Get(XmlConvert.EncodeLocalName(Path.GetFileName(virtualPathExtension.VirtualPath)), 687 string.Format(CultureInfo.InvariantCulture, "/{0}{1}", this.Description.Name, virtualDirectoryPath)); 688 } 689 } 690 } 691 SetupReceiveContextEnabledAttribute(ServiceEndpoint serviceEndpoint)692 void SetupReceiveContextEnabledAttribute(ServiceEndpoint serviceEndpoint) 693 { 694 if (BufferedReceiveServiceBehavior.IsWorkflowEndpoint(serviceEndpoint)) 695 { 696 foreach (OperationDescription operation in serviceEndpoint.Contract.Operations) 697 { 698 ReceiveContextEnabledAttribute behavior = operation.Behaviors.Find<ReceiveContextEnabledAttribute>(); 699 if (behavior == null) 700 { 701 operation.Behaviors.Add(new ReceiveContextEnabledAttribute() { ManualControl = true }); 702 } 703 else 704 { 705 behavior.ManualControl = true; 706 } 707 } 708 } 709 } 710 ValidateBufferedReceiveProperty()711 void ValidateBufferedReceiveProperty() 712 { 713 // Validate that the AttachedProperty is indeed being used when the behavior is also used 714 bool hasBehavior = this.Description.Behaviors.Contains(bufferedReceiveServiceBehaviorType); 715 if (hasBehavior && !this.serviceDefinition.AllowBufferedReceive) 716 { 717 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.BufferedReceiveBehaviorUsedWithoutProperty)); 718 } 719 } 720 721 // specialized WorkflowInstanceExtensionManager that can default in a SendMessageChannelCache 722 class WorkflowServiceHostExtensions : WorkflowInstanceExtensionManager 723 { 724 static Type SendReceiveExtensionType = typeof(SendReceiveExtension); 725 726 bool hasChannelCache; 727 WorkflowServiceHostExtensions()728 public WorkflowServiceHostExtensions() 729 : base() 730 { 731 } 732 Add(Func<T> extensionCreationFunction)733 public override void Add<T>(Func<T> extensionCreationFunction) 734 { 735 ThrowIfNotSupported(typeof(T)); 736 737 if (TypeHelper.AreTypesCompatible(typeof(T), typeof(SendMessageChannelCache))) 738 { 739 this.hasChannelCache = true; 740 } 741 base.Add<T>(extensionCreationFunction); 742 } 743 Add(object singletonExtension)744 public override void Add(object singletonExtension) 745 { 746 ThrowIfNotSupported(singletonExtension.GetType()); 747 748 if (singletonExtension is SendMessageChannelCache) 749 { 750 this.hasChannelCache = true; 751 } 752 base.Add(singletonExtension); 753 } 754 EnsureChannelCache()755 public void EnsureChannelCache() 756 { 757 if (!this.hasChannelCache) 758 { 759 Add(new SendMessageChannelCache()); 760 this.hasChannelCache = true; 761 } 762 } 763 ThrowIfNotSupported(Type type)764 void ThrowIfNotSupported(Type type) 765 { 766 if (TypeHelper.AreTypesCompatible(type, SendReceiveExtensionType)) 767 { 768 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ExtensionTypeNotSupported(SendReceiveExtensionType.FullName))); 769 } 770 } 771 } 772 773 class CloseAsyncResult : AsyncResult 774 { 775 static AsyncCompletion handleDurableInstanceManagerEndClose = new AsyncCompletion(HandleDurableInstanceManagerEndClose); 776 static AsyncCompletion handleEndHostClose = new AsyncCompletion(HandleEndHostClose); 777 778 TimeoutHelper timeoutHelper; 779 WorkflowServiceHost host; 780 CloseAsyncResult(WorkflowServiceHost host, TimeSpan timeout, AsyncCallback callback, object state)781 public CloseAsyncResult(WorkflowServiceHost host, TimeSpan timeout, AsyncCallback callback, object state) 782 : base(callback, state) 783 { 784 this.timeoutHelper = new TimeoutHelper(timeout); 785 this.host = host; 786 787 if (CloseHost()) 788 { 789 Complete(true); 790 } 791 } 792 CloseDurableInstanceManager()793 bool CloseDurableInstanceManager() 794 { 795 IAsyncResult result = this.host.durableInstanceManager.BeginClose( 796 this.timeoutHelper.RemainingTime(), base.PrepareAsyncCompletion(handleDurableInstanceManagerEndClose), this); 797 return SyncContinue(result); 798 } 799 CloseHost()800 bool CloseHost() 801 { 802 IAsyncResult result = this.host.BeginHostClose( 803 this.timeoutHelper.RemainingTime(), base.PrepareAsyncCompletion(handleEndHostClose), this); 804 return SyncContinue(result); 805 } 806 HandleDurableInstanceManagerEndClose(IAsyncResult result)807 static bool HandleDurableInstanceManagerEndClose(IAsyncResult result) 808 { 809 CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState; 810 811 thisPtr.host.durableInstanceManager.EndClose(result); 812 813 thisPtr.host.WorkflowServiceHostPerformanceCounters.Dispose(); 814 return true; 815 } 816 HandleEndHostClose(IAsyncResult result)817 static bool HandleEndHostClose(IAsyncResult result) 818 { 819 CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState; 820 821 thisPtr.host.EndHostClose(result); 822 return thisPtr.CloseDurableInstanceManager(); 823 } 824 End(IAsyncResult result)825 public static void End(IAsyncResult result) 826 { 827 AsyncResult.End<CloseAsyncResult>(result); 828 } 829 } 830 } 831 } 832