Tenantry.EfCore 0.2.3-alpha

Prefix Reserved
This is a prerelease version of Tenantry.EfCore.
dotnet add package Tenantry.EfCore --version 0.2.3-alpha
                    
NuGet\Install-Package Tenantry.EfCore -Version 0.2.3-alpha
                    
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="Tenantry.EfCore" Version="0.2.3-alpha" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tenantry.EfCore" Version="0.2.3-alpha" />
                    
Directory.Packages.props
<PackageReference Include="Tenantry.EfCore" />
                    
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 Tenantry.EfCore --version 0.2.3-alpha
                    
#r "nuget: Tenantry.EfCore, 0.2.3-alpha"
                    
#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 Tenantry.EfCore@0.2.3-alpha
                    
#: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=Tenantry.EfCore&version=0.2.3-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Tenantry.EfCore&version=0.2.3-alpha&prerelease
                    
Install as a Cake Tool

Tenantry

CI Release Quality Gate Status Coverage License .NET

A flexible, modern, and unopinionated multi-tenancy library for .NET.

Tenantry isolates each tenant's data in a shared database using row-level isolation: a TenantId column on the entities you choose to make tenant-scoped. It does this without forcing a base class on your entities, without a custom DbContext, and without taking over your request pipeline. You pick the tenant key type, how tenants are resolved, and where they are stored — and Tenantry wires the isolation in.

builder.Services.AddTenantry<Guid>(tenant =>
{
    tenant.ResolveFromHeader("X-Tenant-Id");                          // where the tenant comes from
    tenant.UseInMemoryStore(tenants);                                 // where tenants are defined
    tenant.AddEfCoreIsolation(options => options.DetectSpoofedWrites = true); // how data is isolated
});

Why Tenantry?

  • Unopinionated. Your tenant key can be a Guid, int, string, or any type that is IEquatable<T> and IParsable<T>. Resolve tenants from a header, subdomain, route, claim, query string, or your own resolver. Store them in memory, a database, or anywhere behind an interface.
  • Interceptor-first isolation. Tenant stamping and cross-tenant write protection work on any DbContext via an EF Core SaveChanges interceptor — no base class required. An optional MultiTenantDbContext<TKey> base class is provided for greenfield convenience.
  • Fails closed. When no tenant is resolved, query filters match nothing rather than leaking every tenant's rows. Cross-tenant writes are rejected before anything is persisted, and a configurable OnMissingTenant policy (warn, allow, or reject) governs writes that run without a tenant context.
  • HTTP and beyond. AddTenantry covers ASP.NET Core (resolution middleware, access validation, endpoint metadata). AddTenantryCore brings the same isolation to console apps, worker services, and desktop UIs with no web stack.
  • Modern .NET. Targets .NET 8, 9, and 10. The core and ASP.NET Core packages are trim- and Native-AOT-compatible (see AOT & trimming).

Packages

Package Version Description
Tenantry.Core NuGet Core interfaces, tenant scope, tenant store, and DI registration
Tenantry.EfCore NuGet EF Core integration — interceptor-based isolation, query filters, isolation policy
Tenantry.AspNetCore NuGet ASP.NET Core integration — resolution middleware, resolvers, access validation

Tenantry.EfCore and Tenantry.AspNetCore both depend on Tenantry.Core. Reference whichever combination matches your host:

# ASP.NET Core app with EF Core isolation (most common)
dotnet add package Tenantry.AspNetCore
dotnet add package Tenantry.EfCore

# Console / worker / desktop app with EF Core isolation
dotnet add package Tenantry.Core
dotnet add package Tenantry.EfCore

Quick start (ASP.NET Core)

using Tenantry.AspNetCore.Extensions;
using Tenantry.Core;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTenantry<Guid>(tenant =>
{
    // 1. How is the tenant identified on each request? (resolvers are tried in order)
    tenant.ResolveFromHeader("X-Tenant-Id");

    // 2. Which tenants exist? (swap for a DB/cache-backed store in production)
    tenant.UseInMemoryStore(
    [
        new TenantDescriptor<Guid> { TenantId = Guid.Parse("…0001"), Name = "Acme" },
        new TenantDescriptor<Guid> { TenantId = Guid.Parse("…0002"), Name = "Globex" },
    ]);
});

