EFCore.MultiTenant.Core 1.0.2

dotnet add package EFCore.MultiTenant.Core --version 1.0.2
                    
NuGet\Install-Package EFCore.MultiTenant.Core -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EFCore.MultiTenant.Core" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EFCore.MultiTenant.Core" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="EFCore.MultiTenant.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EFCore.MultiTenant.Core --version 1.0.2
                    
#r "nuget: EFCore.MultiTenant.Core, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EFCore.MultiTenant.Core@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EFCore.MultiTenant.Core&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=EFCore.MultiTenant.Core&version=1.0.2
                    
Install as a Cake Tool

EFCore.MultiTenant

Production-grade schema-isolated multitenancy for Entity Framework Core inspired by django-tenants.

NuGet License: MIT Build


Overview

EFCore.MultiTenant provides a complete multitenancy framework for Entity Framework Core using schema-per-tenant isolation.

While EF Core supports schemas technically, it does not provide a production-ready multitenancy architecture comparable to what django-tenants offers for Django applications.

This library fills that gap by providing:

  • Automatic tenant resolution
  • Dynamic schema switching
  • Tenant-aware migrations
  • Shared and tenant DbContext separation
  • Tenant provisioning
  • Middleware integration
  • Background job support
  • Connection safety
  • Tenant caching
  • Cross-request isolation guarantees

Features

Core Features

  • Schema-per-tenant architecture
  • Automatic schema switching
  • Per-tenant model caching
  • Tenant-aware migrations
  • Shared + isolated entity support
  • Automatic schema creation
  • Safe connection reuse
  • Tenant lifecycle management
  • Background service support
  • Async tenant provisioning

ASP.NET Core Integration

  • Middleware-based tenant resolution
  • Header resolution
  • Subdomain resolution
  • Route/path resolution
  • JWT claim resolution
  • Composite resolver pipeline

Database Support

Database Support
PostgreSQL ✅ Full Support
SQL Server ✅ Full Support
MySQL ⚠️ Limited
SQLite ❌ Not Supported

Why EFCore.MultiTenant?

EF Core exposes low-level schema APIs such as:

modelBuilder.HasDefaultSchema(schema);
entity.ToTable("Products", schema);

However, production multitenancy requires much more than schema mapping.

This library provides:

Capability EF Core EFCore.MultiTenant
Dynamic schema switching
Tenant resolution
Schema-aware model caching
Tenant migrations
Provisioning APIs
Isolation middleware
Background tenant scopes
Shared/Tenant context separation

Architecture

Architecture

EFCore.MultiTenant is designed around a simple principle:

Resolve the tenant once per scope, then transparently route all EF Core operations to the correct schema.

The library separates the application into two logical layers:

Layer Responsibility
Shared Layer Global application data (public schema)
Tenant Layer Isolated tenant data (tenant_schema)

Request Lifecycle

Incoming Request
       │
       ▼
Tenant Resolver
(Header / Subdomain / Path / Claim)
       │
       ▼
Tenant Store
(Load tenant metadata)
       │
       ▼
Tenant Context
(Current tenant stored per request scope)
       │
       ▼
Tenant DbContext
(Applies tenant schema automatically)
       │
       ▼
Database
(schema.table)

Design Goals

The architecture focuses on:

  • Minimal boilerplate
  • Transparent schema routing
  • Strong tenant isolation
  • High performance
  • Database portability
  • Production readiness
  • Familiar EF Core patterns

Installation

Core Package

dotnet add package EFCore.MultiTenant.Core

ASP.NET Core Integration

dotnet add package EFCore.MultiTenant.AspNetCore

PostgreSQL Support

dotnet add package EFCore.MultiTenant.PostgreSQL

Quick Start

1. Create a Tenant Entity

public class AppTenant : TenantBase
{
    public string? ContactEmail { get; set; }

    public string Plan { get; set; } = "free";
}

2. Configure Shared DbContext

The shared context hosts global tables such as tenants, plans, and application configuration.

public class AppSharedDbContext : SharedDbContext
{
    public AppSharedDbContext(
        DbContextOptions<AppSharedDbContext> options,
        MultiTenantOptions optionsAccessor)
        : base(options, optionsAccessor)
    {
    }

    public DbSet<AppTenant> Tenants => Set<AppTenant>();

    protected override void ConfigureSharedModel(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AppTenant>()
            .HasIndex(t => t.SchemaName)
            .IsUnique();
    }
}

3. Configure Tenant DbContext

All entities automatically resolve to the current tenant schema.

public class AppTenantDbContext : MultiTenantDbContext
{
    public AppTenantDbContext(
        DbContextOptions<AppTenantDbContext> options,
        ITenantContext tenantContext)
        : base(options, tenantContext)
    {
    }

    public DbSet<Product> Products => Set<Product>();

    public DbSet<Order> Orders => Set<Order>();

    protected override void ConfigureTenantModel(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>()
            .Property(x => x.Name)
            .HasMaxLength(300);
    }
}

4. Implement a Tenant Store

public class EfTenantStore : ITenantStore<AppTenant>
{
    private readonly AppSharedDbContext _db;

    public EfTenantStore(AppSharedDbContext db)
    {
        _db = db;
    }

    public async Task<AppTenant?> GetTenantAsync(
        string identifier,
        CancellationToken cancellationToken = default)
    {
        return await _db.Tenants
            .FirstOrDefaultAsync(
                x => x.SchemaName == identifier,
                cancellationToken);
    }

