Ensure legacy scope returned when using legacy scope provider (#12465)

* Separate legacy scope provider interface and explicitly implement.

* Don't rely on legacy scope provider for existing tests.

* Assert correct type returned when using legacy scope provider.
This commit is contained in:
Paul Johnson
2022-05-24 11:26:28 +01:00
committed by GitHub
parent e12c2eca1b
commit 41a003e20e
11 changed files with 152 additions and 14 deletions

View File

@@ -104,7 +104,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging
contentTypeService,
contentService,
propertyEditors,
scopeProvider,
(Umbraco.Cms.Infrastructure.Scoping.IScopeProvider) scopeProvider,
shortStringHelper,
serializer,
mediaService,

View File

@@ -1,9 +1,90 @@
using System;
using System.Data;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Infrastructure.Persistence;
// ReSharper disable once CheckNamespace
namespace Umbraco.Cms.Core.Scoping;
[Obsolete("Please use Umbraco.Cms.Infrastructure.Scoping.IScopeProvider or Umbraco.Cms.Core.Scoping.ICoreScopeProvider instead.")]
public interface IScopeProvider : Infrastructure.Scoping.IScopeProvider
public interface IScopeProvider
{
/// <summary>
/// Creates an ambient scope.
/// </summary>
/// <param name="isolationLevel">The transaction isolation level.</param>
/// <param name="repositoryCacheMode">The repositories cache mode.</param>
/// <param name="eventDispatcher">An optional events dispatcher.</param>
/// <param name="scopedNotificationPublisher">An optional notification publisher.</param>
/// <param name="scopeFileSystems">A value indicating whether to scope the filesystems.</param>
/// <param name="callContext">A value indicating whether this scope should always be registered in the call context.</param>
/// <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>
/// </remarks>
IScope CreateScope(
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
IEventDispatcher? eventDispatcher = null,
IScopedNotificationPublisher? scopedNotificationPublisher = null,
bool? scopeFileSystems = null,
bool callContext = false,
bool autoComplete = false);
/// <summary>
/// Creates a detached scope.
/// </summary>
/// <returns>A detached scope.</returns>
/// <param name="isolationLevel">The transaction isolation level.</param>
/// <param name="repositoryCacheMode">The repositories cache mode.</param>
/// <param name="eventDispatcher">An optional events dispatcher.</param>
/// <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>
/// </remarks>
/// <remarks>
/// This is not used by CMS but is used by Umbraco Deploy.
/// </remarks>
IScope CreateDetachedScope(
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
IEventDispatcher? eventDispatcher = null,
IScopedNotificationPublisher? scopedNotificationPublisher = null,
bool? scopeFileSystems = null);
/// <summary>
/// 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>
/// </remarks>
void AttachScope(IScope scope, bool callContext = false);
/// <summary>
/// Detaches a scope.
/// </summary>
/// <returns>The detached scope.</returns>
/// <remarks>
/// <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.
/// </summary>
ISqlContext SqlContext { get; }
}

View File

@@ -658,6 +658,42 @@ namespace Umbraco.Cms.Infrastructure.Scoping
}
}
#endif
/// <inheritdoc />
Cms.Core.Scoping.IScope Cms.Core.Scoping.IScopeProvider.CreateScope(
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
IEventDispatcher? eventDispatcher = null,
IScopedNotificationPublisher? notificationPublisher = null,
bool? scopeFileSystems = null,
bool callContext = false,
bool autoComplete = false) =>
(Cms.Core.Scoping.IScope) CreateScope(
isolationLevel,
repositoryCacheMode,
eventDispatcher,
notificationPublisher,
scopeFileSystems,
callContext,
autoComplete);
/// <inheritdoc />
Core.Scoping.IScope Core.Scoping.IScopeProvider.CreateDetachedScope(IsolationLevel isolationLevel,
RepositoryCacheMode repositoryCacheMode, IEventDispatcher? eventDispatcher,
IScopedNotificationPublisher? scopedNotificationPublisher, bool? scopeFileSystems) =>
(Core.Scoping.IScope)CreateDetachedScope(
isolationLevel,
repositoryCacheMode,
eventDispatcher,
scopedNotificationPublisher,
scopeFileSystems);
/// <inheritdoc />
void Core.Scoping.IScopeProvider.AttachScope(Core.Scoping.IScope scope, bool callContext) =>
AttachScope(scope, callContext);
/// <inheritdoc />
Core.Scoping.IScope Core.Scoping.IScopeProvider.DetachScope() =>
(Core.Scoping.IScope)DetachScope();
}
#if DEBUG_SCOPES

