diff --git a/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs b/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs index df79c757eea..200e8c5caef 100644 --- a/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs +++ b/src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs @@ -19,11 +19,11 @@ public sealed class ShellScope : IServiceScope, IAsyncDisposable private static readonly AsyncLocal _current = new(); private readonly AsyncServiceScope _serviceScope; - private readonly Dictionary _items = []; - private readonly List> _beforeDispose = []; - private readonly HashSet _deferredSignals = []; - private readonly List> _deferredTasks = []; - private readonly List> _exceptionHandlers = []; + private Dictionary _items; + private List> _beforeDispose; + private HashSet _deferredSignals; + private List> _deferredTasks; + private List> _exceptionHandlers; private bool _serviceScopeOnly; private bool _shellTerminated; @@ -84,35 +84,68 @@ public ShellScope(ShellContext shellContext) /// public static void Set(object key, object value) { - if (Current is not null) + var current = Current; + + if (current != null) { - Current._items[key] = value; + current._items ??= []; + + current._items[key] = value; } } /// /// Gets a shared item from the current shell scope. /// - public static object Get(object key) => Current is null ? null : Current._items.TryGetValue(key, out var value) ? value : null; + public static object Get(object key) + { + var current = Current; + + if (current == null) + { + return null; + } + + current._items ??= []; + + return current._items.TryGetValue(key, out var value) ? value : null; + } /// /// Gets a shared item of a given type from the current shell scope. /// - public static T Get(object key) => Current is null ? default : Current._items.TryGetValue(key, out var value) ? value is T item ? item : default : default; + public static T Get(object key) + { + var current = Current; + + if (current == null) + { + return default; + } + + current._items ??= []; + + return current._items.TryGetValue(key, out var value) ? value is T item ? item : default : default; + } /// /// Gets (or creates) a shared item of a given type from the current shell scope. /// public static T GetOrCreate(object key, Func factory) { - if (Current is null) + var current = Current; + + if (current == null) { return factory(); } - if (!Current._items.TryGetValue(key, out var value) || value is not T item) + current._items ??= []; + + + if (!current._items.TryGetValue(key, out var value) || value is not T item) { - Current._items[key] = item = factory(); + current._items[key] = item = factory(); } return item; @@ -123,14 +156,19 @@ public static T GetOrCreate(object key, Func factory) /// public static T GetOrCreate(object key) where T : class, new() { - if (Current is null) + var current = Current; + + if (current == null) { return new T(); } - if (!Current._items.TryGetValue(key, out var value) || value is not T item) + current._items ??= []; + + + if (!current._items.TryGetValue(key, out var value) || value is not T item) { - Current._items[key] = item = new T(); + current._items[key] = item = new T(); } return item; @@ -338,22 +376,22 @@ internal async Task ActivateShellInternalAsync() /// /// Registers a delegate to be invoked when 'BeforeDisposeAsync()' is called on this scope. /// - internal void BeforeDispose(Func callback) => _beforeDispose.Insert(0, callback); + internal void BeforeDispose(Func callback) => (_beforeDispose ??= []).Insert(0, callback); /// /// Adds a Signal (if not already present) to be sent just after 'BeforeDisposeAsync()'. /// - internal void DeferredSignal(string key) => _deferredSignals.Add(key); + internal void DeferredSignal(string key) => (_deferredSignals ??= []).Add(key); /// /// Adds a Task to be executed in a new scope after 'BeforeDisposeAsync()'. /// - internal void DeferredTask(Func task) => _deferredTasks.Add(task); + internal void DeferredTask(Func task) => (_deferredTasks ??= []).Add(task); /// /// Adds an handler to be invoked if an exception is thrown while executing in this shell scope. /// - internal void ExceptionHandler(Func callback) => _exceptionHandlers.Add(callback); + internal void ExceptionHandler(Func callback) => (_exceptionHandlers ??= []).Add(callback); /// /// Registers a delegate to be invoked before the current shell scope will be disposed. @@ -380,6 +418,11 @@ internal async Task ActivateShellInternalAsync() /// public async Task HandleExceptionAsync(Exception e) { + if (_exceptionHandlers == null) + { + return; + } + foreach (var callback in _exceptionHandlers) { await callback(this, e); @@ -392,9 +435,12 @@ public async Task HandleExceptionAsync(Exception e) /// internal async Task BeforeDisposeAsync() { - foreach (var callback in _beforeDispose) + if (_beforeDispose != null) { - await callback(this); + foreach (var callback in _beforeDispose) + { + await callback(this); + } } if (_serviceScopeOnly) @@ -402,7 +448,7 @@ internal async Task BeforeDisposeAsync() return; } - if (_deferredSignals.Count > 0) + if (_deferredSignals?.Count > 0) { var signal = ShellContext.ServiceProvider.GetRequiredService(); foreach (var key in _deferredSignals) @@ -411,7 +457,7 @@ internal async Task BeforeDisposeAsync() } } - if (_deferredTasks.Count > 0) + if (_deferredTasks?.Count > 0) { var shellHost = ShellContext.ServiceProvider.GetRequiredService();