本文介绍了如果没有实例化两次,则无法在ConfigureServices中使用已注册的singetton的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个.Net Core项目,该项目注册了以下多个Singleton:

I have a .Net Core project that registers a number of Singletons as follows:

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>();
    // ...
}

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

This is the only place these classes are registered, and no other instances are created anywhere, however I have noticed that the constructors are called twice. Why is that?

推荐答案

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

TL;DR - If you need one of your registered singletons to be passed in as a configuration option to AddMvc, Don't do what I did and use GetService in the ConfigureServices method. Skip down to EDIT 2 below.

我找到了此答案,它是针对类似问题的,但对于有关服务的顺序,答案尚不清楚注册,但是确实指出,调用services.BuildServiceProvider()会建立一个新容器,该容器将导致重新注册服务.事后看来,这是有道理的...

I found this answer to a similar question but the answer wasn't clear about the order with respect to the service registration, however it did point out that calling services.BuildServiceProvider() builds a new container which causes the services to be reregistered. Kind of makes sense in hindsight...

我最初的解决方法是将services.BuildServiceProvider()移到AddSingleton注册之前,但是正如佛陀指出的那样,事实证明这并不总是有效.

My original fix was to move services.BuildServiceProvider() before the AddSingleton registrations, however it turns out this doesn't always work, as Buddha Buddy pointed out.

我刚刚看到了这个问题,它给出了很多更详细地了解正在发生的事情.原始注册并没有像我想的那样被丢弃.我当时想的都是错的.

I've just seen this question which gives a lot more detail into what's happening. The original registrations are not discarded, as I thought they were. I was thinking about it all wrong.

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

Calling services.BuildServiceProvider() does build a new service provider/container, but this has nothing to do with registrations. The class implementing IUserRepository and all dependent services are instantiated by the new service provider when sp.GetService<IUserRepository>() is called, but the original IServiceProvider remains, and that's what the rest of the application will use.

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

So down the line when the application requires an instance of IUserRepository (say because it's an injected dependency of UsersController, which has now been requested), the IUserRepository service (and its dependencies, if any) are again instantiated, because it's now under the original IServiceProvider.

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

A comment in the answer to the question above states that you can prevent this and use the one IServiceProvider, "by returning the service provider instance from the ConfigureServices method so that will be the container your application uses as well". I'm not sure how you would do that short of storing a class variable pointing to it, so it can be swapped out in the Configure method by setting app.ApplicationServices - but that doesn't work for me either, because the new service provider is missing all the MVC services.

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

Some people recommend using the app.ApplicationServices in Configure() to access the required service instead, but that won't work for me as I need to use it in ConfigureServices as follows:

//...

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));
    });

找到了解决方案! 这篇精彩的帖子描述了我试图实现并提出一个非常整洁的解决方案.

Found the solution! This brilliant post described what I was trying to achieve and presented a very tidy solution.

如果您需要以MVC配置选项的形式传递对已注册单例之一的引用,而不是尝试在ConfigureServices中实例化它,则可以简单地创建一个实现IConfigureOptions<MvcOptions>的新类,该类可以注入注入的依赖项,将此课程注册为单例-其他所有事务都将得到处理-辉煌!

If you need to pass a reference to one of your registered singletons in as MVC configuration options, rather than try and instantiate it inside ConfigureServices, you can simply create a new class that implements IConfigureOptions<MvcOptions> which can take injected dependencies, register this class as a singleton - and everything else is taken care of - brilliant!

示例:

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的魔力,其他一切都得到了照顾.我的用户存储库单例及其依赖项仅被实例化一次.

Thanks to the magic of DI, everything else is taken care of. My user repository singleton and its dependencies are only instantiated once.

这篇关于如果没有实例化两次,则无法在ConfigureServices中使用已注册的singetton的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-02 03:26