View File

@@ -0,0 +1,21 @@
using NUnit.Framework;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping;
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
public class LegacyScopeProviderTests : UmbracoIntegrationTest
{
[Test]
public void CreateScope_Always_ReturnsLegacyIScope()
{
var scopeProvider = GetRequiredService<global::Umbraco.Cms.Core.Scoping.IScopeProvider>();
using (var scope = scopeProvider.CreateScope())
{
Assert.IsInstanceOf<global::Umbraco.Cms.Core.Scoping.IScope>(scope);
}
}
}

View File

@@ -225,7 +225,7 @@ namespace Umbraco.Cms.Tests.UnitTests.TestHelpers
DomainService = serviceContext.DomainService;
// create a scope provider
IScopeProvider scopeProvider = Mock.Of<IScopeProvider>();
Infrastructure.Scoping.IScopeProvider scopeProvider = Mock.Of<Infrastructure.Scoping.IScopeProvider>();
Mock.Get(scopeProvider)
.Setup(x => x.CreateScope(
It.IsAny<IsolationLevel>(),

View File

@@ -145,7 +145,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HostedServices
var mockMainDom = new Mock<IMainDom>();
mockMainDom.SetupGet(x => x.IsMainDom).Returns(isMainDom);
var mockScopeProvider = new Mock<IScopeProvider>();
var mockScopeProvider = new Mock<global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider>();
var mockLogger = new Mock<ILogger<HealthCheckNotifier>>();
var mockProfilingLogger = new Mock<IProfilingLogger>();

View File

@@ -20,12 +20,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping
[TestFixture]
public class MappingTests
{
private IScopeProvider _scopeProvider;
private global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider _scopeProvider;
[SetUp]
public void MockScopeProvider()
{
var scopeMock = new Mock<IScopeProvider>();
var scopeMock = new Mock<global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider>();
scopeMock.Setup(x => x.CreateScope(
It.IsAny<IsolationLevel>(),
It.IsAny<RepositoryCacheMode>(),

View File

@@ -36,7 +36,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security
public MemberManager CreateSut()
{
IScopeProvider scopeProvider = new Mock<IScopeProvider>().Object;
global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider scopeProvider = new Mock<global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider>().Object;
_mockMemberService = new Mock<IMemberService>();
var mapDefinitions = new List<IMapDefinition>()
@@ -53,8 +53,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security
new UmbracoMapper(new MapDefinitionCollection(() => mapDefinitions), scopeProvider),
scopeProvider,
new IdentityErrorDescriber(),
Mock.Of<IPublishedSnapshotAccessor>(),
Mock.Of<IExternalLoginWithKeyService>(),
Mock.Of<IPublishedSnapshotAccessor>(),
Mock.Of<IExternalLoginWithKeyService>(),
Mock.Of<ITwoFactorLoginService>());
_mockIdentityOptions = new Mock<IOptions<IdentityOptions>>();

View File

@@ -27,7 +27,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security
{
_mockMemberService = new Mock<IMemberService>();
var mockScope = new Mock<IScope>();
var mockScopeProvider = new Mock<IScopeProvider>();
var mockScopeProvider = new Mock<global::Umbraco.Cms.Infrastructure.Scoping.IScopeProvider>();
mockScopeProvider
.Setup(x => x.CreateScope(It.IsAny<IsolationLevel>(), It.IsAny<RepositoryCacheMode>(), It.IsAny<IEventDispatcher>(), It.IsAny<IScopedNotificationPublisher>(), It.IsAny<bool?>(), It.IsAny<bool>(), It.IsAny<bool>()))
.Returns(mockScope.Object);

View File

@@ -1171,9 +1171,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.PublishedCache.NuCache
}
}
private static IScopeProvider GetScopeProvider()
private static ICoreScopeProvider GetScopeProvider()
{
IScopeProvider scopeProvider = Mock.Of<IScopeProvider>();
ICoreScopeProvider scopeProvider = Mock.Of<ICoreScopeProvider>();
Mock.Get(scopeProvider)
.Setup(x => x.Context).Returns(() => null);
return scopeProvider;

View File

@@ -260,7 +260,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers
new ActionCollection(() => null),
Mock.Of<ISqlContext>(),
Mock.Of<IJsonSerializer>(),
Mock.Of<IScopeProvider>(),
Mock.Of<ICoreScopeProvider>(),
Mock.Of<IAuthorizationService>(),
Mock.Of<IContentVersionService>()
);