حسین احمدی
بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

آشنایی با مفهوم AppDomain در سی شارپ و استفاده از کلاس AppDomain

همانطور که اکثر شما دوستان عزیز توسینسو اطلاع دارید کلیه برنامه هایی که در توسط سیستم عامل اجرا می شوند بوسیله Process ها تقسیم بندی می شوند، در حقیقت هر Process یک مرز بندی میان برنامه های در حال اجراست که مشخص می کند هر پراسس از چه منابعی استفاده می کند. اما در محیط دات نت این موضوع کمی تفاوت دارد! زمانی که شما برنامه ای مبتنی بر دات نت را اجرا می کنید، Process ای به صورت مستقیم برای این فایل اجرایی ایجاد نمی شود، به جای ایجاد Process یک فضای منطقی داخل یک Process برای برنامه اجرا شده ایجاد می شود که به آن AppDomain می گویند. ممکن است در داخل یک Process چندین AppDomain داشته باشیم که هر AppDomain می تواند متعلق به یک فایل اجرایی دات نت باشد. استفاده از AppDomain ها به جای Process ها یکسری مزایا دارد که این مزایا عبارتند از:

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران
  1. AppDomain ها قابلیتی مجزا از سیستم عامل هستند، یعنی سیستم عامل درکی از مفهوم AppDomain ندارد و CLR است که وظیفه ایجاد و مدیریت AppDomain ها را به عهده دارد. این قابلیت باعث می شود که برنامه های اجرایی دات نت برای اجرا به سیستم عامل وابستگی نداشته باشند.
  2. ایجاد و حذف AppDomain توسط CLR نسبت به ایجاد و حذف Process ها هزینه کمتری داشته و با سرعت بیشتری انجام می شوند.
  3. در صورت بروز مشکل برای یک AppDomain در یک Process، مابقی AppDomain های داخل Process بدون مشکل به کار خود ادامه می دهند.

همانطور که گفتیم یک Process می تواند شامل چندین AppDomain باشد، اما توجه کنید که این AppDomain ها به صورت کامل از یکدیگر تفکیک شده اند و هیچ AppDomain ای نمی تواند به اطلاعات و یک AppDomain دیگر دسترسی داشته باشد، البته امکان ارتباط بین دو AppDomain بوسیله پروتکل هایی که برای برنامه نویسی توزیع شده (Distributed) استفاده می شوند مانند Windows Communication Foundation وجود دارد.

همانطور که گفتیم Process های ایجاد شده می توانند شامل چندین AppDomain باشند، اما همیشه اینگونه نیست، زمانی که شما یک برنامه دات نت را اجرا می کنید، CLR یک AppDomain اولیه ایجاد می کند که اصطلاحاً به آن Default Application Domain می گویند. بعد از ایجاد AppDomain اولیه و در صورت نیاز، AppDomain های دیگری توسط CLR ایجاد خواهند شد.

کلاس System.AppDomain

در محیط دات نت تنها CLR نیست که امکان ایجاد و مدیریت AppDomain ها را دارد، شما نیز به عنوان یک برنامه نویس می توانید AppDomain ایجاد کرده و آن ها را مدیریت کنید. برای اینکار باید از کلاس AppDomain که در فضای نام System قرار گرفته استفاده کنید. در ادامه این مطلب با این کلاس بیشتر آشنا خواهید شد.برای شروع می خواهیم نحوه کار با Default Application Domain را توضیح دهیم، Default Application Domain همان AppDomain پیش فرض است که زمان اجرای برنامه توسط CLR ایجاد می شود. برای دسترسی به AppDomain پیش فرض می توانیم از خصوصیت CurrentDomain در کلاس AppDomain استفاده کنیم. کد زیر اطلاعاتی از Default Application Domain به ما نمایش می دهد:

static void Main(string[] args)
{
    DisplayAppDomainInfo(AppDomain.CurrentDomain);
}
        
public static void DisplayAppDomainInfo(AppDomain domain)
{
    Console.WriteLine("AppDomain Name: {0}", domain.FriendlyName);
    Console.WriteLine("AppDomain Id: {0}", domain.Id);
    Console.WriteLine("AppDomain Is Default: {0}", domain.IsDefaultAppDomain());
    Console.WriteLine("AppDomain Base Directory: {0}", domain.BaseDirectory);
}

