این مقاله به بررسی عمیق انواع طول عمر سرویسهای Singleton، Scoped و Transient میپردازد، مکانیسم عملکرد آنها را تشریح میکند، سناریوهای استفاده مناسب را مورد بحث قرار میدهد و در نهایت، رهنمودهایی برای انتخاب آگاهانه نوع طول عمر مناسب برای سرویسهای مختلف ارائه میدهد.
.jpg)
مبانی تزریق وابستگی و ثبت سرویسها
پیش از پرداختن به جزئیات طول عمر سرویسها، مروری مختصر بر مفهوم تزریق وابستگی و نحوه ثبت سرویسها در ASP.NET Core ضروری است. تزریق وابستگی یک الگوی طراحی است که در آن وابستگیهای یک شیء (سرویسها) به جای اینکه در داخل خود شیء ایجاد شوند، از خارج به آن تزریق میشوند. این امر منجر به کاهش وابستگی بین اجزاء، افزایش قابلیت آزمایش و بهبود سازماندهی کد میشود.
در ASP.NET Core، کانتینر DI داخلی مسئول مدیریت ایجاد و طول عمر سرویسها است. توسعهدهندگان با استفاده از متدهای توسعهای ارائه شده توسط رابط IServiceCollection (که در شیء builder.Services در Program.cs قابل دسترسی است) سرویسهای خود را در این کانتینر ثبت میکنند. هر ثبت سرویس شامل نوع سرویس (یک رابط یا کلاس پایه) و نوع پیادهسازی (کلاس concrete که رابط یا کلاس پایه را پیادهسازی میکند) و همچنین طول عمر مورد نظر است.
Singleton
Singleton: یک نمونه برای کل عمر برنامه
سرویسهای ثبت شده با طول عمر Singleton، تنها یک نمونه از آنها در طول کل چرخه حیات برنامه ایجاد میشود. این بدان معناست که هرگاه در هر نقطهای از برنامه به این سرویس نیاز باشد، همان نمونه واحد ارائه خواهد شد.
مکانیسم عملکرد:
هنگامی که برای اولین بار یک سرویس Singleton درخواست میشود، کانتینر DI یک نمونه از نوع پیادهسازی ایجاد کرده و آن را در حافظه ذخیره میکند. در درخواستهای بعدی برای همان سرویس، کانتینر به جای ایجاد یک نمونه جدید، همان نمونه ذخیره شده را باز میگرداند. این رفتار تا زمانی که برنامه در حال اجرا است ادامه دارد.
سناریوهای استفاده مناسب:
ملاحظات و موارد احتیاط:
public interface ICounter
{
int Increment();
int GetCount();
}
public class Counter : ICounter
{
private int _count = 0;
public int Increment()
{
return ++_count;
}
public int GetCount()
{
return _count;
}
}
در Program.cs:
builder.Services.AddSingleton();
در این مثال، هر بار که ICounter در هر جای برنامه تزریق شود، یک نمونه واحد از کلاس Counter دریافت خواهد شد و متد Increment() آن برای تمام درخواستها یک شمارنده مشترک را افزایش میدهد.
در مثال ارائه شده که سرویس ICounter با طول عمر Singleton ثبت شده است:
builder.Services.AddSingleton<ICounter, Counter>();
تنها یک نمونه از کلاس Counter در کل طول عمر برنامه ایجاد میشود. این بدان معناست که تمام درخواستها و کاربران مختلف (مانند کاربر شماره یک و کاربر شماره دو که جداگانه لاگین کردهاند) به همان نمونه واحد از شیء Counter دسترسی خواهند داشت.
بنابراین، اگر کاربر شماره یک متد Increment() را فراخوانی کند، مقدار _count در آن نمونه واحد افزایش مییابد. هنگامی که کاربر شماره دو (یا هر کاربر دیگری) متد GetCount() را فراخوانی کند، مقدار بهروز شده را که توسط کاربر شماره یک تغییر داده شده است، مشاهده خواهد کرد.
به عبارت دیگر، وضعیت (_count) در سرویس Singleton بین تمام کاربران و تمام درخواستها به اشتراک گذاشته میشود.
Scoped
Scoped: یک نمونه در هر محدوده (Request Scope)
سرویسهای ثبت شده با طول عمر Scoped، یک نمونه جدید در هر محدوده ایجاد میکنند. در یک برنامه وب ASP.NET Core، یک محدوده معمولاً با هر درخواست HTTP ورودی مرتبط است. این بدان معناست که در طول پردازش یک درخواست HTTP، هر بار که یک سرویس Scoped درخواست شود، یک نمونه یکسان از آن سرویس ارائه خواهد شد. با پایان یافتن درخواست HTTP، نمونه سرویس Scoped نیز از بین میرود.
مکانیسم عملکرد:
کانتینر DI یک محدوده را در ابتدای هر درخواست HTTP ایجاد میکند. هنگامی که یک سرویس Scoped برای اولین بار در طول این محدوده درخواست میشود، یک نمونه از آن ایجاد شده و در داخل محدوده ذخیره میشود. تمام درخواستهای بعدی برای همان سرویس در طول همان محدوده، به همان نمونه ذخیره شده ارجاع داده میشوند. پس از اتمام پردازش درخواست و از بین رفتن محدوده، نمونه سرویس Scoped نیز برای جمع آوری زباله (Garbage Collection) واجد شرایط میشود.
سناریوهای استفاده مناسب:
ملاحظات و موارد احتیاط:
public interface IRequestIdentifier
{
string GetRequestId();
}
public class RequestIdentifier : IRequestIdentifier
{
private readonly string _requestId;
public RequestIdentifier()
{
_requestId = Guid.NewGuid().ToString();
}
public string GetRequestId()
{
return _requestId;
}
}
در Program.cs:
builder.Services.AddScoped();
Transient: یک نمونه جدید در هر بار درخواست
سرویسهای ثبت شده با طول عمر Transient، هر بار که درخواست شوند، یک نمونه جدید ایجاد میکنند. این بدان معناست که اگر یک سرویس Transient چندین بار در طول یک درخواست HTTP (یا در بخشهای مختلف کد) تزریق شود، هر بار یک نمونه متفاوت از آن سرویس دریافت خواهد شد.
مکانیسم عملکرد:
هر زمان که کانتینر DI با یک سرویس Transient مواجه شود، یک نمونه جدید از نوع پیادهسازی ایجاد کرده و آن را برمیگرداند. این رفتار بدون توجه به اینکه چند بار در یک محدوده یا در طول عمر برنامه درخواست شود، تکرار میشود.
سناریوهای استفاده مناسب:
ملاحظات و موارد احتیاط:
public interface IOperation
{
string OperationId { get; }
}
public class Operation : IOperation
{
public string OperationId { get; } = Guid.NewGuid().ToString();
}
بهترین موقعیت برای استفاده از این حالت وقتی است که در یک درخواست انتظار تغییر وضعیت ندارید. فرض کنید که سرویس زیر یک کُد یونیک تولید میکند:
public class UuidGeneratorService : IUuidGeneratorService
{
public string GenerateUuid()
{
return Guid.NewGuid().ToString();
}
}
اگر بصورت Transient ساخته شود، در یک درخواست مانند:
app.MapGet("/scoped-uuid", (IServiceProvider provider) =>
{
using var scope = provider.CreateScope();
var service1 = scope.ServiceProvider.GetRequiredService<IUuidGeneratorService>();
var service2 = scope.ServiceProvider.GetRequiredService<IUuidGeneratorService>();
return $"UUID 1: {service1.GenerateUuid()} | UUID 2: {service2.GenerateUuid()}";
});
مقادیر برگشتی یکی خواهد بود: (چون در یک درخواست ارسال شده است)
UUID 1: 9f3a5b81-7c62-40a7-b5a4-676cb74f3d5e | UUID 2: 9f3a5b81-7c62-40a7-b5a4-676cb74f3d5e
اما اگر همین سرویس را با Scoped بسازیم و دوباره درخواست را ارسال کنیم، مقادیر متفاوت خواهد بود و نیازی به گفتن نیست که اگر بصورت Singleton ساخته میشد، حتی با درخواست های جدا هم مقادیر یکسان بود.
در Program.cs:
builder.Services.AddTransient();
در این مثال، هر بار که IOperation تزریق شود، یک نمونه جدید از Operation با یک OperationId منحصربهفرد ایجاد خواهد شد.
.jpg)
انتخاب طول عمر مناسب: ملاحظات کلیدی
انتخاب طول عمر مناسب برای یک سرویس در ASP.NET Core یک تصمیم مهم است که بر عملکرد، مدیریت منابع و رفتار کلی برنامه تأثیر میگذارد. در اینجا چند نکته کلیدی برای کمک به انتخاب آگاهانه آورده شده است:
.jpg)
بهترین شیوهها و رهنمودها
پیشفرض Transient: اگر در مورد طول عمر یک سرویس مطمئن نیستید، Transient را به عنوان پیشفرض در نظر بگیرید. این امر از مشکلات اشتراکگذاری حالت ناخواسته جلوگیری میکند. سپس، در صورت نیاز به اشتراکگذاری حالت در سطح درخواست یا سراسری، به Scoped یا Singleton تغییر دهید.
آگاهی از همزمانی در Singleton: هنگام استفاده از سرویسهای Singleton که حالت را مدیریت میکنند، اطمینان حاصل کنید که آنها برای دسترسی همزمان ایمن هستند (به عنوان مثال، با استفاده از قفلها یا ساختارهای دادهای thread-safe).
اجتناب از تزریق Scoped به Singleton: سعی کنید از تزریق سرویسهای Scoped به سرویسهای Singleton خودداری کنید، زیرا این میتواند منجر به استفاده از نمونههای قدیمی و مشکلات غیرمنتظره شود. اگر نیاز به انجام این کار دارید، ممکن است لازم باشد یک کارخانه (Factory) را به سرویس Singleton تزریق کنید تا در هر بار نیاز، یک نمونه جدید از سرویس Scoped دریافت کند.
استفاده آگاهانه از Singleton برای سرویسهای سنگین: اگر یک سرویس Singleton منابع زیادی را مصرف میکند، در نظر بگیرید که آیا واقعاً نیاز است که در طول کل عمر برنامه در حافظه بماند. در برخی موارد، ممکن است یک رویکرد حافظه پنهان با انقضا (expiration) مناسبتر باشد.
تست و نظارت: پس از انتخاب طول عمر برای سرویسهای خود، برنامه را به طور کامل تست کنید تا از رفتار مورد انتظار اطمینان حاصل کنید و عملکرد را نظارت کنید تا از هرگونه مشکل مربوط به مدیریت منابع جلوگیری شود.
نتیجهگیری
درک عمیق از طول عمر سرویسها در ASP.NET Core برای ساخت برنامههای کاربردی قوی، مقیاسپذیر و قابل نگهداری ضروری است. انتخاب صحیح بین Singleton، Scoped و Transient تأثیر مستقیمی بر نحوه مدیریت منابع، اشتراکگذاری حالت و عملکرد کلی برنامه دارد. با در نظر گرفتن ماهیت سرویس، نیاز به اشتراکگذاری حالت، هزینه ایجاد و مدیریت منابع، و وابستگیها، توسعهدهندگان میتوانند تصمیمات آگاهانهای بگیرند که منجر به برنامههای کاربردی کارآمدتر و قابل اعتمادتر میشود. تسلط بر این مفاهیم کلیدی، توسعهدهندگان را قادر میسازد تا از قدرت کامل سیستم تزریق وابستگی ASP.NET Core بهرهمند شوند و برنامههایی با معماری تمیز و انعطافپذیر ایجاد کنند.
0 نظر
هنوز نظری برای این مقاله ثبت نشده است.