Search code examples
c#asp.netentity-frameworkasp.net-identity

How do I resolve the correct DbContext type within ConfigureServices when multiple DbContexts are registered


I am trying to create a class that seeds user and role data.

My class that seeds data takes a RoleManager constructor parameter

public class IdentityDataSeeder
{
   private readonly RoleManager<IdentityRole> roleManager;

   public IdentityDataSeeder(RoleManager<IdentityRole> roleManager)
   {
      this.roleManager = roleManager;
   }

   public async Task SeedData()
   {
      // Do stuff with roleManager
   }
}

I call it from Main like this

public static async Task Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var dataSeeder = scope.ServiceProvider.GetService<IdentityDataSeeder>();
        await dataSeeder.SeedData();
    }

    host.Run();
}

I configure my dependenies like this (note that I have two DbContexts in my application; one context has my Identity tables, and the other context has my application tables).

public void ConfigureServices(IServiceCollection services)
{
   services.AddDbContext<MyIdentityDbContext>(options =>
      options.UseSqlServer(
      Configuration.GetConnectionString("DefaultAdminConnection")));
   services.AddDbContext<MyApplicationDbContext>(options =>
      options.UseSqlServer(
      Configuration.GetConnectionString("DefaultConnection")));

   services.AddScoped<IdentityDataSeeder, IdentityDataSeeder>();
   services.AddScoped<IRoleStore<IdentityRole>, RoleStore<IdentityRole>>();
   services.AddScoped<RoleManager<IdentityRole>, RoleManager<IdentityRole>>();
}

My two context classes look like this

public class MyIdentityDbContext : IdentityDbContext
{
   // ...
}

public class MyApplicationDbContext : DbContext
{
   // ...
}

When I run the program, I get the error

InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.RoleStore1[Microsoft.AspNetCore.Identity.IdentityRole]'.

I assume the problem is that since I have two DbContexts registered in my dependencies, the service provider can't figure out which one to use when it tries to resolve RoleStore (correct me if I'm wrong).

How do I tell the service provider to inject the MyIdentityDbContext dependency into RoleStore?


Solution

  • You will need to use the factory delegate with the ActivatorUtilities Class that uses the specific DbContext resolved via the provider.

    //...
    
    services.AddScoped<IRoleStore<IdentityRole>>( sp => {
        DbContext context = sp.GetService<MyIdentityDbContext>();
    
        return ActivatorUtilities.CreateInstance<RoleStore<IdentityRole>>(sp, context);
    });
    
    //...
    

    When resolving the role store, the service provider will use the provided DbContext and resolve the remaining dependencies to be injected into the instance.