با اجرای کد بالا بر اساس پروژه ای که در حال اجرا است خروجی مانند خروجی زیر با کمی تفاوت دریافت خواهید کرد:

AppDomain Name: AppDomains.exe
AppDomain Id: 1
AppDomain Is Default: True
AppDomain Base Directory: F:\Workspaces\AppDomains\AppDomains\bin\Debug

دقت کنید که نام AppDomain بر اساس نام فایل اجرایی مشخص می شود که در مثال بالا AppDomains.exe است، همچنین پوشه AppDomain برابر مسیری است که برنامه اجرایی در آن قرار دارد.

دریافت لیست Assembly های بارگذاری شده در AppDomain

برای دریافت لیست اسمبلی هایی که در AppDomain بارگذاری شده اند می توانید از متد GetAssemblies استفاده کنید، در مثال زیر لیست اسمبلی های بارگذاری شده در Default Application Domain به همراه نسخه اسمبلی نمایش داده می شود:

static void Main(string[] args)
{
    DisplayAppDomainLoadedAssemblies(AppDomain.CurrentDomain);
}

public static void DisplayAppDomainLoadedAssemblies(AppDomain domain)
{
    var assemblies = domain.GetAssemblies();
    foreach(var assembly in assemblies)
    {
        var assemblyName = assembly.GetName();
        Console.WriteLine(assemblyName.Name);
        Console.WriteLine(assemblyName.Version);
    }
}

با اجرای کد بالا خروجی زیر را دریافت خواهید کرد:

mscorlib
4.0.0.0
AppDomains
1.0.0.0

دریافت رویداد زمانی بارگذاری اسمبلی در AppDomain

یکی از قابلیت های AppDomain ها، امکان دریافت رویداد در زمان بارگذاری اسمبلی هاست، زمانی که شما برنامه ای را اجرا می کنید یکسری اسمبلی به صورت خودکار یا به صورت دستی در داخل AppDomain بارگذاری می شوند. شما می توانید با استفاده از رویداد AssemblyLoad در زمان بارگذاری Assembly ها کد مورد نظر خود را اجرا کنید:

static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyLoad += (obj, eventArgs) =>
    {
        Console.WriteLine("Assembly {0} loaded in AppDomain", eventArgs.LoadedAssembly.GetName().Name);
    };
}

همانطور که مشاهده می کنید، پارامتر دوم رویداد AssemblyLoad شامل یک خصوصیت به نام LoadedAssembly است که اطلاعات مربوط به اسمبلی بارگذاری شده را به ما نمایش می دهد.

نحوه ایجاد AppDomain جدید

همانطور که گفتیم می توان با کمک کلاس AppDomain عملیات ایجاد، حذف و مدیریت AppDomain ها را انجام داد. در این بخش می خواهیم با نحوه ایجاد کردن AppDomain های جدید آشنا شویم. مزیت ایجاد AppDomain در برنامه دات نت در دو موضوع زیر نمود پیدا می کند:

  1. ایجاد اسمبلی های Dynamic که برای استفاده از آن ها باید در یک AppDomain جدید آن ها را بارگذاری کرد.
  2. برخی از مباحث امنیتی که نیازمند تفکیک Assembly ها در AppDomain های مختلف و بر اساس نیازمندهای امنیتی مربوطه است.

سعی می کنیم در مطالبی جداگانه در مورد هر یک موارد بالا صحبت کنیم، اما در این بخش تنها می خواهیم با نحوه ایجاد AppDomain ها آشنا شویم. برای ایجاد AppDomain جدید باید از متد CreateDomain که در کلاس AppDomain تعریف شده استفاده کنیم. در مثال یک AppDomain ایجاد کرده و اطلاعات مربوط به آن را نمایش می دهیم:

var newAppDomain = AppDomain.CreateDomain("SecondaryAppDomain");
DisplayAppDomainInfo(newAppDomain);

