Winglet.Tenancy.EntityFrameworkCore
0.1.0
dotnet add package Winglet.Tenancy.EntityFrameworkCore --version 0.1.0
NuGet\Install-Package Winglet.Tenancy.EntityFrameworkCore -Version 0.1.0
<PackageReference Include="Winglet.Tenancy.EntityFrameworkCore" Version="0.1.0" />
<PackageVersion Include="Winglet.Tenancy.EntityFrameworkCore" Version="0.1.0" />
<PackageReference Include="Winglet.Tenancy.EntityFrameworkCore" />
paket add Winglet.Tenancy.EntityFrameworkCore --version 0.1.0
#r "nuget: Winglet.Tenancy.EntityFrameworkCore, 0.1.0"
#:package Winglet.Tenancy.EntityFrameworkCore@0.1.0
#addin nuget:?package=Winglet.Tenancy.EntityFrameworkCore&version=0.1.0
#tool nuget:?package=Winglet.Tenancy.EntityFrameworkCore&version=0.1.0
Winglet.Tenancy.EntityFrameworkCore
Winglet.Tenancy.EntityFrameworkCore applies the ADR-019 SharedDatabase persistence invariants to a normal consumer-owned EF Core DbContext.
Registration
Register the core package, this EF integration, and the consumer context through normal dependency injection:
builder.Services.AddWingletTenancy(
builder.Configuration,
TenancyCapability.SharedDatabaseOrNone);
builder.Services.AddWingletTenancyEntityFrameworkCore();
builder.Services.AddDbContext<AppDbContext>(options =>
options
.UseMySql(connectionString, serverVersion)
.UseWingletTenancy());
The context remains application-owned. It receives a per-context TenantDbContextState, implements ITenantDbContext, and applies model integration once after its own entity mappings:
public sealed class AppDbContext(
DbContextOptions<AppDbContext> options,
TenantDbContextState tenantState)
: DbContext(options), ITenantDbContext
{
public TenantDbContextState TenantState { get; } = tenantState;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.UseWingletTenancy(this);
}
}
Tenant-owned entities implement the marker contract:
public sealed class Document : ITenantOwned
{
public long Id { get; set; }
public TenantId? TenantId { get; set; }
}
The model integration discovers all ITenantOwned entity roots, maps TenantId to the canonical tenant_id Int64 column, preserves compatible explicit mapping, and combines tenant isolation with any existing query filter.
Runtime behavior
In SharedDatabase mode, normal queries require trusted tenant context and are filtered to that tenant. Added entities receive the current trusted TenantId. Tracked updates and deletes require trusted materialized state, matching ownership, and an unchanged TenantId. Detached or fabricated update/delete state fails closed.
A tenant-sensitive DbContext pins its first successfully resolved mode and TenantId for the lifetime of that context. Later tenant-sensitive use must have the same tenant context; changing or removing it fails safely. Repeated scopes for the same tenant are valid, while work for another tenant requires a new DbContext instance.
In None mode, the explicit mode is pinned without a tenant identity, tenant filtering and write enforcement are disabled, and no tenant is fabricated or assigned.
Explicitly authorized infrastructure can create a bounded read-only bypass:
using var bypass = dbContext.BeginTenantQueryFilterBypass();
The caller remains responsible for authorization and audit. Bypass state is per DbContext, nested scopes restore deterministically, and cross-tenant tracked entries are detached when the outer scope ends. The SaveChanges write guard remains active inside the bypass.
Security boundaries
IgnoreQueryFiltersis blocked for tenant-owned entity queries and for global-root queries that explicitly or automatically include/traverse a mapped navigation to a tenant-owned entity.IgnoreAutoIncludescan keep a genuinely navigation-free global query unaffected; controlled tenant-filter bypass remains the supported cross-tenant read path.ExecuteUpdateandExecuteDeleteare blocked for tenant-owned entities because they bypass tracked SaveChanges validation and could mutateTenantId.- Arbitrary raw SQL is not parsed or rewritten. Consumers using
Database.ExecuteSqlRaw, ADO.NET, or equivalent paths against tenant-owned tables own the tenant predicate and security review. AddDbContextPooland pooled factories are not supported. A pooled context reuses context-local state across DI scopes; use ordinaryAddDbContextunless a future reviewed integration explicitly resets trusted tenant state.- The package does not implement authorization, repositories, Unit of Work wrappers, migrations, tenant registries, secret resolution, or DatabasePerTenant routing.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.0)
- Winglet.Tenancy (>= 0.1.0)
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.1.0 | 112 | 8/29/2026 |