Beta · 0.7 · 1.0 not before November 2027

Fail-closed tenant isolation for your existing ASP.NET Core and EF Core apps

One call on the DbContext you already have isolates its data, with no base class, and Tenantry reads tenants from where your application keeps them. With no tenant, queries return nothing, and a forged tenant key changes no rows.

Program.csTenantry Core
builder.Services.AddTenantry<Guid>(tenant => tenant
    .ResolveFromHeader("X-Tenant-Id")           // names the tenant
    .ValidateTenantAccessByClaim("tenant_id")   // the user may use it
    .UseInMemoryStore(tenants));                // lists the tenants

builder.Services.AddDbContext<AppDbContext>(options => options
    .UseSqlServer(connectionString)
    .UseTenantry());                            // isolates the data

// Tenant-owned: stamped on insert, filtered in every query.
public class Order : ITenantEntity<Guid>
{
    public int Id { get; set; }
    public Guid TenantId { get; private set; }
}

Your DbContext stays yours

One call, options.UseTenantry(), isolates any DbContext: no base class, no interface, no SaveChanges override. Pooled contexts too, with a database per tenant.

Fails closed

With no tenant, queries match nothing. Writes to another tenant’s rows are rejected before saving, and the stored tenant is part of every UPDATE and DELETE, so a forged key changes nothing.

Your keys, your registry

A Guid, int, string or any parsable tenant key. Resolve it from a header, subdomain, host name, route value, claim or a resolver of your own, and find a tenant by its slug or custom domain.

HTTP and beyond

ASP.NET Core middleware and access validation, and the same isolation in workers, console and desktop apps. .NET 10, and .NET 8 and 9 until 10 November 2027; Native AOT for every Core package except Tenantry.EfCore.

Open-source Core. Pro for running tenants.

Core is free and isolates tenant data, in a shared database or a database per tenant. Pro adds the operations around it: with a shared database, the tenant in Hangfire, Quartz.NET, MassTransit and Rebus work, audit logging, and offboarding that deletes a tenant’s rows; with a database or schema per tenant, also provisioning, migrations and health checks.

Tenantry Core

Free · Apache-2.0

Tenant resolution and EF Core isolation for an existing application.

  • Tenant resolution, your tenant store and access validation
  • Suspended tenants refused in requests and in RunInScopeAsync (ValidateTenantActivity)
  • Worker scopes for background work as a tenant
  • Options per tenant, authentication settings included (Tenantry.Options)
  • HybridCache and IDistributedCache entries (Tenantry.Caching) and cached responses kept per tenant
  • ITenantInvalidator, which clears what Tenantry keeps for a tenant when the tenant changes
  • The current tenant sent with HttpClient and gRPC calls, and read back only from callers you trust (Tenantry.Http, Tenantry.AspNetCore)
  • Log event ids to alert on, a tenant.id tag on traces, and request metrics per tenant
  • A model check that refuses mappings it cannot isolate, and a list of unisolated entity types for your tests

Tenantry Pro

Subscription

The tenant in your jobs and messages, audit logging, and tenant onboarding, offboarding and migrations, with a shared database or a database or schema per tenant.

  • Hangfire, Quartz.NET, MassTransit and Rebus work run as the tenant it was created for, and refused for a suspended or deleted tenant
  • Recurring jobs and background services that run once for each tenant
  • Audit logging of each tenant’s inserts, updates and deletes
  • Onboarding and offboarding in one call, including deleting a tenant’s rows from a shared database
  • Tenant databases created, migrated and seeded, on SQL Server, PostgreSQL and MySQL
  • Migrations across every tenant database or schema, as a deployment step
  • A schema per tenant (SQL Server, PostgreSQL), and mixed mode
  • Tenant health checks and connection-string caching
  • Private NuGet package feed
  • Email support, with a reply within 48 hours

Bring your own tenant registry

Tenantry reads tenants from where you already keep them: a table, configuration or another service. A store finds a tenant by id and lists them all. Creating and changing tenants stays in your application.

Tenant stores
TenantStore.cs
public sealed class TenantStore(AppDbContext db) : ITenantStore<string>
{
    public async ValueTask<ITenantDescriptor<string>?> GetTenantAsync(
        string tenantId, CancellationToken ct = default) =>
        await db.Tenants.AsNoTracking()
            .FirstOrDefaultAsync(t => t.TenantId == tenantId, ct);

    public async ValueTask<IReadOnlyList<ITenantDescriptor<string>>> GetAllTenantsAsync(
        CancellationToken ct = default) =>
        await db.Tenants.AsNoTracking()
            .ToListAsync<ITenantDescriptor<string>>(ct);
}

builder.Services.AddTenantry<string>(tenant => tenant
    .ResolveFromSubdomain(o => o.BaseDomains.Add("example.com"))
    .UseStore<TenantStore>()
    .CacheTenants());

How Tenantry compares

Tenantry beside your own query filters, Finbuckle.MultiTenant and ABP. The full comparison also lists what Tenantry does not do.

Your own query filtersFinbuckle.MultiTenantABPTenantry
What your DbContext needsA filter on each entity, and a SaveChanges overrideDerive from MultiTenantDbContext, or implement IMultiTenantDbContext, configure entities in OnModelCreating and call EnforceMultiTenant() in SaveChangesDerive from AbpDbContext and register it with AddAbpDbContext, in an ABP applicationOne call, options.UseTenantry()
Saving an entity with another tenant’s TenantIdWhatever your SaveChanges override checksThrows by default; TenantMismatchMode can ignore or overwrite it insteadSaved with that TenantId. ABP sets TenantId from the current tenant when the entity object is created, and does not check it on save; its docs say changing it moves the entity to that tenant, and advise setting it only in the entity’s constructorThrows, and the stored TenantId is in the WHERE clause of every UPDATE and DELETE that SaveChanges sends, so a forged key matches no row
Create and migrate tenant databasesYou write itNoABP provides a base class for a handler you write that migrates a tenant’s database when the tenant is created; managing tenants’ connection strings from a UI needs the commercial SaaS moduleYes, in Pro
Per-tenant options and authenticationYou write itYes, bothPer-tenant settings and featuresYes, both
LicenceYoursApache-2.0. Its README says that from 10 November, use of its official releases in revenue-generating work falls under an Open Source Maintenance Fee.LGPL-3.0; the SaaS module needs a commercial ABP licence (Team or higher)Core Apache-2.0; Pro a subscription
The full comparison

Pricing

Core is free. Pro is one subscription for your whole company, billed monthly or yearly. Both are in beta until 1.0.

Tenantry Core

Free
Apache-2.0, on GitHub
Get started with Core

Tenantry Pro

Beta
per month

14-day refund · Cancel any time · The versions you have keep working