The application is .NET 5.0 with Blazor server side, Entity Framework Core and AspNetCore.Identity
.
Originally its code for setting up DbContext
and IdentityContext
used to look like this:
// ConfigureServices
if (databaseType == "MySQL")
{
services.AddDbContext<ApplicationDbContext, ApplicationDbContextMySQL>(options =>
{
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
}, ServiceLifetime.Transient);
}
else if (databaseType == "SQLServer")
{
services.AddDbContext<ApplicationDbContext, ApplicationDbContextSQLServer>(options =>
{
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
}, ServiceLifetime.Transient);
}
else if (databaseType == "PostgreSQL")
...
// Identity service configuration
services.AddIdentity<User, UserRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddDefaultUI();
// ApplicationDbContext
public class ApplicationDbContext : IdentityContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
protected ApplicationDbContext(DbContextOptions options) : base(options)
{
}
}
// ApplicationDbContextMysql
public class ApplicationDbContextMySQL : ApplicationDbContext
{
public ApplicationDbContextMySQL(DbContextOptions<ApplicationDbContextMySQL> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
//Identity class
public class IdentityContext : IdentityDbContext<User, UserRole, long>
{
public IdentityContext(DbContextOptions<IdentityContext> options) : base(options)
{
}
protected IdentityContext(DbContextOptions options) : base(options)
{
}
It worked fine for the most part, but there was a caching issue with Entity Framework due to app using Blazor. Everything I read on that issue said that Blazor apps should use AddDbContextFactory
to be able properly maintain DbContext
lifetime, which indeed fixed that issue.
So I switched to using AddDbContextFactory
instead of using AddDbContext
:
if (databaseType == "MySQL")
{
services.AddDbContextFactory<ApplicationDbContextMySQL>(options =>
{
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
});
}
...
...
This introduced error on startup:
Unable to resolve service for type 'ModuleLibrary.Data.ModuleLibraryDbContext' while attempting to activate 'ModuleLibrary.Utils.UserInfoClaimsTransformation'.
According to these two questions, one needs to register dbContext
service on top of the existing AddDbContextFactory
for the ASP.Identity
Using Identity with AddDbContextFactory in Blazor ,
Identity stores with DB Context Factory
So I have changed the above code section to be
if (databaseType == "MySQL")
{
services.AddDbContextFactory<ApplicationDbContextMySQL>(options =>
{
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
});
services.AddScoped<ApplicationDbContext>(p => p.GetRequiredService<IDbContextFactory<ApplicationDbContextMySQL>>().CreateDbContext());
}
This worked fine, but since different dbContext
classes are registered depending on the db provider (ApplicationDbContextMySQL, ApplicationDbContextSQLServer) I'm forced to have it hardcoded to a specific class when I need to use dbContext
in the app - eg ApplicationDbContextMySQL. I am not able to use my dbContext
base class ApplicationDbContext
in the application code.
public UserCacheService(IMemoryCache cache, IServiceProvider serviceProvider, IDbContextFactory<ApplicationDbContextMySQL> dbFactory)
{
...
}
When I try to use the base class ApplicationDbContext
, I get this error:
Unable to resolve service for type 'MyApplication.EntityFrameworkCore.IDbContextFactory`1[MyApplication.Data.ApplicationDbContext
What would be the correct way to register dbFactory
with my generic ApplicationDbContext
class so that it can be used instead of its derived classes like ApplicationDbContextMySQL, ApplicationDbContextSQLServer etc?