زمانی که متد CreateDomain را فراخوانی می کنیم می بایست یک نام برای آن انتخای کنیم که در بالا نام SecondaryAppDomain را AppDomain جدید انتخاب کردیم. همچنین متد DisplayAppDomainInfo نیز در ابتدای این مطلب ایجاد شده است.

بارگذاری Assembly ها در AppDomain های ایجاد شده

زمانی که یک AppDomain جدید ایجاد کردیم، می توانیم اسمبلی های مورد نظر خود را در آن بارگذاری کنیم. برای بارگذاری یک اسمبلی در یک AppDomain از متد Load که در کلاس AppDomain تعریف شده استفاده می کنیم. فرض کنید که یک اسمبلی با نام Tools.dll داریم و می خواهیم این اسمبلی را در AppDomain جدیدی که ایجاد کردیم بارگذاری کنیم. دقت کنید که فایل dll ما باید داخل پوشه bin\debug که پوشه فایل اجرایی ما است قرار داشته باشد. برای بارگذاری اسمبلی tools به صورت زیر عمل می کنیم:

var newAppDomain = AppDomain.CreateDomain("SecondaryAppDomain");
newAppDomain.AssemblyLoad += (obj, eventArgs) =>
{
    Console.WriteLine("Assembly {0} loaded into AppDomain.", eventArgs.LoadedAssembly.GetName().Name);
};
try
{
    newAppDomain.Load("Tools");
}
catch (FileNotFoundException exception)
{
    Console.WriteLine(exception.Message);
}

دقت کنید که رویداد AssemblyLoad را استفاده کردیم و پس از اجرای برنامه در صورتی که اسمبلی Tools وجود داشته باشد در AppDomain بارگذاری شده و همچنین پیغامی مبنی بر بارگذاری Assembly در صفحه Console نمایش داده می شود.

Unload کردن یک AppDomain

معمولاً در برنامه هایی که AppDomain های جانبی اضافه می شوند می توان بوسیله متد Unload یک AppDomain را حذف کرد. ممکن است شما تعدادی اسمبلی در یک AppDomain بارگذاری کرده باشید، اما به صورت مستقیم امکان Unload کردن اسمبلی ها وجود ندارد و برای اینکار باید AppDomain را Unload کنید تا Assembly های بارگذاری شده در AppDomain نیز Unload شوند. در کد زیر AppDomain ای ایجاد کرده و در انتها آن را Unload می کنیم:

var newAppDomain = AppDomain.CreateDomain("SecondaryAppDomain");
newAppDomain.AssemblyLoad += (obj, eventArgs) =>
{
    Console.WriteLine("Assembly {0} loaded into AppDomain.", eventArgs.LoadedAssembly.GetName().Name);
};
try
{
    newAppDomain.Load("Tools");
}
catch (FileNotFoundException exception)
{
    Console.WriteLine(exception.Message);
}
DisplayAppDomainInfo(newAppDomain);
AppDomain.Unload(newAppDomain);

همچنین می توانید از رویداد ProcessExit برای دریافت رویداد زمان Unload شدن اسمبلی استفاده کنید:

newAppDomain.ProcessExit += (obj, eventArgs) =>
{
    Console.WriteLine("AppDomain unloaded!");
};

در این مطلب با AppDomain و اینکه چگونه می توان در برنامه های دات نت با آن ها کار کرد آشنا شدیم. مبحث دیگری پیرامون AppDomain ها باقی می ماند به نام Object Context Boundaries که در مطلبی جداگانه با این مبحث آشنا خواهیم شد. ITPRO باشید

نویسنده: حسین احمدی

منبع: ITPRO


حسین احمدی
حسین احمدی

بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب

حسین احمدی ، بنیانگذار TOSINSO ، توسعه دهنده وب و برنامه نویس ، بیش از 12 سال سابقه فعالیت حرفه ای در سطح کلان ، مشاور ، مدیر پروژه و مدرس نهادهای مالی و اعتباری ، تخصص در پلتفرم دات نت و زبان سی شارپ ، طراحی و توسعه وب ، امنیت نرم افزار ، تحلیل سیستم های اطلاعاتی و داده کاوی ...

نظرات