📎 Webclip
How to Implement Multitenancy in ASP.NET Core with EF Core
The post presents multitenancy as a way for one application instance to serve multiple tenants while keeping each tenant’s data isolated. It focuses on a discriminator column approach and shows how ASP.NET Core and EF Core can apply tenant filtering and tenant assignment across the application.
Reading notes#
- Multitenancy lets one application instance serve multiple customers while keeping each tenant’s data isolated and invisible to others.
- The article compares database-per-tenant, schema-per-tenant, table-per-tenant, and discriminator-column approaches, then chooses the discriminator column as the main implementation.
- The example uses Books, Authors, Users, and Tenants, with Book, Author, and User entities implementing
ITenantEntityand carrying a nullableTenantId. - Tenant-related entities can also have a foreign key relationship with
Tenant, although the post notes that a plainTenantIdcolumn is also possible. - A
TenantProviderreadsuser-idandtenant-idfrom claims in the current HTTP request and exposes them throughGetCurrentTenantInfo. - The provider and
IHttpContextAccessorare registered in dependency injection, andApplicationDbContextreceivesITenantProviderthrough its constructor. HasQueryFilteris used inOnModelCreatingforUser,Author, andBookso read queries only return rows whoseTenantIdmatches the current tenant.- The post says
HasQueryFiltershould be set beforebase.OnModelCreating(modelBuilder)and that the tenant provider must be exposed through a public property for the filters to work correctly per request. SaveChangesAsyncis overridden to find added or modifiedITenantEntityentries and assignTenantIdfrom the current tenant info.- If no tenant id is available during write operations, the code throws an exception and aborts the operation.
- On login,
IgnoreQueryFiltersis used so a user can be found across all tenants, and the generated JWT includes atenant-idclaim. - The book creation endpoint checks that the author exists, then adds the book and saves changes without tenant-specific code in the endpoint itself.
- The book lookup endpoint uses normal EF Core queries with
Include, and the global filter keeps it within the current tenant automatically. - For users who can access multiple tenants, the post shows a version of
TenantProviderthat also reads anX-TenantIdheader. - A
TenantCheckerMiddlewarecan compare the requested tenant header with the tenant claim and return403 Forbiddenwhen access is not allowed. - The post also discusses a conditional global query filter for super-admin access, but notes that EF Core model caching prevents per-request conditionals from working normally.
- To support conditional filters, it shows a
DynamicModelCacheKeyFactorythat forces EF Core to rebuild the model per context instance. - The article warns that this dynamic cache key approach hurts database-call performance and should be used carefully and benchmarked.
