Using Identity with AddDbContextFactory in Blazor

Viewed 2720

In my Blazor, .Net Core 3.1 server side app, I recently changed the EF contect scoping from transient to using a factory extension and it works well. However, I added the same dbcontext factory code to a second project that uses Identity & I get Exceptions on startup.

InvalidOperationException: Unable to resolve service for type 'OMS.DALInterfaces.Models.OmsDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9

This worked fine when no factory class was in use (ie let DI handle the OMSDbContext)

services.AddDbContext<OmsDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")),
            ServiceLifetime.Transient
            );
            
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
         .AddRoles<IdentityRole>()
         .AddEntityFrameworkStores<OmsDbContext>();

Now in the project using Identity I tried:

services.AddDbContextFactory<OmsDbContext>(opt =>
                opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
                .EnableSensitiveDataLogging());

services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<OmsDbContext>();

So how do you define Identity in startup when using the Factory extension?

2 Answers

Craig, got it figured out for us.

In ConfigureServices, AddScoped of the DbContext and use the provider to get the factory from the services. Then, return the instance to the provider, as follows. The specs for Identity use scoped so I use scoped here.

        services.AddDbContextFactory<ApplicationDbContext>(options =>
        {
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
            options.EnableSensitiveDataLogging();
        });

        services.AddScoped<ApplicationDbContext>(p => p.GetRequiredService<IDbContextFactory<ApplicationDbContext>>().CreateDbContext());

It's because the Identity EntityFrameworkStores assume your DbContext is also available via dependency injection.

What you're doing is adding the factory but not adding your DbContext itself.

You need to add both.

void BuildOptions(DbContextOptionsBuilder options) => options
    .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
    .EnableSensitiveDataLogging();

services.AddDbContext<OmsDbContext>(BuildOptions);

services.AddDbContextFactory<OmsDbContext>(BuildOptions);

services.AddDefaultIdentity<IdentityUser>(options => 
            options.SignIn.RequireConfirmedAccount = true)
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<OmsDbContext>();

Related