Multi-Tenancy — Overview
Granit.MultiTenancy provides a complete multi-tenancy stack for SaaS applications.
A pipeline of pluggable resolvers extracts the tenant from HTTP requests, sets an
AsyncLocal context for the entire request, and downstream modules — persistence,
caching, messaging — automatically scope their operations to the active tenant.
Why Granit multi-tenancy
Section titled “Why Granit multi-tenancy”- Zero manual WHERE clauses — EF Core query filters handle tenant isolation transparently
- Four built-in resolvers — subdomain, header, JWT claim, query string
- Three isolation strategies — shared database, schema-per-tenant, database-per-tenant — switchable per tenant at runtime
- Cache key isolation — automatic TenantId prefix on all FusionCache keys
- Host/tenant schema separation — configurable via
appsettings.json - Message propagation — Wolverine automatically carries tenant context across async boundaries
- Automatic provisioning — new tenants get schema creation, EF Core migrations, and data seeding out of the box
Package structure
Section titled “Package structure”- Granit.MultiTenancy — Resolvers, CurrentTenant, middleware
- Granit.MultiTenancy.EntityFrameworkCore — Tenant store, EF Core persistence
- Granit.MultiTenancy.Endpoints — Tenant admin API endpoints
- Granit.Persistence.EntityFrameworkCore — Isolation strategies, GranitDbDefaults, SchemaEnsurer
- Granit.MultiTenancy.Provisioning — Automatic tenant provisioning handler
- Granit.Caching — TenantAwareFusionCache decorator
| Package | Role |
|---|---|
Granit.MultiTenancy | Resolvers, CurrentTenant, middleware, options |
Granit.MultiTenancy.EntityFrameworkCore | ITenantReader/ITenantWriter, tenant aggregate, EF persistence |
Granit.Persistence.EntityFrameworkCore | GranitDbDefaults, SchemaEnsurer, isolation strategy factories |
Granit.MultiTenancy.Provisioning | TenantProvisioningHandler — automatic tenant provisioning on creation |
Granit.Persistence.EntityFrameworkCore.Hosting | ITenantProvisioner, AutoTenantProvisioner — schema + migration + seeding |
Granit.Caching | TenantAwareFusionCache — automatic cache key isolation |
Architecture
Section titled “Architecture”flowchart TB
subgraph HTTP["HTTP Pipeline"]
REQ[Request] --> RES[Resolver Pipeline]
RES --> CTX["ICurrentTenant (AsyncLocal)"]
end
subgraph DATA["Data Layer"]
CTX --> EF["EF Core Query Filters"]
CTX --> CACHE["TenantAwareFusionCache"]
CTX --> WV["Wolverine Context Propagation"]
end
subgraph ISOLATION["Isolation Strategy"]
EF --> SD[SharedDatabase<br/>WHERE TenantId = @id]
EF --> SPT[SchemaPerTenant<br/>SET search_path]
EF --> DPT[DatabasePerTenant<br/>per-tenant connection]
end
Quick start
Section titled “Quick start”[DependsOn(typeof(GranitMultiTenancyModule))]public class AppModule : GranitModule { }{ "MultiTenancy": { "IsEnabled": true, "DomainTemplate": "{0}.myapp.com" }, "TenantIsolation": { "Strategy": "SchemaPerTenant", "HostSchema": "host" }}app.UseAuthentication();app.UseGranitMultiTenancy(); // After auth, before authorizationapp.UseAuthorization();Section contents
Section titled “Section contents”This section covers multi-tenancy in depth:
- Tenant Resolvers — the four built-in resolvers and how to add custom ones
- Isolation Strategies — shared database, schema-per-tenant, database-per-tenant, and the hybrid model
- Host vs Tenant — how to separate host infrastructure
from tenant data using
GranitDbDefaults - Cache Isolation — automatic tenant-scoped cache keys with
TenantAwareFusionCache - Automatic Provisioning — zero-config schema creation, migrations, and seeding for new tenants
See also
Section titled “See also”- Multi-tenancy concept — the problem, decision matrix, and transparent query filters
- Configure multi-tenancy — step-by-step setup guide
- Persistence —
ApplyGranitConventions, interceptors - Wolverine — tenant context propagation