I'm upgrading an ASP.NET MVC website from .NET 4.7.2 to ASP.NET Core 8. The old site used Entity Framework 6.4 and Microsoft.AspNet.Identity.Core 2.2. The new site uses Entity Framework Core 8.0 and Microsoft.AspNetCore.Identity.EntityFramework 8.0.
There is an existing database with tables from the earlier Identity framework. The table names and columns are nearly identical:
IdentityRole, IdentityUser, IdentityUserClaim, IdentityUserLogin, IdentityUserRole
However, IdentityUser
does not contain the new ConcurrencyStamp
field, while also containing a custom field LastLoginTime
.
I scaffold reversed-engineered the database to create new Entity Framework Core models, and I think I've correctly determined I should not use the generated Identity...
classes, as these seem to be provided by the identity framework (right?)
So that leaves two needs:
IdentityUser
to include the custom LastLoginTime
columnReading answers such as (How can I change the table names when using ASP.NET Identity?) suggests I should be able to simply call .ToTable("IdentityUser")
from OnModelCreating(ModelBuilder modelBuilder)
.
ApplicationDbContext
:
public partial class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(entity =>
{
entity.ToTable("IdentityUser");
entity.Property(e => e.LastLoginTime).HasColumnType("datetime");
entity.Property(e => e.LockoutEnd).HasColumnName("LockoutEndDateUtc").HasColumnType("datetime)");
});
}
}
ApplicationUser
:
public class ApplicationUser : IdentityUser
{
public DateTime? LastLoginTime { get; set; }
public ApplicationUser() : base()
{ }
public ApplicationUser(string userName) : base(userName)
{ }
}
HomeController
:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
public HomeController(ILogger<HomeController> logger, SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager)
{
_logger = logger;
_signInManager = signInManager;
_userManager = userManager;
}
public async Task<ActionResult> Login(LoginViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var identityUser = await _signInManager.UserManager.FindByNameAsync(model.UserName);
// EXCEPTION
}
}
Program.cs
:
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews().AddRazorRuntimeCompilation();
builder.Services.AddMvcCore().AddJsonOptions(o =>
{
o.JsonSerializerOptions.PropertyNamingPolicy = null;
o.JsonSerializerOptions.DictionaryKeyPolicy = null;
});
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(5);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySQL(builder.Configuration.GetConnectionString("Default") ?? ""));
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequiredLength = 8;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddHttpContextAccessor();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
AppDomain.CurrentDomain.SetData("ContentRootPath", builder.Environment.ContentRootPath);
AppDomain.CurrentDomain.SetData("WebRootPath", builder.Environment.WebRootPath);
app.Run();
}
}
When I test my controller, I see:
MySql.Data.MySqlClient.MySqlException (0x80004005): Table 'MyDb.AspNetUsers' doesn't exist
[...omitted]at Microsoft.AspNetCore.Identity.UserManager`1.FindByNameAsync(String userName)
Why is FindByNameAsync
searching AspNetUsers
instead of the table I specified via ToTable("IdentityUser")
?
Your ApplicationDbContext
should derive from ApplicationUser
; not IdentityUser
ApplicationDbContext : IdentityDbContext<IdentityUser>
should be
ApplicationDbContext : IdentityDbContext<ApplicationUser>