Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why we need to call BuildServiceProvider to get dependencies everytime?

After registering instances on my IServiceCollection, I need to register an IAutomapperProvider that depends on an IAssemblyProvider that was registered before this method call

public static IServiceCollection RegisterAutomapperConfiguration(this IServiceCollection container, ServiceLifetime lifeTime = ServiceLifetime.Scoped)
{
        //creating the provider to get the IAssemblyProvider for my IAutomapperProvider
        var prov = container.BuildServiceProvider();
        var assemblyProvider = prov.GetService<IAssemblyProvider>();
        container.Register<IAutomapperProvider>(aProv => new AutomapperProvider(assemblyProvider), lifeTime);
        var autoMapperProvider = prov.GetService<IAutomapperProvider>();
        var mapperConfig = autoMapperProvider.GetMapperConfiguration();
        ...
}

If right after the call of container.Register<IAutomapperProvider>(aProv => new AutomapperProvider(assemblyProvider), lifeTime); I don't call BuildServiceProvider again, then I would not get the IAutomapperProvider I registered before

public static IServiceCollection RegisterAutomapperConfiguration(this IServiceCollection container, ServiceLifetime lifeTime = ServiceLifetime.Scoped)
{
        //creating the provider to get the IAssemblyProvider for my IAutomapperProvider
        var prov = container.BuildServiceProvider();
        var assemblyProvider = prov.GetService<IAssemblyProvider>();
        container.Register<IAutomapperProvider>(aProv => new AutomapperProvider(assemblyProvider), lifeTime);
        prov = container.BuildServiceProvider();
        var autoMapperProvider = prov.GetService<IAutomapperProvider>();
        var mapperConfig = autoMapperProvider.GetMapperConfiguration();
        ...
}

On the AspNetCore code when you call BuildServiceProvider extension method they use the same IServiceCollection that can change over the time adding more elements, at the end you are pointing to the same reference

public static ServiceProvider BuildServiceProvider(this IServiceCollection services)
{
    return BuildServiceProvider(services, ServiceProviderOptions.Default);
}

Then why I need to call it again to get a new instance that knows how to resolve my Service?

To avoid confusions, the Register method is an extension I created but internally calls the AddSinglenton or Add...

  public static IServiceCollection Register<TService>(this IServiceCollection container, Func<IServiceProvider, TService> implementationFactory, ServiceLifetime lifeTime)
        where TService : class
    {

    if (container == null)
        throw new ArgumentNullException(nameof(container));
    if (implementationFactory == null)
        throw new ArgumentNullException(nameof(implementationFactory));

    switch (lifeTime)
    {
        case ServiceLifetime.Scoped:
            container.AddScoped(typeof(TService), (Func<IServiceProvider, object>)implementationFactory);
            break;
        case ServiceLifetime.Transient:
            container.AddTransient(typeof(TService), (Func<IServiceProvider, object>)implementationFactory);
            break;
        default:// ServiceLifetime.Singleton
            container.AddSingleton(typeof(TService), (Func<IServiceProvider, object>)implementationFactory);
            break;
    }

    return container;
}
like image 746
Heinrich Avatar asked Jul 11 '18 14:07

Heinrich


People also ask

Can you call BuildServiceProvider multiple times?

Calling BuildServiceProvider multiple times can cause serious trouble, because each call to BuildServiceProvider results in a new container instance with its own cache. This means that registrations that are expected to have the Singleton lifestyle, suddenly are created more than once.

What does build service provider do?

BuildServiceProvider(IServiceCollection, ServiceProviderOptions) Creates a ServiceProvider containing services from the provided IServiceCollection optionally enabling service-creation and scope validation.

What is Iservice collection?

IServiceCollection is the collection of the service descriptors. We can register our services in this collection with different lifestyles (Transient, scoped, singleton) IServiceProvider is the simple built-in container that is included in ASP.NET Core that supports constructor injection by default.

What is an IServiceProvider?

What is an IServiceProvider? The IServiceProvider is responsible for resolving instances of types at runtime, as required by the application. These instances can be injected into other services resolved from the same dependency injection container.


1 Answers

BuildServiceProvider does not have to be invoked so often.

That is what the implementation factory delegates are for. They provide access to a service provider for that purpose.

It is still uncertain why you have to build the provider in the first instance but based on currently provided code use the service provider within the implementation factory delegate when registering your provider.

public static IServiceCollection RegisterAutomapperConfiguration(this IServiceCollection services, 
    ServiceLifetime lifeTime = ServiceLifetime.Scoped) {

    services.Register<IAutomapperProvider>(p => //<-- p is IServiceProvider
         //use the provider to get the IAssemblyProvider for my IAutomapperProvider
        new AutomapperProvider(p.GetService<IAssemblyProvider>()), lifeTime);
    prov = services.BuildServiceProvider();
    var autoMapperProvider = prov.GetService<IAutomapperProvider>();
    var mapperConfig = autoMapperProvider.GetMapperConfiguration();
    //...
}

When BuildServiceProvider is invoked, the created service provider will only be aware of types that were in the collection when it was built. It will not be aware of any additional types, which is why is it usually done at the end of registering all types.

like image 63
Nkosi Avatar answered Sep 29 '22 06:09

Nkosi