Home Add Multi-Tenancy to Asp.Net Core Identity
Post
Cancel

Add Multi-Tenancy to Asp.Net Core Identity

Alt text

Now I think it starts to get a bit more interesting. I would like my API to be able to handle Multi-Tenancy. For this situation we need to add a whole new class/table to Asp.Net Core Identity. It is not as bad as it sounds.

Create the Tenant

Let’s start by creating a Tenant object.

Alt text

And let’s look at that new Tenant class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ApothecaricApi.Models.Identity
{
  [Table("AspNetTenants")]
  public class Tenant
  {
    [Key]
    [StringLength(450)]
    public string Id { get; set; }

    [StringLength(250)]
    [Required]
    public string Name { get; set; }

    [StringLength(250)]
    [Required]
    public string DomainName { get; set; }

    [StringLength(50)]
    [Required]
    public string Code { get; set; }

    public bool IsDefault { get; set; }

    public virtual IEnumerable<ApothecaricUser> Users { get; set; }
  }
}

In the Table attribute, I named the table “AspNetTenants” to go along with the Asp.Net Core Identity nomenclature that is already in place. I set the Id property to be the Primary Key when the table is created. I chose to keep using the GUID for the Id just like the other Identity tables. Some people have converted that all over to integers but I am just going to stay consistent here.

Tie Tenant to User

If you look to the bottom of the new Tenant class, you will notice a reference back to our custom IdentityUser object (ApothecaricUser).

1
<public virtual IEnumerable<ApothecaricUser> Users { get; set; }

A user must be tied to a Tenant here so we know who the user belongs to.

In order to tie the User over to this new Tenant entity, we need to go back and update our ApothercaricUser class.

Let’s add the following to our ApothercaricUser class to reference our Tenant

1
2
3
4
[StringLength(450)]
public string TenantId { get; set; }

public virtual Tenant Tenant { get; set; }

The TenantId will add the “TenantId” field to our database and the “Tenant” will tie the user to a Tenant in code. Our ApothecaricUser class should now look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ApplicationUser : IdentityUser
{
  [StringLength(250)]
  public string FirstName { get; set; }

  [StringLength(250)]
  public string LastName { get; set; }

  public bool IsActive { get; set; }

  [StringLength(450)]
  public string TenantId { get; set; }

  public virtual Tenant Tenant { get; set; }
}

Let EF and the database know!

In order to have EF and the database know about our Tenant, we will need to make our Db Context aware of the Tenant. We need to add the Tenant object to the ApothecaricDbContext class. When we access this table through EF, we want to refer to it simply as “Tenants”.

1
2
3
4
5
6
public class ApothecaricDbContext : IdentityDbContext<ApothecaricUser>
{
  public ApothecaricDbContext(DbContextOptions options) : base(options) { }

  public DbSet<Tenant> Tenants { get; set; }
}

Here we add a DbSet typed to the Tenant class and name that property “Tenants”. Now EF will know how to interact with the Tenant object and also allow the Db Migrations to add that new table to our database.

Add Some Indexes using Fluent Api Syntax

To look back at the Tenant class again, there are two ways we can match up an Api Request to a Tenant. First is to use the Domain name that comes in on the http request. The other is to put a code in the URL of any request send to the Api, such as “http://somecompany.com/…/apothecaric/api/login/scca” or something along those lines.

I have chosen to work with the Domain name. A better explanation of how to accomplish using Domains Names for Multi-Tenancy is outlined here.

I want to make sure that both the Code and the DomainName properties/fields on the Tenant class/table are unique. We will need to make sure both of these fields have a Unique Index added to them. To make this happen, we need to go back into our ApothecaricDbContext and add an override method to the base class’ OnModelCreating method. Here we will use the Fluent Api syntax and make reference to our Tenant class and its two Properties that we need to add the unique index on, Code, and DomainName.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ApothecaricDbContext : IdentityDbContext<ApothecaricUser>
{
  public ApothecaricDbContext(DbContextOptions options) : base(options) { }

  public DbSet<Tenant> Tenants { get; set; }

  protected override void OnModelCreating(ModelBuilder builder)
  {
     builder.Entity<Tenant>()
       .HasIndex(u => u.Code)
       .IsUnique();

   builder.Entity<Tenant>()
       .HasIndex(u => u.DomainName)
       .IsUnique();

   base.OnModelCreating(builder);
  }
}

More Migrations

We will then need to run the database Migrations again.

1
2
PM> Add-Migration "3 Add_Tenant_Table"
To undo this action, use Remove-Migration.
1
2
3
4
5
PM> Update-Database
Applying migration '20180524214130_1 Initial_Database'.
Applying migration '20180528190132_2 Custom_App_User'.
Applying migration '20180530200630_3 Add_Tenant_Table'.
Done.

And A Look At The Database

When we refresh the database, we see our new AspNetTentants table.

Alt text

Also, looking at the AspNetUsers table, we can also find our foreign key back to our AspNetTenants table.

Alt text

What’s Ahead

Next set, lets look at adding some controller endpoints to Register a new user and get a Jwt Token back. All with capturing the Tenant on the requests!

Hope to see you then!

This post is licensed under CC BY 4.0 by the author.