diff --git a/.gitignore b/.gitignore
index 17b24b2466..253f845d5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
_ReSharper*/
*.csproj.ReSharper
.cr
+.idea
# Build output
build/
diff --git a/Castle.Windsor.sln b/Castle.Windsor.sln
index b2e7f6ee36..202d7ae4de 100644
--- a/Castle.Windsor.sln
+++ b/Castle.Windsor.sln
@@ -64,6 +64,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildScripts", "BuildScript
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Castle.Windsor.Extensions.Hosting", "src\Castle.Windsor.Extensions.Hosting\Castle.Windsor.Extensions.Hosting.csproj", "{4F123B9A-F9B3-4F81-A0EC-8C8DEF03298A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp", "src\Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp\Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp.csproj", "{418B8D35-3225-45B6-AAE0-B1C4B3303CFE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -142,6 +144,10 @@ Global
{4F123B9A-F9B3-4F81-A0EC-8C8DEF03298A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F123B9A-F9B3-4F81-A0EC-8C8DEF03298A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F123B9A-F9B3-4F81-A0EC-8C8DEF03298A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {418B8D35-3225-45B6-AAE0-B1C4B3303CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {418B8D35-3225-45B6-AAE0-B1C4B3303CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {418B8D35-3225-45B6-AAE0-B1C4B3303CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {418B8D35-3225-45B6-AAE0-B1C4B3303CFE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -164,6 +170,7 @@ Global
{7C2F5733-0E06-4201-A15A-1ED1F3308DB4} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
{DD7F7887-F27C-4C52-AA41-0F386D4D9996} = {7E507A42-984B-470D-8A0C-648B9AF8E1DC}
{4F123B9A-F9B3-4F81-A0EC-8C8DEF03298A} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
+ {418B8D35-3225-45B6-AAE0-B1C4B3303CFE} = {7935AFF5-BF6D-4D59-8D66-058B6557F70F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC2A1EB6-49EC-4064-AD8B-29439E8A45E4}
diff --git a/Castle.Windsor.sln.DotSettings b/Castle.Windsor.sln.DotSettings
index 2a21d2fed7..6b04a377f5 100644
--- a/Castle.Windsor.sln.DotSettings
+++ b/Castle.Windsor.sln.DotSettings
@@ -267,6 +267,7 @@ limitations under the License.
True
True
True
+ True
True
True
True
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp.csproj b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp.csproj
new file mode 100644
index 0000000000..18b78daac6
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp.csproj
@@ -0,0 +1,16 @@
+
+
+
+ netcoreapp3.1;net6.0
+
+ false
+
+ Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp
+ Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp
+
+
+
+
+
+
+
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/DecoratedUserService.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/DecoratedUserService.cs
new file mode 100644
index 0000000000..3c308c5f7b
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/DecoratedUserService.cs
@@ -0,0 +1,26 @@
+// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp.Components
+{
+ public class DecoratedUserService : UserService
+ {
+ public IUserService UserService { get; }
+
+ public DecoratedUserService(IUserService userService)
+ {
+ UserService = userService;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/IUserService.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/IUserService.cs
new file mode 100644
index 0000000000..cf42352e15
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/IUserService.cs
@@ -0,0 +1,21 @@
+// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp.Components
+{
+ public interface IUserService
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/UserService.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/UserService.cs
new file mode 100644
index 0000000000..b20841e0ea
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Components/UserService.cs
@@ -0,0 +1,21 @@
+// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp.Components
+{
+ public class UserService : IUserService
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Program.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Program.cs
new file mode 100644
index 0000000000..16fc933249
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp/Program.cs
@@ -0,0 +1,85 @@
+// Copyright 2004-2023 Castle Project - http://www.castleproject.org/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp
+{
+ using Microsoft.AspNetCore.Builder;
+#if NETCOREAPP3_1
+ using Microsoft.AspNetCore.Hosting;
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.Extensions.DependencyInjection;
+#endif
+ using Microsoft.Extensions.Hosting;
+
+ public class Program
+ {
+#if NET6_0
+ public static void Main(string[] args)
+ {
+ // create web application builder
+ WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
+
+ // configure container
+ WindsorContainer container = new WindsorContainer();
+ WindsorServiceProviderFactory factory = new WindsorServiceProviderFactory(container);
+ IHostBuilder _ = builder.Host.UseServiceProviderFactory(factory);
+
+ // configure pipeline
+ WebApplication app = builder.Build();
+ app.MapGet("/", () => "Hello world");
+
+ // start
+ app.Run();
+ }
+#elif NETCOREAPP3_1
+ public static void Main(string[] args)
+ {
+ // create host builder
+ IHost app =
+ CreateHostBuilder(args)
+ .Build();
+
+ // start
+ app.Run();
+ }
+
+ private static IHostBuilder CreateHostBuilder(string[] args)
+ {
+ WindsorContainer container = new WindsorContainer();
+ WindsorServiceProviderFactory factory = new WindsorServiceProviderFactory(container);
+ return Host
+ .CreateDefaultBuilder(args)
+ .UseServiceProviderFactory(factory)
+ .ConfigureWebHostDefaults(hostBuilder => hostBuilder.UseStartup());
+ }
+
+ public class Startup
+ {
+ public void ConfigureServices(IServiceCollection serviceCollection)
+ {
+ }
+
+ public void Configure(IApplicationBuilder app)
+ {
+ app.Map(
+ string.Empty,
+ appBuilder =>
+ {
+ appBuilder.Run(async (context) => await context.Response.WriteAsync("Hello world"));
+ });
+ }
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/AspNetCoreScopeTests.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/AspNetCoreScopeTests.cs
new file mode 100644
index 0000000000..0543a5051b
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/AspNetCoreScopeTests.cs
@@ -0,0 +1,39 @@
+// Copyright 2004-2023 Castle Project - http://www.castleproject.org/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace Castle.Windsor.Extensions.DependencyInjection.Tests
+{
+ using System.Net;
+ using System.Net.Http;
+ using System.Threading.Tasks;
+
+ using Castle.Windsor.Extensions.DependencyInjection.Tests.AspNetCoreApp;
+
+ using Microsoft.AspNetCore.Mvc.Testing;
+
+ using Xunit;
+
+ public class AspNetCoreScopeTests
+ {
+ [Fact]
+ public async Task TestScopeIsAvailable()
+ {
+ WebApplicationFactory factory = new WebApplicationFactory();
+ HttpClient client = factory.CreateClient();
+ HttpResponseMessage response = await client.GetAsync("/");
+ Assert.NotNull(response);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+ }
+}
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj
index 6ef705103a..1b052eca3b 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/Castle.Windsor.Extensions.DependencyInjection.Tests.csproj
@@ -20,17 +20,22 @@
-
-
+
+
+
-
+
+
+
+
+
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/ResolveFromThreadpoolUnsafe.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/ResolveFromThreadpoolUnsafe.cs
new file mode 100644
index 0000000000..754ea0381f
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/ResolveFromThreadpoolUnsafe.cs
@@ -0,0 +1,56 @@
+using Castle.MicroKernel.Registration;
+using Castle.Windsor.Extensions.DependencyInjection.Tests.Components;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Castle.Windsor.Extensions.DependencyInjection.Tests {
+ public class ResolveFromThreadpoolUnsafe {
+ [Fact]
+ public async Task Can_Resolve_From_ServiceProvider() {
+
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ /*
+ ThreadPool.UnsafeQueueUserWorkItem(state => {
+ // resolving using castle (without scopes) works
+ var actualUserService = container.Resolve(nameof(UserService));
+ Assert.NotNull(actualUserService);
+ }, null);
+ */
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state => {
+ try {
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+ }
+ catch (Exception ex) {
+ tcs.SetException(ex);
+ return;
+ }
+ tcs.SetResult(actualUserService);
+ }, null);
+
+ // Wait for the work item to complete.
+ var task = tcs.Task;
+ IUserService result = await task;
+ Assert.NotNull(result);
+ }
+ }
+}
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/SkippableDependencyInjectionSpecificationTests.cs b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/SkippableDependencyInjectionSpecificationTests.cs
index 12537699c6..8304da2931 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection.Tests/SkippableDependencyInjectionSpecificationTests.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/SkippableDependencyInjectionSpecificationTests.cs
@@ -19,12 +19,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Diagnostics;
-using System.Linq;
-
+// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection.Specification
{
+ using System;
+ using System.Linq;
+
+ using StackTrace = System.Diagnostics.StackTrace;
+
public abstract class SkippableDependencyInjectionSpecificationTests : DependencyInjectionSpecificationTests
{
public string[] SkippedTests => new[] { "SingletonServiceCanBeResolvedFromScope" };
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs
index 38d46d34d7..38e23c1759 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs
@@ -39,7 +39,7 @@ public static ComponentRegistration ScopedToNetServiceScope(
public static ComponentRegistration LifestyleNetTransient(this ComponentRegistration registration) where TService : class
{
return registration
- .Attribute(ExtensionContainerScopeBase.TransientMarker).Eq(Boolean.TrueString)
+ .Attribute(ExtensionContainerScope.TransientMarker).Eq(Boolean.TrueString)
.LifeStyle.ScopedToNetServiceScope(); //.NET core expects new instances but release on scope dispose
}
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScope.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScope.cs
index 95f29a07d3..d2358270b5 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScope.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScope.cs
@@ -14,16 +14,17 @@
namespace Castle.Windsor.Extensions.DependencyInjection.Scope
{
- internal class ExtensionContainerRootScope : ExtensionContainerScopeBase
+ internal class ExtensionContainerRootScope : ExtensionContainerScope
{
-
+ private ExtensionContainerRootScope() : base(null, null)
+ {
+ }
+
public static ExtensionContainerRootScope BeginRootScope()
{
var scope = new ExtensionContainerRootScope();
- ExtensionContainerScopeCache.Current = scope;
+ current.Value = scope;
return scope;
}
-
- internal override ExtensionContainerScopeBase RootScope => this;
}
}
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScopeAccessor.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScopeAccessor.cs
index 25139efec9..e15b5455e0 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScopeAccessor.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerRootScopeAccessor.cs
@@ -23,7 +23,17 @@ internal class ExtensionContainerRootScopeAccessor : IScopeAccessor
{
public ILifetimeScope GetScope(CreationContext context)
{
- return ExtensionContainerScopeCache.Current.RootScope ?? throw new InvalidOperationException("No root scope available");
+ if (ExtensionContainerScope.Current == null)
+ {
+ throw new InvalidOperationException("No root scope");
+ }
+
+ if (ExtensionContainerScope.Current.RootScope == null)
+ {
+ throw new InvalidOperationException("No root scope");
+ }
+
+ return ExtensionContainerScope.Current.RootScope;
}
public void Dispose()
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs
index 2ff5a5c497..ead6cec9ed 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScope.cs
@@ -1,4 +1,4 @@
-// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
+// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,32 +14,93 @@
namespace Castle.Windsor.Extensions.DependencyInjection.Scope
{
- internal class ExtensionContainerScope : ExtensionContainerScopeBase
+ using System;
+ using System.Threading;
+
+ using Castle.Core;
+ using Castle.MicroKernel;
+ using Castle.MicroKernel.Lifestyle.Scoped;
+
+ internal class ExtensionContainerScope : ILifetimeScope, IDisposable
{
- private readonly ExtensionContainerScopeBase parent;
+ public static ExtensionContainerScope Current => current.Value;
+ public static string TransientMarker = "Transient";
+ protected static readonly AsyncLocal current = new AsyncLocal();
+ private readonly ExtensionContainerScope parent;
+ private readonly ExtensionContainerRootScope rootScope;
+ private readonly IScopeCache scopeCache;
- protected ExtensionContainerScope()
+ protected ExtensionContainerScope(
+ ExtensionContainerScope parent,
+ ExtensionContainerRootScope rootScope)
{
- parent = ExtensionContainerScopeCache.Current;
+ scopeCache = new ScopeCache();
+ this.parent = parent ?? rootScope;
+ this.rootScope = rootScope;
}
- internal override ExtensionContainerScopeBase RootScope { get; set; }
-
+ public ExtensionContainerRootScope RootScope
+ => this as ExtensionContainerRootScope ?? rootScope;
- internal static ExtensionContainerScopeBase BeginScope()
+ public static ExtensionContainerScope BeginScope(ExtensionContainerScope parent, ExtensionContainerRootScope rootScope)
{
- var scope = new ExtensionContainerScope { RootScope = ExtensionContainerScopeCache.Current.RootScope };
- ExtensionContainerScopeCache.Current = scope;
+ if (rootScope == null)
+ throw new ArgumentNullException(nameof(rootScope));
+
+ var scope = new ExtensionContainerScope(parent, rootScope);
+ current.Value = scope;
return scope;
}
- public override void Dispose()
+ public void Dispose()
+ {
+ var disposableCache = scopeCache as IDisposable;
+ if (disposableCache != null)
+ {
+ disposableCache.Dispose();
+ }
+
+ current.Value = parent;
+ }
+
+ public Burden GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
{
- if (ExtensionContainerScopeCache.current.Value == this)
+ lock (scopeCache)
+ {
+ // Add transient's burden to scope so it gets released
+ if (model.Configuration.Attributes.Get(TransientMarker) == bool.TrueString)
+ {
+ var transientBurden = createInstance((_) => {});
+ scopeCache[transientBurden] = transientBurden;
+ return transientBurden;
+ }
+
+ var scopedBurden = scopeCache[model];
+ if (scopedBurden != null)
+ {
+ return scopedBurden;
+ }
+ scopedBurden = createInstance((_) => {});
+ scopeCache[model] = scopedBurden;
+ return scopedBurden;
+ }
+ }
+
+ ///
+ /// Forces a specific for 'using' block. In .NET scope is tied to an instance of not a thread or async context
+ ///
+ internal class ForcedScope : IDisposable
+ {
+ private readonly ExtensionContainerScope previousScope;
+ public ForcedScope(ExtensionContainerScope scope)
+ {
+ previousScope = ExtensionContainerScope.Current;
+ ExtensionContainerScope.current.Value = scope;
+ }
+ public void Dispose()
{
- ExtensionContainerScopeCache.current.Value = parent;
+ ExtensionContainerScope.current.Value = previousScope;
}
- base.Dispose();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeAccessor.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeAccessor.cs
index 9042944d75..cf6621fdab 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeAccessor.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeAccessor.cs
@@ -14,6 +14,8 @@
namespace Castle.Windsor.Extensions.DependencyInjection.Scope
{
+ using System;
+
using Castle.MicroKernel.Context;
using Castle.MicroKernel.Lifestyle.Scoped;
@@ -21,7 +23,11 @@ internal class ExtensionContainerScopeAccessor : IScopeAccessor
{
public ILifetimeScope GetScope(CreationContext context)
{
- return ExtensionContainerScopeCache.Current;
+ if(ExtensionContainerScope.Current == null)
+ {
+ throw new InvalidOperationException("No scope available");
+ }
+ return ExtensionContainerScope.Current;
}
public void Dispose()
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeBase.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeBase.cs
deleted file mode 100644
index edb784dfcb..0000000000
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeBase.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-namespace Castle.Windsor.Extensions.DependencyInjection.Scope
-{
- using System;
-
- using Castle.Core;
- using Castle.MicroKernel;
- using Castle.MicroKernel.Lifestyle.Scoped;
-
- internal abstract class ExtensionContainerScopeBase : ILifetimeScope
- {
- public static readonly string TransientMarker = "Transient";
- private readonly IScopeCache scopeCache;
-
- protected ExtensionContainerScopeBase()
- {
- scopeCache = new ScopeCache();
- }
-
- internal virtual ExtensionContainerScopeBase RootScope { get; set; }
-
- public virtual void Dispose()
- {
- if (scopeCache is IDisposable disposableCache)
- {
- disposableCache.Dispose();
- }
- }
-
- public Burden GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
- {
- lock (scopeCache)
- {
- // Add transient's burden to scope so it gets released
- if (model.Configuration.Attributes.Get(TransientMarker) == bool.TrueString)
- {
- var transientBurden = createInstance(_ => {});
- scopeCache[transientBurden] = transientBurden;
- return transientBurden;
- }
-
- var scopedBurden = scopeCache[model];
- if (scopedBurden != null)
- {
- return scopedBurden;
- }
- scopedBurden = createInstance((_) => {});
- scopeCache[model] = scopedBurden;
- return scopedBurden;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs
deleted file mode 100644
index d89f9dd1f2..0000000000
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ExtensionContainerScopeCache.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-namespace Castle.Windsor.Extensions.DependencyInjection.Scope
-{
- using System;
- using System.Threading;
-
- internal static class ExtensionContainerScopeCache
- {
- internal 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
- {
- get => current.Value ?? throw new InvalidOperationException("No scope available");
- set => current.Value = value;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs
deleted file mode 100644
index c9d41dfa66..0000000000
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ForcedScope.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2004-2020 Castle Project - http://www.castleproject.org/
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-namespace Castle.Windsor.Extensions.DependencyInjection.Scope
-{
- using System;
-
- ///
- /// Forces a specific for 'using' block. In .NET scope is tied to an instance of not a thread or async context
- ///
- internal class ForcedScope : IDisposable
- {
- private readonly ExtensionContainerScopeBase scope;
- private readonly ExtensionContainerScopeBase previousScope;
- internal ForcedScope(ExtensionContainerScopeBase scope)
- {
- previousScope = ExtensionContainerScopeCache.Current;
- this.scope = scope;
- ExtensionContainerScopeCache.Current = scope;
- }
- public void Dispose()
- {
- if(ExtensionContainerScopeCache.Current != scope) return;
- ExtensionContainerScopeCache.Current = previousScope;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ServiceScope.cs b/src/Castle.Windsor.Extensions.DependencyInjection/ServiceScope.cs
similarity index 67%
rename from src/Castle.Windsor.Extensions.DependencyInjection/Scope/ServiceScope.cs
rename to src/Castle.Windsor.Extensions.DependencyInjection/ServiceScope.cs
index c033687851..e8a0d24d43 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/ServiceScope.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/ServiceScope.cs
@@ -12,23 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-namespace Castle.Windsor.Extensions.DependencyInjection.Scope
+namespace Castle.Windsor.Extensions.DependencyInjection
{
using System;
-
+
using Microsoft.Extensions.DependencyInjection;
internal class ServiceScope : IServiceScope
{
private readonly IDisposable scope;
+ private readonly IServiceProvider serviceProvider;
public ServiceScope(IDisposable windsorScope, IServiceProvider serviceProvider)
{
- scope = windsorScope ?? throw new ArgumentNullException(nameof(scope));
- ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ if(windsorScope == null)
+ {
+ throw new ArgumentNullException(nameof(scope));
+ }
+
+ if(serviceProvider == null)
+ {
+ throw new ArgumentNullException(nameof(serviceProvider));
+ }
+
+ this.scope = windsorScope;
+ this.serviceProvider = serviceProvider;
}
- public IServiceProvider ServiceProvider { get; }
+ public IServiceProvider ServiceProvider => serviceProvider;
public void Dispose()
{
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/WindsorScopeFactory.cs b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopeFactory.cs
similarity index 72%
rename from src/Castle.Windsor.Extensions.DependencyInjection/Scope/WindsorScopeFactory.cs
rename to src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopeFactory.cs
index 0686ea95a6..4264ff28fb 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Scope/WindsorScopeFactory.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopeFactory.cs
@@ -13,26 +13,35 @@
// limitations under the License.
-namespace Castle.Windsor.Extensions.DependencyInjection.Scope
+namespace Castle.Windsor.Extensions.DependencyInjection
{
using System;
using Castle.Windsor;
+ using Castle.Windsor.Extensions.DependencyInjection.Scope;
using Microsoft.Extensions.DependencyInjection;
internal class WindsorScopeFactory : IServiceScopeFactory
{
private readonly IWindsorContainer scopeFactoryContainer;
-
- public WindsorScopeFactory(IWindsorContainer container)
+ private readonly ExtensionContainerRootScope rootScope;
+
+ public WindsorScopeFactory(
+ IWindsorContainer container,
+ ExtensionContainerRootScope rootScope)
{
scopeFactoryContainer = container;
+ this.rootScope = rootScope;
}
public IServiceScope CreateScope()
{
- var scope = ExtensionContainerScope.BeginScope();
+ var scope =
+ ExtensionContainerScope
+ .BeginScope(
+ ExtensionContainerScope.Current,
+ rootScope);
//since WindsorServiceProvider is scoped, this gives us new instance
var provider = scopeFactoryContainer.Resolve();
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs
index 4e61f2f765..cbbc331b33 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/WindsorScopedServiceProvider.cs
@@ -26,20 +26,20 @@ namespace Castle.Windsor.Extensions.DependencyInjection
internal class WindsorScopedServiceProvider : IServiceProvider, ISupportRequiredService, IDisposable
{
- private readonly ExtensionContainerScopeBase scope;
- private bool disposing;
+ private readonly ExtensionContainerScope scope;
+ private bool disposing = false;
private readonly IWindsorContainer container;
public WindsorScopedServiceProvider(IWindsorContainer container)
{
this.container = container;
- scope = ExtensionContainerScopeCache.Current;
+ this.scope = ExtensionContainerScope.Current;
}
public object GetService(Type serviceType)
{
- using(_ = new ForcedScope(scope))
+ using(var fs = new ExtensionContainerScope.ForcedScope(scope))
{
return ResolveInstanceOrNull(serviceType, true);
}
@@ -47,7 +47,7 @@ public object GetService(Type serviceType)
public object GetRequiredService(Type serviceType)
{
- using(_ = new ForcedScope(scope))
+ using(var fs = new ExtensionContainerScope.ForcedScope(scope))
{
return ResolveInstanceOrNull(serviceType, false);
}
@@ -55,12 +55,20 @@ public object GetRequiredService(Type serviceType)
public void Dispose()
{
- if (!(scope is ExtensionContainerRootScope)) return;
- if (disposing) return;
- disposing = true;
- var disposableScope = scope as IDisposable;
- disposableScope?.Dispose();
- container.Dispose();
+ if(scope is ExtensionContainerRootScope)
+ {
+ if(!disposing)
+ {
+ disposing = true;
+ var disposableScope = scope as IDisposable;
+ if(disposableScope != null)
+ {
+ disposableScope.Dispose();
+ }
+ container.Dispose();
+ }
+
+ }
}
private object ResolveInstanceOrNull(Type serviceType, bool isOptional)
{