Multi-tenant ASP.NET Core 7 - Defensive database context

gpeipman
985 views

Open Source Your Knowledge, Become a Contributor

Technology knowledge has to be shared and made accessible for free. Join the movement.

Create Content

Defensive database context

To make sure that data from different tenants doesn't get mixed in multi-tenant application we can add some defensive behaviour to multi-tenant database context to build additional safety net for application developers.

The following demonstrates defensive database context and it uses the following classes:

  • ITenantProvider - interface for tenant providers,
  • DummyTenantProvider - tenant provider for demo purposes,
  • BaseEntity - base class for all entities,
  • Person - sample entity type for this demo,
  • CrossTenantUpdateException - exception type for operations that use data from multiple tenants,
  • SampleDbContext - defensive database context.

The sample below creates new instance of database context, adds two instances of Person entities that belong to different tenants and then saves changes. If cross-tenant operation exception is thrown then tenant ID-s involved to incident are written out.

Click to run the .NET Core Web app.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
·using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace EFDefensive
{
public interface ITenantProvider
{
Guid GetTenantId();
}
public class DummyTenantProvider : ITenantProvider
{
public static Guid Tenant1Id = Guid.Parse("51aab199-1482-4f0d-8ff1-5ca0e7bc525a");
public static Guid Tenant2Id = Guid.Parse("ae4e21fa-57cb-4733-b971-fdd14c4c667e");
public Guid GetTenantId()
{
return Tenant1Id;
}
}
public abstract class BaseEntity
{
[Key]
public int Id { get; set; }
public Guid TenantId { get; set; }
}
public class Person : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class CrossTenantUpdateException : ApplicationException
{
public IList<Guid> TenantIds { get; private set; }
public CrossTenantUpdateException(IList<Guid> tenantIds)
{
TenantIds = tenantIds;
}
}
public class SampleDbContext : DbContext
{
private Guid _tenantId;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

References

Open Source Your Knowledge: become a Contributor and help others learn. Create New Content