// Disassociates this ThreadContext from the current thread. Any ambient values (e.g., culture) // associated with the current request are stored in the HttpContext object so that they // can be restored the next time a ThreadContext associated with this HttpContext is active. // Impersonation and other similar modifications to the current thread are undone. internal void DisassociateFromCurrentThread() { Debug.Trace("OnThread", GetTraceMessage("Leave1")); Debug.Assert(Current == this, "This ThreadContext isn't associated with current thread."); Debug.Assert(!HasBeenDisassociatedFromThread, "This ThreadContext has already been disassociated from a thread."); /* * !! IMPORTANT !! * Keep this logic in [....] with AssociateWithCurrentThread and EnterExecutionContext. */ Current = _originalThreadContextCurrent; HasBeenDisassociatedFromThread = true; // remove thread if set if (_setCurrentThreadOnHttpContext) { HttpContext.CurrentThread = null; } // this thread should not be locking app state HttpApplicationFactory.ApplicationState.EnsureUnLock(); // stop impersonation UndoImpersonationContext(); // restore culture RestoreRequestLevelCulture(); // restrore synchronization context AsyncOperationManager.SynchronizationContext = _originalSynchronizationContext; // restore thread principal HttpApplication.SetCurrentPrincipalWithAssert(_originalThreadPrincipal); // Remove SqlCacheDependency cookie from call context if necessary HttpContext.RemoveSqlDependencyCookie(); // remove http context from the call context DisposableHttpContextWrapper.SwitchContext(_originalHttpContext); _originalHttpContext = null; Debug.Trace("OnThread", GetTraceMessage("Leave2")); }
// Associates this ThreadContext with the current thread. This restores certain // ambient values associated with the current HttpContext, such as the current // user and cultures. It also sets HttpContext.Current. internal void AssociateWithCurrentThread(bool setImpersonationContext) { Debug.Assert(HttpContext != null); // only to be used when context is available Debug.Assert(Current != this, "This ThreadContext is already associated with this thread."); Debug.Assert(!HasBeenDisassociatedFromThread, "This ThreadContext has already been disassociated from a thread."); Debug.Trace("OnThread", GetTraceMessage("Enter1")); /* * !! IMPORTANT !! * Keep this logic in [....] with DisassociateFromCurrentThread and EnterExecutionContext. */ // attach http context to the call context _originalHttpContext = DisposableHttpContextWrapper.SwitchContext(HttpContext); // set impersonation on the current thread if (setImpersonationContext) { SetImpersonationContext(); } // set synchronization context for the current thread to support the async pattern _originalSynchronizationContext = AsyncOperationManager.SynchronizationContext; AspNetSynchronizationContextBase aspNetSynchronizationContext = HttpContext.SyncContext; AsyncOperationManager.SynchronizationContext = aspNetSynchronizationContext; // set ETW trace ID Guid g = HttpContext.WorkerRequest.RequestTraceIdentifier; if (g != Guid.Empty) { CallContext.LogicalSetData("E2ETrace.ActivityID", g); } // set SqlDependecyCookie HttpContext.ResetSqlDependencyCookie(); // set principal on the current thread _originalThreadPrincipal = Thread.CurrentPrincipal; HttpApplication.SetCurrentPrincipalWithAssert(HttpContext.User); // only set culture on the current thread if it is not initialized SetRequestLevelCulture(); // DevDivBugs 75042 // set current thread in context if there is not there // the timeout manager uses this to abort the correct thread if (HttpContext.CurrentThread == null) { _setCurrentThreadOnHttpContext = true; HttpContext.CurrentThread = Thread.CurrentThread; } // Store a reference to the original ThreadContext.Current. It is possible that a parent // ThreadContext might already be associated with the current thread, e.g. if the current // stack contains a call to MgdIndicateCompletion (via // PipelineRuntime.ProcessRequestNotificationHelper). If this is the case, the child // ThreadContext will temporarily take over. _originalThreadContextCurrent = Current; Current = this; Debug.Trace("OnThread", GetTraceMessage("Enter2")); }
// Called by AspNetHostExecutionContextManager to signal that ExecutionContext.Run // is being called on a thread currently associated with our ThreadContext. Since // ExecutionContext.Run destroys some of our ambient state (HttpContext.Current, etc.), // we need to restore it. This method returns an Action which should be called when // the call to ExecutionContext.Run is concluding. internal Action EnterExecutionContext() { Debug.Trace("OnThread", GetTraceMessage("EnterExecutionContext1")); Debug.Assert(Current == this, "This ThreadContext isn't associated with current thread."); Debug.Assert(!HasBeenDisassociatedFromThread, "This ThreadContext has already been disassociated from a thread."); /* * !! IMPORTANT !! * Keep this logic in [....] with AssociateWithCurrentThread and DisassociateFromCurrentThread. */ // ExecutionContext.Run replaces the current impersonation token, so we need to impersonate // if AssociateWithCurrentThread also did so. ClientImpersonationContext executionContextClientImpersonationContext = null; if (_newImpersonationContext != null) { executionContextClientImpersonationContext = CreateNewClientImpersonationContext(); } // ExecutionContext.Run resets the LogicalCallContext / IllogicalCallContext (which contains HttpContext.Current), // so we need to restore both of them. DisposableHttpContextWrapper.SwitchContext(HttpContext); Guid g = HttpContext.WorkerRequest.RequestTraceIdentifier; if (g != Guid.Empty) { CallContext.LogicalSetData("E2ETrace.ActivityID", g); } HttpContext.ResetSqlDependencyCookie(); // ExecutionContext.Run resets the thread's CurrentPrincipal, so we need to restore it. HttpApplication.SetCurrentPrincipalWithAssert(HttpContext.User); // Other items like [ThreadStatic] fields, culture, etc. are untouched by ExecutionContext.Run, // so we don't need to worry about them. Debug.Trace("OnThread", GetTraceMessage("EnterExecutionContext2")); // This delegate is the cleanup routine. return(() => { Debug.Trace("OnThread", GetTraceMessage("LeaveExecutionContext1")); // Undo any impersonation that we performed. if (executionContextClientImpersonationContext != null) { executionContextClientImpersonationContext.Undo(); } // Other things, e.g. changes to the logical/illogical call contexts, changes // to CurrentPrincipal, etc., will automatically be reverted anyway when // the call to ExecutionContext.Run concludes, so we don't need to clean up // here. Debug.Trace("OnThread", GetTraceMessage("LeaveExecutionContext2")); }); }