diff --git a/src/SimpleInjector.Tests.Unit/Lifestyles/AsyncScopedLifestyleTests.cs b/src/SimpleInjector.Tests.Unit/Lifestyles/AsyncScopedLifestyleTests.cs
index 5b087e194..89ad5578a 100644
--- a/src/SimpleInjector.Tests.Unit/Lifestyles/AsyncScopedLifestyleTests.cs
+++ b/src/SimpleInjector.Tests.Unit/Lifestyles/AsyncScopedLifestyleTests.cs
@@ -1162,6 +1162,22 @@ public void Dispose_ObjectRegisteredForDisposalUsingRequestedCurrentLifetimeScop
Assert.IsTrue(instanceToDispose.HasBeenDisposed);
}
+ [TestMethod]
+ public void GetCurrentScope_WithCurrentScopeSet_ReturnsTheSetScope()
+ {
+ var lifestyle = new AsyncScopedLifestyle();
+ var container = new Container();
+ var expectedScope = new Scope(container);
+
+ // Act
+ lifestyle.SetCurrentScope(expectedScope);
+
+ // Assert
+ var actualScope = lifestyle.GetCurrentScope(container);
+
+ Assert.AreSame(expectedScope, actualScope);
+ }
+
private static async Task Inner(Container container, ICommand command)
{
DisposableCommand cmd1, cmd2;
diff --git a/src/SimpleInjector/Lifestyles/AsyncScopedLifestyle.cs b/src/SimpleInjector/Lifestyles/AsyncScopedLifestyle.cs
index 8f5071be6..6f36be7ce 100644
--- a/src/SimpleInjector/Lifestyles/AsyncScopedLifestyle.cs
+++ b/src/SimpleInjector/Lifestyles/AsyncScopedLifestyle.cs
@@ -90,6 +90,10 @@ public static Scope BeginScope(Container container)
/// A instance or null when there is no scope active in this context.
protected override Scope? GetCurrentScopeCore(Container container) =>
GetScopeManager(container).CurrentScope;
+
+ ///
+ protected override void SetCurrentScopeCore(Scope scope) =>
+ GetScopeManager(scope.Container!).SetCurrentScope(scope);
private static ScopeManager GetScopeManager(Container c) => c.ContainerScope.GetOrSetItem(managerKey, CreateManager);
diff --git a/src/SimpleInjector/Lifestyles/FlowingScopedLifestyle.cs b/src/SimpleInjector/Lifestyles/FlowingScopedLifestyle.cs
index 31bf634a0..2e3083cb2 100644
--- a/src/SimpleInjector/Lifestyles/FlowingScopedLifestyle.cs
+++ b/src/SimpleInjector/Lifestyles/FlowingScopedLifestyle.cs
@@ -28,5 +28,8 @@ public FlowingScopedLifestyle() : base("Scoped")
protected override Scope? GetCurrentScopeCore(Container container) =>
container.GetVerificationOrResolveScopeForCurrentThread();
+
+ protected override void SetCurrentScopeCore(Scope scope) =>
+ scope.Container!.CurrentThreadResolveScope = scope;
}
}
\ No newline at end of file
diff --git a/src/SimpleInjector/Lifestyles/LifestyleSelectorScopedHybridLifestyle.cs b/src/SimpleInjector/Lifestyles/LifestyleSelectorScopedHybridLifestyle.cs
index 3325c4979..abee62d18 100644
--- a/src/SimpleInjector/Lifestyles/LifestyleSelectorScopedHybridLifestyle.cs
+++ b/src/SimpleInjector/Lifestyles/LifestyleSelectorScopedHybridLifestyle.cs
@@ -52,6 +52,9 @@ internal override int DependencyLength(Container container) =>
protected override Scope? GetCurrentScopeCore(Container container) =>
this.CurrentLifestyle(container).GetCurrentScope(container);
+ protected override void SetCurrentScopeCore(Scope scope) =>
+ this.CurrentLifestyle(scope.Container!).SetCurrentScope(scope);
+
private ScopedLifestyle CurrentLifestyle(Container container) =>
this.selector(container) ? this.trueLifestyle : this.falseLifestyle;
diff --git a/src/SimpleInjector/Lifestyles/ScopeManager.cs b/src/SimpleInjector/Lifestyles/ScopeManager.cs
index db0204128..2a567a3d4 100644
--- a/src/SimpleInjector/Lifestyles/ScopeManager.cs
+++ b/src/SimpleInjector/Lifestyles/ScopeManager.cs
@@ -30,6 +30,8 @@ private Scope? CurrentScopeInternal
set { this.scopeReplacer(value); }
}
+ internal void SetCurrentScope(Scope scope) => this.CurrentScopeInternal = scope;
+
internal Scope BeginScope() =>
this.CurrentScopeInternal = new Scope(this.container, this, this.GetCurrentScopeWithAutoCleanup());
diff --git a/src/SimpleInjector/Lifestyles/ScopedProxyLifestyle.cs b/src/SimpleInjector/Lifestyles/ScopedProxyLifestyle.cs
index 0e20d4c4c..93d6e27bd 100644
--- a/src/SimpleInjector/Lifestyles/ScopedProxyLifestyle.cs
+++ b/src/SimpleInjector/Lifestyles/ScopedProxyLifestyle.cs
@@ -5,6 +5,8 @@ namespace SimpleInjector.Lifestyles
{
using System;
+ // This lifestyle is exposed by the Lifestyle.Scoped property and forwards any calls to the lifestyle
+ // configured in Container.Options.DefaultScopedLifestyle.
internal sealed class ScopedProxyLifestyle : ScopedLifestyle
{
public ScopedProxyLifestyle() : base("Scoped")
@@ -30,6 +32,9 @@ protected internal override Registration CreateRegistrationCore(
protected override Scope? GetCurrentScopeCore(Container container) =>
GetDefaultScopedLifestyle(container).GetCurrentScope(container);
+ protected override void SetCurrentScopeCore(Scope scope) =>
+ GetDefaultScopedLifestyle(scope.Container!).SetCurrentScope(scope);
+
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static ScopedLifestyle GetDefaultScopedLifestyle(Container container) =>
container.Options.DefaultScopedLifestyle ?? ThrowDefaultScopeLifestyleIsNotSet();
diff --git a/src/SimpleInjector/Lifestyles/ThreadScopedLifestyle.cs b/src/SimpleInjector/Lifestyles/ThreadScopedLifestyle.cs
index fd2114b19..de3b136c5 100644
--- a/src/SimpleInjector/Lifestyles/ThreadScopedLifestyle.cs
+++ b/src/SimpleInjector/Lifestyles/ThreadScopedLifestyle.cs
@@ -109,6 +109,10 @@ public static Scope BeginScope(Container container)
protected override Scope? GetCurrentScopeCore(Container container) =>
GetScopeManager(container).CurrentScope;
+ ///
+ protected override void SetCurrentScopeCore(Scope scope) =>
+ GetScopeManager(scope.Container!).SetCurrentScope(scope);
+
private static ScopeManager GetScopeManager(Container container) =>
container.ContainerScope.GetOrSetItem(ManagerKey, CreateManager);
diff --git a/src/SimpleInjector/ScopedLifestyle.cs b/src/SimpleInjector/ScopedLifestyle.cs
index b2db281bc..58ca47d85 100644
--- a/src/SimpleInjector/ScopedLifestyle.cs
+++ b/src/SimpleInjector/ScopedLifestyle.cs
@@ -92,6 +92,35 @@ public void RegisterForDisposal(Container container, IDisposable disposable)
return this.GetCurrentScopeInternal(container);
}
+ ///
+ /// Sets the given as current scope in the given context.
+ ///
+ /// The current scope.
+ /// Thrown when is a null reference.
+ /// Thrown when the is not related to
+ /// a .
+ /// Thrown when there is already an active scope in the
+ /// current context.
+ /// Thrown when the implementation does not support setting
+ /// the current scope.
+ public void SetCurrentScope(Scope scope)
+ {
+ Requires.IsNotNull(scope, nameof(scope));
+
+ if (scope.Container is null)
+ {
+ throw new ArgumentException("The scope has no related Container.", nameof(scope));
+ }
+
+ if (this.GetCurrentScope(scope.Container) != null)
+ {
+ throw new InvalidOperationException(
+ $"This method can only be called in case {nameof(GetCurrentScope)} returns null.");
+ }
+
+ this.SetCurrentScopeCore(scope);
+ }
+
///
/// Creates a delegate that upon invocation return the current for this
/// lifestyle and the given , or null when the delegate is executed outside
@@ -140,6 +169,17 @@ protected internal override Registration CreateRegistrationCore(Type concreteTyp
return currentScopeProvider.Invoke();
}
+ ///
+ /// Sets the given as current scope in the given context.
+ ///
+ /// The scope instance to set.
+ protected virtual void SetCurrentScopeCore(Scope scope)
+ {
+ throw new NotSupportedException(
+ $"Setting the current scope is not supported by the {this.Name} lifestyle " +
+ $"({this.GetType().ToFriendlyName()}.");
+ }
+
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private Scope GetCurrentScopeOrThrow(Container container)
{