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
.ApplicationDbContext
My 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