مقدمه

وقتی برنامه‌ی C# شما اجرا می‌شود و به یک نوع داده از یک اسمبلی (DLL) دیگر نیاز پیدا می‌کند، محیط اجرای .NET (CLR) باید آن اسمبلی را پیدا و در حافظه بارگذاری کند. این فرآیند جستجو و بارگذاری، تصادفی نیست و از مجموعه‌ای از قوانین مشخص پیروی می‌کند. این قوانین توسط مفهومی به نام کانتکست بارگذاری (Load Context) تعریف می‌شوند. یک کانتکست، یک حوزه‌ی منطقی است که CLR از آن برای مدیریت و ایزوله کردن اسمبلی‌های بارگذاری شده استفاده می‌کند. درک این کانتکست‌ها برای حل مشکلات رایجی مانند FileNotFoundException یا تداخل نسخه‌های مختلف یک اسمبلی، بسیار مهم است.

سه Load Context اصلی

در .NET، سه کانتکست اصلی برای بارگذاری اسمبلی‌ها وجود دارد که هر کدام قوانین و رفتار خاص خود را دارند.

۱. کانتکست بارگذاری پیش‌فرض (Default Load Context)

این مهم‌ترین و رایج‌ترین کانتکست است. وقتی شما یک برنامه را اجرا می‌کنید، اسمبلی اصلی برنامه (فایل .exe) و تمام وابستگی‌های مستقیم آن (DLLهایی که در پروژه به آن‌ها ارجاع داده‌اید) در این کانتکست بارگذاری می‌شوند.

CLR برای پیدا کردن اسمبلی‌ها در این کانتکست، یک فرآیند جستجوی مشخص (probing) را دنبال می‌کند:

  1. ابتدا Global Assembly Cache (GAC) را بررسی می‌کند (این مفهوم بیشتر به .NET Framework مربوط است).
  2. سپس، پوشه‌ی پایه‌ی برنامه (جایی که فایل اجرایی قرار دارد) را جستجو می‌کند.
  3. در نهایت، زیرپوشه‌های خصوصی که در فایل‌های پیکربندی مشخص شده‌اند را بررسی می‌کند.

در ۹۵ درصد مواقع، تمام اسمبلی‌های برنامه‌ی شما در این کانتکست و به صورت خودکار بارگذاری می‌شوند.

۲. کانتکست Load-From

این کانتکست برای اسمبلی‌هایی است که در مسیر جستجوی پیش‌فرض قرار ندارند و شما آن‌ها را به صورت دینامیک و با استفاده از متد Assembly.LoadFrom() از یک مسیر مشخص بارگذاری می‌کنید.

Copy Icon Program.cs
using System.Reflection;

// Load an assembly from a specific path.
Assembly myAssembly = Assembly.LoadFrom("C:\\MyLibraries\\SomeLibrary.dll");

نکته‌ی مهم در مورد این کانتکست، نحوه‌ی حل وابستگی‌های آن است. اگر SomeLibrary.dll به اسمبلی دیگری نیاز داشته باشد، CLR ابتدا در همان پوشه‌ی C:\MyLibraries به دنبال آن می‌گردد و اگر پیدا نکرد، به سراغ کانتکست بارگذاری پیش‌فرض می‌رود. این رفتار گاهی می‌تواند منجر به بارگذاری نسخه‌های غیرمنتظره از وابستگی‌ها شود.

۳. کانتکست Load-File و جایگزین مدرن

یک روش دیگر، استفاده از متد Assembly.LoadFile() است. این متد یک اسمبلی را بارگذاری می‌کند اما تقریباً هیچ کمکی برای پیدا کردن وابستگی‌های آن نمی‌کند. این کانتکست بسیار محدود است و به ندرت استفاده می‌شود.

همانطور که در درس قبل دیدیم، در .NET مدرن، راهکار اصلی برای سناریوهای بارگذاری دینامیک و ایزوله، استفاده از کلاس AssemblyLoadContext است. این کلاس به شما اجازه می‌دهد تا کانتکست بارگذاری سفارشی خود را با قوانین جستجوی دلخواه ایجاد کنید. این روش، جایگزین امن‌تر و قدرتمندتر برای کانتکست Load-From محسوب می‌شود.

چرا درک کانتکست‌ها مهم است؟

درک این مفاهیم به شما در حل دو مشکل رایج کمک می‌کند:

  • خطای FileNotFoundException یا TypeLoadException: وقتی با این خطاها مواجه می‌شوید، معمولاً به این معنی است که CLR نتوانسته یک اسمبلی مورد نیاز را در مسیرهای جستجوی مربوط به کانتکست فعلی پیدا کند. دانستن اینکه کد شما در کدام کانتکست در حال اجراست، به شما در پیدا کردن منشأ مشکل کمک می‌کند.
  • تداخل نسخه‌ها (The "Diamond Dependency" Problem): گاهی اوقات دو کامپوننت مختلف در برنامه‌ی شما به دو نسخه‌ی متفاوت از یک اسمبلی سوم وابستگی دارند. کانتکست‌های بارگذاری (به خصوص AssemblyLoadContext سفارشی) می‌توانند به ایزوله کردن این وابستگی‌ها و جلوگیری از تداخل آن‌ها کمک کنند.

برای اکثر برنامه‌های کاربردی، شما فقط با کانتکست بارگذاری پیش‌فرض سروکار خواهید داشت. اما زمانی که وارد دنیای ساخت سیستم‌های پلاگین، ابزارهای دینامیک یا معماری‌های پیچیده می‌شوید، تسلط بر این مفاهیم برای نوشتن کدهای قوی و پایدار ضروری خواهد بود.