diff --git a/.editorconfig b/.editorconfig
index 67ae50291..eaf2b54d4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,3 +10,4 @@ indent_size = 2
[*.cs]
indent_style = tab
indent_size = 4
+csharp_new_line_before_open_brace = all
\ No newline at end of file
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 000000000..f8d2ce57a
--- /dev/null
+++ b/src/Castle.Windsor.Extensions.DependencyInjection.Tests/ResolveFromThreadpoolUnsafe.cs
@@ -0,0 +1,757 @@
+using Castle.MicroKernel.Lifestyle;
+using Castle.MicroKernel.Registration;
+using Castle.Windsor.Extensions.DependencyInjection.Extensions;
+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
+{
+ [CollectionDefinition(nameof(DoNotParallelize), DisableParallelization = true)]
+ public class DoNotParallelize { }
+
+ ///
+ /// These is the original Castle Windsor Dependency Injection behavior.
+ ///
+ public class ResolveFromThreadpoolUnsafe_NetStatic : AbstractResolveFromThreadpoolUnsafe
+ {
+ public ResolveFromThreadpoolUnsafe_NetStatic() : base(false)
+ {
+ }
+
+ #region "Singleton"
+
+ ///
+ /// This test will Succeed is we use standard Castle Windsor Singleton lifestyle instead of the custom
+ /// NetStatic lifestyle.
+ ///
+ [Fact]
+ public async Task Cannot_Resolve_LifestyleNetStatic_From_WindsorContainer_NoRootScopeAvailable()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifeStyle.NetStatic()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ try
+ {
+ var actualUserService = container.Resolve();
+ Assert.NotNull(actualUserService);
+ }
+ catch (Exception ex)
+ {
+ tcs.SetException(ex);
+ return;
+ }
+ tcs.SetResult(actualUserService);
+ }, null);
+
+ // Wait for the work item to complete.
+ var ex = await Catches.ExceptionAsync(async () =>
+ {
+ var task = tcs.Task;
+ IUserService result = await task;
+ // The test succeeds if we use standard Castle Windsor Singleton lifestyle instead of the custom NetStatic lifestyle.
+ Assert.NotNull(result);
+ });
+
+ // This test will fail if we use NetStatic lifestyle
+ Assert.NotNull(ex);
+ Assert.IsType(ex);
+ Assert.Equal("No root scope available.", ex.Message);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ #endregion
+ }
+
+ ///
+ /// Mapping NetStatic to usual Singleton lifestyle.
+ ///
+ public class ResolveFromThreadpoolUnsafe_Singleton : AbstractResolveFromThreadpoolUnsafe
+ {
+ public ResolveFromThreadpoolUnsafe_Singleton() : base(true)
+ {
+ }
+
+ #region "Singleton"
+
+ ///
+ /// This test will Succeed is we use standard Castle Windsor Singleton lifestyle instead of the custom
+ /// NetStatic lifestyle.
+ ///
+ [Fact]
+ public async Task Can_Resolve_LifestyleNetStatic_From_WindsorContainer()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifeStyle.NetStatic()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ try
+ {
+ var actualUserService = container.Resolve();
+ Assert.NotNull(actualUserService);
+ }
+ catch (Exception ex)
+ {
+ tcs.SetException(ex);
+ return;
+ }
+ tcs.SetResult(actualUserService);
+ }, null);
+
+ // Wait for the work item to complete.
+ var ex = await Catches.ExceptionAsync(async () =>
+ {
+ var task = tcs.Task;
+ IUserService result = await task;
+ // The test succeeds if we use standard Castle Windsor Singleton lifestyle instead of the custom NetStatic lifestyle.
+ Assert.NotNull(result);
+ });
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ #endregion
+ }
+
+ ///
+ /// relying on static state (WindsorDependencyInjectionOptions) is not good for tests
+ /// that might run in parallel, can lead to false positives / negatives.
+ ///
+ [Collection(nameof(DoNotParallelize))]
+ public abstract class AbstractResolveFromThreadpoolUnsafe
+ {
+ protected AbstractResolveFromThreadpoolUnsafe(bool mapNetStaticToSingleton)
+ {
+ WindsorDependencyInjectionOptions.MapNetStaticToSingleton = mapNetStaticToSingleton;
+ }
+
+ #region Singleton
+
+ /*
+ * Singleton tests should never fail, given you have a container instance you should always
+ * be able to resolve a singleton from it.
+ */
+
+ [Fact]
+ public async Task Can_Resolve_LifestyleSingleton_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);
+
+ 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);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ [Fact]
+ public async Task Can_Resolve_LifestyleSingleton_From_WindsorContainer()
+ {
+ 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);
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ try
+ {
+ var actualUserService = container.Resolve();
+ 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);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ [Fact]
+ public async Task Can_Resolve_LifestyleNetStatic_From_ServiceProvider()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifeStyle.NetStatic()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ 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);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ #endregion
+
+ #region Scoped
+
+ /*
+ * Scoped tests might fail if for whatever reason you do not have a current scope
+ * (like when you run from Threadpool.UnsafeQueueUserWorkItem).
+ */
+
+ ///
+ /// This test will fail because the service provider adapter
+ /// does not create a standard Castle Windsor scope
+ ///
+ [Fact]
+ public async Task Cannot_Resolve_LifestyleScoped_From_ServiceProvider()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifestyleScoped()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ // must create a standard Castle Windsor scope (not managed by the adapter)
+ using (var s = container.BeginScope())
+ {
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ 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 ex = await Catches.ExceptionAsync(async () =>
+ {
+ var task = tcs.Task;
+ IUserService result = await task;
+ Assert.NotNull(result);
+ });
+
+ Assert.NotNull(ex);
+ Assert.IsType(ex);
+ Assert.StartsWith("Scope was not available. Did you forget to call container.BeginScope()?", ex.Message);
+ }
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ ///
+ /// This test will fail because the service provider adapter
+ /// does not create a standard Castle Windsor scope
+ ///
+ [Fact]
+ public async Task Cannot_Resolve_LifestyleScoped_From_WindsorContainer()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifestyleScoped()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ // must create a standard Castle Windsor scope (not managed by the adapter)
+ using (var s = container.BeginScope())
+ {
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ try
+ {
+ var actualUserService = container.Resolve();
+ Assert.NotNull(actualUserService);
+ }
+ catch (Exception ex)
+ {
+ tcs.SetException(ex);
+ return;
+ }
+ tcs.SetResult(actualUserService);
+ }, null);
+
+ // Wait for the work item to complete.
+ var ex = await Catches.ExceptionAsync(async () =>
+ {
+ var task = tcs.Task;
+ IUserService result = await task;
+ Assert.NotNull(result);
+ });
+
+ Assert.NotNull(ex);
+ Assert.IsType(ex);
+ Assert.StartsWith("Scope was not available. Did you forget to call container.BeginScope()?", ex.Message);
+ }
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ ///
+ /// This test succeeds because WindsorScopedServiceProvider captured the root scope on creation
+ /// and forced it to be current before service resolution.
+ /// Scoped is tied to the rootscope = potential memory leak.
+ ///
+ [Fact]
+ public async Task Can_Resolve_LifestyleScopedToNetServiceScope_From_ServiceProvider_MemoryLeak()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifeStyle.ScopedToNetServiceScope()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ 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);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ [Fact]
+ public async Task Cannot_Resolve_LifestyleScopedToNetServiceScope_From_WindsorContainer()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifeStyle.ScopedToNetServiceScope()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ try
+ {
+ var actualUserService = container.Resolve();
+ Assert.NotNull(actualUserService);
+ }
+ catch (Exception ex)
+ {
+ tcs.SetException(ex);
+ return;
+ }
+ tcs.SetResult(actualUserService);
+ }, null);
+
+ // Wait for the work item to complete.
+ var ex = await Catches.ExceptionAsync(async () =>
+ {
+ var task = tcs.Task;
+ IUserService result = await task;
+ Assert.NotNull(result);
+ });
+
+ Assert.NotNull(ex);
+ Assert.IsType(ex);
+ Assert.StartsWith("No scope available", ex.Message);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ #endregion
+
+ #region Transient
+
+ /*
+ * Transient tests failure is questionable:
+ * - if you have a container you should be able to resolve transient without a scope,
+ * but they might be tracked by the container itself (or the IServiceProvider)
+ * - when windsor container is disposed all transient services are disposed as well
+ * - when a IServiceProvider is disposed all transient services (created by it) are disposed as well
+ * - problem is: we have una instance of a windsor container passed on to multiple instances of IServiceProvider
+ * one solution will be to tie the Transients to a scope, and the scope is tied to service provider
+ * when both of them are disposed, the transient services are disposed as well
+ */
+
+ [Fact]
+ public async Task Can_Resolve_LifestyleTransient_From_ServiceProvider()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifestyleTransient()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ 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);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ [Fact]
+ public async Task Can_Resolve_LifestyleTransient_From_WindsorContainer()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifestyleTransient()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ try
+ {
+ var actualUserService = container.Resolve();
+ 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);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ ///
+ /// This test succeeds because WindsorScopedServiceProvider captured the root scope on creation
+ /// and forced it to be current before service resolution.
+ /// Transient is tied to the rootscope = potential memory leak.
+ ///
+ [Fact]
+ public async Task Can_Resolve_LifestyleNetTransient_From_ServiceProvider_MemoryLeak()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifestyleNetTransient()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ 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);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ [Fact]
+ public async Task Cannot_Resolve_LifestyleNetTransient_From_WindsorContainer_NoScopeAvailable()
+ {
+ var serviceProvider = new ServiceCollection();
+ var container = new WindsorContainer();
+ var f = new WindsorServiceProviderFactory(container);
+ f.CreateBuilder(serviceProvider);
+
+ container.Register(
+ Component.For().ImplementedBy().LifestyleNetTransient()
+ );
+
+ IServiceProvider sp = f.CreateServiceProvider(container);
+
+ var actualUserService = sp.GetService();
+ Assert.NotNull(actualUserService);
+
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ ThreadPool.UnsafeQueueUserWorkItem(state =>
+ {
+ try
+ {
+ var actualUserService = container.Resolve();
+ Assert.NotNull(actualUserService);
+ }
+ catch (Exception ex)
+ {
+ tcs.SetException(ex);
+ return;
+ }
+ tcs.SetResult(actualUserService);
+ }, null);
+
+ // Wait for the work item to complete.
+ var ex = await Catches.ExceptionAsync(async () =>
+ {
+ var task = tcs.Task;
+ IUserService result = await task;
+ Assert.NotNull(result);
+ });
+
+ Assert.NotNull(ex);
+ Assert.IsType(ex);
+ Assert.StartsWith("No scope available", ex.Message);
+
+ (sp as IDisposable)?.Dispose();
+ container.Dispose();
+ }
+
+ #endregion
+
+ /*
+ * Missing tests: we should also test what happens with injected IServiceProvider (what scope do they get?)
+ * Injected IServiceProvider might or might not have a scope (it depends on AsyncLocal value).
+ */
+ }
+
+ public static class Catches
+ {
+ public static Exception Exception(Action action)
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ return null;
+ }
+
+ public async static Task ExceptionAsync(Func func)
+ {
+ try
+ {
+ await func();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs b/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs
index 38d46d34d..5b7b95f9d 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/Extensions/WindsorExtensions.cs
@@ -49,6 +49,13 @@ public static ComponentRegistration LifestyleNetTransient(th
///
public static ComponentRegistration NetStatic(this LifestyleGroup lifestyle) where TService : class
{
+ // I don't think we need this lifestyle at all, usual Singleton should be good enough;
+ // also we maybe don't need the whole rootscope thing. A normal scope set as current should be enough
+ // otherwise we should revert to static rootscope
+ if (WindsorDependencyInjectionOptions.MapNetStaticToSingleton)
+ {
+ return lifestyle.Singleton;
+ }
return lifestyle
.Scoped();
}
diff --git a/src/Castle.Windsor.Extensions.DependencyInjection/RegistrationAdapter.cs b/src/Castle.Windsor.Extensions.DependencyInjection/RegistrationAdapter.cs
index 974c02ae8..ad27dce4e 100644
--- a/src/Castle.Windsor.Extensions.DependencyInjection/RegistrationAdapter.cs
+++ b/src/Castle.Windsor.Extensions.DependencyInjection/RegistrationAdapter.cs
@@ -18,17 +18,17 @@ namespace Castle.Windsor.Extensions.DependencyInjection
using Castle.MicroKernel.Registration;
using Castle.Windsor.Extensions.DependencyInjection.Extensions;
-
+
using Microsoft.Extensions.DependencyInjection;
- internal class RegistrationAdapter
+ internal static class RegistrationAdapter
{
public static IRegistration FromOpenGenericServiceDescriptor(Microsoft.Extensions.DependencyInjection.ServiceDescriptor service)
{
ComponentRegistration