0

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:

  1. Customize IdentityUser to include the custom LastLoginTime column
  2. Ensure default Identity classes are mapped to existing table names

Reading 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")?

1 Answer 1

0

Your ApplicationDbContext should derive from ApplicationUser; not IdentityUser

ApplicationDbContext : IdentityDbContext<IdentityUser>

should be

ApplicationDbContext : IdentityDbContext<ApplicationUser>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.