From 2825ec6693e2c47741cebaefd2cdb1ecfac31746 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Mon, 7 Jun 2021 21:10:50 +1000 Subject: [PATCH] Removes CallContext (#10398) --- src/Umbraco.Core/HybridAccessorBase.cs | 8 +++-- src/Umbraco.Core/Scoping/CallContext.cs | 48 ------------------------- 2 files changed, 5 insertions(+), 51 deletions(-) delete mode 100644 src/Umbraco.Core/Scoping/CallContext.cs diff --git a/src/Umbraco.Core/HybridAccessorBase.cs b/src/Umbraco.Core/HybridAccessorBase.cs index 530d1d5689..1bb7635dcc 100644 --- a/src/Umbraco.Core/HybridAccessorBase.cs +++ b/src/Umbraco.Core/HybridAccessorBase.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Scoping; @@ -16,9 +17,10 @@ namespace Umbraco.Cms.Core public abstract class HybridAccessorBase where T : class { + private static readonly AsyncLocal s_ambientContext = new AsyncLocal(); + private readonly IRequestCache _requestCache; private string _itemKey; - protected string ItemKey => _itemKey ??= GetType().FullName; // read @@ -38,8 +40,8 @@ namespace Umbraco.Cms.Core // yes! flows with async! private T NonContextValue { - get => CallContext.GetData(ItemKey); - set => CallContext.SetData(ItemKey, value); + get => s_ambientContext.Value ?? default; + set => s_ambientContext.Value = value; } protected HybridAccessorBase(IRequestCache requestCache) diff --git a/src/Umbraco.Core/Scoping/CallContext.cs b/src/Umbraco.Core/Scoping/CallContext.cs deleted file mode 100644 index b77414ddd6..0000000000 --- a/src/Umbraco.Core/Scoping/CallContext.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading; - -namespace Umbraco.Cms.Core.Scoping -{ - /// - /// Represents ambient data that is local to a given asynchronous control flow, such as an asynchronous method. - /// - /// - /// This is just a simple wrapper around - /// - public static class CallContext - { - // TODO: Kill this. Wherever we need AsyncLocal, we should just use it there. - - private static readonly ConcurrentDictionary> s_state = new ConcurrentDictionary>(); - - /// - /// Stores a given object and associates it with the specified name. - /// - /// The name with which to associate the new item in the call context. - /// The object to store in the call context. - public static void SetData(string name, T data) => s_state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; - - //Replace the SetData with the following when you need to debug AsyncLocal. The args.ThreadContextChanged can be usefull - //public static void SetData(string name, T data) => _state.GetOrAdd(name, _ => new AsyncLocal(OnValueChanged)).Value = data; - // public static void OnValueChanged(AsyncLocalValueChangedArgs args) - // { - // var typeName = typeof(T).ToString(); - // Console.WriteLine($"OnValueChanged!, Type: {typeName} Prev: #{args.PreviousValue} Current: #{args.CurrentValue}"); - // } - - /// - /// Retrieves an object with the specified name from the . - /// - /// The type of the data being retrieved. Must match the type used when the was set via . - /// The name of the item in the call context. - /// The object in the call context associated with the specified name, or a default value for if none is found. - public static T GetData(string name) => s_state.TryGetValue(name, out AsyncLocal data) ? data.Value : default; - - // NOTE: If you have used the old CallContext in the past you might be thinking you need to clean this up but that is not the case. - // With CallContext you had to call FreeNamedDataSlot to prevent leaks but with AsyncLocal this is not the case, there is no way to clean this up. - // The above dictionary is sort of a trick because sure, there is always going to be a string key that will exist in the collection but the values - // themselves are managed per ExecutionContext so they don't build up. - // There's an SO article relating to this here https://stackoverflow.com/questions/36511243/safety-of-asynclocal-in-asp-net-core - - } -}