Craig
Craig

Reputation: 585

Using Identity with AddDbContextFactory in Blazor

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?

Upvotes: 12

Views: 4986

Answers (3)

Danil Poletavkin
Danil Poletavkin

Reputation: 33

Another option that works for me is to use ServiceLifetime.Transient when adding DbContext

public void ConfigureServices(IServiceCollection services){
services
    .AddDbContextFactory<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString));
services
    .AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString),ServiceLifetime.Transient);
services
    .AddDefaultIdentity<IdentityUser>(options =>
        options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
}

Upvotes: 0

Moaaz Alkhrfan
Moaaz Alkhrfan

Reputation: 99

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>();

Upvotes: 0

Jason Furr
Jason Furr

Reputation: 327

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());

Upvotes: 24

Related Questions