From aca87ba5480f5e6420b4aac4a1f28319af8c13c0 Mon Sep 17 00:00:00 2001 From: Lukas Tines Date: Sun, 19 May 2024 01:43:05 +0100 Subject: [PATCH 1/4] Pass root scope to WindsorScopedServiceProvider --- .../WindsorScopedServiceProvider.cs | 11 +++++++---- .../WindsorServiceProviderFactoryBase.cs | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs index 4e61f2f765..6e80f87928 100644 --- a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs +++ b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs @@ -30,16 +30,19 @@ internal class WindsorScopedServiceProvider : IServiceProvider, ISupportRequired private bool disposing; private readonly IWindsorContainer container; + + private readonly ExtensionContainerRootScope rootScope; - public WindsorScopedServiceProvider(IWindsorContainer container) + public WindsorScopedServiceProvider(IWindsorContainer container, ExtensionContainerRootScope rootScope) { this.container = container; - scope = ExtensionContainerScopeCache.Current; + this.scope = ExtensionContainerScopeCache.Current; + this.rootScope = rootScope; } public object GetService(Type serviceType) { - using(_ = new ForcedScope(scope)) + using(_ = new ForcedScope(scope, rootScope)) { return ResolveInstanceOrNull(serviceType, true); } @@ -47,7 +50,7 @@ public object GetService(Type serviceType) public object GetRequiredService(Type serviceType) { - using(_ = new ForcedScope(scope)) + using(_ = new ForcedScope(scope, rootScope)) { return ResolveInstanceOrNull(serviceType, false); } diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorServiceProviderFactoryBase.cs b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorServiceProviderFactoryBase.cs index 21363c4fd9..a51f1f7801 100644 --- a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorServiceProviderFactoryBase.cs +++ b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorServiceProviderFactoryBase.cs @@ -103,6 +103,7 @@ protected virtual void RegisterProviders(IWindsorContainer container) container.Register(Component .For() .ImplementedBy() + .DependsOn(Dependency.OnValue(rootScope)) .LifeStyle.ScopedToNetServiceScope()); } From 5aca312628e9fbe5f5aab4af637adaaa6d2d3651 Mon Sep 17 00:00:00 2001 From: Lukas Tines Date: Sun, 19 May 2024 01:43:42 +0100 Subject: [PATCH 2/4] Add TryCurrent scope to check for empty scope without throwing --- .../Scope/ExtensionContainerScopeCache.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs index d89f9dd1f2..39ad88b36b 100644 --- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs +++ b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs @@ -19,7 +19,7 @@ namespace Castle.Windsor.Extensions.DependencyInjection.Scope internal static class ExtensionContainerScopeCache { - internal static readonly AsyncLocal current = new AsyncLocal(); + private static readonly AsyncLocal current = new AsyncLocal(); /// Current scope for the thread. Initial scope will be set when calling BeginRootScope from a ExtensionContainerRootScope instance. /// Thrown when there is no scope available. internal static ExtensionContainerScopeBase Current @@ -27,5 +27,7 @@ internal static ExtensionContainerScopeBase Current get => current.Value ?? throw new InvalidOperationException("No scope available"); set => current.Value = value; } + + internal static ExtensionContainerScopeBase TryCurrent => current.Value; } } \ No newline at end of file From ab8a3511363e8f36a4ed65766065a47814233b75 Mon Sep 17 00:00:00 2001 From: Lukas Tines Date: Sun, 19 May 2024 01:44:06 +0100 Subject: [PATCH 3/4] Use TryCurrent instead of accessing private fields --- .../Scope/ExtensionContainerScope.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs index 2ff5a5c497..8fc610fc10 100644 --- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs +++ b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs @@ -35,9 +35,9 @@ internal static ExtensionContainerScopeBase BeginScope() public override void Dispose() { - if (ExtensionContainerScopeCache.current.Value == this) + if (ExtensionContainerScopeCache.TryCurrent == this) { - ExtensionContainerScopeCache.current.Value = parent; + ExtensionContainerScopeCache.Current = parent; } base.Dispose(); } From 1700f88992cb52dc01443fbc332aa03a40ebfbe2 Mon Sep 17 00:00:00 2001 From: Lukas Tines Date: Sun, 19 May 2024 01:45:03 +0100 Subject: [PATCH 4/4] When there isn't a scope use root as previous. This can happen with called from a new thread --- .../Scope/ForcedScope.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs index c9d41dfa66..c4fb8fe5ee 100644 --- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs +++ b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs @@ -23,9 +23,9 @@ internal class ForcedScope : IDisposable { private readonly ExtensionContainerScopeBase scope; private readonly ExtensionContainerScopeBase previousScope; - internal ForcedScope(ExtensionContainerScopeBase scope) + internal ForcedScope(ExtensionContainerScopeBase scope, ExtensionContainerRootScope rootScope) { - previousScope = ExtensionContainerScopeCache.Current; + previousScope = ExtensionContainerScopeCache.TryCurrent ?? rootScope; this.scope = scope; ExtensionContainerScopeCache.Current = scope; }