【问题标题】:Can I change the service configuration for a child scope?我可以更改子范围的服务配置吗?
【发布时间】:2021-05-07 03:59:36
【问题描述】:

我想创建一个新的 DI 范围来更改服务注册,我有几个需要它的用例,例如我曾经使用过 ICurrentPrincipalAccessorThreadPrincipalAccessorHttpContextPrincipalAccessor 的两个实现ThreadPrincipalAccesor 用于控制台,HttpContextPrincipalAccesor 用于 ASP.NET。在我注入 ICurrentPrincipalAccesor 的库类中,直到我需要从 ASP.NET 应用程序中的托管服务访问主体时,我才遇到问题。

由于它是我注册的 ASP.NET 应用程序:

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,如果它是空访问Thread.CurrentPrincipal,好的!但在更多用例中,我想更改子范围的服务配置。

你能帮帮我吗?

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

【问题讨论】:

  • answer。类似的解决方案适用于您的情况,或者您将IHttpContextAccessor 注入SwitchableBus(即CurrentPrincipalAccessorProxy)的变体。

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


【解决方案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());
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-18
    • 1970-01-01
    • 2011-08-21
    • 2021-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多