ASP.NET Core 2.x now hides the Identity pages and logic inside of a library. In order to extend the IdentityUser entity with new properties and add UI and logic to deal with those new properties, we now get to run the "Identity scaffolder" (under MyProject -> Add -> New scaffolded item). Unfortunately, the docs are incomplete at best and, in more than a few instance, flat out wrong.
I'm struggling to figure out how to wrestle the scaffolder and the resultant code to achieve what I would assume is a pretty standard use case:
IdentityUser with my own properties. Since this involves creating an inherited class, I want the name of that new class to be ApplicationUser.ApplicationDbContextMy problem is that, reading the docs and playing with the Identity scaffolder, I can't figure out how to get the scaffolder to let me extend IdentityUser and keep using the existing ApplicationDbContext.
After staring at the docs and playing with the scaffolder and the resultant code, I think I've figure out most of it.
ApplicationDbContext.ApplicationDbContext
At this point, your project is wired up to the existing ApplicationDbContext and IdentityUser classes. Now, you can extend IdentityUser and wire up the Identity pages to use the new, extended entity:
Add a new ApplicationUser class that inherits from IdentityUser:
public class ApplicationUser : IdentityUser
{
public string Nickname { get; set; }
}
Add the new ApplicationUser entity to the ApplicationDbContext class:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<ApplicationUser> AppUsers { get; set; }
}
Replace all instances of <IdentityUser> with <ApplicationUser>. This means that you will change startup.cs to call services.AddDefaultIdentity<ApplicationUser>(), and you will replace all of the references to SigninManager and UserManager to specify the new <ApplicationUser> Type parameter.
Whew... now, you can go into things like Register.cshtml.cs and create (or fetch) a new ApplicationUser rather than an IdentityUser, and all of the plumbing will be wired up to ApplicationUser. For example, the code in Register.cshtml.cs might look like this:
var user = new ApplicationUser {
UserName = Input.Email,
Email = Input.Email,
Nickname = Input.Nickname // New property
};
var result = await _userManager.CreateAsync(user, Input.Password);
Normally, without Identity, ApplicationDbContext inherits from DbContext.
I have a solution with four projects. WEB, WebAPI, DATA and ENTITIES.
I have a single ApplicationDbContext located in DATA and have modified it to inherit from IdentityDbContext e.g.:
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
Now, ENTITIES is where the models (POCOs) are defined.
And, AppliationDbContext in DATA contains references to these classes and contains the DbSet<T> property setter used by EF. The Person(aka, ApplicationUser) class in ENTITIES looks like this:
public class Person : IdentityUser
{
[PersonalData]
public string LastName { get; set; }
In the WEB project, with the appropriate references to DATA and ApplicationDContext, I have only one services.AddDbContext<T> dependency reference; then use that context within AddDefaultIdentity<IdentityUser>() service addition.:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
That seemed to do the trick to use only one DbContext service. It also extended the AspNetUsers table to include the [PersonalData] properties I had set in the POCO Person model class located within ENTITIES.
Now, since
<IdentityUser> has been extended, in my case by Person, all references to IdentityUser should be updated to reflect that. So Startup.ConfigureServices now looks like this:
services.AddDefaultIdentity<Person>()
.AddEntityFrameworkStores<ApplicationDbContext>();
And, within the UI, to gain access to the properties added by 'Person', references in ~/Identity/Account/Manage/Index.cshtml.cs need to be updated as well:
private readonly UserManager<Person> _userManager;
private readonly SignInManager<Person> _signInManager;
private readonly IEmailSender _emailSender;
public IndexModel(
UserManager<Person> userManager,
SignInManager<Person> signInManager,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
}
You will have to update the 'InputModel' in index.cshtml.cs as well as the 'new up' in OnGetAsync() method to map the properties -- similar to how Email is handled by the default settings.
And then, finally, there is the index.cshtml view file to give access to the UI/razor display.
This should get you on your way with items 1 - 4 in your question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With