TGViewer
C# Geeks (.NET) C# Geeks (.NET) @csharpgeeks · 548 subscribers
Post #133 106
🏢 معماری چند-مستأجره (Multi-Tenancy) با EF Core (قسمت ۱):

یک دیتابیس برای همه
اپلیکیشن‌های مدرن SaaS (نرم‌افزار به عنوان سرویس) یک ویژگی مشترک دارن: چند-مستأجری (Multi-Tenancy).

یعنی یک اپلیکیشن به چندین مشتری (مستأجر یا tenant) سرویس میده، ولی داده‌های هر مشتری کاملاً از بقیه ایزوله هست. امروز در قسمت اول از این سری، با این معماری و روش پیاده‌سازی "یک دیتابیس برای همه" با EF Core آشنا میشیم.

1️⃣ دو رویکرد اصلی برای چند-مستأجری
برای ایزوله کردن داده‌های مشتریان، دو راه اصلی وجود داره:

🧠 ایزوله‌سازی منطقی (Logical Isolation):

یک دیتابیس واحد برای همه مشتریان، که داده‌ها با یک شناسه مثل TenantId از هم جدا میشن. (موضوع این پست)

🏬 ایزوله‌سازی فیزیکی (Physical Isolation):

یک دیتابیس کاملاً مجزا برای هر مشتری.

انتخاب بین این دو، به نیاز پروژه بستگی داره. برای صنایعی مثل حوزه سلامت که به ایزوله‌سازی بالا نیاز دارن، روش دوم الزامیه.

2️⃣ ابزار اصلی ما: EF Core Query Filters 🔍
برای پیاده‌سازی روش "یک دیتابیس برای همه"، ابزار اصلی ما در EF Core، فیلترهای کوئری سراسری (Global Query Filters) هست. این قابلیت به ما اجازه میده یه شرط WHERE رو به صورت خودکار به تمام کوئری‌های یک انتیتی خاص اضافه کنیم. یک بار پیاده‌سازیش می‌کنیم و دیگه تقریباً فراموشش می‌کنیم!

3️⃣ پیاده‌سازی قدم به قدم
برای این کار به دو چیز نیاز داریم: ۱. راهی برای شناسایی مستأجر فعلی. ۲. راهی برای فیلتر کردن داده‌ها برای اون مستأجر.

• قدم اول: شناسایی مستأجر با TenantProvider
اول از همه، باید بفهمیم درخواست فعلی برای کدوم مستأجره. ما این منطق رو تو یه کلاس جدا به اسم TenantProvider کپسوله می‌کنیم. این کلاس، TenantId رو از هدر (X-TenantId) درخواست HTTP می‌خونه.
public sealed class TenantProvider
{
private const string TenantIdHeaderName = "X-TenantId";
private readonly IHttpContextAccessor _httpContextAccessor;

public TenantProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public string TenantId => _httpContextAccessor
.HttpContext?
.Request
.Headers[TenantIdHeaderName];
}


💡نکته: راه‌های امن‌تر دیگری هم برای گرفتن TenantId وجود داره مثل JWT Claim یا API Key.


• قدم دوم: اعمال فیلتر سراسری در DbContext
حالا که می‌تونیم مستأجر فعلی رو پیدا کنیم، باید به EF Core بگیم که تمام کوئری‌ها رو بر اساس اون فیلتر کنه. بهترین جا برای این کار، متد OnModelCreating در DbContext ماست.
public class OrdersDbContext : DbContext
{
private readonly string _tenantId;

public OrdersDbContext(
DbContextOptions<OrdersDbContext> options,
TenantProvider tenantProvider)
: base(options)
{
// TenantId رو از TenantProvider می‌گیریم
_tenantId = tenantProvider.TenantId;
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// این فیلتر به صورت خودکار به تمام کوئری‌های انتیتی Order اضافه میشه
modelBuilder
.Entity<Order>()
.HasQueryFilter(o => o.TenantId == _tenantId);
}
}


حالا هر کوئری‌ای که روی جدول Order زده بشه، EF Core به صورت خودکار شرط WHERE TenantId = 'current_tenant_id' رو بهش اضافه می‌کنه!


🔖 هشتگ‌ها:
#CSharp #ASPNETCore #DotNet #MultiTenancy #EntityFrameworkCore #SoftwareArchitecture #Backend
More from @csharpgeeks
  1. Sep 22, 2026یه مدتی قراره از دنیای NET. فاصله بگیرم، چون وقتشه برم سربازی. راستش نمیدونم این مدت رو چج…
  2. Sep 20, 2026🔥 حالا مشکل اصلی: Alert Storm فرض کن Database از دسترس خارج شده. ۱۰۰ Pod داری. هر Pod می‌…
  3. Sep 20, 2026🚨 طراحی سیستم Monitoring و Alerting در یک سیستم بزرگ فرض کن ساعت ۳ صبح است. سیستم شما با…
  4. Sep 19, 2026#Engineering_Leadership تصمیم نگرفتن هم یک تصمیم است یه چیز عجیب توی تیم‌های مهندسی: گاهی…
  5. Sep 19, 2026☑ چک‌لیست آماده‌سازی تیم، فرایندها و زیرساخت برای توسعه با AI توجه: هیچ چک‌لیستی جهان‌شمول…
  6. Sep 19, 2026📌پایان یک انتظار طولانی: اعتبارسنجی ناهمگام (Async Validation) در NET 11.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →