I had to extend IdentityUser
model (and AspNetUser table of course) with some extra field (RegistrationDate
) so I created ExtendIdentityUser
model class. It works nice.
Then, I needed to change some user default fields (UserName
and Email
) to required ones. After this change method await userManager.FindByEmailAsync("[email protected]");
started returns user object with empty username and email fields. Rest of them are properly fullfileed, even NormalizedUserName and NormalizedEmail. Before making UserName
and Email
to required it worked properly.
My custom model class looks like:
public class ExtendedIdentityUser : IdentityUser
{
public override string UserName { get; set; } = string.Empty;
public override string Email { get; set; } = string.Empty;
public DateTime RegistrationDate { get; set; }
}
My custom map model class is:
public class ExtendedIdentityUserMap : IEntityTypeConfiguration<ExtendedIdentityUser>
{
public void Configure(EntityTypeBuilder<ExtendedIdentityUser> builder)
{
// Properties
builder.Property(u => u.UserName).HasColumnName("UserName").IsRequired();
builder.Property(u => u.Email).HasColumnName("Email").IsRequired();
builder.Property(u => u.RegistrationDate).IsRequired();
// Default values
builder.Property(u => u.RegistrationDate).HasDefaultValueSql("GETDATE()")
}
}
ApplicationDbContext
was also updated with my map class:
public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options)
{
public DbSet<ExtendedIdentityUser> ExtendedIdentityUsers { get; set; }
// another tables
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new ExtendedIdentityUserMap());
// another mappings
}
}
Generated migrations code looks good:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "UserName",
table: "AspNetUsers",
type: "nvarchar(256)",
maxLength: 256,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(256)",
oldMaxLength: 256,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Email",
table: "AspNetUsers",
type: "nvarchar(256)",
maxLength: 256,
nullable: false,
defaultValue: "",
oldClrType: typeof(string),
oldType: "nvarchar(256)",
oldMaxLength: 256,
oldNullable: true);
}
Changes are appiled successfully to my local DB - columns UserName
and Email
became non nullable. Data is there. But calling await userManager.FindByEmailAsync("[email protected]");
returns user object with empty username and email fields.
My question is: what am I missing? Why username and email fields are empty for my user object?
If it's possible I would like to avoid implement own userManager as a solution.
@EDIT
I had investigated it deeply, and I discovered that property Users
in Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore
already contains list of users without filled email and username fields. That's why the method FindByEmailAsync()
from userManager returns not completed data as well, because it uses UserStore
class for obtaining userData.
Please check your Identity Configuration code, you are using the following code to configure the Identity, right?
builder.Services.AddDefaultIdentity<ExtendedIdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
After testing it with your custom model class and ApplicationDbContext
, I reproduce the problem, when query the data using usermanager, the result is null, even though the data exists.
As you said, the issue relates the UserStore, in the deeply, it might still using the IdentityUser.
To solve this issue, you can modify the ApplicationDbContext as below: use IdentityDbContext<ExtendedIdentityUser>
:
public class ApplicationDbContext : IdentityDbContext<ExtendedIdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<WebApplication5.Models.Product> Product { get; set; } = default!;
public DbSet<ExtendedIdentityUser> ExtendedIdentityUsers { get; set; }
// another tables
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new ExtendedIdentityUserMap());
// another mappings
}
}
It will specify the ExtendedIdentityUser class as the default user class. So, when query the data via usermanager, the result as below:
Reference link: Custom user data.