TCIS.MultiTenancy 1.0.0-rc.33

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

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

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.TenantContextTenantInfo.IsolationTiercả 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.AspNetCoreTCIS.MultiTenancy.EntityFrameworkCoretá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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed