Muonroi.Tenancy.Core
1.0.0-alpha.16
dotnet add package Muonroi.Tenancy.Core --version 1.0.0-alpha.16
NuGet\Install-Package Muonroi.Tenancy.Core -Version 1.0.0-alpha.16
<PackageReference Include="Muonroi.Tenancy.Core" Version="1.0.0-alpha.16" />
<PackageVersion Include="Muonroi.Tenancy.Core" Version="1.0.0-alpha.16" />
<PackageReference Include="Muonroi.Tenancy.Core" />
paket add Muonroi.Tenancy.Core --version 1.0.0-alpha.16
#r "nuget: Muonroi.Tenancy.Core, 1.0.0-alpha.16"
#:package Muonroi.Tenancy.Core@1.0.0-alpha.16
#addin nuget:?package=Muonroi.Tenancy.Core&version=1.0.0-alpha.16&prerelease
#tool nuget:?package=Muonroi.Tenancy.Core&version=1.0.0-alpha.16&prerelease
Muonroi.Tenancy.Core
Runtime building blocks for shared-database multi-tenancy:
AsyncLocal-backed tenant context, multi-source tenant ID resolution, per-tenant connection-string mapping, schema selection, quota tracking, and security validation.
Muonroi.Tenancy.Core implements the core tenancy services consumed by Muonroi.Tenancy (the ASP.NET middleware layer) and your own application code. It provides TenantContext — an AsyncLocal<string>-backed store for the current tenant ID — alongside a five-source DefaultTenantIdResolver (claim → header → route → path → subdomain), MappingTenantConnectionStringFactory for per-tenant connection strings, TenantSchemaSelector for separate-schema isolation, and TenantQuotaTracker for distributed-cache-backed quota enforcement. It depends on Muonroi.Tenancy.Abstractions for contracts and Muonroi.Quota.Abstractions for quota types.
Installation
dotnet add package Muonroi.Tenancy.Core --prerelease
Quick Start
The services in this package can be registered individually without the license-gated AddTenantContext() helper. The Quickstart.Tenancy sample demonstrates this pattern:
using Muonroi.Tenancy.Abstractions;
using Muonroi.Tenancy.Abstractions.Interfaces;
using Muonroi.Tenancy.Core;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Bind options consumed by TenantSchemaSelector and MappingTenantConnectionStringFactory.
builder.Services.Configure<MultiTenantOptions>(
builder.Configuration.GetSection(MultiTenantOptions.SectionName)); // "MultiTenantConfigs"
builder.Services.Configure<TenantConnectionStringsOptions>(
builder.Configuration.GetSection(TenantConnectionStringsOptions.SectionName)); // "TenantConnectionStrings"
// Ambient tenant context (AsyncLocal-backed).
builder.Services.AddScoped<ITenantContext, TenantContext>();
// HTTP-based tenant ID resolver: claim → header → route → path → subdomain.
builder.Services.AddScoped<ITenantIdResolver, DefaultTenantIdResolver>();
// Schema selector for SeparateSchema isolation.
builder.Services.AddSingleton<TenantSchemaSelector>();
// Per-tenant connection-string factory with a fallback.
builder.Services.AddSingleton<ITenantConnectionStringFactory>(sp =>
{
IOptions<TenantConnectionStringsOptions> opts =
sp.GetRequiredService<IOptions<TenantConnectionStringsOptions>>();
string fallback = builder.Configuration.GetConnectionString("Default")
?? "Host=localhost;Database=app;Username=app;Password=app";
return new MappingTenantConnectionStringFactory(opts, fallback);
});
// Quota management (distributed-cache-backed tracker + in-memory store).
builder.Services.AddTenantQuotaManagement();
Read the current tenant ID anywhere in your application:
// Via DI (request-scoped):
public class MyService(ITenantContext tenantContext)
{
public void DoWork()
{
string? tenantId = tenantContext.TenantId;
}
}
// Via static ambient (background jobs, EF filters):
string? tenantId = TenantContext.CurrentTenantId;
For admin/seeding operations that must bypass EF global query filters:
using (new MSeedingScope())
{
// TenantContext.AllowCrossTenantAccess == true here.
// EF global filters for ITenantScoped entities are bypassed.
await SeedDefaultRolesAsync(dbContext);
} // AllowCrossTenantAccess restored to previous value on dispose.
Features
TenantContext—AsyncLocal<string?>-backedITenantContext; exposesCurrentTenantId(static, for EF filters) andAllowCrossTenantAccess(static, bypasses EF global filters whentrue)DefaultTenantIdResolver— resolves tenant ID from JWT claim →X-Tenant-Idheader → route value (tenantId/tenant) → URL path segment → subdomain; skips reserved prefixes (api,swagger,health,grpc,v1–v3)TenantSchemaSelector— maps tenant ID to a database schema name forSeparateSchemaisolation and optionally rewrites PostgreSQL connection strings withSearchPathMappingTenantConnectionStringFactory— looks up a per-tenant connection string fromTenantConnectionStringsOptions.ConnectionStrings; falls back to a supplied defaultDefaultTenantConnectionStringFactory— single-connection-string factory for shared-database setupsTenantSecurityValidator— static validator that cross-checks context, claim, and header tenant IDs; returns typed error codes (missing-tenant-context,missing-tenant-claim,tenant-mismatch,header-claim-mismatch)TenantQuotaTracker— distributed-cache-backedITenantQuotaTracker; checks and increments usage counters keyed by tenant +QuotaType+ time window; falls back toTenantQuotaPresets.Freewhen no quota record is foundInMemoryTenantQuotaStore— in-processITenantQuotaStorefor development and testingMSeedingScope—IDisposablescope that setsTenantContext.AllowCrossTenantAccess = truefor the duration of database seeding; restores the previous value on disposeContextMirrorScope—IDisposablescope that mirrors anISystemExecutionContextintoTenantContext.CurrentTenantId,UserContext, and an optional log scope; used by background jobs and message consumersAddTenantQuotaManagement()— registersInMemoryTenantQuotaStore(singleton) andTenantQuotaTracker(scoped)- Legacy namespace (
Muonroi.Tenancy.Core.Legacy) — preservedMultiTenantConfigs,TenantContextMiddleware, andAddTenantContext()+AddTenantIdResolver<Tr>()helpers for projects that have not yet migrated to the current API
Configuration
MultiTenantOptions (section: "MultiTenantConfigs")
Defined in Muonroi.Tenancy.Abstractions. Consumed by TenantSchemaSelector.
{
"MultiTenantConfigs": {
"Strategy": "SharedDatabase"
}
}
Strategy values: SharedDatabase, SeparateSchema. When SeparateSchema is set, TenantSchemaSelector.ResolveSchema(tenantId) returns a sanitized schema name and ApplyToConnectionString appends SearchPath=<schema> to PostgreSQL connection strings.
TenantConnectionStringsOptions (section: "TenantConnectionStrings")
Consumed by MappingTenantConnectionStringFactory.
{
"TenantConnectionStrings": {
"ConnectionStrings": {
"tenant-a": "Host=pg-a;Database=tenant_a;Username=app;Password=secret",
"tenant-b": "Host=pg-b;Database=tenant_b;Username=app;Password=secret"
}
}
}
Legacy MultiTenantConfigs (section: "MultiTenantConfigs")
Used by Legacy.AddTenantContext() only:
{
"MultiTenantConfigs": {
"Enabled": true,
"RequireTenantClaimForAuthenticatedUser": true
}
}
AddTenantContext() requires AddLicenseProtection() to be called first and enforces the MultiTenant license feature.
API Reference
| Type | Purpose |
|---|---|
TenantContext |
ITenantContext implementation; AsyncLocal-backed TenantId and CurrentTenantId; AllowCrossTenantAccess flag for EF filter bypass |
DefaultTenantIdResolver |
ITenantIdResolver — claim → header → route → path → subdomain resolution |
MappingTenantConnectionStringFactory |
ITenantConnectionStringFactory — dictionary lookup with fallback |
DefaultTenantConnectionStringFactory |
ITenantConnectionStringFactory — single connection string, shared-database |
TenantSchemaSelector |
Schema name resolution and PostgreSQL SearchPath injection for separate-schema isolation |
TenantSecurityValidator |
Static TryValidate() — cross-checks context, claim, and header; returns error codes |
TenantQuotaTracker |
ITenantQuotaTracker — CheckQuotaAsync, IncrementUsageAsync, GetUsageAsync, ResetDailyQuotasAsync |
InMemoryTenantQuotaStore |
ITenantQuotaStore — in-memory implementation for dev/test |
MSeedingScope |
IDisposable — enables cross-tenant EF access during database seeding |
ContextMirrorScope |
IDisposable — mirrors ISystemExecutionContext into ambient tenant/user context + log scope |
TenantQuotaServiceCollectionExtensions.AddTenantQuotaManagement() |
Registers InMemoryTenantQuotaStore (singleton) and TenantQuotaTracker (scoped) |
Legacy.TenantServiceCollectionExtensions.AddTenantContext() |
License-gated full registration: context, resolver, middleware (legacy API) |
Legacy.TenantServiceCollectionExtensions.AddTenantIdResolver<Tr>() |
Replaces DefaultTenantIdResolver with a custom resolver (legacy API) |
Samples
- Quickstart.Tenancy — registers
TenantContext,DefaultTenantIdResolver,TenantSchemaSelector, andMappingTenantConnectionStringFactorywithout the license gate; mountsTenantResolutionMiddlewareon a branch
Compatibility
- Target framework:
net8.0 - License: Apache-2.0 (OSS)
Related Packages
Muonroi.Tenancy.Abstractions— contracts:ITenantContext,ITenantIdResolver,ITenantConnectionStringFactory,MultiTenantOptions,TenantConnectionStringsOptionsMuonroi.Tenancy— ASP.NET integration:TenantResolutionMiddlewareand Redis tenant cacheMuonroi.Quota.Abstractions—ITenantQuotaTracker,ITenantQuotaStore,QuotaType,TenantQuota
License
Apache-2.0. See LICENSE-APACHE for the full text.
| Product | Versions 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. |
-
net8.0
- Muonroi.Core.Abstractions (>= 1.0.0-alpha.16)
- Muonroi.Logging.Abstractions (>= 1.0.0-alpha.16)
- Muonroi.Quota.Abstractions (>= 1.0.0-alpha.16)
- Muonroi.Tenancy.Abstractions (>= 1.0.0-alpha.16)
NuGet packages (18)
Showing the top 5 NuGet packages that depend on Muonroi.Tenancy.Core:
| Package | Downloads |
|---|---|
|
Muonroi.Data.EntityFrameworkCore
Entity Framework Core infrastructure for Muonroi: MDbContext with audit, soft-delete, multi-tenant filters, and repository base. |
|
|
Muonroi.Caching.Memory
Multi-level in-memory + distributed cache implementation with tenant-aware key isolation and stampede protection. |
|
|
Muonroi.Tenancy.SiteProfile
Per-site DI registration pattern for schema-divergent multi-tenancy. Wire correct DbContext, services, and mappers per site deployment. |
|
|
Muonroi.BackgroundJobs.Abstractions
Background job contracts: IBackgroundJob, IScheduledJob, and job context interfaces for Muonroi job scheduler integrations. |
|
|
Muonroi.Governance
OSS license governance implementation: basic license verification, capability resolution, and no-op implementations for free-tier usage. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-alpha.16 | 227 | 6/22/2026 |
| 1.0.0-alpha.15 | 252 | 5/31/2026 |
| 1.0.0-alpha.14 | 226 | 5/15/2026 |
| 1.0.0-alpha.13 | 195 | 5/2/2026 |
| 1.0.0-alpha.12 | 118 | 4/2/2026 |
| 1.0.0-alpha.11 | 159 | 4/2/2026 |
| 1.0.0-alpha.9 | 104 | 3/30/2026 |
| 1.0.0-alpha.8 | 358 | 3/28/2026 |
| 1.0.0-alpha.7 | 83 | 3/27/2026 |
| 1.0.0-alpha.5 | 84 | 3/27/2026 |
| 1.0.0-alpha.4 | 82 | 3/27/2026 |
| 1.0.0-alpha.3 | 78 | 3/27/2026 |
| 1.0.0-alpha.2 | 92 | 3/26/2026 |
| 1.0.0-alpha.1 | 129 | 3/8/2026 |