diff --git a/src/Umbraco.Web/HybridAccessorBase.cs b/src/Umbraco.Web/HybridAccessorBase.cs index 5bb5f8babb..829d8cfc29 100644 --- a/src/Umbraco.Web/HybridAccessorBase.cs +++ b/src/Umbraco.Web/HybridAccessorBase.cs @@ -1,16 +1,41 @@ -using System; +using System.Runtime.Remoting.Messaging; namespace Umbraco.Web { - // fixme - must ensure that the ThreadStatic value is properly cleared! internal abstract class HybridAccessorBase { private readonly IHttpContextAccessor _httpContextAccessor; - protected abstract string HttpContextItemKey { get; } + protected abstract string ItemKey { get; } - [ThreadStatic] - private static T _value; + // read + // http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html + // http://stackoverflow.com/questions/14176028/why-does-logicalcallcontext-not-work-with-async + // http://stackoverflow.com/questions/854976/will-values-in-my-threadstatic-variables-still-be-there-when-cycled-via-threadpo + // https://msdn.microsoft.com/en-us/library/dd642243.aspx?f=255&MSPPError=-2147217396 ThreadLocal + // http://stackoverflow.com/questions/29001266/cleaning-up-callcontext-in-tpl clearing call context + // + // anything that is ThreadStatic will stay with the thread and NOT flow in async threads + // the only thing that flows is the logical call context (safe in 4.5+) + // now... + // tests seem to show that either newing Thread or enqueuing in the ThreadPool both produce a thread + // with a clear logical call context, which would mean that it is somewhat safe to "fire and forget" + // because whatever is in the call context will be gone when the thread returns to the pool + + // no! + //[ThreadStatic] + //private static T _value; + + // yes! flows with async! + private T NonContextValue + { + get { return (T) CallContext.LogicalGetData(ItemKey); } + set + { + if (value == null) CallContext.FreeNamedDataSlot(ItemKey); + else CallContext.LogicalSetData(ItemKey, value); + } + } protected HybridAccessorBase(IHttpContextAccessor httpContextAccessor) { @@ -22,8 +47,8 @@ namespace Umbraco.Web get { var httpContext = _httpContextAccessor.HttpContext; - if (httpContext == null) return _value; - return (T) httpContext.Items[HttpContextItemKey]; + if (httpContext == null) return NonContextValue; + return (T) httpContext.Items[ItemKey]; } set @@ -31,10 +56,10 @@ namespace Umbraco.Web var httpContext = _httpContextAccessor.HttpContext; if (httpContext == null) { - _value = value; + NonContextValue = value; return; } - httpContext.Items[HttpContextItemKey] = value; + httpContext.Items[ItemKey] = value; } } } diff --git a/src/Umbraco.Web/HybridEventMessagesAccessor.cs b/src/Umbraco.Web/HybridEventMessagesAccessor.cs index cb2423203a..c66cd84d29 100644 --- a/src/Umbraco.Web/HybridEventMessagesAccessor.cs +++ b/src/Umbraco.Web/HybridEventMessagesAccessor.cs @@ -4,7 +4,7 @@ namespace Umbraco.Web { internal class HybridEventMessagesAccessor : HybridAccessorBase, IEventMessagesAccessor { - protected override string HttpContextItemKey => "Umbraco.Core.Events.EventMessages"; + protected override string ItemKey => "Umbraco.Core.Events.EventMessages"; public HybridEventMessagesAccessor(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor) diff --git a/src/Umbraco.Web/HybridUmbracoContextAccessor.cs b/src/Umbraco.Web/HybridUmbracoContextAccessor.cs index d67a0fb8d9..ea65fd2548 100644 --- a/src/Umbraco.Web/HybridUmbracoContextAccessor.cs +++ b/src/Umbraco.Web/HybridUmbracoContextAccessor.cs @@ -2,7 +2,7 @@ { internal class HybridUmbracoContextAccessor : HybridAccessorBase, IUmbracoContextAccessor { - protected override string HttpContextItemKey => "Umbraco.Web.UmbracoContext"; + protected override string ItemKey => "Umbraco.Web.UmbracoContext"; public HybridUmbracoContextAccessor(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor) diff --git a/src/Umbraco.Web/HybridUmbracoDatabaseAccessor.cs b/src/Umbraco.Web/HybridUmbracoDatabaseAccessor.cs index ff2860840e..cdfccebfa9 100644 --- a/src/Umbraco.Web/HybridUmbracoDatabaseAccessor.cs +++ b/src/Umbraco.Web/HybridUmbracoDatabaseAccessor.cs @@ -4,7 +4,7 @@ namespace Umbraco.Web { internal class HybridUmbracoDatabaseAccessor : HybridAccessorBase, IUmbracoDatabaseAccessor { - protected override string HttpContextItemKey => "Umbraco.Core.Persistence.UmbracoDatabase"; + protected override string ItemKey => "Umbraco.Core.Persistence.UmbracoDatabase"; public HybridUmbracoDatabaseAccessor(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)