| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.ServiceModel.Dispatcher
- {
- using System.Collections.ObjectModel;
- using System.ServiceModel.Channels;
- using System.ServiceModel;
- using System.ServiceModel.Description;
- using System.Collections.Generic;
- using System.Runtime.CompilerServices;
- using System.ServiceModel.Transactions;
- class TransactionValidationBehavior : IEndpointBehavior, IServiceBehavior
- {
- static TransactionValidationBehavior instance;
- internal static TransactionValidationBehavior Instance
- {
- get
- {
- if (instance == null)
- instance = new TransactionValidationBehavior();
- return instance;
- }
- }
- TransactionValidationBehavior() { }
- void ValidateTransactionFlowRequired(string resource, string name, ServiceEndpoint endpoint)
- {
- bool anOperationRequiresTxFlow = false;
- for (int i = 0; i < endpoint.Contract.Operations.Count; i++)
- {
- OperationDescription operationDescription = endpoint.Contract.Operations[i];
- TransactionFlowAttribute transactionFlow = operationDescription.Behaviors.Find<TransactionFlowAttribute>();
- if (transactionFlow != null && transactionFlow.Transactions == TransactionFlowOption.Mandatory)
- {
- anOperationRequiresTxFlow = true;
- break;
- }
- }
- if (anOperationRequiresTxFlow)
- {
- CustomBinding binding = new CustomBinding(endpoint.Binding);
- TransactionFlowBindingElement transactionFlowBindingElement =
- binding.Elements.Find<TransactionFlowBindingElement>();
- if (transactionFlowBindingElement == null || !transactionFlowBindingElement.Transactions)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- String.Format(Globalization.CultureInfo.CurrentCulture, SR.GetString(resource), name, binding.Name)));
- }
- }
- }
- void IEndpointBehavior.Validate(ServiceEndpoint serviceEndpoint)
- {
- if (serviceEndpoint == null)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serviceEndpoint");
- ValidateTransactionFlowRequired(SR.ChannelHasAtLeastOneOperationWithTransactionFlowEnabled,
- serviceEndpoint.Contract.Name,
- serviceEndpoint);
- EnsureNoOneWayTransactions(serviceEndpoint);
- ValidateNoMSMQandTransactionFlow(serviceEndpoint);
- ValidateCallbackBehaviorAttributeWithNoScopeRequired(serviceEndpoint);
- OperationDescription autoCompleteFalseOperation = GetAutoCompleteFalseOperation(serviceEndpoint);
- if (autoCompleteFalseOperation != null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionAutoCompleteFalseOnCallbackContract, autoCompleteFalseOperation.Name, serviceEndpoint.Contract.Name)));
- }
- }
- void ValidateCallbackBehaviorAttributeWithNoScopeRequired(ServiceEndpoint endpoint)
- {
- // If the endpoint has no operations with TransactionScopeRequired=true, disallow any
- // transaction-related properties on the CallbackBehaviorAttribute
- if (!HasTransactedOperations(endpoint))
- {
- CallbackBehaviorAttribute attribute = endpoint.Behaviors.Find<CallbackBehaviorAttribute>();
- if (attribute != null)
- {
- if (attribute.TransactionTimeoutSet)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionTransactionTimeoutNeedsScope, endpoint.Contract.Name)));
- }
- if (attribute.IsolationLevelSet)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionIsolationLevelNeedsScope, endpoint.Contract.Name)));
- }
- }
- }
- }
- void IEndpointBehavior.AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters)
- {
- }
- void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
- {
- }
- void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)
- {
- }
- void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
- {
- }
- void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription service, ServiceHostBase serviceHostBase)
- {
- }
- void IServiceBehavior.Validate(ServiceDescription service, ServiceHostBase serviceHostBase)
- {
- if (service == null)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("service");
- ValidateNotConcurrentWhenReleaseServiceInstanceOnTxComplete(service);
- bool singleThreaded = IsSingleThreaded(service);
- for (int i = 0; i < service.Endpoints.Count; i++)
- {
- ServiceEndpoint endpoint = service.Endpoints[i];
- ValidateTransactionFlowRequired(SR.ServiceHasAtLeastOneOperationWithTransactionFlowEnabled,
- service.Name,
- endpoint);
- EnsureNoOneWayTransactions(endpoint);
- ValidateNoMSMQandTransactionFlow(endpoint);
- ContractDescription contract = endpoint.Contract;
- for (int j = 0; j < contract.Operations.Count; j++)
- {
- OperationDescription operation = contract.Operations[j];
- ValidateScopeRequiredAndAutoComplete(operation, singleThreaded, contract.Name);
- }
- ValidateAutoCompleteFalseRequirements(service, endpoint);
- }
- ValidateServiceBehaviorAttributeWithNoScopeRequired(service);
- ValidateTransactionAutoCompleteOnSessionCloseHasSession(service);
- }
- void ValidateAutoCompleteFalseRequirements(ServiceDescription service, ServiceEndpoint endpoint)
- {
- OperationDescription autoCompleteFalseOperation = GetAutoCompleteFalseOperation(endpoint);
- if (autoCompleteFalseOperation != null)
- {
- // Does the service have InstanceContextMode.PerSession or Shareable?
- ServiceBehaviorAttribute serviceBehavior = service.Behaviors.Find<ServiceBehaviorAttribute>();
- if (serviceBehavior != null)
- {
- InstanceContextMode instanceMode = serviceBehavior.InstanceContextMode;
- if (instanceMode != InstanceContextMode.PerSession)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionAutoCompleteFalseAndInstanceContextMode,
- endpoint.Contract.Name, autoCompleteFalseOperation.Name)));
- }
- }
- // Does the binding support sessions?
- if (!autoCompleteFalseOperation.IsInsideTransactedReceiveScope)
- {
- if (!RequiresSessions(endpoint))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionAutoCompleteFalseAndSupportsSession,
- endpoint.Contract.Name, autoCompleteFalseOperation.Name)));
- }
- }
- }
- }
- OperationDescription GetAutoCompleteFalseOperation(ServiceEndpoint endpoint)
- {
- foreach (OperationDescription operation in endpoint.Contract.Operations)
- {
- if (!IsAutoComplete(operation))
- {
- return operation;
- }
- }
- return null;
- }
- void ValidateTransactionAutoCompleteOnSessionCloseHasSession(ServiceDescription service)
- {
- ServiceBehaviorAttribute serviceBehavior = service.Behaviors.Find<ServiceBehaviorAttribute>();
- if (serviceBehavior != null)
- {
- InstanceContextMode instanceMode = serviceBehavior.InstanceContextMode;
- if (serviceBehavior.TransactionAutoCompleteOnSessionClose &&
- instanceMode != InstanceContextMode.PerSession)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionAutoCompleteOnSessionCloseNoSession, service.Name)));
- }
- }
- }
- void ValidateServiceBehaviorAttributeWithNoScopeRequired(ServiceDescription service)
- {
- // If the service has no operations with TransactionScopeRequired=true, disallow any
- // transaction-related properties on the ServiceBehaviorAttribute
- if (!HasTransactedOperations(service))
- {
- ServiceBehaviorAttribute attribute = service.Behaviors.Find<ServiceBehaviorAttribute>();
- if (attribute != null)
- {
- if (attribute.TransactionTimeoutSet)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionTransactionTimeoutNeedsScope, service.Name)));
- }
- if (attribute.IsolationLevelSet)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionIsolationLevelNeedsScope, service.Name)));
- }
- if (attribute.ReleaseServiceInstanceOnTransactionCompleteSet)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionReleaseServiceInstanceOnTransactionCompleteNeedsScope, service.Name)));
- }
- if (attribute.TransactionAutoCompleteOnSessionCloseSet)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionTransactionAutoCompleteOnSessionCloseNeedsScope, service.Name)));
- }
- }
- }
- }
- void EnsureNoOneWayTransactions(ServiceEndpoint endpoint)
- {
- CustomBinding binding = new CustomBinding(endpoint.Binding);
- TransactionFlowBindingElement txFlowBindingElement = binding.Elements.Find<TransactionFlowBindingElement>();
- if (txFlowBindingElement != null)
- {
- for (int i = 0; i < endpoint.Contract.Operations.Count; i++)
- {
- OperationDescription operation = endpoint.Contract.Operations[i];
- if (operation.IsOneWay)
- {
- TransactionFlowAttribute tfbp = operation.Behaviors.Find<TransactionFlowAttribute>();
- TransactionFlowOption transactions;
- if (tfbp != null)
- {
- transactions = tfbp.Transactions;
- }
- else
- {
- transactions = TransactionFlowOption.NotAllowed;
- }
- if (TransactionFlowOptionHelper.AllowedOrRequired(transactions))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxOneWayAndTransactionsIncompatible, endpoint.Contract.Name, operation.Name)));
- }
- }
- }
- }
- }
- bool HasTransactedOperations(ServiceDescription service)
- {
- for (int i = 0; i < service.Endpoints.Count; i++)
- {
- if (HasTransactedOperations(service.Endpoints[i]))
- {
- return true;
- }
- }
- return false;
- }
- bool HasTransactedOperations(ServiceEndpoint endpoint)
- {
- for (int j = 0; j < endpoint.Contract.Operations.Count; j++)
- {
- OperationDescription operation = endpoint.Contract.Operations[j];
- OperationBehaviorAttribute attribute = operation.Behaviors.Find<OperationBehaviorAttribute>();
- if (attribute != null && attribute.TransactionScopeRequired)
- {
- return true;
- }
- }
- return false;
- }
- bool IsSingleThreaded(ServiceDescription service)
- {
- ServiceBehaviorAttribute attribute = service.Behaviors.Find<ServiceBehaviorAttribute>();
- if (attribute != null)
- {
- return (attribute.ConcurrencyMode == ConcurrencyMode.Single);
- }
- // The default is ConcurrencyMode.Single
- return true;
- }
- bool IsAutoComplete(OperationDescription operation)
- {
- OperationBehaviorAttribute attribute = operation.Behaviors.Find<OperationBehaviorAttribute>();
- if (attribute != null)
- {
- return attribute.TransactionAutoComplete;
- }
- // The default is TransactionAutoComplete=true
- return true;
- }
- bool RequiresSessions(ServiceEndpoint endpoint)
- {
- return endpoint.Contract.SessionMode == SessionMode.Required;
- }
- void ValidateScopeRequiredAndAutoComplete(OperationDescription operation,
- bool singleThreaded,
- string contractName)
- {
- OperationBehaviorAttribute attribute = operation.Behaviors.Find<OperationBehaviorAttribute>();
- if (attribute != null)
- {
- if (!singleThreaded && !attribute.TransactionAutoComplete)
- {
- string id = SR.SFxTransactionNonConcurrentOrAutoComplete2;
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(id, contractName, operation.Name)));
- }
- }
- }
- void ValidateNoMSMQandTransactionFlow(ServiceEndpoint endpoint)
- {
- BindingElementCollection bindingElements = endpoint.Binding.CreateBindingElements();
- if (bindingElements.Find<TransactionFlowBindingElement>() != null &&
- bindingElements.Find<MsmqTransportBindingElement>() != null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
- SR.GetString(SR.SFxTransactionFlowAndMSMQ, endpoint.Address.Uri.AbsoluteUri)));
- }
- }
- void ValidateNotConcurrentWhenReleaseServiceInstanceOnTxComplete(ServiceDescription service)
- {
- ServiceBehaviorAttribute attribute = service.Behaviors.Find<ServiceBehaviorAttribute>();
- if (attribute != null && HasTransactedOperations(service))
- {
- if (attribute.ReleaseServiceInstanceOnTransactionComplete && !IsSingleThreaded(service))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new InvalidOperationException(SR.GetString(
- SR.SFxTransactionNonConcurrentOrReleaseServiceInstanceOnTxComplete, service.Name)));
- }
- }
- }
- }
- }
|