Home > Enterprise >  Disable .NET Core IServiceProvider lazy registration
Disable .NET Core IServiceProvider lazy registration

Time:04-15

I'm using the .NET Core's ServiceCollection and ServiceProvider for dependency injection in a Xamarin Forms app. I have singleton instances which do a lot of slow running work in their constructor / Init method. The problem is that the provider creates the singleton instances when they are used at the first time. I want to override this functionality in order to do the instantiation of these classes when I register them. For example my code:

services.AddSingleton<ILocalizationService, LocalizationService>();

I tried the following but it not works if the class needs instances via constructor injection (because in that scenario in need to pass them manually):

services.AddSingleton<ILocalizationService>(new LocalizationService());

Can I override the basic functionality somehow without any workaround like instantiate all of them when I build my provider?

CodePudding user response:

There is no way to override or disable that behavior in MS.DI and to my knowledge, neither is such feature available in any other DI Container in the .NET ecosystem.

What you can do, however, is resolve that instance immediately after the Service Provider was built:

provider.GetRequiredService<ILocalizationService>();

This constructs that instance and runs its constructor.

Some DI Container provider a 'validation' mechanism that can test all registrations by creating them. There exists, however, no such feature in MS.DI.

But instead of having this "slow running work" in the class's constructor, I would suggest moving the initialization out of the constructor, and into an Init method of some sort, because Injection Constructors should be simple and should do no work other than storing its dependencies. Also note that if you want make this heavy operation asynchronous in the future, you will be certainly out of luck, because this is impossible to make constructors asynchronous (see this, this, and this, for more information on this).

Instead, move the logic out of the constructor into an Init method of some sort, e.g:

public class LocalizationService : ILocalizationService
{
    private readonly IDependency1 dep1;
    private readonly IDependency2 dep2;

    public LocalizationService(IDependency1 dep1, IDependency2 dep2)
    {
        // Just pre-condition checks and storing dependencies.
        // Never use injected dependencies inside the constructor.
        this.dep1 = dep1 ?? throw new ArgumentNullException();
        this.dep2 = dep2 ?? throw new ArgumentNullException();
    }

    public void Init() => /* Do heavy initialization here */

    public Task InitAsync() => /* Or do async initialization here */
}

This allows you to resolve LocalizationService from the ServiceProvider at startsup and call its Init method:

var service = provider.GetRequiredService<LocalizationService>();

// Forces initialization at startup
service.Init();
// or
await service.InitAsync();

This requires LocalizationService to be registered as well, next to its ILocalizationService interface:

services.AddSingleton<LocalizationService>();
services.AddSingleton<ILocalizationService>(c =>
    c.GetRequiredService<LocalizationService>>();

This last registration is a bit sneaky, but it allows both registrations for ILocalizationService and LocalizationService to point at the exact same instance.

  • Related