مقدمه

در درس قبل با پروسه‌های سیستم‌عامل و نحوه‌ی تعامل با آن‌ها آشنا شدیم. یک پروسه‌، یک مرز ایزولاسیون است که توسط سیستم‌عامل فراهم می‌شود؛ یعنی حافظه و منابع یک پروسه از پروسه‌های دیگر کاملاً جداست. در دنیای .NET Framework (نسخه‌های قدیمی‌تر .NET)، یک واحد ایزولاسیون سبک‌تر و مدیریت‌شده در داخل یک پروسه‌ی واحد نیز وجود داشت که به آن دامنه‌ی برنامه (Application Domain) یا AppDomain گفته می‌شد. درک مفهوم AppDomain برای فهم معماری تاریخی .NET و همچنین درک دلیل تغییرات در .NET Core و نسخه‌های مدرن‌تر، بسیار مهم است.

AppDomain چیست؟

یک AppDomain را می‌توان به عنوان یک "پروسه در پروسه" در نظر گرفت. این یک مرز منطقی و ایزوله در داخل یک پروسه‌ی سیستم‌عامل است که CLR آن را مدیریت می‌کند. در .NET Framework، هر برنامه حداقل در یک AppDomain پیش‌فرض اجرا می‌شد، اما امکان ساخت چندین AppDomain در یک پروسه‌ی واحد وجود داشت.

اهداف اصلی AppDomain در .NET Framework

  • ایزولاسیون (Isolation): کد در حال اجرا در یک AppDomain نمی‌توانست به طور مستقیم به کد یا داده‌های یک AppDomain دیگر دسترسی پیدا کند. این ویژگی بسیار مهم بود. برای مثال، یک وب سرور مانند IIS می‌توانست هر وب‌سایت را در AppDomain جداگانه‌ی خود اجرا کند. به این ترتیب، اگر یک وب‌سایت به دلیل یک خطای جدی از کار می‌افتاد، فقط AppDomain مربوط به خودش دچار مشکل می‌شد و روی دیگر وب‌سایت‌هایی که در همان پروسه در حال اجرا بودند، تأثیری نمی‌گذاشت.
  • بارگذاری و تخلیه‌ی اسمبلی‌ها (Loading and Unloading Assemblies): این مهم‌ترین کاربرد AppDomain بود. در .NET Framework، تنها راه برای "تخلیه" (unload) یک اسمبلی (DLL) از حافظه، تخلیه‌ی کامل AppDomainای بود که آن را بارگذاری کرده بود. این برای سیستم‌هایی که نیاز به بارگذاری و حذف دینامیک پلاگین‌ها داشتند، حیاتی بود.

تغییر بزرگ: AppDomain در .NET Core و .NET مدرن

یکی از بزرگ‌ترین تغییرات معماری در گذار از .NET Framework به .NET Core (و نسخه‌های بعدی مانند .NET 5/6/7/8)، مربوط به AppDomain بود.

در .NET مدرن، قابلیت ایجاد چندین AppDomain در یک پروسه حذف شده است. هر برنامه‌ی .NET اکنون تنها در یک AppDomain واحد اجرا می‌شود و شما نمی‌توانید دامنه‌های جدیدی بسازید.

چرا این قابلیت حذف شد؟

  • پیچیدگی: پیاده‌سازی و مدیریت صحیح ایزولاسیون بین AppDomainها بسیار پیچیده و هزینه‌بر بود.
  • جایگزین‌های بهتر برای ایزولاسیون: با ظهور تکنولوژی‌هایی مانند کانتینرها (مثل Docker) و معماری میکروسرویس، الگوهای بهتری برای ایزولاسیون در سطح پروسه‌ی سیستم‌عامل فراهم شد که بسیار قوی‌تر و استانداردتر هستند.
  • جایگزین برای تخلیه‌ی اسمبلی‌ها: برای حل مشکل اصلی یعنی تخلیه‌ی اسمبلی‌ها، .NET Core یک مکانیزم جدید و سبک‌تر به نام AssemblyLoadContext را معرفی کرد.

جایگزین مدرن: AssemblyLoadContext

در .NET مدرن، اگر نیاز به بارگذاری دینامیک اسمبلی‌ها و سپس تخلیه‌ی آن‌ها از حافظه دارید (مثلاً در یک سیستم پلاگین)، باید از AssemblyLoadContext استفاده کنید. این کلاس به شما اجازه می‌دهد تا یک محدوده‌ی ایزوله برای بارگذاری اسمبلی‌ها ایجاد کنید. مهم‌ترین ویژگی آن این است که می‌توان آن را "قابل جمع‌آوری" (collectible) تعریف کرد.

Copy Icon Program.cs (Conceptual Example)
using System.Reflection;
using System.Runtime.Loader;

// Create a collectible AssemblyLoadContext.
var loadContext = new AssemblyLoadContext("MyPluginContext", isCollectible: true);

// Load an assembly into the context.
Assembly pluginAssembly = loadContext.LoadFromAssemblyPath("Path/To/MyPlugin.dll");

// ... Use types from the loaded assembly ...

// Unload the context and all assemblies within it.
loadContext.Unload();

با فراخوانی متد Unload، همه‌ی اسمبلی‌هایی که در آن AssemblyLoadContext بارگذاری شده‌اند، از حافظه تخلیه می‌شوند و زباله‌روب می‌تواند حافظه‌ی آن‌ها را بازپس گیرد. این مکانیزم، جایگزین اصلی و مدرن برای قابلیت تخلیه‌ی اسمبلی‌ها در AppDomain است.

خلاصه و نتیجه‌گیری

  • AppDomain در .NET Framework: یک واحد ایزولاسیون سبک در داخل یک پروسه بود که برای جداسازی کد و تخلیه‌ی اسمبلی‌ها استفاده می‌شد.
  • AppDomain در .NET Core/5+: هر برنامه فقط یک AppDomain دارد و قابلیت ایجاد دامنه‌های جدید حذف شده است. این مفهوم دیگر برای ایزولاسیون به کار نمی‌رود.
  • جایگزین مدرن برای ایزولاسیون: استفاده از پروسه‌های مجزای سیستم‌عامل یا تکنولوژی‌های کانتینرسازی مانند Docker.
  • جایگزین مدرن برای تخلیه‌ی اسمبلی‌ها: استفاده از کلاس AssemblyLoadContext.

بنابراین، هرچند دانستن مفهوم AppDomain برای درک معماری .NET و کار با کدهای قدیمی‌تر مفید است، اما در پروژه‌های جدید .NET، شما برای ایزولاسیون و مدیریت پلاگین‌ها از ابزارهای دیگری استفاده خواهید کرد.