我可以更改子范围的服务配置吗?

时间:2020-05-14 15:08:43

标签: asp.net-core .net-core dependency-injection

我想创建一个新的DI范围来更改服务注册,我有几个需要的用例,例如,对于ICurrentPrincipalAccessorThreadPrincipalAccessor和{{1}有两个实现},我以前在控制台中使用HttpContextPrincipalAccessor,在ASP.NET中使用ThreadPrincipalAccesor。在注入HttpContextPrincipalAccesor的库类中,直到需要从ASP.NET应用程序中的托管服务访问主体时,我才没有遇到问题。

这是我已注册的ASP.NET应用程序:

ICurrentPrincipalAccesor

然后在托管服务中,我创建一个子范围:

services.AddTransient<ICurrentPrincipalAccessor, HttpContextPrincipalAccesor>();

using (var scope = _serviceProvider.CreateScope()) { var service = scope.ServiceProvider.GetService<MyGreatServiceWhatDoSomethingWithPrincipal>(); service.DoSomething(); } 具有MyGreatServiceWhatDoSomethingWithPrincipal依赖性,并且注入了ICurrentPrincipalAccessor,但是在这种情况下,从托管服务调用,我想注入HttpContextPrincipalAccesor,我想这样做像这样:

ThreadPrincipalAccessor

是的,我可以访问using (var scope = _serviceProvider.CreateScope(collectionService => { // Define here the new scope service configurations collectionService.AddTransient<ICurrentPrincipalAccessor, ThreadPrincipalAccesor>(); })) { var service = scope.ServiceProvider.GetService<MyGreatServiceWhatDoSomethingWithPrincipal>(); service.DoSomething(); } ,如果对HttpContext.User的访问权限为空,那么好!但在更多的用例中,我想更改子作用域的服务配置。

你能帮我吗?

另一方面,我将Autofac用作IoC容器,也许我可以对Autofac做一些特定的事情。

1 个答案:

答案 0 :(得分:1)

好吧,我自己问题中的最后一个音符为我打开了一条新路。

我一直在查看ServiceProvider代码,但没有发现这样做的可能性。但是,我一直在搜索Autofac documentation,并且看到了可能。

我已经创建了自己的服务范围工厂:

public interface ISinaiServiceScopeFactory
{
    IServiceScope CreateScope(Action<IServiceCollection> configureServices);
}
public class SinaiServiceScopeFactory : ISinaiServiceScopeFactory
{
    private readonly ILifetimeScope _lifetimeScope;

    public SinaiServiceScopeFactory(ILifetimeScope lifetimeScope)
    {
        _lifetimeScope = lifetimeScope;
    }
    public IServiceScope CreateScope(Action<IServiceCollection> configureServices)
    {
        var sc = new SinaiServiceCollection();
        configureServices.Invoke(sc);

        var scope = this._lifetimeScope.BeginLifetimeScope(container =>
        {
            container.Populate(sc);
        });
        return new AutofacServiceScope(scope); // That is internal in Autofac, I have created a copy :P
    }
}

AutofacServiceScope sourcecode

以及服务提供商的扩展方法:

public static class SinaiServiceProviderExtensions
{
    public static IServiceScope CreateScope(this IServiceProvider serviceProvider,
        Action<IServiceCollection> configureServices)
    {
        var serviceScopeFactory = serviceProvider.GetService<ISinaiServiceScopeFactory>();
        return serviceScopeFactory.CreateScope(configureServices);
    }
}

测试正常:

public class SinaiServiceScopeFactoryTests
{
    interface I1 {}
    class C1 : I1 {}
    class C2 : I1 {}

    [Fact]
    public void Should_Override_Services_On_New_Scope()
    {
        var sc = new SinaiServiceCollection();
        sc.AddTransient<I1, C1>();
        sc.AddTransient<ISinaiServiceScopeFactory, SinaiServiceScopeFactory>();

        var spf = new AutofacServiceProviderFactory();
        var builder = spf.CreateBuilder(sc);
        var sp = spf.CreateServiceProvider(builder);

        var service1 = sp.GetService<I1>();

        using (var scope = sp.CreateScope(sc => sc.AddTransient<I1, C2>()))
        {
            var service2 = scope.ServiceProvider.GetService<I1>();

            Assert.Equal(typeof(C1), service1.GetType());
            Assert.Equal(typeof(C2), service2.GetType());
        }
    }
}