using Umbraco.Cms.Core.Cache;
namespace Umbraco.Cms.Core;
///
/// Provides a base class for hybrid accessors.
///
/// The type of the accessed object.
///
///
/// Hybrid accessors store the accessed object in HttpContext if they can,
/// otherwise they rely on the logical call context, to maintain an ambient
/// object that flows with async.
///
///
public abstract class HybridAccessorBase
where T : class
{
private static readonly AsyncLocal AmbientContext = new();
private readonly IRequestCache _requestCache;
private string? _itemKey;
protected HybridAccessorBase(IRequestCache requestCache)
=> _requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
protected string ItemKey => _itemKey ??= GetType().FullName!;
protected T? Value
{
get
{
if (!_requestCache.IsAvailable)
{
return NonContextValue;
}
return (T?)_requestCache.Get(ItemKey);
}
set
{
if (!_requestCache.IsAvailable)
{
NonContextValue = value;
}
else if (value == null)
{
_requestCache.Remove(ItemKey);
}
else
{
_requestCache.Set(ItemKey, 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+)
// no!
// [ThreadStatic]
// private static T _value;
// yes! flows with async!
private T? NonContextValue
{
get => AmbientContext.Value ?? default;
set => AmbientContext.Value = value;
}
}