简体   繁体   中英

.NET Core Singleton Creation is called multiple times

I'm registering a service as a singleton in .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.

During the registration of my services, I also register dynamic Validators with my container registered in 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. 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?

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?

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 .

The DI container is then responsible for resolving the object graphs of the application. 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.

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).

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 . 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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