//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.ServiceModel.Routing { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; [Fx.Tag.XamlVisible(false)] public sealed class RoutingBehavior : IServiceBehavior { RoutingConfiguration configuration; public RoutingBehavior(RoutingConfiguration routingConfiguration) { if (routingConfiguration == null) { throw FxTrace.Exception.ArgumentNull("routingConfiguration"); } this.configuration = routingConfiguration; } void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters) { } void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { RoutingExtension routingExtension = new RoutingExtension(this.configuration); serviceHostBase.Extensions.Add(routingExtension); for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++) { ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher; if (channelDispatcher != null) { foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) { if (!endpointDispatcher.IsSystemEndpoint && RoutingUtilities.IsRoutingServiceNamespace(endpointDispatcher.ContractNamespace)) { DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime; //Since we use PerSession instancing this concurrency only applies to messages //in the same session, also needed to maintain order. dispatchRuntime.ConcurrencyMode = ConcurrencyMode.Single; dispatchRuntime.EnsureOrderedDispatch = this.configuration.EnsureOrderedDispatch; } } } } } void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { HashSet endpoints = new HashSet(); foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints) { if (!endpoint.IsSystemEndpoint && RoutingUtilities.IsRoutingServiceNamespace(endpoint.Contract.Namespace)) { endpoint.Behaviors.Add(new RoutingEndpointBehavior(endpoint)); endpoints.Add(endpoint.Name); } } EndpointNameMessageFilter.Validate(this.configuration.InternalFilterTable.Keys, endpoints); } public static Type GetContractForDescription(ContractDescription description) { if (description == null) { throw FxTrace.Exception.ArgumentNull("description"); } if (description.CallbackContractType != null) { return typeof(IDuplexSessionRouter); } bool allOneWay = true; foreach (OperationDescription operation in description.Operations) { if (!operation.IsOneWay) { allOneWay = false; break; } } if (allOneWay) { if (description.SessionMode == SessionMode.Required) { return typeof(ISimplexSessionRouter); } else { return typeof(ISimplexDatagramRouter); } } else { return typeof(IRequestReplyRouter); } } internal class RoutingEndpointBehavior : IEndpointBehavior, IChannelInitializer, IInputSessionShutdown { public RoutingEndpointBehavior(ServiceEndpoint endpoint) { this.Endpoint = endpoint; this.EndpointName = endpoint.Name; } internal ChannelDispatcher ChannelDispatcher { get; private set; } internal ServiceEndpoint Endpoint { get; private set; } internal string EndpointName { get; private set; } internal bool ImpersonationRequired { get; private set; } internal bool ReceiveContextEnabled { get; private set; } internal bool TransactedReceiveEnabled { get; private set; } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { //Turn on ReceiveContext here if supported IReceiveContextSettings receiveContextSettings = endpoint.Binding.GetProperty(bindingParameters); if (receiveContextSettings != null) { receiveContextSettings.Enabled = true; } } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { this.ChannelDispatcher = endpointDispatcher.ChannelDispatcher; this.ChannelDispatcher.ChannelInitializers.Add(this); endpointDispatcher.DispatchRuntime.InputSessionShutdownHandlers.Add(this); endpointDispatcher.DispatchRuntime.AutomaticInputSessionShutdown = false; if (endpointDispatcher.DispatchRuntime.ImpersonateCallerForAllOperations) { this.ImpersonationRequired = true; } else if (AspNetEnvironment.Current.AspNetCompatibilityEnabled) { this.ImpersonationRequired = true; } BindingParameterCollection bindingParams = new BindingParameterCollection(); if (RoutingUtilities.IsTransactedReceive(endpoint.Binding, bindingParams)) { foreach (OperationDescription operation in endpoint.Contract.Operations) { if (operation.Behaviors.Find() == null) { operation.Behaviors.Add(new TransactedReceiveOperationBehavior()); } } this.ChannelDispatcher.IsTransactedReceive = true; endpointDispatcher.DispatchRuntime.TransactionAutoCompleteOnSessionClose = true; this.TransactedReceiveEnabled = true; } IReceiveContextSettings rcSettings = endpoint.Binding.GetProperty(bindingParams); if (rcSettings != null && rcSettings.Enabled) { foreach (OperationDescription operation in endpoint.Contract.Operations) { ReceiveContextEnabledAttribute rcEnabled = new ReceiveContextEnabledAttribute(); rcEnabled.ManualControl = true; operation.Behaviors.Add(rcEnabled); } this.ReceiveContextEnabled = true; //Switch TransactedReceive off, because we don't want the Dispatcher creating any Transaction endpointDispatcher.ChannelDispatcher.IsTransactedReceive = false; endpointDispatcher.DispatchRuntime.TransactionAutoCompleteOnSessionClose = false; } } public void Validate(ServiceEndpoint endpoint) { } void IChannelInitializer.Initialize(IClientChannel channel) { RoutingChannelExtension channelState = RoutingChannelExtension.Create(this); channel.Extensions.Add(channelState); } void IInputSessionShutdown.ChannelFaulted(IDuplexContextChannel channel) { RoutingChannelExtension channelExtension = channel.Extensions.Find(); if (channelExtension != null) { channelExtension.Fault(new CommunicationObjectFaultedException()); } else { RoutingUtilities.Abort(channel, channel.LocalAddress); } } void IInputSessionShutdown.DoneReceiving(IDuplexContextChannel channel) { RoutingChannelExtension channelExtension = channel.Extensions.Find(); channelExtension.DoneReceiving(this.Endpoint.Binding.CloseTimeout); } } class TransactedReceiveOperationBehavior : IOperationBehavior { public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { if (operationDescription.Behaviors.Find() == null) { dispatchOperation.TransactionRequired = true; } ContractDescription contract = operationDescription.DeclaringContract; if (dispatchOperation.IsOneWay && contract.SessionMode == SessionMode.Required) { dispatchOperation.Parent.ConcurrencyMode = ConcurrencyMode.Single; dispatchOperation.Parent.ReleaseServiceInstanceOnTransactionComplete = false; dispatchOperation.TransactionAutoComplete = false; } } public void Validate(OperationDescription operationDescription) { } } } }