其他分享
首页 > 其他分享> > 不能实例化两次,不能在ConfigureServices中使用注册的singetons

不能实例化两次,不能在ConfigureServices中使用注册的singetons

作者:互联网

我有一个.Net Core项目,注册了多个Singleton,如下所示:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddLogging();

    services.AddSingleton<IConfiguration>(Configuration);
    services.AddSingleton<IDbFactory, DefaultDbFactory>();
    services.AddSingleton<IUserRepository, UserRepository>();    
    services.AddSingleton<IEmailService, EmailService>();          
    services.AddSingleton<IHostedService, BackgroundService>();
    services.AddSingleton<ISettingsRepository, SettingsRepository>();
    services.AddSingleton(typeof(TokenManager));

    var sp = services.BuildServiceProvider();
    var userRepository = sp.GetService<IUserRepository>();
    // ...
}

这是注册这些类的唯一位置,并且在任何地方都不会创建其他实例,但是我注意到构造函数被调用了两次.这是为什么?

解决方法:

TL; DR-如果您需要将注册的单例之一作为配置选项传递给AddMvc,请不要执行我所做的操作,并在ConfigureServices方法中使用GetService.跳到下面的编辑2.

我发现this answer出现了类似的问题,但是关于服务注册的顺序尚不清楚,但是确实指出了调用services.BuildServiceProvider()会生成一个新容器,该容器将导致服务被重新注册.事后看来有点道理…

编辑:

我最初的解决方法是在AddSingleton注册之前移动services.BuildServiceProvider(),但是事实却并非总是如此,正如佛陀巴迪指出的那样.

我刚刚看到了this question,它提供了更多有关正在发生的事情的详细信息.原始注册并没有像我想的那样被丢弃.我在想这一切都错了.

调用services.BuildServiceProvider()确实会构建一个新的服务提供者/容器,但这与注册无关.当调用sp.GetService< IUserRepository>()时,由新服务提供程序实例化实现IUserRepository和所有相关服务的类,但是保留了原来的IServiceProvider,这就是应用程序其余部分所使用的.

因此,当应用程序需要IUserRepository实例时(例如,因为它是UsersController的注入依赖项,现在已被请求),因此再次实例化了IUserRepository服务(及其依赖项,如果有的话),因为它现在位于原始IServiceProvider.

在上述问题的答案中,有一条评论指出,您可以防止这种情况,并使用一个IServiceProvider,“通过从ConfigureServices方法返回服务提供者实例,以便它也成为您的应用程序使用的容器”.我不确定在存储指向它的类变量之前该怎么做,因此可以通过设置app.ApplicationServices在Configure方法中将其换出-但这对我也不起作用,因为新服务提供程序缺少所有MVC服务.

有人建议改为使用Configure()中的app.ApplicationServices来访问所需的服务,但这对我不起作用,因为我需要按如下方式在ConfigureServices中使用它:

//...

serviceProvider = services.BuildServiceProvider();
var userRepository = serviceProvider.GetService<IUserRepository>();

// Add framework services
services.AddMvc(

    config =>
    {
        var policy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .Build();
        config.Filters.Add(new RolesAuthorizationFilter(userRepository));
    });

编辑2:

找到了解决方案! This brilliant post描述了我要实现的目标,并提出了一个非常整洁的解决方案.

如果您需要以MVC配置选项的形式传递对已注册单例之一的引用,而不是尝试在ConfigureServices中实例化它,则只需创建一个新类即可实现IConfigureOptions< MvcOptions>.可以接受注入的依赖关系,将该类注册为单例-其他所有内容都将得到照顾-很棒!

例:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddLogging();

    services.AddSingleton<IConfiguration>(Configuration);
    services.AddSingleton<IDbFactory, DefaultDbFactory>();
    services.AddSingleton<IUserRepository, UserRepository>();    
    services.AddSingleton<IEmailService, EmailService>();          
    services.AddSingleton<IHostedService, BackgroundService>();
    services.AddSingleton<ISettingsRepository, SettingsRepository>();
    services.AddSingleton(typeof(TokenManager));

    // var sp = services.BuildServiceProvider(); // NO NEED for this after all
    // var userRepository = sp.GetService<IUserRepository>();

    services.AddMvc(); // No config options required any more
    services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();  // Here be the magic...

    // ...
}

然后创建新类:

public class ConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
    private readonly IUserRepository userRepository;
    public ConfigureMvcOptions(IUserRepository userRepository)
    {
        this.userRepository = userRepository;
    }

    public void Configure(MvcOptions options)
    {
        var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
        options.Filters.Add(new RolesAuthorizationFilter(userRepository));
    }
}

多亏了DI的魔力,其他一切都得到了照顾.我的用户存储库单例及其依赖项仅实例化一次.

标签:service-locator,dependency-injection,singleton,asp-net-core-2-0,c
来源: https://codeday.me/bug/20191025/1925599.html