    public async Task<IReadOnlyList<AppTenant>> GetAllTenantsAsync(
        CancellationToken cancellationToken = default)
    {
        return await _db.Tenants
            .Where(x => x.IsActive)
            .ToListAsync(cancellationToken);
    }
}

5. Register Services

builder.Services.AddDbContext<AppSharedDbContext>(options =>
{
    options.UseNpgsql(connectionString);
});

builder.Services.AddDbContext<AppTenantDbContext>(options =>
{
    options.UseNpgsql(connectionString);
});

builder.Services
    .AddMultiTenancy<
        AppTenant,
        AppTenantDbContext,
        AppSharedDbContext>(options =>
    {
        options.SharedSchema = "public";
        options.AutoMigrateOnProvision = true;
        options.Return404OnUnknownTenant = true;
    })
    .WithHeaderResolution("X-Tenant-ID")
    .WithSubdomainResolution()
    .WithTenantStore<EfTenantStore>()
    .UsePostgres();

builder.Services.AddTenantCaching<AppTenant>();

6. Add Middleware

app.UseRouting();

app.UseAuthentication();

app.UseMultiTenancy<AppTenant>();

app.UseAuthorization();

app.MapControllers();

7. Use Tenant DbContext

[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
    private readonly AppTenantDbContext _db;

    public ProductsController(AppTenantDbContext db)
    {
        _db = db;
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        return Ok(await _db.Products.ToListAsync());
    }
}

Queries automatically resolve to:

acme.products
globex.products
contoso.products

depending on the current tenant.


Tenant Provisioning

public class TenantProvisioningService
{
    private readonly ITenantProvisioner<AppTenant> _provisioner;

    private readonly AppSharedDbContext _db;

    public TenantProvisioningService(
        ITenantProvisioner<AppTenant> provisioner,
        AppSharedDbContext db)
    {
        _provisioner = provisioner;
        _db = db;
    }

    public async Task<ProvisionResult> CreateTenantAsync(
        string name,
        string schema)
    {
        var tenant = new AppTenant
        {
            Name = name,
            SchemaName = schema
        };

        _db.Tenants.Add(tenant);

        await _db.SaveChangesAsync();

        return await _provisioner.ProvisionAsync(tenant);
    }
}

Provisioning automatically:

  • Creates the schema
  • Applies tenant migrations
  • Seeds default data
  • Validates schema state

Migrations

The library supports separate migration pipelines.

Shared Schema Migrations

dotnet ef migrations add InitShared \
  --context AppSharedDbContext \
  --output-dir Migrations/Shared

Tenant Schema Migrations

dotnet ef migrations add InitTenant \
  --context AppTenantDbContext \
  --output-dir Migrations/Tenant

Apply Shared Migrations

dotnet ef database update \
  --context AppSharedDbContext

Apply Tenant Migrations

await provisioner.MigrateAllAsync();

Tenant Resolution Strategies

Strategy Example
Subdomain acme.myapp.com
Header X-Tenant-ID: acme
Path Segment /acme/api/products
JWT Claim tenant_id
Composite First successful match

Background Jobs

Execute for a Single Tenant

await services.RunForTenantAsync(
    tenant,
    async scope =>
    {
        var db = scope.ServiceProvider
            .GetRequiredService<AppTenantDbContext>();

        await db.Products.ToListAsync();
    });

Execute for All Tenants

await services.RunForAllTenantsAsync<AppTenant>(
    async (scope, tenant) =>
    {
        var db = scope.ServiceProvider
            .GetRequiredService<AppTenantDbContext>();

        await db.Products.CountAsync();
    },
    maxParallelism: 4);

Package Structure

EFCore.MultiTenant/
│
├── src/
│   ├── EFCore.MultiTenant.Core/
│   ├── EFCore.MultiTenant.AspNetCore/
│   ├── EFCore.MultiTenant.PostgreSQL/
│   ├── EFCore.MultiTenant.SqlServer/
│   └── EFCore.MultiTenant.Abstractions/
│
├── samples/
│   ├── MinimalApiSample/
│   ├── MVCSample/
│   └── SaaSSample/
│
├── tests/
│   ├── IntegrationTests/
│   └── UnitTests/
│
└── docs/

Roadmap

v1.0

  • Schema isolation
  • Middleware integration
  • Tenant-aware migrations
  • Provisioning APIs
  • Background job scopes
  • PostgreSQL support
  • SQL Server support

v1.1

  • CLI tooling
  • Distributed cache namespacing
  • OpenTelemetry integration
  • Hangfire integration
  • Quartz integration

v2.0

  • Row-level security mode
  • Hybrid multitenancy mode
  • Blazor admin dashboard
  • Tenant plan enforcement

Performance

Designed for high-scale SaaS applications:

  • Zero model rebuilds per request
  • Cached tenant lookups
  • Safe pooled connections
  • Async provisioning pipeline
  • Bounded parallel tenant migrations

Security

  • Strong schema isolation
  • No shared tenant tables
  • Query-level tenant enforcement
  • Middleware validation
  • Tenant context immutability
  • Injection-safe schema handling

Contributing

Contributions are welcome.

  1. Fork the repository
  2. Create a feature branch
  3. Submit a pull request

Please read the contributing guidelines before submitting changes.


License

MIT License

Copyright (c) 2026 EFCore.MultiTenant Contributors

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on EFCore.MultiTenant.Core:

Package Downloads
EFCore.MultiTenant.AspNetCore

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 94 5/12/2026
1.0.1 110 5/12/2026
1.0.0 112 5/11/2026