481 lines
19 KiB
C#
481 lines
19 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel
|
||
|
{
|
||
|
using System.ServiceModel.Administration;
|
||
|
using System.ServiceModel.Channels;
|
||
|
using System.ServiceModel.Dispatcher;
|
||
|
using System.ServiceModel.Description;
|
||
|
using System.ServiceModel.Configuration;
|
||
|
using System.Runtime.Serialization;
|
||
|
using System.Collections.ObjectModel;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Threading;
|
||
|
using System.Transactions;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.ComponentModel;
|
||
|
using System.Globalization;
|
||
|
|
||
|
[AttributeUsage(ServiceModelAttributeTargets.ServiceBehavior)]
|
||
|
public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
|
||
|
{
|
||
|
internal static IsolationLevel DefaultIsolationLevel = IsolationLevel.Unspecified;
|
||
|
ConcurrencyMode concurrencyMode = ConcurrencyMode.Single;
|
||
|
bool ensureOrderedDispatch = false;
|
||
|
string configurationName;
|
||
|
bool includeExceptionDetailInFaults = false;
|
||
|
InstanceContextMode instanceMode;
|
||
|
bool releaseServiceInstanceOnTransactionComplete = true;
|
||
|
bool releaseServiceInstanceOnTransactionCompleteSet = false;
|
||
|
bool transactionAutoCompleteOnSessionClose = false;
|
||
|
bool transactionAutoCompleteOnSessionCloseSet = false;
|
||
|
object wellKnownSingleton = null; // if the user passes an object to the ServiceHost, it is stored here
|
||
|
object hiddenSingleton = null; // if the user passes a type to the ServiceHost, and instanceMode==Single, we store the instance here
|
||
|
bool validateMustUnderstand = true;
|
||
|
bool ignoreExtensionDataObject = DataContractSerializerDefaults.IgnoreExtensionDataObject;
|
||
|
int maxItemsInObjectGraph = DataContractSerializerDefaults.MaxItemsInObjectGraph;
|
||
|
IsolationLevel transactionIsolationLevel = DefaultIsolationLevel;
|
||
|
bool isolationLevelSet = false;
|
||
|
bool automaticSessionShutdown = true;
|
||
|
IInstanceProvider instanceProvider = null;
|
||
|
TimeSpan transactionTimeout = TimeSpan.Zero;
|
||
|
string transactionTimeoutString;
|
||
|
bool transactionTimeoutSet = false;
|
||
|
bool useSynchronizationContext = true;
|
||
|
string serviceName = null;
|
||
|
string serviceNamespace = null;
|
||
|
AddressFilterMode addressFilterMode = AddressFilterMode.Exact;
|
||
|
|
||
|
[DefaultValue(null)]
|
||
|
public string Name
|
||
|
{
|
||
|
get { return serviceName; }
|
||
|
set { serviceName = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(null)]
|
||
|
public string Namespace
|
||
|
{
|
||
|
get { return serviceNamespace; }
|
||
|
set { serviceNamespace = value; }
|
||
|
}
|
||
|
|
||
|
internal IInstanceProvider InstanceProvider
|
||
|
{
|
||
|
set { this.instanceProvider = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(AddressFilterMode.Exact)]
|
||
|
public AddressFilterMode AddressFilterMode
|
||
|
{
|
||
|
get { return this.addressFilterMode; }
|
||
|
set
|
||
|
{
|
||
|
if (!AddressFilterModeHelper.IsDefined(value))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||
|
}
|
||
|
|
||
|
this.addressFilterMode = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[DefaultValue(true)]
|
||
|
public bool AutomaticSessionShutdown
|
||
|
{
|
||
|
get { return this.automaticSessionShutdown; }
|
||
|
set { this.automaticSessionShutdown = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(null)]
|
||
|
public string ConfigurationName
|
||
|
{
|
||
|
get { return this.configurationName; }
|
||
|
set
|
||
|
{
|
||
|
if (value == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
||
|
}
|
||
|
if (value == string.Empty)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value",
|
||
|
SR.GetString(SR.SFxConfigurationNameCannotBeEmpty)));
|
||
|
}
|
||
|
this.configurationName = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IsolationLevel TransactionIsolationLevel
|
||
|
{
|
||
|
get { return this.transactionIsolationLevel; }
|
||
|
set
|
||
|
{
|
||
|
switch (value)
|
||
|
{
|
||
|
case IsolationLevel.Serializable:
|
||
|
case IsolationLevel.RepeatableRead:
|
||
|
case IsolationLevel.ReadCommitted:
|
||
|
case IsolationLevel.ReadUncommitted:
|
||
|
case IsolationLevel.Unspecified:
|
||
|
case IsolationLevel.Chaos:
|
||
|
case IsolationLevel.Snapshot:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||
|
}
|
||
|
|
||
|
this.transactionIsolationLevel = value;
|
||
|
isolationLevelSet = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool ShouldSerializeTransactionIsolationLevel()
|
||
|
{
|
||
|
return IsolationLevelSet;
|
||
|
}
|
||
|
|
||
|
internal bool IsolationLevelSet
|
||
|
{
|
||
|
get { return this.isolationLevelSet; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(false)]
|
||
|
public bool IncludeExceptionDetailInFaults
|
||
|
{
|
||
|
get { return this.includeExceptionDetailInFaults; }
|
||
|
set { this.includeExceptionDetailInFaults = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(ConcurrencyMode.Single)]
|
||
|
public ConcurrencyMode ConcurrencyMode
|
||
|
{
|
||
|
get { return this.concurrencyMode; }
|
||
|
set
|
||
|
{
|
||
|
if (!ConcurrencyModeHelper.IsDefined(value))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||
|
}
|
||
|
|
||
|
this.concurrencyMode = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[DefaultValue(false)]
|
||
|
public bool EnsureOrderedDispatch
|
||
|
{
|
||
|
get { return this.ensureOrderedDispatch; }
|
||
|
set { this.ensureOrderedDispatch = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(InstanceContextMode.PerSession)]
|
||
|
public InstanceContextMode InstanceContextMode
|
||
|
{
|
||
|
get { return this.instanceMode; }
|
||
|
set
|
||
|
{
|
||
|
if (!InstanceContextModeHelper.IsDefined(value))
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||
|
}
|
||
|
|
||
|
this.instanceMode = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool ReleaseServiceInstanceOnTransactionComplete
|
||
|
{
|
||
|
get { return releaseServiceInstanceOnTransactionComplete; }
|
||
|
set
|
||
|
{
|
||
|
this.releaseServiceInstanceOnTransactionComplete = value;
|
||
|
this.releaseServiceInstanceOnTransactionCompleteSet = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool ShouldSerializeConfigurationName()
|
||
|
{
|
||
|
return this.configurationName != null;
|
||
|
}
|
||
|
|
||
|
public bool ShouldSerializeReleaseServiceInstanceOnTransactionComplete()
|
||
|
{
|
||
|
return ReleaseServiceInstanceOnTransactionCompleteSet;
|
||
|
}
|
||
|
|
||
|
internal bool ReleaseServiceInstanceOnTransactionCompleteSet
|
||
|
{
|
||
|
get { return this.releaseServiceInstanceOnTransactionCompleteSet; }
|
||
|
}
|
||
|
|
||
|
public bool TransactionAutoCompleteOnSessionClose
|
||
|
{
|
||
|
get { return transactionAutoCompleteOnSessionClose; }
|
||
|
set
|
||
|
{
|
||
|
this.transactionAutoCompleteOnSessionClose = value;
|
||
|
this.transactionAutoCompleteOnSessionCloseSet = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool ShouldSerializeTransactionAutoCompleteOnSessionClose()
|
||
|
{
|
||
|
return TransactionAutoCompleteOnSessionCloseSet;
|
||
|
}
|
||
|
|
||
|
internal bool TransactionAutoCompleteOnSessionCloseSet
|
||
|
{
|
||
|
get { return this.transactionAutoCompleteOnSessionCloseSet; }
|
||
|
}
|
||
|
|
||
|
public string TransactionTimeout
|
||
|
{
|
||
|
get { return transactionTimeoutString; }
|
||
|
set
|
||
|
{
|
||
|
if (value == null)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
TimeSpan timeout = TimeSpan.Parse(value, CultureInfo.InvariantCulture);
|
||
|
|
||
|
if (timeout < TimeSpan.Zero)
|
||
|
{
|
||
|
string message = SR.GetString(SR.SFxTimeoutOutOfRange0);
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, message));
|
||
|
}
|
||
|
|
||
|
this.transactionTimeout = timeout;
|
||
|
this.transactionTimeoutString = value;
|
||
|
this.transactionTimeoutSet = true;
|
||
|
}
|
||
|
catch (FormatException e)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxTimeoutInvalidStringFormat), "value", e));
|
||
|
}
|
||
|
catch (OverflowException)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool ShouldSerializeTransactionTimeout()
|
||
|
{
|
||
|
return TransactionTimeoutSet;
|
||
|
}
|
||
|
|
||
|
internal TimeSpan TransactionTimeoutTimespan
|
||
|
{
|
||
|
get { return this.transactionTimeout; }
|
||
|
}
|
||
|
|
||
|
internal bool TransactionTimeoutSet
|
||
|
{
|
||
|
get { return this.transactionTimeoutSet; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(true)]
|
||
|
public bool ValidateMustUnderstand
|
||
|
{
|
||
|
get { return validateMustUnderstand; }
|
||
|
set { validateMustUnderstand = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(DataContractSerializerDefaults.IgnoreExtensionDataObject)]
|
||
|
public bool IgnoreExtensionDataObject
|
||
|
{
|
||
|
get { return ignoreExtensionDataObject; }
|
||
|
set { ignoreExtensionDataObject = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(DataContractSerializerDefaults.MaxItemsInObjectGraph)]
|
||
|
public int MaxItemsInObjectGraph
|
||
|
{
|
||
|
get { return maxItemsInObjectGraph; }
|
||
|
set { maxItemsInObjectGraph = value; }
|
||
|
}
|
||
|
|
||
|
[DefaultValue(true)]
|
||
|
public bool UseSynchronizationContext
|
||
|
{
|
||
|
get { return this.useSynchronizationContext; }
|
||
|
set { this.useSynchronizationContext = value; }
|
||
|
}
|
||
|
|
||
|
public object GetWellKnownSingleton()
|
||
|
{
|
||
|
return this.wellKnownSingleton;
|
||
|
}
|
||
|
|
||
|
public void SetWellKnownSingleton(object value)
|
||
|
{
|
||
|
if (value == null)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
||
|
|
||
|
this.wellKnownSingleton = value;
|
||
|
}
|
||
|
|
||
|
internal object GetHiddenSingleton()
|
||
|
{
|
||
|
return this.hiddenSingleton;
|
||
|
}
|
||
|
|
||
|
internal void SetHiddenSingleton(object value)
|
||
|
{
|
||
|
if (value == null)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
|
||
|
|
||
|
this.hiddenSingleton = value;
|
||
|
}
|
||
|
|
||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
void SetIsolationLevel(ChannelDispatcher channelDispatcher)
|
||
|
{
|
||
|
if (channelDispatcher == null)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("channelDispatcher");
|
||
|
|
||
|
channelDispatcher.TransactionIsolationLevel = this.transactionIsolationLevel;
|
||
|
}
|
||
|
|
||
|
void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
|
||
|
{
|
||
|
if (this.concurrencyMode != ConcurrencyMode.Single && this.ensureOrderedDispatch)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxNonConcurrentOrEnsureOrderedDispatch, description.Name)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
|
||
|
{
|
||
|
for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
|
||
|
{
|
||
|
ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
|
||
|
if (channelDispatcher != null)
|
||
|
{
|
||
|
channelDispatcher.IncludeExceptionDetailInFaults = this.includeExceptionDetailInFaults;
|
||
|
|
||
|
if (channelDispatcher.HasApplicationEndpoints())
|
||
|
{
|
||
|
channelDispatcher.TransactionTimeout = transactionTimeout;
|
||
|
if (isolationLevelSet)
|
||
|
SetIsolationLevel(channelDispatcher);
|
||
|
|
||
|
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
|
||
|
{
|
||
|
if (endpointDispatcher.IsSystemEndpoint)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
DispatchRuntime behavior = endpointDispatcher.DispatchRuntime;
|
||
|
behavior.ConcurrencyMode = this.concurrencyMode;
|
||
|
behavior.EnsureOrderedDispatch = this.ensureOrderedDispatch;
|
||
|
behavior.ValidateMustUnderstand = validateMustUnderstand;
|
||
|
behavior.AutomaticInputSessionShutdown = this.automaticSessionShutdown;
|
||
|
behavior.TransactionAutoCompleteOnSessionClose = this.transactionAutoCompleteOnSessionClose;
|
||
|
behavior.ReleaseServiceInstanceOnTransactionComplete = this.releaseServiceInstanceOnTransactionComplete;
|
||
|
if (!this.useSynchronizationContext)
|
||
|
{
|
||
|
behavior.SynchronizationContext = null;
|
||
|
}
|
||
|
|
||
|
if (!endpointDispatcher.AddressFilterSetExplicit)
|
||
|
{
|
||
|
EndpointAddress address = endpointDispatcher.OriginalAddress;
|
||
|
if (address == null || this.AddressFilterMode == AddressFilterMode.Any)
|
||
|
{
|
||
|
endpointDispatcher.AddressFilter = new MatchAllMessageFilter();
|
||
|
}
|
||
|
else if (this.AddressFilterMode == AddressFilterMode.Prefix)
|
||
|
{
|
||
|
endpointDispatcher.AddressFilter = new PrefixEndpointAddressMessageFilter(address);
|
||
|
}
|
||
|
else if (this.AddressFilterMode == AddressFilterMode.Exact)
|
||
|
{
|
||
|
endpointDispatcher.AddressFilter = new EndpointAddressMessageFilter(address);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#pragma warning suppress 56506
|
||
|
}
|
||
|
}
|
||
|
DataContractSerializerServiceBehavior.ApplySerializationSettings(description, ignoreExtensionDataObject, maxItemsInObjectGraph);
|
||
|
ApplyInstancing(description, serviceHostBase);
|
||
|
}
|
||
|
|
||
|
void ApplyInstancing(ServiceDescription description, ServiceHostBase serviceHostBase)
|
||
|
{
|
||
|
Type serviceType = description.ServiceType;
|
||
|
InstanceContext singleton = null;
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
DispatchRuntime dispatch = endpointDispatcher.DispatchRuntime;
|
||
|
if (dispatch.InstanceProvider == null)
|
||
|
{
|
||
|
if (instanceProvider == null)
|
||
|
{
|
||
|
if (serviceType == null && this.wellKnownSingleton == null)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.InstanceSettingsMustHaveTypeOrWellKnownObject0)));
|
||
|
|
||
|
if (this.instanceMode != InstanceContextMode.Single && this.wellKnownSingleton != null)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxWellKnownNonSingleton0)));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dispatch.InstanceProvider = instanceProvider;
|
||
|
}
|
||
|
}
|
||
|
dispatch.Type = serviceType;
|
||
|
dispatch.InstanceContextProvider = InstanceContextProviderBase.GetProviderForMode(this.instanceMode, dispatch);
|
||
|
|
||
|
if ((this.instanceMode == InstanceContextMode.Single) &&
|
||
|
(dispatch.SingletonInstanceContext == null))
|
||
|
{
|
||
|
if (singleton == null)
|
||
|
{
|
||
|
if (this.wellKnownSingleton != null)
|
||
|
{
|
||
|
singleton = new InstanceContext(serviceHostBase, this.wellKnownSingleton, true, false);
|
||
|
}
|
||
|
else if (this.hiddenSingleton != null)
|
||
|
{
|
||
|
singleton = new InstanceContext(serviceHostBase, this.hiddenSingleton, false, false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
singleton = new InstanceContext(serviceHostBase, false);
|
||
|
}
|
||
|
|
||
|
singleton.AutoClose = false;
|
||
|
}
|
||
|
dispatch.SingletonInstanceContext = singleton;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|