简体   繁体   English

.NET Core单例创建被多次调用

[英].NET Core Singleton Creation is called multiple times

I'm registering a service as a singleton in .NET Core. 我在.NET Core中将服务注册为单例。 Yet I'm seeing the constructor for the singleton called multiple times. 但是我看到单例的构造函数被多次调用。

services.AddSingleton<DbAuthorizationOptions, ContextAuthorizationOptions>();

My context authorization options is just Dictionary of Entity Types to IValidators , The context authorization options are passed into the DBContext , to automatically run validations. 我的上下文授权选项只是将实体类型字典发送给IValidators ,上下文授权选项被传递到DBContext ,以自动运行验证。

During the registration of my services, I also register dynamic Validators with my container registered in DI. 在注册服务期间,我还将动态验证器与在DI中注册的容器一起注册。

var useDynamicValidator = serviceOption.ValidatorOptions != null;
if(useDynamicValidator)
{
    //TODO: Extract this to before the register service no sense in building the provider each time
    //TODO: Make this cleaner don't be dependent on Authorization options
    var provider = services.BuildServiceProvider();
    var authOptions = provider.GetService<DbAuthorizationOptions>();
    var validator = BuildDynamicValidatorFactory(serviceOption).Invoke(provider, null);
    authOptions.ValidatorOptions.AddValidatorForSet(validator);
}

I notice that when I call GetService on the provider I receive a new singleton instead of the existing one. 我注意到,当我在提供程序上调用GetService时,会收到一个新的单例,而不是现有的单例。 Does building the provider create a new container so all of the services get re-registered? 构建提供者是否会创建一个新的容器,以便重新注册所有服务?

If so, How can I call a method to register my dynamic validators in the singleton container with the existing IServiceProvider , is there a way to invoke some registration once after the service container is built? 如果是这样,我该如何调用一种方法来在现有IServiceProvider的单例容器中注册我的动态验证器,是否可以在构建服务容器后立即调用一些注册?

Does building the provider create a new container so all of the services get reregistered? 构建提供者是否会创建一个新容器,以便重新注册所有服务?

Yes. 是。 See the source code . 参见源代码

If so, How can I call a method to register my dynamic validators in the singleton container with the existing IServiceProvider, is there a way to invoke some registration once after the servicecontainer is built? 如果是这样,我该如何调用一种方法来向现有IServiceProvider在单例容器中注册我的动态验证器,是否可以在构建servicecontainer之后调用一次注册?

I'm not really understanding why this is a problem. 我不是很了解为什么这是一个问题。 You should be registering all of your services one time at application startup in the Composition Root . 您应该在应用程序启动时一次Composition Root中注册所有服务。

The DI container is then responsible for resolving the object graphs of the application. 然后,DI容器负责解析应用程序的对象图 The application itself shouldn't have a dependency on it, nor be required to update it. 应用程序本身不应该依赖它,也不需要更新它。

You should be injecting DbAuthorizationOptions in the place where you need to use it. 您应该在需要使用DbAuthorizationOptions的地方注入它。

public class Foo : IFoo
{
    private readonly DbAuthorizationOptions authOptions;

    public Foo(DbAuthorizationOptions authOptions) // <-- Inject parameters
    {
        this.authOptions = authOptions ??
            throw new ArgumentNullException(nameof(authOptions));
    }

    public void DoSomething()
    {
        // TODO: Inject the type that has the BuildDynamicValidatorFactory
        // method and the serviceOption (whatever type that is) here
        // either as a method parameter of this method, or a constructor
        // parameter of this class.
        var validator = BuildDynamicValidatorFactory(serviceOption).Invoke(provider, null);
        // Now we have an instance of authOptions that can be used
        authOptions.ValidatorOptions.AddValidatorForSet(validator);
    }
}

Note that the DI container automatically provides the DbAuthorizationOptions if injected into another type that is also resolved through DI (such as a controller or filter). 请注意,如果将DI容器注入也通过DI解析的另一种类型(例如,控制器或过滤器),则DI容器会自动提供DbAuthorizationOptions

NOTE: It isn't very clear from your question where you need to do this. 注意:从您的问题尚不清楚,您需要在哪里执行此操作。 You mention that you want it to happen once, which usually means to put it at application startup. 您提到您希望它发生一次,这通常意味着将其放在应用程序启动时。 But users cannot interact with code that runs at startup. 但是用户无法与启动时运行的代码进行交互。 So, maybe you could use a filter . 因此,也许您可​​以使用filter It really all depends on where in the lifecycle of the application it has to happen. 这一切都是在那里它发生在应用程序的生命周期取决于。

You can declare a dependency on IServiceProvider -- don't build it, inject it. 您可以声明对IServiceProvider的依赖关系-不要构建它,注入它。

public class SomeController
{
    DbAuthorizationOptions authOptions;
    public SomeController(IServiceProvider provider)
    {
        authOptions = provider.GetSerivce<DbAuthorizationOptions>();
    }
}

But this is the service locator anti-pattern. 但这是服务定位器的反模式。 As I commented on NightOwl888's post after you gave more details, a factory is probably a better approach. 在您提供了更多详细信息之后,我对NightOwl888的帖子发表了评论时,工厂可能是一个更好的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM