ما هو .NET Generic Host - شرح DI والإعدادات والـ logging و BackgroundService
· 小村 豪 · C#, .NET, Generic Host, Worker, التصميم
عندما تبدأ بكتابة تطبيق console أو worker في .NET، يمكنك في الغالب أن تكتفي بوضع منطق بسيط مباشرةً في Main.
لكن حالما يكبر التطبيق ولو قليلاً، تميل نفس الاحتياجات إلى الظهور.
- تريد قراءة
appsettings.json - تريد أن تتمكّن متغيّرات البيئة من تجاوز الإعدادات
- تريد إخراج الـ logs عبر
ILogger - لا تريد أن يتحوّل إنشاء الكائنات إلى جدار من
new - تريد حلقة تعمل في الخلفيّة
- تريد أن يُوقَف التطبيق بشكل نظيف عند
Ctrl+Cأو عند إيقاف الخدمة
هنا يدخل Generic Host إلى الصورة.
لكنّ الاسم نفسه أيضاً سهل أن يختلط قليلاً.
- ما الفرق بين
Host.CreateApplicationBuilderوHost.CreateDefaultBuilder؟ - هل
IHostهو نفسه DI container؟ - كيف يرتبط بـ
BackgroundService؟ - هل هو منفصل عن
WebApplicationBuilderفي ASP.NET Core؟ - هل يستحقّ الاستخدام في تطبيق console أصلاً؟
عندما تختلط هذه الأفكار، قد يبدو Generic Host وكأنّه «شيء لتطبيقات الويب فقط»، أو على النقيض «شيء يجب أن يُبنى عليه كلّ تطبيق دائماً». كلتا النظرتين خشنتان أكثر من اللازم.
يفترض هذا المقال الإحساس العمليّ في .NET 6 وما بعده كقاعدة، وينظّم هذه النقاط الأربع أوّلاً:
- ما هو Generic Host فعلاً
- ما الذي يجمعه ويديره من أجلك
- كيف يرتبط
Host.CreateApplicationBuilderوHost.CreateDefaultBuilderوWebApplication.CreateBuilderببعضها - أين يكون أهدأ مكان للبدء
المحتويات
- الجواب القصير أوّلاً
- الجداول الأولى للنظر إليها
- 2.1. ما الذي يحتويه Generic Host
- 2.2. الفروق بين نقاط دخول الـ builder
- الصورة الإجماليّة لـ Generic Host
- ما الذي يحسّنه Generic Host
- 4.1. يمركز توصيل بدء التشغيل
- 4.2. DI والإعدادات والـ logging مرتبطة من البداية
- 4.3. يتعامل مع الإيقاف النظيف والعمليّات طويلة الأمد بشكل أسهل
- الإعداد الأدنى
- 5.1. مثال أدنى لتطبيق console
- 5.2.
appsettings.json - 5.3. إضافة
BackgroundService
- الأنماط النموذجيّة
- 6.1. أدوات console قصيرة العمر
- 6.2. workers والخدمات في الخلفيّة
- 6.3. يجلس أيضاً تحت ASP.NET Core
- الحالات التي يلائمها جيّداً
- الحالات التي يكون فيها غير ضروريّ أو مفرطاً
- الفخاخ الشائعة
- الخلاصة
- المراجع
1. الجواب القصير أوّلاً
- Generic Host هو الأساس الذي يدير بدء التشغيل والـ lifetime لتطبيق .NET.
- يشمل DI والإعدادات والـ logging و
IHostedService/BackgroundServiceومعالجة إيقاف التطبيق. - في التطبيقات الجديدة غير الويب، يكون
Host.CreateApplicationBuilder(args)عادةً نقطة الدخول الأنظف. WebApplicationBuilderفي ASP.NET Core ليس عالماً منفصلاً. إنّه نقطة دخول موجَّهة للويب مبنيّة على نفس نموذج الاستضافة.- بعبارة أخرى، Generic Host ليس فقط حول DI container بمفرده. إنّه الآليّة التي تجمع تركيب التطبيق وإدارة الـ lifetime في مكان واحد.
القاعدة العمليّة هي أنّ Generic Host يبدأ بإعطاء عائد بمجرّد أن يكبر تطبيقك أكثر من «اقرأ الوسائط، اطبع مرّة، اخرج».
في الوقت نفسه، فهو ليس شيئاً يجب جرّه إلى كلّ أداة صغيرة.
2. الجداول الأولى للنظر إليها
2.1. ما الذي يحتويه Generic Host
يساعد كثيراً أن تفصل ما هو داخل الصندوق أوّلاً.
| الجزء | ما يديره Generic Host | لماذا يفيد ذلك |
|---|---|---|
| DI | يبني الخدمات من IServiceCollection |
يجعل تجنّب سلاسل new أسهل |
| Configuration | يجمع appsettings.json ومتغيّرات البيئة ووسائط سطر الأوامر وغيرها |
يجعل اختلافات البيئات أسهل في الإدارة |
| Logging | يهيّئ الأساس لـ ILogger<T> |
يجعل تغيير وجهات الـ logs لاحقاً أسهل |
| Hosted service | يبدأ ويوقف IHostedService / BackgroundService |
يجعل فصل العمل طويل الأمد عن نقطة دخول التطبيق أسهل |
| Lifetime | يتعامل مع بدء التشغيل والإيقاف عبر IHostApplicationLifetime و IHostEnvironment والتجريدات ذات الصلة |
يجعل توحيد طريقة انتهاء التطبيق عند Ctrl+C أو SIGTERM أو إيقاف الخدمة أسهل |
النقطة المهمّة هنا هي أنّ Generic Host ليس مجرّد «غلاف DI مريح».
في الممارسة العمليّة، يُفهَم بشكل أفضل على أنّه الصندوق الذي يربط منطقة دخول التطبيق ببعضها.
2.2. الفروق بين نقاط دخول الـ builder
هذا أيضاً أسهل للرؤية في جدول واحد.
| نقطة الدخول | الاستخدام الرئيس | أسلوب الكتابة | الخيار الأوّل |
|---|---|---|---|
Host.CreateApplicationBuilder(args) |
تطبيقات console / worker جديدة غير ويب | الكتابة مباشرةً إلى builder.Services و builder.Configuration و builder.Logging |
استخدمه للتطبيقات الجديدة |
Host.CreateDefaultBuilder(args) |
قواعد كود قائمة أو إعدادات قديمة كثيفة الاعتماد على extension methods | يستخدم تكويناً مسلسلاً مثل ConfigureServices |
استخدمه عند مطابقة الكود القائم |
WebApplication.CreateBuilder(args) |
تطبيقات / APIs ويب ASP.NET Core | نقطة دخول موجَّهة للويب مبنيّة على Generic Host | استخدمه لتطبيقات الويب |
CreateApplicationBuilder و CreateDefaultBuilder ليسا «شيئاً جديداً مقابل شيء قديم منفصل».
كلاهما يرتكز على نفس قدرات الاستضافة الأساسيّة والقيم الافتراضيّة.
الفرق الأكبر هو بشكل رئيس أسلوب كود الإعداد.
لتطبيق جديد غير ويب، يكون Host.CreateApplicationBuilder(args) عادةً نقطة الدخول الأنظف اليوم.
أمّا WebApplication.CreateBuilder(args) فهو أسهل للفهم إذا اعتبرته نفس التدفّق، موسَّعاً لسيناريوهات الويب.
3. الصورة الإجماليّة لـ Generic Host
إذا رسمت الصورة كاملةً، فإنّها تبدو هكذا.
flowchart LR
Args["args / environment variables / appsettings.json"] --> Builder["Host.CreateApplicationBuilder(args)"]
Builder --> Config["builder.Configuration"]
Builder --> Services["builder.Services"]
Builder --> Logging["builder.Logging"]
Services --> Hosted["IHostedService / BackgroundService"]
Builder --> Build["builder.Build()"]
Build --> Host["IHost"]
Host --> Run["Run / RunAsync"]
Run --> Lifetime["startup / shutdown / Ctrl+C / SIGTERM"]
Lifetime --> Hosted
في التدفّق الشائع، تنشئ الـ builder في Program.cs، وتضيف الخدمات عبر builder.Services، وتضبط الإعدادات والـ logging حسب الحاجة، ثمّ تستدعي Build() للحصول على IHost، وأخيراً تشغّله عبر Run() أو RunAsync().
شيء مهمّ بصمت هو كم القدر المتضمَّن مسبقاً عند نقطة Host.CreateApplicationBuilder(args).
بشكل افتراضيّ، على سبيل المثال، تحصل بالفعل على:
- المسار الحاليّ كـ content root
- إعدادات الـ host من متغيّرات البيئة المسبوقة بـ
DOTNET_ووسائط سطر الأوامر - إعدادات التطبيق من
appsettings.jsonوappsettings.{Environment}.jsonو user secrets في Development ومتغيّرات البيئة ووسائط سطر الأوامر - مزوّدي logging مثل Console و Debug و EventSource و EventLog على Windows
- التحقّق من الـ scope والتحقّق من dependencies في
Development
لذا فأنت لا تربط كلّ شيء يدويّاً من الصفر.
أنت تبدأ من أساس مكتمل إلى حدّ معقول للاستخدام العاديّ.
4. ما الذي يحسّنه Generic Host
4.1. يمركز توصيل بدء التشغيل
أحد أكبر الفوائد الهادئة هو أنّ نقطة دخول التطبيق تصبح أقلّ ميلاً للتشتّت.
حالما يكبر التطبيق قليلاً، يميل Main إلى جمع أشياء مثل:
- تحميل ملفّات الإعدادات
- التجاوزات الخاصّة بالبيئة
- تهيئة الـ logger
- توصيل
HttpClientو repositories والخدمات - بدء العمليّات في الخلفيّة
- التنظيف عند إشارات الإيقاف
إذا رُبط كلّ ذلك يدويّاً دون host، فإنّ نقطة الدخول كثيراً ما تصبح أكثر لزوجةً مع الوقت حتّى لو بدأت صغيرة.
مع Generic Host، يصبح Program.cs بشكل واضح «المكان الذي تُجمَع فيه الـ dependencies وتوصيل بدء التشغيل».
ذلك وحده غالباً ما يجعل مراجعة الكود أسهل بكثير.
4.2. DI والإعدادات والـ logging مرتبطة من البداية
مع Generic Host، تجلس DI والإعدادات والـ logging جميعاً على نفس الأساس منذ البداية.
يعني ذلك أنّ الـ classes تستطيع بشكل طبيعيّ أن تستقبل أشياء مثل:
ILogger<T>IConfigurationIHostEnvironmentIOptions<T>
العائد العمليّ هو أنّ الوصول إلى الإعدادات وبناء الخدمات أقلّ احتمالاً للانحراف نحو أساليب مختلفة.
إذا كنت تحتاج إلى قيمة أو قيمتين فقط، فقد تكون قراءة IConfiguration["Section:Key"] مباشرةً كافيةً.
لكن حالما تبدأ الإعدادات بالنموّ، يكون عادةً أهدأ ربطها في classes باستخدام IOptions<T>.
ينطبق الشيء نفسه على الـ logging. بدلاً من بناء ILoggerFactory يدويّاً في أماكن مختلفة، فإنّ حقن ILogger<T> في الـ classes التي تحتاج إليه يحافظ عادةً على شكل الكود أوضح.
تلك إحدى نقاط القوّة الحقيقيّة لـ Generic Host: فهو يتيح التعامل مع هذه الأشياء معاً كجزء من أساس التطبيق بدلاً من كونها اهتمامات غير مترابطة.
4.3. يتعامل مع الإيقاف النظيف والعمليّات طويلة الأمد بشكل أسهل
لا يساعد Generic Host فقط في كيفيّة بدء التطبيق. بل يساعد أيضاً في كيفيّة إيقافه.
عندما يبدأ الـ host، يُستدعى StartAsync لكلّ IHostedService مسجَّل.
في تطبيقات نمط worker، تعمل الخدمات المستضافة بما فيها BackgroundService عبر ExecuteAsync.
في هذا السياق، يعني «الإيقاف النظيف» عدم قتل العمليّة فوراً، بل بدلاً من ذلك:
- إرسال إشارة الإيقاف
- الخروج من الحلقات وحالات الانتظار
- تنظيف الاتّصالات والموارد
ذلك مهمّ جدّاً في التطبيقات طويلة الأمد.
يصبح من الأسهل بكثير توحيد سلوك الإيقاف عبر أحداث Ctrl+C أو SIGTERM أو إيقاف الخدمة.
وعندما يريد التطبيق نفسه أن يطلب من الـ host التوقّف، يكون IHostApplicationLifetime.StopApplication() متاحاً.
يعطيك ذلك طريقة طبيعيّة على مستوى الـ host لتقول: «انتهى العمل، أوقف الآن بشكل نظيف».
5. الإعداد الأدنى
5.1. مثال أدنى لتطبيق console
نقطة مهمّة هي أنّ استخدام Generic Host لا يعني تلقائيّاً أنّك تحتاج إلى BackgroundService.
حتّى أداة console تعمل مرّة واحدة وتخرج يمكن أن تستفيد من Generic Host إن كانت تحتاج إلى DI أو إعدادات أو logging.
إن كنت تضيفه إلى مشروع console عاديّ، فإنّ الخطوة الأولى المعتادة هي الإشارة إلى Microsoft.Extensions.Hosting.
dotnet add package Microsoft.Extensions.Hosting
قد يبدو Program.cs الأدنى هكذا:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<JobRunner>();
using IHost host = builder.Build();
try
{
JobRunner runner = host.Services.GetRequiredService<JobRunner>();
await runner.RunAsync();
return 0;
}
catch (Exception ex)
{
ILogger logger = host.Services
.GetRequiredService<ILoggerFactory>()
.CreateLogger("Program");
logger.LogError(ex, "Unhandled exception occurred during job execution.");
return 1;
}
internal sealed class JobRunner(
ILogger<JobRunner> logger,
IConfiguration configuration,
IHostEnvironment hostEnvironment)
{
public Task RunAsync()
{
string message = configuration["Sample:Message"] ?? "(no message)";
logger.LogInformation("Environment: {EnvironmentName}", hostEnvironment.EnvironmentName);
logger.LogInformation("Message: {Message}", message);
return Task.CompletedTask;
}
}
إن لم يكن التطبيق طويل الأمد، فلا تحتاج إلى الذهاب حتّى RunAsync().
يمكنك بناء الـ host وحلّ الخدمات اللازمة وأداء العمل والخروج.
لا يزال ذلك يمنحك الكثير من قيمة Generic Host.
من السهل إغفال هذا.
لا تحتاج إلى جرّ قالب الـ worker إلى كلّ مهمّة قصيرة العمر.
5.2. appsettings.json
للمثال أعلاه، يكون ملفّ إعدادات أدنى مثل هذا كافياً:
{
"Sample": {
"Message": "hello from Generic Host"
}
}
هنا يقرأ الكود configuration["Sample:Message"] مباشرةً.
لقيمة أو قيمتين، هذا جيّد تماماً.
لكن حالما تكبر الإعدادات في نظام حقيقيّ، يكون عادةً من الأفضل التحرّك نحو:
- classes منفصلة لكلّ section
- حقن قائم على
IOptions<T> - التحقّق عند بدء التشغيل
يجعل ذلك تجنّب نشر مفاتيح من نوع stringly-typed في كلّ مكان أسهل.
أيضاً، لأنّ الإعداد الافتراضيّ لـ Generic Host يشمل ليس فقط appsettings.json بل أيضاً appsettings.{Environment}.json ومتغيّرات البيئة ووسائط سطر الأوامر، يصبح من الطبيعيّ دعم أنماط مثل «التجاوز فقط في التطوير» أو «استبدال القيم عبر متغيّرات البيئة في الإنتاج».
5.3. إضافة BackgroundService
إن كان العمل طويل الأمد، فإنّ BackgroundService كثيراً ما يكون ملاءمة طبيعيّة جدّاً.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddScoped<PollingJob>();
builder.Services.AddHostedService<PollingWorker>();
using IHost host = builder.Build();
await host.RunAsync();
internal sealed class PollingWorker(
IServiceScopeFactory scopeFactory,
ILogger<PollingWorker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));
while (await timer.WaitForNextTickAsync(stoppingToken))
{
using IServiceScope scope = scopeFactory.CreateScope();
PollingJob job = scope.ServiceProvider.GetRequiredService<PollingJob>();
await job.RunAsync(stoppingToken);
logger.LogInformation("Polling completed.");
}
}
}
internal sealed class PollingJob(ILogger<PollingJob> logger)
{
public Task RunAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Do work here.");
return Task.CompletedTask;
}
}
نقطتان جديرتان بالملاحظة بشكل خاصّ هنا:
- الجسم الرئيس لـ
BackgroundServiceيعيش فيExecuteAsync - إذا كنت تحتاج إلى dependencies من نوع scoped، فأنشئ scope عبر
IServiceScopeFactory
BackgroundService بحدّ ذاته لا يأتي تلقائيّاً مع scope.
لذا إن أردت استخدام شيء scoped مثل DbContext، فإنّ حلّ المهمّة داخل scope منشأ كما في الأعلى هو الشكل الأكثر أماناً.
اختيار الـ timer نفسه موضوع منفصل، لكن عندما يكون التدفّق قائماً على async، يكون PeriodicTimer كثيراً ما خياراً هادئاً إلى حدّ معقول.
ذلك يرتبط أيضاً بشكل طبيعيّ بمقال الـ timer ذي الصلة.
6. الأنماط النموذجيّة
6.1. أدوات console قصيرة العمر
مهامّ الـ batch وأدوات التحويل أو أوامر الصيانة التي تعمل مرّة واحدة وتخرج يمكنها أن تستفيد من Generic Host.
تشمل الحالات النموذجيّة:
- قراءة ملفّات الإعدادات
- كتابة الـ logs
- حقن
HttpClientأو repositories - إعادة كود خروج
إذا أخذت ذلك النوع من التطبيقات ولفّفته فوراً في BackgroundService و RunAsync()، فقد ينتهي بك الأمر باستخدام إدارة lifetime الـ host بشكل أثقل ممّا يحتاجه المشكل فعلاً.
للمهامّ القصيرة العمر، يكون مجرّد حلّ JobRunner وتنفيذه، كما في المثال الأدنى، كافياً في الغالب.
6.2. workers والخدمات في الخلفيّة
للـ workers المقيمة والـ polling ومستهلكي الـ queue والمراقبة والمعالجة المجدولة، يكون اقتران Generic Host و BackgroundService عادةً طبيعيّاً جدّاً.
تشمل الفوائد العمليّة:
- توحيد سلوك بدء التشغيل والإيقاف عبر الـ host
- توفّر الـ logging والإعدادات و DI من البداية
- أن يصبح تدفّق الإلغاء من
Ctrl+Cأو إشارات الإيقاف أسهل - جعل العمل طويل الأمد أسهل للفصل عن
Program.cs
كما يرتبط بشكل طبيعيّ بسياقات مثل Windows Services والـ containers.
إن كان من المرجّح أن يكبر التطبيق ليصبح خدمة مقيمة، فإنّ Generic Host هو أساس طبيعيّ جدّاً.
في سيناريوهات Windows Service بشكل خاصّ، يكون عادةً أكثر أماناً اعتماد البحث عن الملفّات على IHostEnvironment.ContentRootPath بدلاً من المسار الحاليّ.
يعطيك الـ host فكرة واضحة عن المسار الأساس للتطبيق.
6.3. يجلس أيضاً تحت ASP.NET Core
تستخدم تطبيقات الويب والـ APIs WebApplication.CreateBuilder(args)، لذا قد تبدو للوهلة الأولى عالماً منفصلاً عن Generic Host.
لكن في الممارسة العمليّة، الأفكار مرتبطة بقوّة.
builder.Servicesbuilder.Configurationbuilder.Logging
تشعر بأنّها متشابهة لسبب.
في ASP.NET Core، يعيش خادم HTTP أيضاً داخل إدارة lifetime الـ host.
لذا فإنّ فهم Generic Host يساعد في فهم لماذا يُكوَّن DI والإعدادات والـ logging جميعاً في Program.cs للويب أيضاً.
7. الحالات التي يلائمها جيّداً
يميل Generic Host إلى الملاءمة بشكل طبيعيّ في حالات مثل هذه:
- تطبيقات console التي تستخدم الإعدادات والـ logging و DI
- مستهلكي الـ queue والـ pollers والـ watchdogs والـ workers الشبيهة بالـ scheduler
- التطبيقات طويلة الأمد التي تحتاج إلى تنظيف graceful عند
Ctrl+Cأو SIGTERM - التطبيقات التي قد تكبر لاحقاً لتصبح Windows Services أو عمليّات مقيمة في containers
- التطبيقات التي تريد فيها نفس الأسلوب القائم على extensions كما في ASP.NET Core
ما تشترك فيه هذه الحالات هو الرغبة في منع نقطة دخول التطبيق وإدارة الـ lifetime من أن تصبح فوضويّة.
8. الحالات التي يكون فيها غير ضروريّ أو مفرطاً
هناك أيضاً حالات لا يحتاج فيها Generic Host لأن يكون الأداة الرئيسة من البداية.
- أدوات صغيرة جدّاً تقرأ الوسائط مرّة وتطبع مرّة وتخرج
- كود تجريبيّ خشن يُستخدم لفترة قصيرة فقط
- مشاريع المكتبات
- الحالات التي تحتاج فيها إلى إعداد واحد فقط ولا تحتاج فعلاً إلى DI أو logging أو إدارة lifetime
في تلك الحالات، يكون التنفيذ الأبسط في الغالب أهدأ من إقامة host.
النقطة المهمّة هي أنّ مجرّد كون Generic Host قويّاً لا يعني أنّ كلّ executable يجب أن يستخدمه.
9. الفخاخ الشائعة
فيما يلي بعض الأخطاء السهلة في البداية:
- التفكير في أنّ Generic Host هو فقط DI container
- في الواقع هو الأساس الذي يشمل بدء التشغيل والإيقاف والإعدادات والـ logging والخدمات المستضافة.
- بدء تطبيق جديد بـ
Host.CreateDefaultBuilderبحكم العادة- ما لم تكن تطابق قاعدة كود قائمة، يكون
Host.CreateApplicationBuilderعادةً نقطة البداية الأنظف.
- ما لم تكن تطابق قاعدة كود قائمة، يكون
- حقن خدمات scoped مباشرةً في
BackgroundService- الخدمات المستضافة ليس لديها scope افتراضيّ. إنشاء واحد عبر
IServiceScopeFactoryهو المسار الأكثر أماناً.
- الخدمات المستضافة ليس لديها scope افتراضيّ. إنشاء واحد عبر
- بناء worker ذي تشغيل واحد دون إخبار الـ host بالتوقّف
- إن استخدمت قالب worker لتطبيق «يعمل مرّة»، فأنت تحتاج إلى
IHostApplicationLifetime.StopApplication()عند انتهاء العمل، وإلّا يستمرّ الـ host بالعمل.
- إن استخدمت قالب worker لتطبيق «يعمل مرّة»، فأنت تحتاج إلى
- استخدام
Environment.Exitعندما تريد فعلاً إيقافاً نظيفاً- إن كنت تستخدم الـ host بالفعل، فإنّ
StopApplication()هو عادةً الطريقة الأنظف للتوقّف.
- إن كنت تستخدم الـ host بالفعل، فإنّ
- افتراض المسار الحاليّ في Windows Service
- يكون البحث عن الملفّات أكثر استقراراً عادةً عند الاعتماد على
IHostEnvironment.ContentRootPath.
- يكون البحث عن الملفّات أكثر استقراراً عادةً عند الاعتماد على
- لفّ CLI قصير العمر في
BackgroundServiceمن البداية- إن كان العمل يعمل مرّة واحدة، فإنّ حلّ class خدمة عاديّ وتنفيذه كافٍ في الغالب.
- استخدام callback timers باستخفاف داخل
BackgroundService- إن كان التدفّق قائماً على
async، فإنّPeriodicTimerيؤدّي عادةً إلى كود أسهل للقراءة وأقلّ احتمالاً للانحراف.
- إن كان التدفّق قائماً على
مع Generic Host، فإنّ مجرّد فصل «مهمّة قصيرة العمر» مقابل «مهمّة مقيمة» مبكّراً يزيل بالفعل الكثير من الالتباس.
10. الخلاصة
إذا وضعتها في جملة واحدة، فإنّ Generic Host هو الأساس الذي يجمع نقطة دخول تطبيق .NET وإدارة الـ lifetime.
النقاط الرئيسة التي تستحقّ التذكّر هي:
- يشمل Generic Host ليس فقط DI بل أيضاً الإعدادات والـ logging ومعالجة الإيقاف والخدمات المستضافة
- للتطبيقات الجديدة غير الويب، يكون
Host.CreateApplicationBuilder(args)عادةً نقطة البداية الأنظف - للمهامّ القصيرة العمر، لا بأس ببناء الـ host وتنفيذ خدمة مباشرةً دون
BackgroundService - للمعالجة المقيمة، يصبح
BackgroundServiceبالإضافة إلى إدارة lifetime الـ host قيّماً جدّاً BackgroundServiceليس له scope افتراضيّ، لذا ينبغي حلّ الخدمات scoped داخل scope صريحWebApplicationBuilderفي ASP.NET Core يجلس على نفس النموذج الإجماليّ
Generic Host ليس طقساً ثقيلاً لذاته.
حالما تبدأ الإعدادات والـ logging وتوصيل الـ dependencies وبدء التشغيل والإيقاف بالنموّ، يصبح طريقة لإبقاء تلك الاهتمامات معاً عند نقطة الدخول بدلاً من السماح لها بالتسرّب في كلّ أنحاء التطبيق.
من ناحية أخرى، إن كانت الأداة لا تزال صغيرة جدّاً، فلست مضطرّاً لإحضاره.
حالما تستطيع التمييز بين تلك الحالات، يتوقّف Generic Host عن أن يبدو وكأنّه «شيء تضيفه بحكم العادة» ويبدأ بأن يبدو أساساً عمليّاً جدّاً.
11. المراجع
- .NET Generic Host - .NET
- Worker Services in .NET
- Use scoped services within a BackgroundService - .NET
- Configuration in .NET
- Options pattern in .NET
- .NET Generic Host in ASP.NET Core
- Create Windows Service using BackgroundService - .NET
- مقال ذو صلة: ترتيب PeriodicTimer / System.Threading.Timer / DispatcherTimer أوّلاً
- مقال ذو صلة: أفضل الممارسات لـ C# async/await - جدول قرار لـ Task.Run و ConfigureAwait
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
ما هو Native AOT في .NET - الفروق عن JIT و ReadyToRun و trimming
شرح Native AOT في .NET كنموذج نشر يقدّم الترجمة الأصليّة، مع الفروق عن JIT و ReadyToRun و trimming، والأنسب أن يكون أدوات CLI و workers و...
لماذا يستحقّ الأمر إدخال Generic Host / BackgroundService إلى تطبيق سطح المكتب - يصبح تنظيم البدء والـ lifetime والـ graceful shutdown أسهل بكثير
يشرح هذا المقال متى يستحقّ إدخال Generic Host و BackgroundService إلى تطبيقات سطح المكتب على Windows لتنظيم البدء وإدارة الـ lifetime وال...
كيفيّة استخدام FileSystemWatcher بأمان - الأحداث المفقودة والإشعارات المكرّرة وفخاخ كشف الاكتمال
دليل عمليّ يشرح لماذا ينبغي اعتبار FileSystemWatcher مجرّد محفّز للمسح وليس إشارة اكتمال، ويقدّم أنماط المطالبة الذرّيّة و idempotency.
أفضل الممارسات لـ C# async/await - جدول قرار لـ Task.Run و ConfigureAwait
دليل عمليّ لـ async/await في C# يبدأ بفصل عمل I/O-bound عن CPU-bound، ثمّ يقدّم جداول قرار حول Task.Run و WhenAll و ConfigureAwait و fire...
إلى أين ينتهي unit test وأين يبدأ integration test - دليل عمليّ لرسم الحدّ الفاصل
دليل عمليّ يميّز unit test وintegration test بأربعة أسئلة: نتحقّق من منطقنا أم من الغراء، ويبقى المعنى مع fake، وما طبيعة الاعتماد، ومدى ...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.
روابط عامة