V10: fix build warnings infrastructure (#12369)
* Run code cleanup * Run dotnet format * Start manual fixes * Manual fixing of warnings * Fix nullability in columnalias * Fix tests * Fix up after merge * Start updating after review * Update editorconfig to contain new static & const rules * Fix up editorconfig to not contain duplicate rules * Fix up static member names * Fix up according to review * Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Extensions/InstanceIdentifiableExtensions.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Macros/MacroTagParser.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Macros/MacroTagParser.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Macros/MacroTagParser.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Migrations/Expressions/Alter/Table/IAlterTableColumnOptionBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_0_0/AddMemberPropertiesAsColumns.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/AuditEntryMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/MediaMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/MemberMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyGroupMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyGroupMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/RelationTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/RelationTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/NPocoMapperCollectionBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Querying/ExpressionVisitorBase.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Fix [..] to substring * Fix after merge with 10/dev * Fox ContentValueSetValidator.cs * Update LoggerConfigExtensions Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk> Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
This commit is contained in:
@@ -1,48 +1,49 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
namespace Umbraco.Cms.Core.Scoping
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Core.Scoping;
|
||||
|
||||
/// <summary>
|
||||
/// Disposed at the end of the request to cleanup any orphaned Scopes.
|
||||
/// </summary>
|
||||
/// <remarks>Registered as Scoped in DI (per request)</remarks>
|
||||
internal class HttpScopeReference : IHttpScopeReference
|
||||
{
|
||||
private readonly ScopeProvider _scopeProvider;
|
||||
private bool _disposedValue;
|
||||
private bool _registered;
|
||||
|
||||
/// <summary>
|
||||
/// Disposed at the end of the request to cleanup any orphaned Scopes.
|
||||
/// </summary>
|
||||
/// <remarks>Registered as Scoped in DI (per request)</remarks>
|
||||
internal class HttpScopeReference : IHttpScopeReference
|
||||
public HttpScopeReference(ScopeProvider scopeProvider) => _scopeProvider = scopeProvider;
|
||||
|
||||
public void Dispose() =>
|
||||
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(true);
|
||||
|
||||
public void Register() => _registered = true;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
private readonly Infrastructure.Scoping.ScopeProvider _scopeProvider;
|
||||
private bool _disposedValue;
|
||||
private bool _registered = false;
|
||||
|
||||
public HttpScopeReference(Infrastructure.Scoping.ScopeProvider scopeProvider) => _scopeProvider = scopeProvider;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
if (disposing)
|
||||
{
|
||||
if (disposing)
|
||||
if (_registered)
|
||||
{
|
||||
if (_registered)
|
||||
// dispose the entire chain (if any)
|
||||
// reset (don't commit by default)
|
||||
Scope? scope;
|
||||
while ((scope = _scopeProvider.AmbientScope) != null)
|
||||
{
|
||||
// dispose the entire chain (if any)
|
||||
// reset (don't commit by default)
|
||||
Infrastructure.Scoping.Scope? scope;
|
||||
while ((scope = _scopeProvider.AmbientScope) != null)
|
||||
{
|
||||
scope.Reset();
|
||||
scope.Dispose();
|
||||
}
|
||||
scope.Reset();
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
|
||||
public void Register() => _registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
namespace Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Core.Scoping
|
||||
/// <summary>
|
||||
/// Cleans up orphaned <see cref="IScope" /> references at the end of a request
|
||||
/// </summary>
|
||||
public interface IHttpScopeReference : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Cleans up orphaned <see cref="IScope"/> references at the end of a request
|
||||
/// Register for cleanup in the request
|
||||
/// </summary>
|
||||
public interface IHttpScopeReference : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Register for cleanup in the request
|
||||
/// </summary>
|
||||
void Register();
|
||||
}
|
||||
void Register();
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ namespace Umbraco.Cms.Infrastructure.Scoping;
|
||||
public interface IScope : ICoreScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the scope database.
|
||||
/// Gets the scope database.
|
||||
/// </summary>
|
||||
IUmbracoDatabase Database { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sql context.
|
||||
/// Gets the Sql context.
|
||||
/// </summary>
|
||||
ISqlContext SqlContext { get; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
public interface IScopeAccessor
|
||||
{
|
||||
public interface IScopeAccessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the ambient scope.
|
||||
/// </summary>
|
||||
/// <remarks>Returns <c>null</c> if there is no ambient scope.</remarks>
|
||||
IScope? AmbientScope { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the ambient scope.
|
||||
/// </summary>
|
||||
/// <remarks>Returns <c>null</c> if there is no ambient scope.</remarks>
|
||||
IScope? AmbientScope { get; }
|
||||
}
|
||||
|
||||
@@ -7,10 +7,15 @@ using Umbraco.Cms.Infrastructure.Persistence;
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
/// <summary>
|
||||
/// Provides scopes.
|
||||
/// Provides scopes.
|
||||
/// </summary>
|
||||
public interface IScopeProvider : ICoreScopeProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the scope context.
|
||||
/// </summary>
|
||||
IScopeContext? Context { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
ICoreScope ICoreScopeProvider.CreateCoreScope(
|
||||
IsolationLevel isolationLevel,
|
||||
@@ -34,7 +39,7 @@ public interface IScopeProvider : ICoreScopeProvider
|
||||
SqlContext.Query<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an ambient scope.
|
||||
/// Creates an ambient scope.
|
||||
/// </summary>
|
||||
/// <param name="isolationLevel">The transaction isolation level.</param>
|
||||
/// <param name="repositoryCacheMode">The repositories cache mode.</param>
|
||||
@@ -45,12 +50,14 @@ public interface IScopeProvider : ICoreScopeProvider
|
||||
/// <param name="autoComplete">A value indicating whether this scope is auto-completed.</param>
|
||||
/// <returns>The created ambient scope.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The created scope becomes the ambient scope.</para>
|
||||
/// <para>If an ambient scope already exists, it becomes the parent of the created scope.</para>
|
||||
/// <para>When the created scope is disposed, the parent scope becomes the ambient scope again.</para>
|
||||
/// <para>Parameters must be specified on the outermost scope, or must be compatible with the parents.</para>
|
||||
/// <para>Auto-completed scopes should be used for read-only operations ONLY. Do not use them if you do not
|
||||
/// understand the associated issues, such as the scope being completed even though an exception is thrown.</para>
|
||||
/// <para>The created scope becomes the ambient scope.</para>
|
||||
/// <para>If an ambient scope already exists, it becomes the parent of the created scope.</para>
|
||||
/// <para>When the created scope is disposed, the parent scope becomes the ambient scope again.</para>
|
||||
/// <para>Parameters must be specified on the outermost scope, or must be compatible with the parents.</para>
|
||||
/// <para>
|
||||
/// Auto-completed scopes should be used for read-only operations ONLY. Do not use them if you do not
|
||||
/// understand the associated issues, such as the scope being completed even though an exception is thrown.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
IScope CreateScope(
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
@@ -62,7 +69,7 @@ public interface IScopeProvider : ICoreScopeProvider
|
||||
bool autoComplete = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a detached scope.
|
||||
/// Creates a detached scope.
|
||||
/// </summary>
|
||||
/// <returns>A detached scope.</returns>
|
||||
/// <param name="isolationLevel">The transaction isolation level.</param>
|
||||
@@ -71,8 +78,8 @@ public interface IScopeProvider : ICoreScopeProvider
|
||||
/// <param name="scopedNotificationPublisher">An option notification publisher.</param>
|
||||
/// <param name="scopeFileSystems">A value indicating whether to scope the filesystems.</param>
|
||||
/// <remarks>
|
||||
/// <para>A detached scope is not ambient and has no parent.</para>
|
||||
/// <para>It is meant to be attached by <see cref="AttachScope"/>.</para>
|
||||
/// <para>A detached scope is not ambient and has no parent.</para>
|
||||
/// <para>It is meant to be attached by <see cref="AttachScope" />.</para>
|
||||
/// </remarks>
|
||||
/// <remarks>
|
||||
/// This is not used by CMS but is used by Umbraco Deploy.
|
||||
@@ -85,36 +92,30 @@ public interface IScopeProvider : ICoreScopeProvider
|
||||
bool? scopeFileSystems = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a scope.
|
||||
/// Attaches a scope.
|
||||
/// </summary>
|
||||
/// <param name="scope">The scope to attach.</param>
|
||||
/// <param name="callContext">A value indicating whether to force usage of call context.</param>
|
||||
/// <remarks>
|
||||
/// <para>Only a scope created by <see cref="CreateDetachedScope"/> can be attached.</para>
|
||||
/// <para>Only a scope created by <see cref="CreateDetachedScope" /> can be attached.</para>
|
||||
/// </remarks>
|
||||
void AttachScope(IScope scope, bool callContext = false);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches a scope.
|
||||
/// Detaches a scope.
|
||||
/// </summary>
|
||||
/// <returns>The detached scope.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Only a scope previously attached by <see cref="AttachScope"/> can be detached.</para>
|
||||
/// <para>Only a scope previously attached by <see cref="AttachScope" /> can be detached.</para>
|
||||
/// </remarks>
|
||||
IScope DetachScope();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scope context.
|
||||
/// </summary>
|
||||
IScopeContext? Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sql context.
|
||||
/// Gets the sql context.
|
||||
/// </summary>
|
||||
ISqlContext SqlContext { get; }
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
|
||||
IEnumerable<ScopeInfo> ScopeInfos { get; }
|
||||
ScopeInfo GetScopeInfo(IScope scope);
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
@@ -8,12 +7,12 @@ namespace Umbraco.Cms.Core.Scoping;
|
||||
public interface IScope : Infrastructure.Scoping.IScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the scope event messages.
|
||||
/// Gets the scope event messages.
|
||||
/// </summary>
|
||||
EventMessages Messages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scope event dispatcher.
|
||||
/// Gets the scope event dispatcher.
|
||||
/// </summary>
|
||||
IEventDispatcher Events { get; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
@@ -22,10 +18,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
/// Implements <see cref="IScope" />.
|
||||
/// </summary>
|
||||
/// <remarks>Not thread-safe obviously.</remarks>
|
||||
internal class Scope :
|
||||
ICoreScope,
|
||||
IScope,
|
||||
Core.Scoping.IScope
|
||||
internal class Scope : ICoreScope, IScope, Core.Scoping.IScope
|
||||
{
|
||||
private readonly bool _autoComplete;
|
||||
private readonly CoreDebugSettings _coreDebugSettings;
|
||||
@@ -101,8 +94,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
#if DEBUG_SCOPES
|
||||
_scopeProvider.RegisterScope(this);
|
||||
#endif
|
||||
logger.LogTrace("Create {InstanceId} on thread {ThreadId}", InstanceId.ToString("N").Substring(0, 8),
|
||||
Thread.CurrentThread.ManagedThreadId);
|
||||
logger.LogTrace("Create {InstanceId} on thread {ThreadId}", InstanceId.ToString("N").Substring(0, 8), Thread.CurrentThread.ManagedThreadId);
|
||||
|
||||
if (detachable)
|
||||
{
|
||||
@@ -146,8 +138,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
parent.RepositoryCacheMode > repositoryCacheMode)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Value '{repositoryCacheMode}' cannot be lower than parent value '{parent.RepositoryCacheMode}'.",
|
||||
nameof(repositoryCacheMode));
|
||||
$"Value '{repositoryCacheMode}' cannot be lower than parent value '{parent.RepositoryCacheMode}'.", nameof(repositoryCacheMode));
|
||||
}
|
||||
|
||||
// cannot specify a dispatcher!
|
||||
@@ -159,8 +150,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
// Only the outermost scope can specify the notification publisher
|
||||
if (_notificationPublisher != null)
|
||||
{
|
||||
throw new ArgumentException("Value cannot be specified on nested scope.",
|
||||
nameof(notificationPublisher));
|
||||
throw new ArgumentException("Value cannot be specified on nested scope.", nameof(notificationPublisher));
|
||||
}
|
||||
|
||||
// cannot specify a different fs scope!
|
||||
@@ -168,8 +158,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
if (scopeFileSystems != null && parent._scopeFileSystem != scopeFileSystems)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Value '{scopeFileSystems.Value}' be different from parent value '{parent._scopeFileSystem}'.",
|
||||
nameof(scopeFileSystems));
|
||||
$"Value '{scopeFileSystems.Value}' be different from parent value '{parent._scopeFileSystem}'.", nameof(scopeFileSystems));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -696,14 +685,10 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
switch (currentType)
|
||||
{
|
||||
case DistributedLockType.ReadLock:
|
||||
EagerReadLockInner(currentInstanceId,
|
||||
currentTimeout == TimeSpan.Zero ? null : currentTimeout,
|
||||
collectedIds.ToArray());
|
||||
EagerReadLockInner(currentInstanceId, currentTimeout == TimeSpan.Zero ? null : currentTimeout, collectedIds.ToArray());
|
||||
break;
|
||||
case DistributedLockType.WriteLock:
|
||||
EagerWriteLockInner(currentInstanceId,
|
||||
currentTimeout == TimeSpan.Zero ? null : currentTimeout,
|
||||
collectedIds.ToArray());
|
||||
EagerWriteLockInner(currentInstanceId, currentTimeout == TimeSpan.Zero ? null : currentTimeout, collectedIds.ToArray());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -722,12 +707,10 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
switch (currentType)
|
||||
{
|
||||
case DistributedLockType.ReadLock:
|
||||
EagerReadLockInner(currentInstanceId,
|
||||
currentTimeout == TimeSpan.Zero ? null : currentTimeout, collectedIds.ToArray());
|
||||
EagerReadLockInner(currentInstanceId, currentTimeout == TimeSpan.Zero ? null : currentTimeout, collectedIds.ToArray());
|
||||
break;
|
||||
case DistributedLockType.WriteLock:
|
||||
EagerWriteLockInner(currentInstanceId,
|
||||
currentTimeout == TimeSpan.Zero ? null : currentTimeout, collectedIds.ToArray());
|
||||
EagerWriteLockInner(currentInstanceId, currentTimeout == TimeSpan.Zero ? null : currentTimeout, collectedIds.ToArray());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -776,8 +759,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
/// <param name="dict">Lock dictionary to report on.</param>
|
||||
/// <param name="builder">String builder to write to.</param>
|
||||
/// <param name="dictName">The name to report the dictionary as.</param>
|
||||
private void WriteLockDictionaryToString(Dictionary<Guid, Dictionary<int, int>> dict, StringBuilder builder,
|
||||
string dictName)
|
||||
private void WriteLockDictionaryToString(Dictionary<Guid, Dictionary<int, int>> dict, StringBuilder builder, string dictName)
|
||||
{
|
||||
if (dict?.Count > 0)
|
||||
{
|
||||
@@ -921,8 +903,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
HandleScopedFileSystems,
|
||||
HandleScopedNotifications,
|
||||
HandleScopeContext,
|
||||
HandleDetachedScopes
|
||||
);
|
||||
HandleDetachedScopes);
|
||||
}
|
||||
|
||||
private static void TryFinally(params Action[] actions)
|
||||
|
||||
@@ -1,115 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
namespace Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Core.Scoping
|
||||
internal class ScopeContext : IScopeContext, IInstanceIdentifiable
|
||||
{
|
||||
internal class ScopeContext : IScopeContext, IInstanceIdentifiable
|
||||
private Dictionary<string, IEnlistedObject>? _enlisted;
|
||||
|
||||
private interface IEnlistedObject
|
||||
{
|
||||
private Dictionary<string, IEnlistedObject>? _enlisted;
|
||||
int Priority { get; }
|
||||
|
||||
public void ScopeExit(bool completed)
|
||||
void Execute(bool completed);
|
||||
}
|
||||
|
||||
public Guid InstanceId { get; } = Guid.NewGuid();
|
||||
|
||||
private IDictionary<string, IEnlistedObject> Enlisted => _enlisted ??= new Dictionary<string, IEnlistedObject>();
|
||||
|
||||
public void ScopeExit(bool completed)
|
||||
{
|
||||
if (_enlisted == null)
|
||||
{
|
||||
if (_enlisted == null)
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: can we create infinite loops? - what about nested events? will they just be plainly ignored = really bad?
|
||||
List<Exception>? exceptions = null;
|
||||
List<IEnlistedObject> orderedEnlisted;
|
||||
while ((orderedEnlisted = _enlisted.Values.OrderBy(x => x.Priority).ToList()).Count > 0)
|
||||
// TODO: can we create infinite loops? - what about nested events? will they just be plainly ignored = really bad?
|
||||
List<Exception>? exceptions = null;
|
||||
List<IEnlistedObject> orderedEnlisted;
|
||||
while ((orderedEnlisted = _enlisted.Values.OrderBy(x => x.Priority).ToList()).Count > 0)
|
||||
{
|
||||
_enlisted.Clear();
|
||||
foreach (IEnlistedObject enlisted in orderedEnlisted)
|
||||
{
|
||||
_enlisted.Clear();
|
||||
foreach (var enlisted in orderedEnlisted)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
enlisted.Execute(completed);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (exceptions == null)
|
||||
exceptions = new List<Exception>();
|
||||
exceptions.Add(e);
|
||||
}
|
||||
enlisted.Execute(completed);
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions != null)
|
||||
throw new AggregateException("Exceptions were thrown by listed actions.", exceptions);
|
||||
}
|
||||
|
||||
public Guid InstanceId { get; } = Guid.NewGuid();
|
||||
|
||||
public int CreatedThreadId { get; } = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
private IDictionary<string, IEnlistedObject> Enlisted => _enlisted
|
||||
?? (_enlisted = new Dictionary<string, IEnlistedObject>());
|
||||
|
||||
private interface IEnlistedObject
|
||||
{
|
||||
void Execute(bool completed);
|
||||
int Priority { get; }
|
||||
}
|
||||
|
||||
private class EnlistedObject<T> : IEnlistedObject
|
||||
{
|
||||
private readonly Action<bool, T?>? _action;
|
||||
|
||||
public EnlistedObject(T? item, Action<bool, T?>? action, int priority)
|
||||
{
|
||||
Item = item;
|
||||
Priority = priority;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public T? Item { get; }
|
||||
|
||||
public int Priority { get; }
|
||||
|
||||
public void Execute(bool completed)
|
||||
{
|
||||
if (_action is not null)
|
||||
catch (Exception e)
|
||||
{
|
||||
_action(completed, Item);
|
||||
if (exceptions == null)
|
||||
{
|
||||
exceptions = new List<Exception>();
|
||||
}
|
||||
|
||||
exceptions.Add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Enlist(string key, Action<bool> action, int priority = 100)
|
||||
if (exceptions != null)
|
||||
{
|
||||
Enlist<object>(key, null, (completed, item) => action(completed), priority);
|
||||
throw new AggregateException("Exceptions were thrown by listed actions.", exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
public T? Enlist<T>(string key, Func<T>? creator, Action<bool, T?>? action = null, int priority = 100)
|
||||
public int CreatedThreadId { get; } = Thread.CurrentThread.ManagedThreadId;
|
||||
|
||||
public void Enlist(string key, Action<bool> action, int priority = 100) =>
|
||||
Enlist<object>(key, null, (completed, item) => action(completed), priority);
|
||||
|
||||
public T? Enlist<T>(string key, Func<T>? creator, Action<bool, T?>? action = null, int priority = 100)
|
||||
{
|
||||
Dictionary<string, IEnlistedObject> enlistedObjects =
|
||||
_enlisted ??= new Dictionary<string, IEnlistedObject>();
|
||||
|
||||
if (enlistedObjects.TryGetValue(key, out IEnlistedObject? enlisted))
|
||||
{
|
||||
var enlistedObjects = _enlisted ?? (_enlisted = new Dictionary<string, IEnlistedObject>());
|
||||
|
||||
if (enlistedObjects.TryGetValue(key, out var enlisted))
|
||||
{
|
||||
if (!(enlisted is EnlistedObject<T> enlistedAs))
|
||||
throw new InvalidOperationException("An item with the key already exists, but with a different type.");
|
||||
if (enlistedAs.Priority != priority)
|
||||
throw new InvalidOperationException("An item with the key already exits, but with a different priority.");
|
||||
return enlistedAs.Item;
|
||||
}
|
||||
var enlistedOfT = new EnlistedObject<T>(creator == null ? default : creator(), action, priority);
|
||||
Enlisted[key] = enlistedOfT;
|
||||
return enlistedOfT.Item;
|
||||
}
|
||||
|
||||
public T? GetEnlisted<T>(string key)
|
||||
{
|
||||
var enlistedObjects = _enlisted;
|
||||
if (enlistedObjects == null) return default;
|
||||
|
||||
if (enlistedObjects.TryGetValue(key, out var enlisted) == false)
|
||||
return default;
|
||||
|
||||
if (!(enlisted is EnlistedObject<T> enlistedAs))
|
||||
throw new InvalidOperationException("An item with the key exists, but with a different type.");
|
||||
{
|
||||
throw new InvalidOperationException("An item with the key already exists, but with a different type.");
|
||||
}
|
||||
|
||||
if (enlistedAs.Priority != priority)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"An item with the key already exits, but with a different priority.");
|
||||
}
|
||||
|
||||
return enlistedAs.Item;
|
||||
}
|
||||
|
||||
var enlistedOfT = new EnlistedObject<T>(creator == null ? default : creator(), action, priority);
|
||||
Enlisted[key] = enlistedOfT;
|
||||
return enlistedOfT.Item;
|
||||
}
|
||||
|
||||
public T? GetEnlisted<T>(string key)
|
||||
{
|
||||
Dictionary<string, IEnlistedObject>? enlistedObjects = _enlisted;
|
||||
if (enlistedObjects == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (enlistedObjects.TryGetValue(key, out IEnlistedObject? enlisted) == false)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (!(enlisted is EnlistedObject<T> enlistedAs))
|
||||
{
|
||||
throw new InvalidOperationException("An item with the key exists, but with a different type.");
|
||||
}
|
||||
|
||||
return enlistedAs.Item;
|
||||
}
|
||||
|
||||
private class EnlistedObject<T> : IEnlistedObject
|
||||
{
|
||||
private readonly Action<bool, T?>? _action;
|
||||
|
||||
public EnlistedObject(T? item, Action<bool, T?>? action, int priority)
|
||||
{
|
||||
Item = item;
|
||||
Priority = priority;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public T? Item { get; }
|
||||
|
||||
public int Priority { get; }
|
||||
|
||||
public void Execute(bool completed)
|
||||
{
|
||||
if (_action is not null)
|
||||
{
|
||||
_action(completed, Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,69 @@
|
||||
using System;
|
||||
namespace Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Core.Scoping
|
||||
/// <summary>
|
||||
/// Provides a base class for scope contextual objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A scope contextual object is enlisted in the current scope context,
|
||||
/// if any, and released when the context exists. It must be used in a 'using'
|
||||
/// block, and will be released when disposed, if not part of a scope.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public abstract class ScopeContextualBase : IDisposable
|
||||
{
|
||||
private bool _scoped;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a base class for scope contextual objects.
|
||||
/// Gets a contextual object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <param name="scopeProvider">A scope provider.</param>
|
||||
/// <param name="key">A context key for the object.</param>
|
||||
/// <param name="ctor">A function producing the contextual object.</param>
|
||||
/// <returns>The contextual object.</returns>
|
||||
/// <remarks>
|
||||
/// <para>A scope contextual object is enlisted in the current scope context,
|
||||
/// if any, and released when the context exists. It must be used in a 'using'
|
||||
/// block, and will be released when disposed, if not part of a scope.</para>
|
||||
/// <para></para>
|
||||
/// </remarks>
|
||||
public abstract class ScopeContextualBase : IDisposable
|
||||
public static T? Get<T>(ICoreScopeProvider scopeProvider, string key, Func<bool, T> ctor)
|
||||
where T : ScopeContextualBase
|
||||
{
|
||||
private bool _scoped;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a contextual object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <param name="scopeProvider">A scope provider.</param>
|
||||
/// <param name="key">A context key for the object.</param>
|
||||
/// <param name="ctor">A function producing the contextual object.</param>
|
||||
/// <returns>The contextual object.</returns>
|
||||
/// <remarks>
|
||||
/// <para></para>
|
||||
/// </remarks>
|
||||
public static T? Get<T>(ICoreScopeProvider scopeProvider, string key, Func<bool, T> ctor)
|
||||
where T : ScopeContextualBase
|
||||
// no scope context = create a non-scoped object
|
||||
IScopeContext? scopeContext = scopeProvider.Context;
|
||||
if (scopeContext == null)
|
||||
{
|
||||
// no scope context = create a non-scoped object
|
||||
var scopeContext = scopeProvider.Context;
|
||||
if (scopeContext == null)
|
||||
return ctor(false);
|
||||
|
||||
// create & enlist the scoped object
|
||||
var w = scopeContext.Enlist("ScopeContextualBase_" + key,
|
||||
() => ctor(true),
|
||||
(completed, item) => { item?.Release(completed); });
|
||||
|
||||
if (w is not null)
|
||||
{
|
||||
w._scoped = true;
|
||||
}
|
||||
|
||||
return w;
|
||||
return ctor(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <para>If not scoped, then this releases the contextual object.</para>
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
// create & enlist the scoped object
|
||||
T? w = scopeContext.Enlist(
|
||||
"ScopeContextualBase_" + key,
|
||||
() => ctor(true),
|
||||
(completed, item) => { item?.Release(completed); });
|
||||
|
||||
if (w is not null)
|
||||
{
|
||||
if (_scoped == false)
|
||||
Release(true);
|
||||
w._scoped = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the contextual object.
|
||||
/// </summary>
|
||||
/// <param name="completed">A value indicating whether the scoped operation completed.</param>
|
||||
public abstract void Release(bool completed);
|
||||
return w;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <para>If not scoped, then this releases the contextual object.</para>
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_scoped == false)
|
||||
{
|
||||
Release(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the contextual object.
|
||||
/// </summary>
|
||||
/// <param name="completed">A value indicating whether the scoped operation completed.</param>
|
||||
public abstract void Release(bool completed);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Data;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DistributedLocking;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using CoreDebugSettings = Umbraco.Cms.Core.Configuration.Models.CoreDebugSettings;
|
||||
using Umbraco.Extensions;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using Umbraco.Cms.Core.DistributedLocking;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Extensions;
|
||||
#if DEBUG_SCOPES
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -21,7 +18,7 @@ using System.Text;
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IScopeProvider"/>.
|
||||
/// Implements <see cref="IScopeProvider" />.
|
||||
/// </summary>
|
||||
internal class ScopeProvider :
|
||||
ICoreScopeProvider,
|
||||
@@ -35,10 +32,10 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
private readonly FileSystems _fileSystems;
|
||||
private CoreDebugSettings _coreDebugSettings;
|
||||
private readonly MediaFileManager _mediaFileManager;
|
||||
private static readonly AsyncLocal<ConcurrentStack<IScope>> s_scopeStack = new AsyncLocal<ConcurrentStack<IScope>>();
|
||||
private static readonly AsyncLocal<ConcurrentStack<IScopeContext>> s_scopeContextStack = new AsyncLocal<ConcurrentStack<IScopeContext>>();
|
||||
private static readonly string s_scopeItemKey = typeof(Scope).FullName!;
|
||||
private static readonly string s_contextItemKey = typeof(ScopeProvider).FullName!;
|
||||
private static readonly AsyncLocal<ConcurrentStack<IScope>> _scopeStack = new();
|
||||
private static readonly AsyncLocal<ConcurrentStack<IScopeContext>> _scopeContextStack = new();
|
||||
private static readonly string _scopeItemKey = typeof(Scope).FullName!;
|
||||
private static readonly string _contextItemKey = typeof(ScopeProvider).FullName!;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public ScopeProvider(
|
||||
@@ -60,6 +57,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
_loggerFactory = loggerFactory;
|
||||
_requestCache = requestCache;
|
||||
_eventAggregator = eventAggregator;
|
||||
|
||||
// take control of the FileSystems
|
||||
_fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems;
|
||||
|
||||
@@ -76,30 +74,30 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
|
||||
private void MoveHttpContextScopeToCallContext()
|
||||
{
|
||||
var source = (ConcurrentStack<IScope>?)_requestCache.Get(s_scopeItemKey);
|
||||
ConcurrentStack<IScope>? stack = s_scopeStack.Value;
|
||||
MoveContexts(s_scopeItemKey, source, stack, (_, v) => s_scopeStack.Value = v);
|
||||
var source = (ConcurrentStack<IScope>?)_requestCache.Get(_scopeItemKey);
|
||||
ConcurrentStack<IScope>? stack = _scopeStack.Value;
|
||||
MoveContexts(_scopeItemKey, source, stack, (_, v) => _scopeStack.Value = v);
|
||||
}
|
||||
|
||||
private void MoveHttpContextScopeContextToCallContext()
|
||||
{
|
||||
var source = (ConcurrentStack<IScopeContext>?)_requestCache.Get(s_contextItemKey);
|
||||
ConcurrentStack<IScopeContext>? stack = s_scopeContextStack.Value;
|
||||
MoveContexts(s_contextItemKey, source, stack, (_, v) => s_scopeContextStack.Value = v);
|
||||
var source = (ConcurrentStack<IScopeContext>?)_requestCache.Get(_contextItemKey);
|
||||
ConcurrentStack<IScopeContext>? stack = _scopeContextStack.Value;
|
||||
MoveContexts(_contextItemKey, source, stack, (_, v) => _scopeContextStack.Value = v);
|
||||
}
|
||||
|
||||
private void MoveCallContextScopeToHttpContext()
|
||||
{
|
||||
ConcurrentStack<IScope>? source = s_scopeStack.Value;
|
||||
var stack = (ConcurrentStack<IScope>?)_requestCache.Get(s_scopeItemKey);
|
||||
MoveContexts(s_scopeItemKey, source, stack, (k, v) => _requestCache.Set(k, v));
|
||||
ConcurrentStack<IScope>? source = _scopeStack.Value;
|
||||
var stack = (ConcurrentStack<IScope>?)_requestCache.Get(_scopeItemKey);
|
||||
MoveContexts(_scopeItemKey, source, stack, (k, v) => _requestCache.Set(k, v));
|
||||
}
|
||||
|
||||
private void MoveCallContextScopeContextToHttpContext()
|
||||
{
|
||||
ConcurrentStack<IScopeContext>? source = s_scopeContextStack.Value;
|
||||
var stack = (ConcurrentStack<IScopeContext>?)_requestCache.Get(s_contextItemKey);
|
||||
MoveContexts(s_contextItemKey, source, stack, (k, v) => _requestCache.Set(k, v));
|
||||
ConcurrentStack<IScopeContext>? source = _scopeContextStack.Value;
|
||||
var stack = (ConcurrentStack<IScopeContext>?)_requestCache.Get(_contextItemKey);
|
||||
MoveContexts(_contextItemKey, source, stack, (k, v) => _requestCache.Set(k, v));
|
||||
}
|
||||
|
||||
private void MoveContexts<T>(string key, ConcurrentStack<T>? source, ConcurrentStack<T>? stack, Action<string, ConcurrentStack<T>> setter)
|
||||
@@ -134,7 +132,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
|
||||
private void SetCallContextScope(IScope? value)
|
||||
{
|
||||
ConcurrentStack<IScope>? stack = s_scopeStack.Value;
|
||||
ConcurrentStack<IScope>? stack = _scopeStack.Value;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
// first, null-register the existing value
|
||||
@@ -159,7 +157,6 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
_logger.LogDebug("AddObject " + value.InstanceId.ToString("N").Substring(0, 8));
|
||||
#endif
|
||||
@@ -167,14 +164,15 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
stack = new ConcurrentStack<IScope>();
|
||||
}
|
||||
|
||||
stack.Push(value);
|
||||
s_scopeStack.Value = stack;
|
||||
_scopeStack.Value = stack;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCallContextScopeContext(IScopeContext? value)
|
||||
{
|
||||
ConcurrentStack<IScopeContext>? stack = s_scopeContextStack.Value;
|
||||
ConcurrentStack<IScopeContext>? stack = _scopeContextStack.Value;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
@@ -189,12 +187,12 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
stack = new ConcurrentStack<IScopeContext>();
|
||||
}
|
||||
|
||||
stack.Push(value);
|
||||
s_scopeContextStack.Value = stack;
|
||||
_scopeContextStack.Value = stack;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private T? GetHttpContextObject<T>(string key, bool required = true)
|
||||
where T : class
|
||||
{
|
||||
@@ -253,6 +251,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
stack = new ConcurrentStack<T>();
|
||||
}
|
||||
|
||||
stack.Push(value);
|
||||
_requestCache.Set(key, stack);
|
||||
}
|
||||
@@ -265,23 +264,23 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
#region Ambient Context
|
||||
|
||||
/// <summary>
|
||||
/// Get the Ambient (Current) <see cref="IScopeContext"/> for the current execution context.
|
||||
/// Get the Ambient (Current) <see cref="IScopeContext" /> for the current execution context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// </remarks>
|
||||
public IScopeContext? AmbientContext
|
||||
{
|
||||
get
|
||||
{
|
||||
// try http context, fallback onto call context
|
||||
IScopeContext? value = GetHttpContextObject<IScopeContext>(s_contextItemKey, false);
|
||||
IScopeContext? value = GetHttpContextObject<IScopeContext>(_contextItemKey, false);
|
||||
if (value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
ConcurrentStack<IScopeContext>? stack = s_scopeContextStack.Value;
|
||||
ConcurrentStack<IScopeContext>? stack = _scopeContextStack.Value;
|
||||
if (stack == null || !stack.TryPeek(out IScopeContext? peek))
|
||||
{
|
||||
return null;
|
||||
@@ -298,23 +297,23 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
IScope? IScopeAccessor.AmbientScope => AmbientScope;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the Ambient (Current) <see cref="Scope"/> for the current execution context.
|
||||
/// Gets or set the Ambient (Current) <see cref="Scope" /> for the current execution context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// </remarks>
|
||||
public Scope? AmbientScope
|
||||
{
|
||||
get
|
||||
{
|
||||
// try http context, fallback onto call context
|
||||
IScope? value = GetHttpContextObject<IScope>(s_scopeItemKey, false);
|
||||
IScope? value = GetHttpContextObject<IScope>(_scopeItemKey, false);
|
||||
if (value != null)
|
||||
{
|
||||
return (Scope)value;
|
||||
}
|
||||
|
||||
ConcurrentStack<IScope>? stack = s_scopeStack.Value;
|
||||
ConcurrentStack<IScope>? stack = _scopeStack.Value;
|
||||
if (stack == null || !stack.TryPeek(out IScope? peek))
|
||||
{
|
||||
return null;
|
||||
@@ -327,7 +326,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
public void PopAmbientScope(Scope? scope)
|
||||
{
|
||||
// pop the stack from all contexts
|
||||
SetHttpContextObject<IScope>(s_scopeItemKey, null, false);
|
||||
SetHttpContextObject<IScope>(_scopeItemKey, null, false);
|
||||
SetCallContextScope(null);
|
||||
|
||||
// We need to move the stack to a different context if the parent scope
|
||||
@@ -335,7 +334,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
// if creating a child scope with callContext: true (thus forcing CallContext)
|
||||
// when there is actually a current HttpContext available.
|
||||
// It's weird but is required for Deploy somehow.
|
||||
bool parentScopeCallContext = (scope?.ParentScope?.CallContext ?? false);
|
||||
var parentScopeCallContext = scope?.ParentScope?.CallContext ?? false;
|
||||
if ((scope?.CallContext ?? false) && !parentScopeCallContext)
|
||||
{
|
||||
MoveCallContextScopeToHttpContext();
|
||||
@@ -357,14 +356,13 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
throw new ArgumentNullException(nameof(scope));
|
||||
}
|
||||
|
||||
if (scope.CallContext != false || !SetHttpContextObject<IScope>(s_scopeItemKey, scope, false))
|
||||
if (scope.CallContext || !SetHttpContextObject<IScope>(_scopeItemKey, scope, false))
|
||||
{
|
||||
// In this case, always ensure that the HttpContext items
|
||||
// is transfered to CallContext and then cleared since we
|
||||
// may be migrating context with the callContext = true flag.
|
||||
// This is a weird case when forcing callContext when HttpContext
|
||||
// is available. Required by Deploy.
|
||||
|
||||
if (_requestCache.IsAvailable)
|
||||
{
|
||||
MoveHttpContextScopeToCallContext();
|
||||
@@ -382,14 +380,14 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
throw new ArgumentNullException(nameof(scopeContext));
|
||||
}
|
||||
|
||||
SetHttpContextObject<IScopeContext>(s_contextItemKey, scopeContext, false);
|
||||
SetHttpContextObject(_contextItemKey, scopeContext, false);
|
||||
SetCallContextScopeContext(scopeContext);
|
||||
}
|
||||
|
||||
public void PopAmbientScopeContext()
|
||||
{
|
||||
// pop stack from all contexts
|
||||
SetHttpContextObject<IScopeContext>(s_contextItemKey, null, false);
|
||||
SetHttpContextObject<IScopeContext>(_contextItemKey, null, false);
|
||||
SetCallContextScopeContext(null);
|
||||
}
|
||||
|
||||
@@ -400,7 +398,21 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
IEventDispatcher? eventDispatcher = null,
|
||||
IScopedNotificationPublisher? scopedNotificationPublisher = null,
|
||||
bool? scopeFileSystems = null)
|
||||
=> new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopedNotificationPublisher, scopeFileSystems);
|
||||
=>
|
||||
new Scope(
|
||||
this,
|
||||
_coreDebugSettings,
|
||||
_mediaFileManager,
|
||||
_eventAggregator,
|
||||
_loggerFactory.CreateLogger<Scope>(),
|
||||
_fileSystems,
|
||||
true,
|
||||
null,
|
||||
isolationLevel,
|
||||
repositoryCacheMode,
|
||||
eventDispatcher,
|
||||
scopedNotificationPublisher,
|
||||
scopeFileSystems);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AttachScope(IScope other, bool callContext = false)
|
||||
@@ -451,12 +463,14 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
Scope? originalScope = AmbientScope;
|
||||
if (originalScope != ambientScope.OrigScope)
|
||||
{
|
||||
throw new InvalidOperationException($"The detatched scope ({ambientScope.GetDebugInfo()}) does not match the original ({originalScope?.GetDebugInfo()})");
|
||||
throw new InvalidOperationException(
|
||||
$"The detatched scope ({ambientScope.GetDebugInfo()}) does not match the original ({originalScope?.GetDebugInfo()})");
|
||||
}
|
||||
|
||||
IScopeContext? originalScopeContext = AmbientContext;
|
||||
if (originalScopeContext != ambientScope.OrigContext)
|
||||
{
|
||||
throw new InvalidOperationException($"The detatched scope context does not match the original");
|
||||
throw new InvalidOperationException("The detatched scope context does not match the original");
|
||||
}
|
||||
|
||||
ambientScope.OrigScope = null;
|
||||
@@ -480,7 +494,22 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
IScopeContext? ambientContext = AmbientContext;
|
||||
ScopeContext? newContext = ambientContext == null ? new ScopeContext() : null;
|
||||
var scope = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete);
|
||||
var scope = new Scope(
|
||||
this,
|
||||
_coreDebugSettings,
|
||||
_mediaFileManager,
|
||||
_eventAggregator,
|
||||
_loggerFactory.CreateLogger<Scope>(),
|
||||
_fileSystems,
|
||||
false,
|
||||
newContext,
|
||||
isolationLevel,
|
||||
repositoryCacheMode,
|
||||
eventDispatcher,
|
||||
notificationPublisher,
|
||||
scopeFileSystems,
|
||||
callContext,
|
||||
autoComplete);
|
||||
|
||||
// assign only if scope creation did not throw!
|
||||
PushAmbientScope(scope);
|
||||
@@ -488,19 +517,33 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
PushAmbientScopeContext(newContext);
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
var nested = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete);
|
||||
var nested = new Scope(
|
||||
this,
|
||||
_coreDebugSettings,
|
||||
_mediaFileManager,
|
||||
_eventAggregator,
|
||||
_loggerFactory.CreateLogger<Scope>(),
|
||||
_fileSystems,
|
||||
ambientScope,
|
||||
isolationLevel,
|
||||
repositoryCacheMode,
|
||||
eventDispatcher,
|
||||
notificationPublisher,
|
||||
scopeFileSystems,
|
||||
callContext,
|
||||
autoComplete);
|
||||
PushAmbientScope(nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScopeContext? Context => AmbientContext;
|
||||
|
||||
// for testing
|
||||
internal ConcurrentStack<IScope>? GetCallContextScopeValue() => s_scopeStack.Value;
|
||||
internal ConcurrentStack<IScope>? GetCallContextScopeValue() => _scopeStack.Value;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
// this code needs TLC
|
||||
|
||||
Reference in New Issue
Block a user