1 //----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------------------
5 namespace System.ServiceModel.Activation
7 using System.Collections.Generic;
8 using System.Configuration;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Globalization;
13 using System.Runtime.CompilerServices;
14 using System.Security;
15 using System.Security.Authentication.ExtendedProtection;
16 using System.Security.Permissions;
17 using System.ServiceModel;
18 using System.ServiceModel.Channels;
19 using System.ServiceModel.Description;
20 using System.Threading;
21 using System.Transactions;
23 using System.Web.Compilation;
24 using System.Web.Configuration;
26 class HostedAspNetEnvironment : AspNetEnvironment
28 // On IIS 8.0 (or later) the "WEBSOCKET_VERSION" server property indicates the WebSocket protocol version supported by the server.
29 // The IIS WebSocket module sets this property when initialized.
30 private const string WebSocketVersionServerProperty = "WEBSOCKET_VERSION";
32 // Indicates if we determined the WebSocket version. If false, we'll need to check the "WEBSOCKET_VERSION" server property.
33 private static bool isWebSocketVersionSet = false;
35 // Provides the version of the WebSocket protocol supported by IIS.
36 private static string webSocketVersion;
38 // used to cache SiteName|ApplicationVirtualPath
39 static string cachedServiceReference;
41 // used to cache if windows auth is being used
42 Nullable<bool> isWindowsAuthentication;
44 HostedAspNetEnvironment()
49 public override bool AspNetCompatibilityEnabled
53 return ServiceHostingEnvironment.AspNetCompatibilityEnabled;
57 public override string ConfigurationPath
61 if (ServiceHostingEnvironment.CurrentVirtualPath != null)
63 return ServiceHostingEnvironment.CurrentVirtualPath + "web.config";
67 return base.ConfigurationPath;
72 public override bool IsConfigurationBased
76 return ServiceHostingEnvironment.IsConfigurationBased;
80 public override string CurrentVirtualPath
84 return ServiceHostingEnvironment.CurrentVirtualPath;
88 public override string XamlFileBaseLocation
92 return ServiceHostingEnvironment.XamlFileBaseLocation;
96 public override bool UsingIntegratedPipeline
100 return HttpRuntime.UsingIntegratedPipeline;
104 // Provides the version of the WebSocket protocol supported by IIS.
105 // Returns null if WebSockets are not supported (because the IIS WebSocketModule is not installed or enabled).
106 public override string WebSocketVersion
110 return isWebSocketVersionSet ? webSocketVersion : null;
114 public static void Enable()
116 AspNetEnvironment hostedEnvironment = new HostedAspNetEnvironment();
117 AspNetEnvironment.Current = hostedEnvironment;
121 /// Tries to set the 'WebSocketVersion' property. The first call of this method sets the property (based on the "WEBSOCKET_VERSION" server property).
122 /// Subsequent calls do nothing.
124 /// <param name="application">The HttpApplication used to determine the WebSocket version.</param>
126 /// Take caution when calling this method. The method initializes the 'WebSocketVersion' property based on the "WEBSOCKET_VERSION" server variable.
127 /// This variable gets set by the WebSocketModule when it's loaded by IIS. If you call this method too early (before IIS got a chance to load the module list),
128 /// this method might incorrectly set 'WebSocketVersion' to 'null'.
130 public static void TrySetWebSocketVersion(HttpApplication application)
132 if (!isWebSocketVersionSet)
134 webSocketVersion = application.Request.ServerVariables[WebSocketVersionServerProperty];
135 isWebSocketVersionSet = true;
139 public override void AddHostingBehavior(ServiceHostBase serviceHost, ServiceDescription description)
141 VirtualPathExtension virtualPathExtension = serviceHost.Extensions.Find<VirtualPathExtension>();
142 if (virtualPathExtension != null)
144 description.Behaviors.Add(new HostedBindingBehavior(virtualPathExtension));
147 foreach (ServiceEndpoint endpoint in description.Endpoints)
149 if (ServiceMetadataBehavior.IsMetadataEndpoint(description, endpoint))
151 endpoint.Behaviors.Add(new HostedMetadataExchangeEndpointBehavior());
156 public override bool IsWebConfigAboveApplication(object configHostingContext)
158 AspNetPartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
160 WebContext context = configHostingContext as WebContext;
163 return context.ApplicationLevel == WebApplicationLevel.AboveApplication;
166 return false; // if we don't recognize the context we can't enforce the special web.config logic
169 public override void EnsureCompatibilityRequirements(ServiceDescription description)
171 AspNetCompatibilityRequirementsAttribute aspNetCompatibilityRequirements = description.Behaviors.Find<AspNetCompatibilityRequirementsAttribute>();
172 if (aspNetCompatibilityRequirements == null)
174 aspNetCompatibilityRequirements = new AspNetCompatibilityRequirementsAttribute();
175 description.Behaviors.Add(aspNetCompatibilityRequirements);
179 public override bool TryGetFullVirtualPath(out string virtualPath)
181 // subclass will use the virtual path from the compiled string
182 virtualPath = ServiceHostingEnvironment.FullVirtualPath;
186 public override string GetAnnotationFromHost(ServiceHostBase host)
188 //Format Website name\Application Virtual Path|\relative service virtual path|serviceName
189 if (host != null && host.Extensions != null)
191 string serviceName = (host.Description != null) ? host.Description.Name : string.Empty;
192 string application = ServiceHostingEnvironment.ApplicationVirtualPath;
193 string servicePath = string.Empty;
194 VirtualPathExtension extension = host.Extensions.Find<VirtualPathExtension>();
195 if (extension != null && extension.VirtualPath != null)
197 servicePath = extension.VirtualPath.Replace("~", application + "|");
198 return string.Format(CultureInfo.InvariantCulture, "{0}{1}|{2}", ServiceHostingEnvironment.SiteName, servicePath, serviceName);
201 if (string.IsNullOrEmpty(HostedAspNetEnvironment.cachedServiceReference))
203 HostedAspNetEnvironment.cachedServiceReference = string.Format(CultureInfo.InvariantCulture, "{0}{1}", ServiceHostingEnvironment.SiteName, ServiceHostingEnvironment.ApplicationVirtualPath);
205 return HostedAspNetEnvironment.cachedServiceReference;
208 [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - can be called outside of user context.")]
209 public override void EnsureAllReferencedAssemblyLoaded()
211 AspNetPartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
212 BuildManager.GetReferencedAssemblies();
215 public override BaseUriWithWildcard GetBaseUri(string transportScheme, Uri listenUri)
217 BaseUriWithWildcard baseAddress = null;
218 HostedTransportConfigurationBase hostedConfiguration =
219 HostedTransportConfigurationManager.GetConfiguration(transportScheme) as HostedTransportConfigurationBase;
220 if (hostedConfiguration != null)
222 baseAddress = hostedConfiguration.FindBaseAddress(listenUri);
223 if (baseAddress == null)
225 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.Hosting_TransportBindingNotFound(listenUri.ToString())));
231 public override void ValidateHttpSettings(string virtualPath, bool isMetadataListener, bool usingDefaultSpnList, ref AuthenticationSchemes bindingElementAuthenticationSchemes, ref ExtendedProtectionPolicy extendedProtectionPolicy, ref string realm)
233 // Verify the authentication settings
234 AuthenticationSchemes hostSupportedSchemes = HostedTransportConfigurationManager.MetabaseSettings.GetAuthenticationSchemes(virtualPath);
236 if ((bindingElementAuthenticationSchemes & hostSupportedSchemes) == 0)
238 if (bindingElementAuthenticationSchemes == AuthenticationSchemes.Negotiate ||
239 bindingElementAuthenticationSchemes == AuthenticationSchemes.Ntlm ||
240 bindingElementAuthenticationSchemes == AuthenticationSchemes.IntegratedWindowsAuthentication)
242 throw FxTrace.Exception.AsError(new NotSupportedException(SR.Hosting_AuthSchemesRequireWindowsAuth));
246 throw FxTrace.Exception.AsError(new NotSupportedException(SR.Hosting_AuthSchemesRequireOtherAuth(bindingElementAuthenticationSchemes.ToString())));
250 //only use AuthenticationSchemes, which are supported both in IIS and the WCF binding
251 bindingElementAuthenticationSchemes &= hostSupportedSchemes;
253 if (bindingElementAuthenticationSchemes != AuthenticationSchemes.Anonymous)
255 //Compare the ExtendedProtectionPolicy setttings to IIS
256 ExtendedProtectionPolicy iisPolicy = HostedTransportConfigurationManager.MetabaseSettings.GetExtendedProtectionPolicy(virtualPath);
258 if (iisPolicy == null) //OS doesn't support CBT
260 if (extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always)
262 throw FxTrace.Exception.AsError(new NotSupportedException(SR.ExtendedProtectionNotSupported));
267 if (isMetadataListener && ChannelBindingUtility.IsDefaultPolicy(extendedProtectionPolicy))
269 //push the IIS policy onto the metadataListener if and only if the default policy is
270 //in force. policy for non metadata listeners will still have to match IIS policy.
271 extendedProtectionPolicy = iisPolicy;
275 if (!ChannelBindingUtility.AreEqual(iisPolicy, extendedProtectionPolicy))
277 string mismatchErrorMessage;
278 if (iisPolicy.PolicyEnforcement != extendedProtectionPolicy.PolicyEnforcement)
280 mismatchErrorMessage = SR.ExtendedProtectionPolicyEnforcementMismatch(iisPolicy.PolicyEnforcement, extendedProtectionPolicy.PolicyEnforcement);
282 else if (iisPolicy.ProtectionScenario != extendedProtectionPolicy.ProtectionScenario)
284 mismatchErrorMessage = SR.ExtendedProtectionPolicyScenarioMismatch(iisPolicy.ProtectionScenario, extendedProtectionPolicy.ProtectionScenario);
288 Fx.Assert(iisPolicy.CustomChannelBinding != extendedProtectionPolicy.CustomChannelBinding, "new case in ChannelBindingUtility.AreEqual to account for");
289 mismatchErrorMessage = SR.ExtendedProtectionPolicyCustomChannelBindingMismatch;
292 if (mismatchErrorMessage != null)
294 throw FxTrace.Exception.AsError(new NotSupportedException(SR.Hosting_ExtendedProtectionPoliciesMustMatch(mismatchErrorMessage)));
298 //when using the default SPN list we auto generate, we should make sure that the IIS policy is also the default...
299 ServiceNameCollection listenerSpnList = usingDefaultSpnList ? null : extendedProtectionPolicy.CustomServiceNames;
300 if (!ChannelBindingUtility.IsSubset(iisPolicy.CustomServiceNames, listenerSpnList))
302 throw FxTrace.Exception.AsError(new NotSupportedException(SR.Hosting_ExtendedProtectionPoliciesMustMatch(SR.Hosting_ExtendedProtectionSPNListNotSubset)));
310 // Do not set realm for Cassini.
311 if (!ServiceHostingEnvironment.IsSimpleApplicationHost)
314 realm = HostedTransportConfigurationManager.MetabaseSettings.GetRealm(virtualPath);
318 public override bool ValidateHttpsSettings(string virtualPath, ref bool requireClientCertificate)
320 // Do not validate settings for Cassini. Actually current implementation of Cassini does not support HTTPS.
321 if (ServiceHostingEnvironment.IsSimpleApplicationHost)
326 // Validate Ssl Settings
327 HttpAccessSslFlags sslFlags = HostedTransportConfigurationManager.MetabaseSettings.GetAccessSslFlags(virtualPath);
328 HttpAccessSslFlags channelListenerSslFlags = HttpAccessSslFlags.None;
330 // Validating SSL flags. SslRequireCert means "require client certificate" in IIS terminology.
331 if ((sslFlags & HttpAccessSslFlags.SslRequireCert) != 0)
334 // We apply IIS settings to the ChannelListener to fix the endpoint
335 requireClientCertificate = true;
337 else if (requireClientCertificate &&
338 // Validating SSL flags. SslNegotiateCert means "accept client certificate" in IIS terminology.
339 // We want to allow SslNegotiateCert in IIS to support hosting one endpoint requiring client
340 // certificates and another endpoint not using client certificates in the same VirtualDirectory.
341 // HttpsChannelListener.ValidateAuthentication ensures that authentication is denied for services
342 // requiring client certificates when the client does not present one.
343 (sslFlags & HttpAccessSslFlags.SslNegotiateCert) == 0)
345 // IIS ignores client cert but the binding requires it.
346 channelListenerSslFlags |= HttpAccessSslFlags.SslRequireCert;
348 throw FxTrace.Exception.AsError(new NotSupportedException(SR.Hosting_SslSettingsMisconfigured(
349 channelListenerSslFlags.ToString(), sslFlags.ToString())));
352 return (sslFlags & HttpAccessSslFlags.SslMapCert) != 0;
355 public override void ProcessNotMatchedEndpointAddress(Uri uri, string endpointName)
357 if (!object.ReferenceEquals(uri.Scheme, Uri.UriSchemeHttp) &&
358 !object.ReferenceEquals(uri.Scheme, Uri.UriSchemeHttps))
360 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.Hosting_NonHTTPInCompatibilityMode(endpointName)));
364 public override void ValidateCompatibilityRequirements(AspNetCompatibilityRequirementsMode compatibilityMode)
366 if (compatibilityMode == AspNetCompatibilityRequirementsMode.Allowed)
370 else if (ServiceHostingEnvironment.AspNetCompatibilityEnabled &&
371 compatibilityMode == AspNetCompatibilityRequirementsMode.NotAllowed)
373 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.Hosting_ServiceCompatibilityNotAllowed));
375 else if (!ServiceHostingEnvironment.AspNetCompatibilityEnabled &&
376 compatibilityMode == AspNetCompatibilityRequirementsMode.Required)
378 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.Hosting_ServiceCompatibilityRequire));
382 public override IAspNetMessageProperty GetHostingProperty(Message message)
384 return GetHostingProperty(message, false);
387 public override IAspNetMessageProperty GetHostingProperty(Message message, bool removeFromMessage)
389 IAspNetMessageProperty result = null;
392 if (message.Properties.TryGetValue(HostingMessageProperty.Name, out property))
394 result = (HostingMessageProperty)property;
395 if (removeFromMessage)
397 message.Properties.Remove(HostingMessageProperty.Name);
404 public override void PrepareMessageForDispatch(Message message)
406 ReceiveContext context = null;
407 if (ReceiveContext.TryGet(message, out context) && !(context is ReceiveContextBusyCountWrapper))
409 ReceiveContextBusyCountWrapper wrapper = new ReceiveContextBusyCountWrapper(context);
410 message.Properties.Remove(ReceiveContext.Name);
411 message.Properties.Add(ReceiveContext.Name, wrapper);
415 public override void ApplyHostedContext(TransportChannelListener listener, BindingContext context)
417 VirtualPathExtension virtualPathExtension = context.BindingParameters.Find<VirtualPathExtension>();
419 if (virtualPathExtension != null)
421 HostedMetadataBindingParameter metadataBindingParameter = context.BindingParameters.Find<HostedMetadataBindingParameter>();
422 listener.ApplyHostedContext(virtualPathExtension.VirtualPath, metadataBindingParameter != null);
426 internal override void AddMetadataBindingParameters(Uri listenUri, KeyedByTypeCollection<IServiceBehavior> serviceBehaviors, BindingParameterCollection bindingParameters)
428 if (serviceBehaviors.Find<HostedBindingBehavior>() != null)
430 bindingParameters.Add(new HostedMetadataBindingParameter());
433 VirtualPathExtension virtualPathExtension = bindingParameters.Find<VirtualPathExtension>();
435 if (virtualPathExtension != null)
437 AuthenticationSchemes hostSupportedAuthenticationSchemes = AspNetEnvironment.Current.GetAuthenticationSchemes(listenUri);
439 if (hostSupportedAuthenticationSchemes != AuthenticationSchemes.None)
441 if (bindingParameters.Find<AuthenticationSchemesBindingParameter>() == null)
443 bindingParameters.Add(new AuthenticationSchemesBindingParameter(hostSupportedAuthenticationSchemes));
448 base.AddMetadataBindingParameters(listenUri, serviceBehaviors, bindingParameters);
451 internal override bool IsMetadataListener(BindingParameterCollection bindingParameters)
453 return base.IsMetadataListener(bindingParameters) || bindingParameters.Find<HostedMetadataBindingParameter>() != null;
456 public override void IncrementBusyCount()
458 HostingEnvironmentWrapper.IncrementBusyCount();
461 public override void DecrementBusyCount()
463 HostingEnvironmentWrapper.DecrementBusyCount();
466 public override bool TraceIncrementBusyCountIsEnabled()
468 return TD.IncrementBusyCountIsEnabled();
471 public override bool TraceDecrementBusyCountIsEnabled()
473 return TD.DecrementBusyCountIsEnabled();
475 public override void TraceIncrementBusyCount(string data)
479 data = SR.DefaultBusyCountSource;
481 TD.IncrementBusyCount(data);
484 public override void TraceDecrementBusyCount(string data)
488 data = SR.DefaultBusyCountSource;
490 TD.DecrementBusyCount(data);
493 public override object GetConfigurationSection(string sectionPath)
495 return GetSectionFromWebConfigurationManager(sectionPath, ServiceHostingEnvironment.FullVirtualPath);
498 [Fx.Tag.SecurityNote(Critical = "Uses SecurityCritical method UnsafeGetSectionFromWebConfigurationManager which elevates.")]
500 public override object UnsafeGetConfigurationSection(string sectionPath)
502 return UnsafeGetSectionFromWebConfigurationManager(sectionPath, ServiceHostingEnvironment.FullVirtualPath);
505 public override bool IsSimpleApplicationHost
509 return ServiceHostingEnvironment.IsSimpleApplicationHost;
513 public override AuthenticationSchemes GetAuthenticationSchemes(Uri baseAddress)
515 AspNetPartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
517 string fileName = VirtualPathUtility.GetFileName(baseAddress.AbsolutePath);
518 string virtualPath = ServiceHostingEnvironment.CurrentVirtualPath;
520 if (virtualPath != null && virtualPath.EndsWith("/", StringComparison.Ordinal))
522 completePath = virtualPath + fileName;
526 completePath = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", virtualPath, fileName);
528 AuthenticationSchemes supportedSchemes = HostedTransportConfigurationManager.MetabaseSettings.GetAuthenticationSchemes(completePath);
530 return supportedSchemes;
533 [Fx.Tag.SecurityNote(Critical = "Handles config objects, which should not be leaked.",
534 Safe = "Doesn't leak config objects out of SecurityCritical code.")]
535 [SecuritySafeCritical]
536 [MethodImpl(MethodImplOptions.NoInlining)]
537 public override bool IsWindowsAuthenticationConfigured()
539 if (!this.isWindowsAuthentication.HasValue)
541 AspNetPartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
543 AuthenticationSection authSection = (AuthenticationSection)UnsafeGetConfigurationSection("system.web/authentication");
544 if (authSection != null)
546 this.isWindowsAuthentication = (authSection.Mode == AuthenticationMode.Windows);
550 this.isWindowsAuthentication = false;
554 return this.isWindowsAuthentication.Value;
557 /// Be sure to update UnsafeGetSectionFromWebConfigurationManager if you modify this method
558 [MethodImpl(MethodImplOptions.NoInlining)]
559 static object GetSectionFromWebConfigurationManager(string sectionPath, string virtualPath)
561 AspNetPartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
563 if (virtualPath != null)
565 return WebConfigurationManager.GetSection(sectionPath, virtualPath);
569 return WebConfigurationManager.GetSection(sectionPath);
573 // Be sure to update GetSectionFromWebConfigurationManager if you modify this method
574 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.SecureAsserts, Justification = "This is from an internal helper class and users have no way to pass arbitrary information to this code.")]
575 [Fx.Tag.SecurityNote(Critical = "Asserts ConfigurationPermission in order to fetch config from WebConfigurationManager,"
576 + "caller must guard return value.")]
578 [ConfigurationPermission(SecurityAction.Assert, Unrestricted = true)]
579 [MethodImpl(MethodImplOptions.NoInlining)]
580 internal static object UnsafeGetSectionFromWebConfigurationManager(string sectionPath, string virtualPath)
582 AspNetPartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
584 if (virtualPath != null)
586 return WebConfigurationManager.GetSection(sectionPath, virtualPath);
590 return WebConfigurationManager.GetSection(sectionPath);
594 public override bool IsWithinApp(string absoluteVirtualPath)
596 return HostedTransportConfigurationManager.MetabaseSettings.IsWithinApp(absoluteVirtualPath);
599 // This class is intended to be empty.
600 class HostedMetadataBindingParameter
604 class HostedMetadataExchangeEndpointBehavior : IEndpointBehavior
606 void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
608 bindingParameters.Add(new HostedMetadataBindingParameter());
611 void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, Dispatcher.ClientRuntime clientRuntime)
615 void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, Dispatcher.EndpointDispatcher endpointDispatcher)
619 void IEndpointBehavior.Validate(ServiceEndpoint endpoint)
624 class ReceiveContextBusyCountWrapper : ReceiveContext
626 ReceiveContext wrappedContext;
627 //possible values are 0 and 1.
628 //using an integer to allow usage with Interlocked methods
629 //synchronized access needed as there could be ---- between calls
630 //to EndComplete and Tx notification.
632 //possible values are 0 and 1
633 //using an integer to allow usage with Interlocked methods
634 //synchronized access needed as there could be ---- between calls
635 //to EndComplete and Tx Status notification.
636 int ambientTransactionCount;
638 internal ReceiveContextBusyCountWrapper(ReceiveContext context)
640 this.wrappedContext = context;
641 this.wrappedContext.Faulted += new EventHandler(OnWrappedContextFaulted);
642 AspNetEnvironment.Current.IncrementBusyCount();
643 if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled())
645 AspNetEnvironment.Current.TraceIncrementBusyCount(this.GetType().FullName);
647 Interlocked.Increment(ref busyCount);
650 protected override void OnAbandon(TimeSpan timeout)
652 this.wrappedContext.Abandon(timeout);
653 DecrementBusyCount();
656 protected override IAsyncResult OnBeginAbandon(TimeSpan timeout, AsyncCallback callback, object state)
658 return this.wrappedContext.BeginAbandon(timeout, callback, state);
661 protected override IAsyncResult OnBeginComplete(TimeSpan timeout, AsyncCallback callback, object state)
663 RegisterForTransactionNotification(Transaction.Current);
664 return this.wrappedContext.BeginComplete(timeout, callback, state);
667 protected override void OnComplete(TimeSpan timeout)
669 RegisterForTransactionNotification(Transaction.Current);
670 this.wrappedContext.Complete(timeout);
671 DecrementOnNoAmbientTransaction();
675 protected override void OnEndAbandon(IAsyncResult result)
677 this.wrappedContext.EndAbandon(result);
678 DecrementBusyCount();
681 protected override void OnEndComplete(IAsyncResult result)
683 this.wrappedContext.EndComplete(result);
684 DecrementOnNoAmbientTransaction();
687 protected override void OnFaulted()
691 this.wrappedContext.Fault();
699 void OnWrappedContextFaulted(object sender, EventArgs e)
707 DecrementBusyCount();
711 void RegisterForTransactionNotification(Transaction transaction)
713 if (Transaction.Current != null)
715 ReceiveContextEnlistmentNotification notification = new ReceiveContextEnlistmentNotification(this);
716 transaction.EnlistVolatile(notification, EnlistmentOptions.None);
717 Interlocked.Increment(ref this.ambientTransactionCount);
721 void DecrementOnNoAmbientTransaction()
723 if (Interlocked.Exchange(ref this.ambientTransactionCount, 0) != 1)
725 DecrementBusyCount();
730 void DecrementBusyCount()
732 if (Interlocked.Exchange(ref this.busyCount, 0) == 1)
734 AspNetEnvironment.Current.DecrementBusyCount();
735 if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled())
737 AspNetEnvironment.Current.TraceDecrementBusyCount(this.GetType().FullName);
742 class ReceiveContextEnlistmentNotification : IEnlistmentNotification
744 ReceiveContextBusyCountWrapper context;
746 internal ReceiveContextEnlistmentNotification(ReceiveContextBusyCountWrapper context)
748 this.context = context;
751 public void Commit(Enlistment enlistment)
753 this.context.DecrementBusyCount();
757 public void InDoubt(Enlistment enlistment)
759 this.context.DecrementBusyCount();
763 public void Prepare(PreparingEnlistment preparingEnlistment)
765 preparingEnlistment.Prepared();
768 public void Rollback(Enlistment enlistment)