'.net core injecting and resolving services

I am trying my hand at .net core web api. I have created a static method for registering my controllers like this:

public static class RegistrationExtensions
{
    public static void RegisterApplicationServices(this IServiceCollection services, IServiceProvider serviceProvider)
    {
        services.RegisterSingletons();
        services.RegisterRequests();
        services.RegisterTransient(serviceProvider);
    }

    public static void RegisterSingletons(this IServiceCollection services)
    {
        services.AddSingleton<Configuration>();
    }

    public static void RegisterRequests(this IServiceCollection services)
    {
        services.AddScoped<ISettingsService, SettingsService>();
    }

    public static void RegisterTransient(this IServiceCollection services, IServiceProvider serviceProvider)
    {
        var config = serviceProvider.GetService<Configuration>();
        services.AddDbContext<InteractiveChoicesContext>(m => m.UseSqlServer(config.ConnectionString));
    }
}

As you can see, I want to resolve my Configuration class which contains my ConnectionString, which I pass into the DbContext. Do resolve the Configuration I was trying to use IServiceProvider which is injected into the RegisterApplicationServices method.

To invoke this method, I changed the Startup class's ConfigureServices to this:

public void ConfigureServices(IServiceCollection services, IServiceProvider serviceProvider)
{
    services.AddMvc();
    services.RegisterApplicationServices(serviceProvider);
}

But when I run my application, I get an error:

The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection.

The error is obvious. My question is: How can I resolve the Configuration class in my RegisterTransient method?



Solution 1:[1]

There is actually an overload of AddDbContext that takes an Action<IServiceProvider, DbContextOptionsBuilder> which should be exactly what you are looking for.

Ex:

services.AddDbContext<InteractiveChoicesContext>((provider, options) =>
{
    var config = provider.GetService<Configuration>();
    options.UseSqlServer(config.ConnectionString);
});

I have tested this with v2, v3, and v5. The documentation for this overload, as specified in EF Core 2.0.0 is included verbatim for posterity and relevance

//
// Summary:
//     Registers the given context as a service in the 
//     Microsoft.Extensions.DependencyInjection.IServiceCollection.
//     You use this method when using dependency injection in your application, such
//     as with ASP.NET. For more information on setting up dependency injection, see
//     http://go.microsoft.com/fwlink/?LinkId=526890.
//     This overload has an optionsAction that provides the applications 
//     System.IServiceProvider.
//     This is useful if you want to setup Entity Framework to resolve its internal
//     services from the primary application service provider. By default, we recommend
//     using the other overload, which allows Entity Framework to create and maintain
//     its own System.IServiceProvider for internal Entity Framework services.
//
// Parameters:
//   serviceCollection:
//     The Microsoft.Extensions.DependencyInjection.IServiceCollection to add services
//     to.
//
//   optionsAction:
//     An optional action to configure the Microsoft.EntityFrameworkCore.DbContextOptions
//     for the context. This provides an alternative to performing configuration of
//     the context by overriding the Microsoft.EntityFrameworkCore.DbContext
//     .OnConfiguring(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder)
//     
//     method in your derived context.
//     If an action is supplied here, the 
//     Microsoft.EntityFrameworkCore.DbContext
//     .OnConfiguring(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder)
//     method will still be run if it has been overridden on the 
//     derived context. Microsoft.EntityFrameworkCore.DbContext
//     .OnConfiguring(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder)
//     configuration will be applied in addition to configuration performed here.
//     In order for the options to be passed into your context, you need 
//     to expose a constructor on your context that takes 
//     Microsoft.EntityFrameworkCore.DbContextOptions`1
//     and passes it to the base constructor of 
//     Microsoft.EntityFrameworkCore.DbContext.
//
//   contextLifetime:
//     The lifetime with which to register the DbContext 
//   service in the container.
//
//   optionsLifetime:
//     The lifetime with which to register the DbContextOptions
//     service in the container.
//
// Type parameters:
//   TContext:
//     The type of context to be registered.
//
// Returns:
//     The same service collection so that multiple calls can be chained.
public static IServiceCollection AddDbContext<TContext>(
    [NotNullAttribute] this IServiceCollection serviceCollection, 
    [CanBeNullAttribute] Action<IServiceProvider, DbContextOptionsBuilder> optionsAction, 
    ServiceLifetime contextLifetime = ServiceLifetime.Scoped, 
    ServiceLifetime optionsLifetime = ServiceLifetime.Scoped
) where TContext : DbContext;

Solution 2:[2]

If you have to use your own configuration class instead of using IConfiguration, you could do this to resolve your IServiceCollection configured thus far, which in your case already has the registration for your Configuration singleton:

public static void RegisterTransient(this IServiceCollection services)
{
    // Use services registered so far
    var serviceProvider = services.BuildServiceProvider();

    var config = serviceProvider.GetRequiredService<Configuration>();
    services.AddDbContext<InteractiveChoicesContext>(m => m.UseSqlServer(config.ConnectionString));
}

where the BuildServiceProvider() is an extension method in assembly Microsoft.Extensions.DependencyInjection, namespace Microsoft.Extensions.DependencyInjection.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Jeff P