TCIS.MultiTenancy
1.0.0-rc.33
dotnet add package TCIS.MultiTenancy --version 1.0.0-rc.33
NuGet\Install-Package TCIS.MultiTenancy -Version 1.0.0-rc.33
<PackageReference Include="TCIS.MultiTenancy" Version="1.0.0-rc.33" />
<PackageVersion Include="TCIS.MultiTenancy" Version="1.0.0-rc.33" />
<PackageReference Include="TCIS.MultiTenancy" />
paket add TCIS.MultiTenancy --version 1.0.0-rc.33
#r "nuget: TCIS.MultiTenancy, 1.0.0-rc.33"
#:package TCIS.MultiTenancy@1.0.0-rc.33
#addin nuget:?package=TCIS.MultiTenancy&version=1.0.0-rc.33&prerelease
#tool nuget:?package=TCIS.MultiTenancy&version=1.0.0-rc.33&prerelease
TCIS.MultiTenancy
The library providing the core architecture to manage and resolve Multi-Tenancy information within the TCIS system.
This package is derived from Finbuckle.MultiTenant, which is licensed under the Apache License 2.0 — not MIT. Fork point, divergence and the obligations that come with Apache-2.0 are recorded in Provenance — fork point at the end of this document. Read it before changing a file or publishing a package.
🌟 Overview
TCIS.MultiTenancy provides a robust and flexible framework for:
- Tenant Resolver: Identifies the current tenant through various Strategies such as: Host-based, Header-based (for gRPC/REST), custom Delegate, or Static.
- Tenant Stores: Stores and retrieves tenant configuration via: Static Configuration (
IConfiguration), In-Memory, or distributed cache (IDistributedCache). - Flexible Integration: Suitable for all isolation levels (Isolation Tier 1 - RLS, Tier 2 - Schema, Tier 3 - Database).
📦 Installation
dotnet add package TCIS.MultiTenancy
⚙️ Configuration (appsettings.json)
Example with ConfigurationStore (reading the tenant list from appsettings):
{
"MultiTenancy": {
"Stores": {
"ConfigurationStore": {
"Tenants": [
{
"Id": "tenant-1",
"Identifier": "customer-a",
"Name": "Customer A",
"IsolationLevel": 2
},
{
"Id": "tenant-2",
"Identifier": "customer-b",
"Name": "Customer B",
"IsolationLevel": 3
}
]
}
}
}
}
IsolationLevel là bắt buộc cho mọi tenant. Tên khoá là IsolationLevel — ví dụ trong README
này trước đây ghi IsolationTier, một khoá không tồn tại, nên Bind bỏ qua nó im lặng và
tenant nhận giá trị mặc định.
Giá trị: 1 = Tier 1 (Row-Level Security), 2 = Tier 2 (Schema-per-tenant), 3 = Tier 3
(Database-per-tenant). Dùng số, hoặc tên đầy đủ của enum (Tier1_RowLevel…) — không phải
"Tier1".
Khai thiếu thì tenant mang IsolationTier.Unknown và bị từ chối kích hoạt với mã
CONFIG_TENANT_ISOLATION_TIER_MISSING. Đây là fail-closed có chủ đích: trước đây mặc định là
Tier 3, mà Tier 3 nghĩa là "tenant có database riêng nên không cần lọc theo tenant" — nên một
dòng cấu hình bị quên sẽ âm thầm tắt Global Query Filter cho tenant đó, và vì Tier 3 là giá
trị hợp lệ nên không cơ chế kiểm tra nào phát hiện được.
Khai ở Defaults cũng được nếu mọi tenant cùng tier:
"ConfigurationStore": {
"Defaults": { "IsolationLevel": 1 },
"Tenants": [ … ]
}
🚀 Usage
In Program.cs, register the MultiTenancy service using the Fluent Builder API:
using TCIS.MultiTenancy.Extensions;
using TCIS.MultiTenancy.Abstractions; // Contains the TenantInfo record
var builder = WebApplication.CreateBuilder(args);
// Register services
builder.Services.AddTMultiTenant<TenantInfo>()
// 1. Configure the Tenant storage location (Store)
.WithConfigurationStore()
// Or a distributed cache
// .WithDistributedCacheStore(TimeSpan.FromHours(1))
// 2. Configure how to determine the Tenant (Strategy) from the request Context
.WithDelegateStrategy(context =>
{
if (context is HttpContext httpContext)
{
// Extract from the "X-Tenant-Id" Http Header
return Task.FromResult(httpContext.Request.Headers["X-Tenant-Id"].FirstOrDefault());
}
return Task.FromResult<string?>(null);
});
var app = builder.Build();
// Add the Middleware to the pipeline (if using ASP.NET) to set the Tenant for the current Request
// app.UseMultiTenant();
app.Run();
💡 Basic Example
Có hai accessor, và chọn đúng cái là quan trọng:
| Cần gì | Inject | Vì sao |
|---|---|---|
| Mã cảng, TenantId cho nghiệp vụ | IWorkContextAccessor |
Ngữ cảnh vận hành của luồng hiện tại |
| Chuỗi kết nối, mức cách ly — hạ tầng | IMultiTenantContextAccessor<TenantInfo> |
Hồ sơ do Tenant Store khai |
using TCIS.Core.Abstractions.Context;
using TCIS.MultiTenancy.Abstractions;
public class MyService(
IWorkContextAccessor workContextAccessor,
IMultiTenantContextAccessor<TenantInfo> tenantAccessor)
{
public void DoSomething()
{
// Nghiệp vụ: "tôi đang làm việc cho cảng nào"
string? siteCode = workContextAccessor.WorkContext?.Tenant.SiteCode;
// Hạ tầng: chỉ lấy từ đây. ITenantContext KHÔNG mang IsolationLevel.
TenantInfo? tenant = tenantAccessor.MultiTenantContext?.TenantInfo;
IsolationTier tier = tenant?.IsolationLevel ?? IsolationTier.Unknown;
}
}
Ví dụ trong README này trước đây dùng _contextAccessor.TenantContext và
TenantInfo.IsolationTier — cả hai đều không tồn tại, đoạn code đó không biên dịch được.
🧵 Kích hoạt tenant ở luồng KHÔNG phải HTTP
EventBus consumer, Hangfire worker, worker nền — những luồng này không đi qua MultiTenantMiddleware,
nên phải tự nạp ngữ cảnh. Bộ ba hàm, và thứ tự lẫn tính đồng bộ đều bắt buộc:
var tenant = await sp.ResolveTenantAsync(siteCode, ct); // async — chỉ tra Store
try
{
if (!sp.ApplyTenant(tenant)) // ĐỒNG BỘ — ghi ngữ cảnh
{
return; // fail-closed: không chạy nghiệp vụ
}
// … công việc …
}
finally
{
sp.ClearTenant(); // dọn CẢ HAI ngữ cảnh
}
ApplyTenant không được gộp vào một hàm async. Ngữ cảnh nằm trong AsyncLocal, mà
AsyncLocal chảy xuôi theo ExecutionContext chứ không chảy ngược: ghi nó sau một await đã thực
sự nhường luồng thì thay đổi biến mất khi hàm trả về. Bản đầu của lớp này gộp hai bước và bộ
test đỏ với NullReferenceException.
Tệ hơn, nó hỏng không tất định — store trả kết quả đồng bộ (cache nóng) thì thay đổi lan ra bình thường. Chạy đúng trên máy dev, hỏng lần đầu gặp cache lạnh trên production.
ClearTenant dọn cả MultiTenantContext lẫn phần Tenant của WorkContext — đúng bằng những
gì ApplyTenant đã ghi. Nó giữ nguyên User/Trace/Client/Business, vì những thứ đó thuộc về đơn vị
công việc chứ không thuộc về tenant.
Với EventBus, đừng viết tay bộ ba này — gọi services.AddTEventBusTenantContext() của gói
TCIS.EventBus.MultiTenancy, nó móc sẵn vào đúng chỗ trong vòng đời consumer.
📌 Provenance — fork point
TCIS.MultiTenancy, TCIS.MultiTenancy.AspNetCore và TCIS.MultiTenancy.EntityFrameworkCore là
tác phẩm phái sinh của Finbuckle.MultiTenant,
không phải mã gốc của TCIS.
⚠️ Finbuckle dùng Apache License 2.0, KHÔNG phải MIT. Apache-2.0 có bốn điều kiện ở §4 chứ không phải một như MIT — trong đó có hai điều kiện mà MIT không có: phải ghi chú rằng bạn đã sửa file, và phải giữ lại mọi thông báo attribution của bản gốc. Đừng áp thói quen xử lý
TCIS.EventBus(fork của CAP, MIT) sang đây.
Điểm fork
| Upstream | https://github.com/Finbuckle/Finbuckle.MultiTenant.git |
| Commit | d69145ba9cfdc168a4b6fe44a0efebc6776d6119 (d69145b) |
| Ngày | 2025-12-13 |
git describe |
v10.0.1-19-gd69145b — 19 commit sau tag v10.0.1 |
| Giấy phép | Apache License 2.0 — xem LICENSE, giữ nguyên văn theo yêu cầu của chính giấy phép |
Tái lập phép so sánh:
git clone https://github.com/Finbuckle/Finbuckle.MultiTenant.git && cd Finbuckle.MultiTenant
git log --oneline d69145b..origin/master | wc -l # ta đang tụt lại bao nhiêu
Trạng thái đo ngày 2026-08-18
| Upstream HEAD | 9d2a9ee, 2026-08-11, tag v10.1.2 |
| Commit tụt lại | 117 |
| File phái sinh | 38 |
| Dòng code trùng khớp nguyên văn với upstream | ~842 |
So với TCIS.EventBus (fork CAP, chỉ 10 commit sau upstream), fork này xa hơn nhiều — 8 tháng và
117 commit. Nếu định kéo bản vá upstream về thì chi phí ở đây lớn hơn hẳn.
Nghĩa vụ Apache-2.0 §4 và cách TCIS đáp ứng
| §4 | Yêu cầu | Cách đáp ứng |
|---|---|---|
| (a) | Trao cho người nhận một bản sao giấy phép | LICENSE được đóng vào .nupkg qua <None Include="LICENSE" Pack="true"> |
| (b) | File đã sửa phải mang ghi chú nổi bật rằng bạn đã sửa | Dòng // This file has been modified by TCIS. trong header của 38 file phái sinh |
| (c) | Giữ lại mọi thông báo bản quyền/attribution của bản gốc | Header // Copyright Finbuckle LLC, Andrew White, and Contributors. đã được khôi phục trên 38 file đó, cộng <Copyright> của gói |
| (d) | Kèm NOTICE nếu bản gốc có |
Finbuckle không có NOTICE → không áp dụng |
Gói khai PackageLicenseExpression = MIT AND Apache-2.0: phần TCIS viết thêm theo MIT như mọi gói
TCIS.* khác, phần kế thừa từ Finbuckle vẫn mang Apache-2.0.
File nào là phái sinh, file nào là của TCIS
38 file phái sinh mang header attribution — nhận ra bằng dòng // Derived from Finbuckle.MultiTenant.
9 file là mã gốc của TCIS, không mang header đó và không được thêm vào:
| File | Vì sao là của TCIS |
|---|---|
Abstractions/TMultiTenantException.cs |
Kế thừa TBaseException, mang taxonomy tiền tố SEC_ — không liên quan MultiTenantException của Finbuckle |
Abstractions/ITenantContextSynchronizer.cs · Internal/TenantContextSynchronizer.cs |
Đồng bộ TenantInfo sang WorkContext — khái niệm chỉ có ở TCIS |
Extensions/TenantActivationExtensions.cs |
ResolveTenantAsync/ApplyTenant/ClearTenant — viết mới, tách async/sync có chủ đích |
Internal/TenantProfileGuard.cs |
Chặn tenant thiếu IsolationLevel (fail-closed) |
Internal/EagerServiceValidator.cs |
Kiểm cấu hình lúc khởi động |
Repositories/TReadRepository.cs · Repositories/TRepository.cs |
Lớp repository cơ sở của TCIS |
GlobalUsings.cs |
Tệp kỹ thuật |
Cách làm việc với fork này
- Thêm hành vi mới thì đặt vào file của TCIS, đừng sửa file phái sinh. Mỗi dòng thêm vào file phái sinh là một xung đột merge trong tương lai, và làm ranh giới attribution mờ đi.
- Sửa file phái sinh thì giữ nguyên header — xoá nó là vi phạm Apache-2.0 §4(c).
- Thêm file phái sinh mới thì phải chép cả header sang, và cập nhật con số ở mục này.
- Cảnh báo bảo mật của Finbuckle áp cho ta và phải port tay.
- Giữ mục này cập nhật. Chạy lại lệnh ở phần "Điểm fork" khi đụng vào gói và sửa lại con số.
| 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
- Microsoft.Extensions.Caching.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration (>= 9.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Http (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.0)
- TCIS.Core (>= 1.0.0-rc.33)
- TCIS.Logging.Abstractions (>= 1.0.0-rc.33)
NuGet packages (8)
Showing the top 5 NuGet packages that depend on TCIS.MultiTenancy:
| Package | Downloads |
|---|---|
|
TCIS.Pluggable.Persistence.EntityFrameworkCore
TCIS Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. Pluggable Persistence EntityFrameworkCore |
|
|
TCIS.MultiTenancy.AspNetCore
TCIS Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. ASP.NET Core multi-tenancy integration for TCIS Framework. |
|
|
TCIS.MultiTenancy.EntityFrameworkCore
TCIS Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. Entity Framework Core multi-tenancy integration for TCIS Framework. |
|
|
TCIS.Pluggable.Persistence.SqlServer
TCIS Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. Pluggable Persistence SqlServer |
|
|
TCIS.Pluggable.Persistence.PostgreSql
TCIS Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. Pluggable Persistence PostgreSQL |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-rc.33 | 59 | 8/21/2026 |
| 1.0.0-rc.32 | 62 | 8/21/2026 |
| 1.0.0-rc.31 | 63 | 8/21/2026 |
| 1.0.0-rc.30 | 61 | 8/21/2026 |
| 1.0.0-rc.29 | 61 | 8/21/2026 |
| 1.0.0-rc.28 | 62 | 8/21/2026 |
| 1.0.0-rc.27 | 72 | 8/21/2026 |
| 1.0.0-rc.26 | 83 | 8/20/2026 |
| 1.0.0-rc.25 | 75 | 8/20/2026 |
| 1.0.0-rc.24 | 74 | 8/20/2026 |
| 1.0.0-rc.23 | 93 | 8/20/2026 |
| 1.0.0-rc.22 | 91 | 8/19/2026 |
| 1.0.0-rc.21 | 92 | 8/19/2026 |
| 1.0.0-rc.20 | 99 | 8/18/2026 |
| 1.0.0-rc.19 | 78 | 8/13/2026 |
| 1.0.0-rc.18 | 80 | 8/13/2026 |
| 1.0.0-rc.17 | 79 | 8/13/2026 |
| 1.0.0-rc.16 | 86 | 8/13/2026 |
| 1.0.0-rc.15 | 87 | 8/12/2026 |
| 1.0.0-rc.14 | 83 | 8/12/2026 |