Alexander Zeitler

Entity Framework Core: Why you should never name your DbContext 'IdentityDbContext'

Published on Saturday, March 18, 2023

Don't do this

This is more or less a follow up to the previous post.

As said in the post already, I was playing around with Startup.cs style of application start up together with Entity Framework Core / ASP.NET Identity scaffolding.

So after I got Identity scaffolded, I re-added the Startup.cs and wanted to move on with migrations.

My Program.cs looked like this:

public class Program
{
  public static void Main(
    string[] args
  )
  {
    CreateHostBuilder(args)
      .Build()
      .Run();
  }

  public static IHostBuilder CreateHostBuilder(
    string[] args
  ) =>
    Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}

This was the Startup.cs:

public class Startup
{
  public IConfiguration Configuration { get; }

  public Startup(
    IHostEnvironment env
  )
  {
    var builder = new ConfigurationBuilder()
      .SetBasePath(env.ContentRootPath)
      .AddJsonFile(
        "appsettings.json",
        optional: false,
        reloadOnChange: true
      )
      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
      .AddEnvironmentVariables();
    Configuration = builder.Build();
  }


  // This method gets called by the runtime. Use this method to add services to the container
  public void ConfigureServices(
    IServiceCollection services
  )
  {
    var connectionString = Configuration.GetConnectionString("IdentityDbContextConnection") ??
                           throw new InvalidOperationException(
                             "Connection string 'IdentityDbContextConnection' not found."
                           );

    services.AddDbContext<IdentityDbContext>(options => options.UseNpgsql(connectionString));

    services.AddDefaultIdentity<AppUser>(options => options.SignIn.RequireConfirmedAccount = true)
      .AddEntityFrameworkStores<IdentityDbContext>();

    services.AddControllersWithViews();
  }

  // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
  public void Configure(
    IApplicationBuilder app,
    IWebHostEnvironment env
  )
  {
    // Configure the HTTP request pipeline.
    if (!env.IsDevelopment())
    {
      app.UseExceptionHandler("/Home/Error");
      // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
      app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(
      endpoints =>
      {
        endpoints.MapControllerRoute(
          name: "default",
          pattern: "{controller=Home}/{action=Index}/{id?}"
        );
      }
    );
  }
}

The next step would be to create the first migration and seed the database:

dotnet ef migrations add CreateIdentitySchema

Straight forward, one might think... but no:

More than one DbContext was found. Specify which one to use. Use the '-Context' parameter for PowerShell commands and the '--context' parameter for dotnet commands.

So, let's get a list of the contexts available using dotnet ef dbcontext list:

dotnet ef dbcontext list
Build started...
Build succeeded.
Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext
WebAppWithIdentity.Areas.Identity.Data.IdentityDbContext

Ok, let's try to use a full qualified name then:

dotnet ef database update --context WebAppWithIdentity.Areas.Identity.Data.IdentityDbContext

Build started... Build succeeded. Unable to create an object of type 'IdentityDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

That's not funny, tbh.

Well, let's go back to scaffolding and try this one (still with Startup.cs in use):

dotnet aspnet-codegenerator identity -dc SecurityDbContext -u AppUser -f -gl -dbProvider sqlite

Result:

Building project ...
Finding the generator 'identity'...
Running the generator 'identity'...
RunTime 00:00:17.25

Umm... no let's list the contexts again:

dotnet ef dbcontext list
Build started...
Build succeeded.
WebAppWithIdentity.Areas.Identity.Data.SecurityDbContext

Well, that's interesting.

Now, let's try to run the migrations:

dotnet ef migrations add CreateIdentitySchema
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'

Wow!

And the final step: apply the changes to the actual database:

dotnet ef database update

Build started...
Build succeeded.

# shortened the output a bit here
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (14ms) [Parameters=[], CommandType='Text', CommandTimeout='20']
      CREATE UNIQUE INDEX "UserNameIndex" ON "AspNetUsers" ("NormalizedUserName");
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (14ms) [Parameters=[], CommandType='Text', CommandTimeout='20']
      INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
      VALUES ('20230318144833_CreateIdentitySchema', '7.0.4');
Done.

Ok, lessons learned:

Never name your DbContent IdentityDbContext

What are your thoughts about
"Entity Framework Core: Why you should never name your DbContext 'IdentityDbContext'"?
Drop me a line - I'm looking forward to your feedback!
Please be aware that I'm no longer active on social media. I'm just cross posting things over there (it's a bot).
Imprint | Privacy