Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Extend IdentityUser in ASP.NET Core

Suggested Videos
Part 74 - ASP.NET Core client side validation | Text | Slides
Part 75 - ASP.NET core remote validation | Text | Slides
Part 76 - Custom validation attribute in asp.net core | Text | Slides

In this video we will discuss how to extend the built-in IdentityUser class in ASP.NET Core.

Why should we extend IdentityUser class

The built-in IdentityUser class has very limited set of properties like Id, Username, Email, PasswordHash etc. The source code of this class is shown below.


public class IdentityUser<TKey> where TKey : IEquatable<TKey>
{
    public IdentityUser() { }
    public IdentityUser(string userName) : this()
    {
        UserName = userName;
    }
    [PersonalData]
    public virtual TKey Id { get; set; }
    [ProtectedPersonalData]
    public virtual string UserName { get; set; }
    public virtual string NormalizedUserName { get; set; }
    [ProtectedPersonalData]
    public virtual string Email { get; set; }
    public virtual string NormalizedEmail { get; set; }
    [PersonalData]
    public virtual bool EmailConfirmed { get; set; }
    public virtual string PasswordHash { get; set; }
    public virtual string SecurityStamp { get; set; }
    public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
    [ProtectedPersonalData]
    public virtual string PhoneNumber { get; set; }
    [PersonalData]
    public virtual bool PhoneNumberConfirmed { get; set; }
    [PersonalData]
    public virtual bool TwoFactorEnabled { get; set; }
    public virtual DateTimeOffset? LockoutEnd { get; set; }
    public virtual bool LockoutEnabled { get; set; }
    public virtual int AccessFailedCount { get; set; }
    public override string ToString()
        => UserName;
}


What if I want to store additional data about the user like Gender, City, Country etc. The built-in IdentityUser class does not have these properties. To store custom user data like Gender, City, Country etc, extend the IdentityUser class.

Extend IdentityUser Class

You can name the class that extends the IdentityUser class anything you want, but it is customary to name it ApplicationUser. In the example below, ApplicationUser class extends the IdentityUser class. We have included just one custom property City, but you can include as many properties as you want.

public class ApplicationUser : IdentityUser
{
    public string City { get; set; }
}

Find all references of IdentityUser class and replace it with our custom ApplicationUser class. The easiest way to do this is to, right click on the IdentityUser class and then select "Find All References" from the context menu. Navigate to each reference in the list and replace IdentityUser class with ApplicationUser class.

asp.net core custom identity user

Specify ApplicationUser class as the generic argument for the IdentityDbContext class

This is how the IdentityDbContext class knows it has to work with our custom user class (in this case ApplicationUser class) instead of the default built-in IdentityUser class. 

public class AppDbContext : IdentityDbContext<ApplicationUser>
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    { }

    public DbSet<Employee> Employees { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Seed();
    }
}

Generate a new migration to add columns to AspNetUsers table

Add-Migration Extend_IdentityUser

The above command generates the required migration code to add City column to AspNetUsers table. 

public partial class Extend_IdentityUser : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<string>(
            name: "City",
            table: "AspNetUsers",
            nullable: true);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "City",
            table: "AspNetUsers");
    }
}

Next, apply the migration to the database using the following command

Update-Database

Extending IdentityUser - Add-Migration Not Working 

If ApplicationUser class (the class that extends IdentityUser class) is not specified as the generic argument for IdentityDbContext class, Add-Migration command does not work. It does not generate the required migration code to add the additional columns to the AspNetUsers identity table.

To fix this, specify ApplicationUser class as the generic argument for IdentityDbContext class.

public class AppDbContext : IdentityDbContext<ApplicationUser>
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    { }

    public DbSet<Employee> Employees { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Seed();
    }
}

If you get the following exception, the most likely cause is somewhere with in your application you are still using IdentityUser class instead of the ApplicationUser class.

No service for type 'Microsoft.AspNetCore.Identity.SignInManager`1[Microsoft.AspNetCore.Identity.IdentityUser]' has been registered.

In Visual Studio, search for IdentityUser class throughout your application using CTRL + SHIFT + F

visual studio find window

Replace IdentityUser class with ApplicationUser class and rerun the project.

Storing custom data in AspNetUsers table 

We register a new user using the following Register view.

asp.net core custom data in aspnetusers table

To be able to store data for the custom City column in AspNetUsers table we need to alter
  • RegisterViewModel class
  • Register View
  • Register action in the AccountController
RegisterViewModel class 

This is the model class for the Register view. Modify it to include City property.

public class RegisterViewModel
{
    // Other Properties

    public string City { get; set; }
}

Register View

On the Register view, include a field to capture a value for City.

@model RegisterViewModel

@{
    ViewBag.Title = "User Registration";
}

<h1>User Registration</h1>

<div class="row">
    <div class="col-md-12">
        <form method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Email"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>

            @*Other Fields*@

            <div class="form-group">
                <label asp-for="City"></label>
                <input asp-for="City" class="form-control" />
            </div>

            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
</div>

AccountController - Register action
  • Populate City property of ApplicationUser instance which is then passed to the CreateAsync() method of UserManager class.
  • The data in the ApplicationUser instance is then saved to the AspNetUsers table by the IdentityDbContext class.
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser
        {
            UserName = model.Email,
            Email = model.Email,
            City = model.City
        };

        var result = await userManager.CreateAsync(user, model.Password);

        if (result.Succeeded)
        {
            await signInManager.SignInAsync(user, isPersistent: false);
            return RedirectToAction("index", "home");
        }

        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    return View(model);
}

asp.net core tutorial for beginners

1 comment:

  1. what if we want more than one identity user, like identity for student, identity for instructor.. how can we do that?

    ReplyDelete

It would be great if you can help share these free resources