var app = builder.Build();

// Resolves the tenant and populates ITenantContext<Guid> for the rest of the request.
app.UseTenantry();

app.MapGet("/me", (ITenantContext<Guid> ctx) =>
        ctx.HasTenant ? Results.Ok(ctx.CurrentTenant!.Name) : Results.NotFound())
   .RequireTenant();

app.Run();

Add EF Core isolation by registering tenant.AddEfCoreIsolation() in the lambda above and calling options.AddTenantInterceptors(sp) in your AddDbContext callback. See the EF Core integration guide for the full picture.

Quick start (console / worker — no ASP.NET Core)

There is no request to resolve a tenant from, so you open and close the tenant scope yourself:

using Tenantry.Core;
using Tenantry.Core.Extensions;

builder.Services.AddTenantryCore<Guid>(tenant =>
{
    tenant.AddEfCoreIsolation(options => options.DetectSpoofedWrites = true);
});

// …later, around a unit of work:
var scope = sp.GetRequiredService<ITenantScope<Guid>>();
using (scope.BeginScope(new TenantDescriptor<Guid> { TenantId = tenantId, Name = "Acme" }))
{
    // EF Core reads are filtered to this tenant and writes are stamped with it.
    await db.SaveChangesAsync();
}

See the runnable Tenantry.Samples.EfCoreConsole project and the non-HTTP hosts guide.

AOT & trimming

Tenantry is built with the trim and AOT analyzers enabled and ships annotated for both. Support differs by package because EF Core's query-filter mechanism requires runtime code generation:

Package Trimming Native AOT
Tenantry.Core ✅ Fully compatible ✅ Fully compatible (IsAotCompatible)
Tenantry.AspNetCore ✅ Fully compatible ✅ Fully compatible (IsAotCompatible) — see the Aot sample
Tenantry.EfCore ✅ Trim-compatible ⚠️ Not AOT-compatible — query filters require dynamic code (see below)
  • Tenantry.Core and Tenantry.AspNetCore are marked IsAotCompatible and IsTrimmable and carry no trim/AOT warnings. The Tenantry.Samples.Aot project publishes with PublishAot=true against a slim host and source-generated JSON.
  • Tenantry.EfCore is IsTrimmable but not AOT-compatible. The read-side query filters (ApplyTenantFilters and the MultiTenantDbContext<TKey> base class) build LINQ expression trees by reflecting over the EF Core model, so they are annotated [RequiresDynamicCode] and [RequiresUnreferencedCode]. This mirrors EF Core itself, which does not support Native AOT. The write-side interceptor does not generate code, but the integration as a whole should be treated as non-AOT.

Full details and guidance are in AOT & trimming.

Documentation

Guide What it covers
Getting started Install, your first tenant-aware app, end to end
Core concepts Tenant key, descriptor, context vs. scope, the AsyncLocal model
Tenant stores In-memory and custom stores, service lifetimes
ASP.NET Core integration Registration, middleware, pipeline ordering, status codes
Tenant resolution Header, subdomain, route, claim, query-string, and custom resolvers
Access control Requiring tenants, access validators, claim-based validation
EF Core integration Query filters, the interceptor, isolation policy, migrations, admin queries
Non-HTTP hosts AddTenantryCore in console apps, workers, and background jobs
AOT & trimming What is supported, per package, and why
Troubleshooting Common pitfalls and how to diagnose them

Samples

Sample Demonstrates
Quickstart Minimal ASP.NET Core setup, resolvers, access validators, endpoint metadata
EfCoreWeb Realistic EF Core app: migrations, DB-backed store, mixed tenanted/global entities, admin queries
EfCoreConsole EF Core isolation with no ASP.NET Core, using AddTenantryCore and manual scopes
Aot Native-AOT-published ASP.NET Core app

License

Licensed under the Apache License 2.0.

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 is compatible.  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 is compatible.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.3-alpha 98 6/27/2026
0.1.0-alpha 83 6/21/2026