ما هو Generic Host في .NET - أساس DI والإعدادات والسجلات
· آخر تحديث: · 小村 豪 · C#, .NET, Generic Host, Worker, التصميم
سجل التعديلات (3 تحديثات، آخر تحديث 3 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُضيفت روابط الاستشارة الموجودة في الأصل الياباني (consultation_services). ولم يتغيّر نصّ المقالة نفسه. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22240829)
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621357)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
小村 豪 (2026). ما هو Generic Host في .NET - أساس DI والإعدادات والسجلات. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621357 https://comcomponent.com/ar/blog/2026/03/14/000-dotnet-generic-host-what-is/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621357
- DOI (هذه النسخة)
- 10.5281/zenodo.22279756
عندما تبدأ كتابة تطبيق console أو worker في .NET، يكفي أوّلاً وضع معالجة قليلة في Main.
غير أنه ما إن ينمو قليلاً حتى تظهر تقريباً هذه الاحتياجات.
- قراءة
appsettings.json - التجاوز بمتغيّرات البيئة
- إخراج السجلات عبر
ILogger - تجنّب أن يصير إنشاء الخدمات جداراً من
new - تشغيل حلقة في الخلفية
- الإنهاء النظيف عند
Ctrl+Cأو إيقاف الخدمة
هنا يظهر Generic Host.
غير أن الاسم نفسه يختلط قليلاً.
- ما الفرق بين
Host.CreateApplicationBuilderوHost.CreateDefaultBuilder؟ - هل
IHostهو حاوية DI نفسها؟ - ما صلته بـ
BackgroundService؟ - هل هو شيء منفصل عن
WebApplicationBuilderفي ASP.NET Core؟ - هل يستحق الاستخدام في تطبيق console أيضاً؟
إذا اختلطت هذه الأسئلة بدا Generic Host «شيئاً خاصّاً بتطبيقات الويب»، أو على العكس «شيئاً يجب وضع كل شيء على host». كلا النظرتين فضفاضتان.
تفترض هذه المقالة الإحساس العملي الحالي منذ .NET 6 فصاعداً، وترتّب أوّلاً أربع نقاط.
- ما هو Generic Host فعلاً
- ماذا يجمع ويتولّى؟
- علاقة
Host.CreateApplicationBuilder/Host.CreateDefaultBuilder/WebApplication.CreateBuilder - من أين يبدأ الدخول بهدوء
الفهرس
- الخلاصة في جملة
- 1.1. نثبّت المصطلحات أوّلاً
- جداول للنظر أوّلاً
- 2.1. ما يحمله Generic Host
- 2.2. الفرق بين الـ builder
- 2.3. لماذا توجد مداخل متعدّدة؟
- الصورة الكاملة لـ Generic Host (رسم)
- ماذا يفيد Generic Host؟
- 4.1. تجميع معالجة الإقلاع في موضع واحد
- 4.2. DI والإعدادات والسجلات تتّصل من البداية
- 4.3. يسهّل الإنهاء السليم والتشغيل المقيم
- الحدّ الأدنى من التكوين
- 5.1. أصغر مثال على تطبيق console
- 5.2.
appsettings.json - 5.3. إضافة
BackgroundService
- أنماط نمطية
- 6.1. أدوات console قصيرة العمر
- 6.2. worker / خدمات خلفية
- 6.3. يوجد أيضاً تحت ASP.NET Core
- الحالات التي يناسبه
- الحالات التي لا يناسبه / يكون فيها زائداً
- مواضع التعثّر
- الخلاصة
- روابط مرجعية
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 16، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
1. الخلاصة في جملة
- Generic Host أساس يجمع تشغيل تطبيق .NET وعمره.
- داخله DI والإعدادات والسجلات و
IHostedService/BackgroundServiceومعالجة إيقاف التطبيق. - للتطبيق الجديد غير الويب، الدخول أوّلاً من
Host.CreateApplicationBuilder(args)أوضح. WebApplicationBuilderفي ASP.NET Core ليس عالماً منفصلاً، بل مدخل وسّع فكرة الـ host نفسها نحو الويب.- أي أن Generic Host ليس حديث حاوية DI وحدها، بل آلية تجمع نقطة تجميع التطبيق وإدارة عمره.
باختصار، ما إن يتجاوز التطبيق قليلاً حدّ «اقرأ الوسائط واعرض مرّة وانتهِ» حتى يبدأ Generic Host بالتأثير بقوّة. وفي المقابل ليس شيئاً يُجلَب حتماً في كلّ مرّة إلى أداة صغيرة لم تبلغ ذلك بعد.
flowchart TB
accTitle: النطاق الذي يبدأ فيه Generic Host بالتأثير
accDescr: لا يُجلَب Generic Host في كلّ مرّة إلى أداة صغيرة تعرض مرّة وتنتهي، ويبدأ بالتأثير بقوّة ما إن يتجاوز التطبيق ذلك قليلاً.
small1["أداة تعرض مرّة وتنتهي"] -.->|"لا يُجلَب في كلّ مرّة"| ghost0["Generic Host"]
grown1["تطبيق يتجاوز ذلك قليلاً"] -->|"يبدأ بالتأثير بقوّة"| ghost0
الشكل 1: ما إن يتجاوز التطبيق قليلاً حدّ «اعرض مرّة وانتهِ» حتى يبدأ Generic Host بالتأثير.
1.1. نثبّت المصطلحات أوّلاً
هذه المقالة تستخدم بعد ذلك مجازات كثيرة، لذا أضع الصياغة الدقيقة أوّلاً.
| المصطلح | بالدقة | المجاز في هذه المقالة |
|---|---|---|
| DI (حقن التبعيات / Dependency Injection) | أسلوب يتلقّى فيه الصنف ما يحتاجه من الخارج بدل أن ينشئه بـ new. موضع تسجيل الأطراف المطلوب تمريرها هو حاوية DI (IServiceProvider)، و Generic Host يحملها من البداية |
التوصيل |
Builder (HostApplicationBuilder) |
كائن لتجميع الـ host. يحمل خصائص مثل Services و Configuration و Logging، وإليها تسجّل. التطبيق لا يعمل حتى استدعاء Build() |
منصّة التجميع |
Host (IHost) |
التطبيق المجمَّع نفسه الناتج عن Build(). يحمل حاوية DI والإعدادات والسجلات و hosted service، ويتولّى المسار من البدء حتى الإيقاف عبر Run() / RunAsync() |
الأساس |
Hosted service (IHostedService / BackgroundService) |
وعاء معالجة يتحرّك مع بدء الـ host وإيقافه. عند إقلاع الـ host يُستدعى StartAsync، ومع BackgroundService يجري ExecuteAsync |
العمل المقيم |
| Lifetime | إدارة المسار من بدء التطبيق حتى إيقافه. تستقبل إشارات مثل Ctrl+C و SIGTERM وإيقاف الخدمة، وتوحّد طريقة التوقّف |
العمر |
أكثر ما يختلط هو Builder و Host. Builder جهة التجميع، و Host نتيجة التجميع، و Build() هو الحدّ بينهما. إن ثبت هذا، صارت مجازات لاحقة مثل «الأساس» و«الصندوق» و«المدخل» و«البوابة» تُقرأ دون حيرة عمّا تشير إليه.
flowchart TB
accTitle: الحدّ بين Builder و Host
accDescr: Builder جهة التجميع، و IHost نتيجة التجميع، واستدعاء Build هو الحدّ بينهما.
bld1["Builder (جهة التجميع)"] -->|"Build()"| hst1["IHost (نتيجة التجميع)"]
hst1 --> life1["من البدء حتى الإيقاف عبر Run / RunAsync"]
الشكل 2: Builder جهة التجميع، و IHost نتيجة التجميع، و Build() هو الحدّ بينهما.
إن كان DI جديداً عليك، فكّر هكذا فلا تخطئ. بدل كتابة سلسلة new بنفسك، تسجّل عند الإقلاع «إذا لزم هذا النوع فمرّر هذا التنفيذ»، والمستقبِل يأخذه فقط كوسيط في المنشئ. موضع ذلك التسجيل هو builder.Services.
2. جداول للنظر أوّلاً
2.1. ما يحمله Generic Host
فصل محتويات هذا الصندوق أوّلاً يسهّل الأمر كثيراً.
| العنصر | ما يتولّاه Generic Host | ماذا يفيد |
|---|---|---|
| DI | تجميع الخدمات من IServiceCollection |
يسهّل تقليل سلسلة new |
| Configuration | جمع appsettings.json ومتغيّرات البيئة ووسائط سطر الأوامر وغيرها |
يسهّل التعامل مع فروق البيئات |
| Logging | بناء الأساس لاستخدام ILogger<T> |
يسهّل استبدال وجهة السجلات لاحقاً |
| 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) |
كود قائم أو تكوين قديم قائم على توابع التوسعة | تسلسل ConfigureServices وما شابه |
هذا إن وُجد أصل قائم |
WebApplication.CreateBuilder(args) |
تطبيق ويب / API في ASP.NET Core | مدخل أضاف إلى Generic Host ما يلزم الويب | هذا للويب |
CreateApplicationBuilder و CreateDefaultBuilder ليسا
قصّة «أحدهما ميزة جديدة والآخر شيء منفصل».
كلاهما يحمل الوظائف الجوهرية نفسها والسلوك الافتراضي. الفرق أساساً أسلوب الكتابة.
للتطبيق الجديد غير الويب، الدخول اليوم من Host.CreateApplicationBuilder(args) أوضح.
و WebApplication.CreateBuilder(args) يسهل ترتيبه إذا اعتبرته مدخلاً وسّع ذلك المسار نحو الويب.
flowchart TB
accTitle: علاقة المداخل الثلاثة
accDescr: CreateApplicationBuilder و CreateDefaultBuilder يحملان الوظائف الجوهرية نفسها والسلوك الافتراضي ويختلفان بأسلوب الكتابة فقط، و WebApplication.CreateBuilder مدخل وسّع ذلك المسار نحو الويب.
appb1["CreateApplicationBuilder"] --> core1["الوظائف الجوهرية والسلوك الافتراضي نفسيهما"]
defb1["CreateDefaultBuilder"] --> core1
appb1 -.-> sty1["أسلوب الكتابة المباشرة"]
defb1 -.-> sty2["أسلوب التسلسل"]
core1 -.->|"مدخل وسّعه نحو الويب"| webb1["WebApplication.CreateBuilder"]
الشكل 3: الـ builderان يختلفان بأسلوب الكتابة فوق الوظائف الجوهرية والسلوك الافتراضي نفسيهما، وللويب مدخل وسّع ذلك.
2.3. لماذا توجد مداخل متعدّدة؟
المداخل متعدّدة لأن جهة الويب وجهة غير الويب نمتا منفصلتين ثم التقتا.
- كان لـ ASP.NET Core في الأصل Web Host خاص بالويب (
IWebHostBuilder)، وأُعدّ Generic Host (IHostBuilder) منفصلاً لتطبيقات غير الويب. - بعد ذلك مالت جهة ASP.NET Core نحو Generic Host، فصار الويب وغير الويب يقومان على فكرة الـ host نفسها.
- ثم أُضيف، إلى جانب أسلوب ربط الاستدعاءات الراجعة (
ConfigureServicesوغيره)، مدخل يكتب مباشرة إلى الخصائص (builder.Servicesوغيره).Host.CreateApplicationBuilderوWebApplication.CreateBuilderمن هذا الجانب.
في التوثيق الرسمي الحالي، مسار Host.CreateApplicationBuilder (IHostApplicationBuilder) موجَّه للمشاريع الجديدة وهو افتراضي القوالب الحالية، ومسار Host.CreateDefaultBuilder (IHostBuilder) الطريقة التقليدية المتبقية للتوافق مع الكود القائم. وموثَّق أيضاً أن كليهما يحمل الوظائف الجوهرية نفسها والسلوك الافتراضي.
إن أتيت من كود عهد .NET Framework أو .NET Core 3.1 فقد تتساءل «لماذا أسلوبان للكتابة؟»، لكن الأمر أوضح إذا رأيته ليس شيئين جديد وقديم متجاورين، بل مداخل زادت أثناء الالتقاء. إن لم يكن هناك سبب لمواءمة أصل قائم، فالجديد يكفي معه Host.CreateApplicationBuilder.
flowchart TB
accTitle: خلفية تعدّد المداخل
accDescr: كان Web Host الخاص بالويب و Generic Host لغير الويب منفصلين، ثم التقت جهة ASP.NET Core مع Generic Host، ثم أُضيف مدخل يكتب مباشرة إلى الخصائص.
wh1["Web Host (IWebHostBuilder)"] --> mg1["ASP.NET Core يلتقي مع Generic Host"]
gh1["Generic Host (IHostBuilder)"] --> mg1
mg1 --> ad1["إضافة مدخل يكتب مباشرة إلى الخصائص"]
ad1 -.-> ex1["CreateApplicationBuilder و WebApplication.CreateBuilder"]
الشكل 4: Web Host و Generic Host نمتا منفصلتين ثم التقتا، وفي أثناء ذلك زاد مدخل أسلوب الكتابة المباشرة.
3. الصورة الكاملة لـ Generic Host (رسم)
إذا رسمنا الصورة الكاملة بصورة تقريبية، يكون كالتالي.
flowchart LR
Args["args / متغيّرات البيئة / 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["البدء والإيقاف و Ctrl+C و SIGTERM"]
Lifetime --> Hosted
الشكل 5: تسجّل الإعدادات والخدمات والسجلات في الـ builder، وتشغّل IHost الناتج عن Build() عبر Run/RunAsync، فيتّصل العمر ببدء hosted service وإيقافه.
عادة تصنع الـ builder في Program.cs،
وتضيف الخدمات إلى builder.Services،
وتضبط builder.Configuration أو builder.Logging حسب الحاجة،
ثم تستدعي Build() لتحصل على IHost، وتشغّله بـ Run() / RunAsync().
المكسب الهادئ الكبير أن كثيراً من الأشياء محمولة أصلاً عند Host.CreateApplicationBuilder(args).
افتراضيّاً يدخل مثلاً ما يلي.
- جذر المحتوى هو الدليل الحالي
- تكوين الـ host من متغيّرات بيئة ذات بادئة
DOTNET_ووسائط سطر الأوامر - تكوين التطبيق من
appsettings.jsonوappsettings.{Environment}.jsonو user secrets في Development ومتغيّرات البيئة ووسائط سطر الأوامر - السجلات: Console / Debug / EventSource / EventLog (Windows فقط)
- في بيئة
Development: التحقّق من النطاق والتحقّق من التبعيات
أي أنك لا توصّل كل شيء من الصفر بلا تفكير، بل يُوضَع من البداية «أساس يكفي للاستخدام العادي».
flowchart TB
accTitle: الافتراضات المحمولة من البداية
accDescr: عند Host.CreateApplicationBuilder تكون تكوينات الـ host والتطبيق والسجلات الافتراضية محمولة أصلاً، فيُوضَع من البداية أساس يكفي للاستخدام العادي.
ent1["CreateApplicationBuilder(args)"] --> dz1["تكوين الـ host (عائلة DOTNET_ والوسائط)"]
ent1 --> dz2["تكوين التطبيق (appsettings.json وغيره)"]
ent1 --> dz3["السجلات الافتراضية (Console وغيره)"]
dz1 --> rdy1["أساس يكفي للاستخدام العادي"]
dz2 --> rdy1
dz3 --> rdy1
الشكل 6: عند صنع الـ builder تكون افتراضات التكوين والسجلات محمولة، ولست توصّل من الصفر.
4. ماذا يفيد Generic Host؟
4.1. تجميع معالجة الإقلاع في موضع واحد
أهدأ أثر كبير لـ Generic Host أن مدخل التطبيق يصير أقلّ تشتّتاً.
ما إن ينمو التطبيق قليلاً حتى يزيد حول Main ما يلي.
- قراءة ملفات الإعداد
- الاستبدال حسب البيئة
- تهيئة المسجّل
- تجميع
HttpClientو repository و service - بدء معالجة الخلفية
- التنظيف عند إشارة الإنهاء
إن وصّلت هذا كلّه يدوياً بلا host، بدا خفيفاً أوّلاً، ثم يلتصق المدخل تدريجيّاً لاحقاً.
مع Generic Host يتّضح Program.cs كـ «موضع تجميع التبعيات».
هذا الترتيب وحده يغيّر سهولة مراجعة الشيفرة كثيراً.
flowchart TB
accTitle: معالجة الإقلاع تتجمّع في موضع واحد
accDescr: التوصيل اليدوي بلا host يجعل المدخل يلتصق لاحقاً، أمّا Generic Host فيجعل Program.cs موضع تجميع التبعيات بوضوح.
no1["توصيل يدوي بلا host"] --> st1["المدخل يلتصق لاحقاً"]
yes1["استخدام Generic Host"] --> pg1["Program.cs يصير موضع التجميع"]
pg1 -.-> rv1["مراجعة الشيفرة أسهل"]
الشكل 7: التوصيل اليدوي يجعل المدخل يلتصق تدريجيّاً، والتجميع على الـ host يجعل Program.cs موضع التجميع بوضوح.
4.2. DI والإعدادات والسجلات تتّصل من البداية
مع Generic Host تركب DI والإعدادات والسجلات الأساس نفسه من البداية.
مثلاً يستطيع طرف الصنف تلقّي هذه الأشياء بشكل طبيعي.
ILogger<T>IConfigurationIHostEnvironmentIOptions<T>
ما يؤثّر هنا أن طريقة قراءة الإعدادات وطريقة صنع الخدمات لا تميلان إلى أسلوبين منفصلين.
إن كان الإعداد واحداً أو اثنين، تكفي قراءة IConfiguration["Section:Key"] مباشرة.
غير أنه في العمل اليومي إذا نمت الإعدادات، فالأأمن جمع كل قسم في صنف عبر IOptions<T>. المقياس تقريباً تجاوز مفاتيح النصّ خمسة. عند هذا الحجم تبدأ أخطاء الكتابة بالظهور كفشل لا يُلاحَظ حتى وقت التشغيل، ويصعب تتبّع أي مفتاح يُقرأ من أين.
بالمثل، السجلات أوضح إن حقنت ILogger<T> في الأصناف التي تحتاجه،
بدل صنع ILoggerFactory يدوياً في مواضع متفرّقة.
فائدة Generic Host أنه لا يفصل هذه الأمور، بل يمكن تناولها معاً كأساس للتطبيق كلّه.
flowchart TB
accTitle: كيف تُنمّى طريقة قراءة الإعدادات
accDescr: إن كان الإعداد واحداً أو اثنين تكفي قراءة IConfiguration مباشرة، وما إن تتجاوز مفاتيح النصّ خمسة حتى يصير جمع كل قسم في صنف عبر IOptions أأمن.
few1["إعداد واحد أو اثنان"] --> dr1["قراءة IConfiguration مباشرة"]
many1["مفاتيح النصّ تتجاوز خمسة"] --> op1["الجمع في أصناف عبر IOptions"]
many1 -.-> rk1["أخطاء الكتابة لا تُلاحَظ حتى وقت التشغيل"]
الشكل 8: مع قلّة الإعدادات تكفي القراءة المباشرة، وما إن تتجاوز المفاتيح خمسة حتى يصير الجمع عبر IOptions أأمن.
4.3. يسهّل الإنهاء السليم والتشغيل المقيم
Generic Host لا يتولّى «كيف يبدأ» فقط، بل «كيف يتوقّف» أيضاً.
عند إقلاع الـ host يُستدعى StartAsync لكل IHostedService مسجَّل.
في خدمات worker يجري ExecuteAsync لـ hosted service بما فيه BackgroundService.
«الإنهاء السليم» هنا ليس قطع المعالجة فجأة، بل الإنهاء بهذا الترتيب:
- بثّ إشارة التوقّف
- الخروج من الحلقات والانتظار
- تنظيف الاتّصالات والموارد
في تطبيق يعمل طويلاً هذه النقطة مهمّة جدّاً.
يسهل توحيد طريقة توقّف التطبيق كلّه عند أحداث مثل Ctrl+C و SIGTERM وإيقاف الخدمة.
وإذا أراد التطبيق طلب الإنهاء من جهته، يُستخدم IHostApplicationLifetime.StopApplication().
يمكن إرسال إشارة «العمل انتهى، أريد التوقف بنظافة» في سياق الـ host.
flowchart TB
accTitle: ترتيب الإنهاء السليم
accDescr: عند استقبال Ctrl+C أو SIGTERM أو إيقاف الخدمة يُبثّ إشارة التوقّف، ثم يُخرَج من الحلقات والانتظار، ثم تُنظَّف الاتّصالات والموارد.
sg1["Ctrl+C / SIGTERM / إيقاف الخدمة"] --> p1["بثّ إشارة التوقّف"]
p1 --> p2["الخروج من الحلقات والانتظار"]
p2 --> p3["تنظيف الاتّصالات والموارد"]
ap1["طلب إنهاء من جهة التطبيق"] -.->|"StopApplication()"| p1
الشكل 9: الإنهاء السليم يمرّ بإشارة التوقّف ثم الخروج من الحلقة ثم التنظيف، ومن جهة التطبيق يمكن إرسال الإشارة نفسها عبر StopApplication().
5. الحدّ الأدنى من التكوين
5.1. أصغر مثال على تطبيق console
المهم أوّلاً أن استخدام Generic Host
لا يعني حتماً صنع BackgroundService.
حتّى أداة console تعمل مرّة واحدة، إن احتجت DI أو إعدادات أو سجلات فـ Generic Host كافٍ تماماً.
إن أضفته لاحقاً إلى مشروع 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;
}
}
عند dotnet run يظهر في وحدة التحكّم ما يلي (قيمة Message تأتي من appsettings.json الذي نضعه في 5.2 التالي).
info: JobRunner[0]
Environment: Production
info: JobRunner[0]
Message: hello from Generic Host
ما على يمين info: هو فئة السجلّ (هنا اسم النوع لأنّه ILogger<JobRunner>) ومعرّف الحدث. مسجّل وحدة التحكّم الافتراضي يخرج بهذا الشكل: «السطر الأوّل الفئة، والسطر الثاني المتن». كون Environment هو Production لأن القيمة الافتراضية عند عدم ضبط DOTNET_ENVIRONMENT ولا ASPNETCORE_ENVIRONMENT. للتبديل أثناء التطوير شغّل مع DOTNET_ENVIRONMENT=Development.
إن لم يكن مقيماً طويلاً، فلا حاجة للوصول حتى RunAsync().
Build() ثم حلّ الخدمات اللازمة، وإنهاء العمل بعد ذلك مباشرة.
حتّى بهذا الشكل تُستفاد فائدة Generic Host تماماً.
هذه النقطة أهمّ ممّا تبدو. لا حاجة لجلب قالب Worker في كلّ مرّة حتى إلى عمل قصير العمر.
flowchart TB
accTitle: طريقة الاستخدام في عمل قصير العمر
accDescr: إن لم يكن مقيماً طويلاً فلا حاجة للوصول حتى RunAsync؛ يكفي Build ثم حلّ الخدمات اللازمة وإنهاء العمل بعد ذلك، وتبقى فائدة Generic Host.
st2["Build()"] --> rs1["حلّ الخدمات اللازمة"]
rs1 --> jb1["إنجاز العمل"]
jb1 --> fin1["الإنهاء مباشرة"]
fin1 -.-> nt1["لا حاجة للوصول حتى RunAsync()"]
الشكل 10: في عمل قصير العمر يكفي Build() ثم حلّ الخدمة وتشغيلها والإنهاء، وتبقى فائدة الـ host.
5.2. appsettings.json
للمثال أعلاه يكفي ملف إعداد بهذا الحدّ الأدنى.
{
"Sample": {
"Message": "hello from Generic Host"
}
}
موضع تعثّر نمطي واحد. في مشروع console، إضافة appsettings.json وحدها لا تنسخه إلى مجلد الخرج. إمّا ضبط «نسخ إلى دليل الخرج» في خصائص المشروع على «نسخ إذا كان أحدث»، أو كتابة التالي في csproj.
<ItemGroup>
<Content Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
معرفة العَرَض عند النسيان تسرّع التشخيص. Generic Host يقرأ appsettings.json كملف اختياري، فلا استثناء إن غاب. ببساطة لا تُؤخَذ القيمة. في المثال الأدنى يظهر Message: (no message). عندما «لا يظهر خطأ لكن الإعداد لا يعمل»، افحص أوّلاً وجود appsettings.json في مجلد الخرج.
flowchart TB
accTitle: العَرَض عندما يغيب appsettings.json
accDescr: حتى إن لم يُنسَخ appsettings.json إلى مجلد الخرج يُقرأ كملف اختياري فلا استثناء، وببساطة لا تُؤخَذ القيمة.
ms1["الملف غائب عن مجلد الخرج"] --> rd1["يُقرأ كملف اختياري"]
rd1 --> ne1["لا استثناء"]
ne1 --> nv1["ببساطة لا تُؤخَذ القيمة"]
nv1 -.-> ck1["افحص مجلد الخرج أوّلاً"]
الشكل 11: غياب appsettings.json لا يرفع استثناء بل «لا تُؤخَذ القيمة» فقط، لذا افحص مجلد الخرج أوّلاً.
في هذا المثال نقرأ configuration["Sample:Message"] خاماً.
إن كنت تنظر إلى قيمة أو اثنتين فهذا كافٍ.
غير أنه في العمل اليومي إذا نمت الإعدادات، فالإزاح نحو:
- فصل كل قسم إلى صنف
- الحقن عبر
IOptions<T> - التحقّق عند الإقلاع
يسهّل تجنّب نثر مفاتيح النصّ.
وأيضاً، القيم الافتراضية لـ 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 - إن احتجت تبعية scoped فاصنع نطاقاً عبر
IServiceScopeFactory
BackgroundService نفسه بلا نطاق افتراضي.
مثلاً إن أردت استخدام خدمة scoped مثل DbContext،
فالشكل الآمن حلّ جهة العمل داخل النطاق كما أعلاه.
flowchart TB
accTitle: شكل استخدام scoped داخل BackgroundService
accDescr: BackgroundService نفسه بلا نطاق افتراضي، لذا الشكل الآمن حقن IServiceScopeFactory وصنع نطاق داخل ExecuteAsync وحلّ خدمة جهة العمل داخل ذلك النطاق.
bg1["BackgroundService (بلا نطاق افتراضي)"] --> fc1["حقن IServiceScopeFactory"]
fc1 --> mk1["صنع نطاق داخل ExecuteAsync"]
mk1 --> rv2["حلّ العمل داخل النطاق"]
الشكل 12: في BackgroundService بلا نطاق افتراضي تُصنع النطاق عبر IServiceScopeFactory ويُحَلّ العمل داخله.
اختيار أداة التشغيل الدوري نفسها موضوع منفصل،
غير أن PeriodicTimer هادئ جدّاً إن كنت تكتب على أساس async.
هذا يتّصل أيضاً بمقالة المؤقّتات ذات الصلة.
6. أنماط نمطية
6.1. أدوات console قصيرة العمر
حتّى تطبيق ينجز عملاً مرّة وينتهي، مثل الدفعات وأدوات التحويل وأوامر الصيانة، يمكنه استخدام Generic Host بشكل طبيعي.
يناسبه مواضع كهذه.
- قراءة ملف إعداد
- إخراج سجلات
- حقن
HttpClientأو repository - إعادة رمز إنهاء
إن جلبت إلى هذا النوع فوراً BackgroundService و RunAsync()،
صار ثقيلاً قليلاً واستخدمت إدارة عمر الـ host بما يفوق الحاجة.
في عمل قصير العمر يكفي حلّ JobRunner وتشغيله كما في المثال الأدنى السابق.
flowchart TB
accTitle: الاختيار في أداة console قصيرة العمر
accDescr: في تطبيق ينجز عملاً مرّة وينتهي يصير جلب BackgroundService و RunAsync استخداماً زائداً لإدارة العمر، ويكفي حلّ الخدمة وتشغيلها.
on1["تطبيق ينجز عملاً مرّة وينتهي"] -->|"كافٍ"| lg1["حلّ JobRunner وتشغيله"]
on1 -.->|"يميل إلى الزيادة"| hv1["BackgroundService و RunAsync()"]
الشكل 13: جلب BackgroundService إلى تطبيق لمرّة واحدة زائد، ويكفي حلّ الخدمة وتشغيلها.
6.2. worker / خدمات خلفية
في معالجة مثل worker مقيم والاستطلاع واستهلاك الطوابير والمراقبة والتشغيل الدوري،
تركيب Generic Host و BackgroundService أوضح كثيراً.
ما يفيد خصوصاً:
- مسار البدء والإيقاف يتوحّد في جهة الـ host
- السجلات والإعدادات و DI متاحة من البداية
- يسهل بثّ الإلغاء عند
Ctrl+Cأو إشارة التوقّف - يسهل فصل متن العمل المقيم عن
Program.cs
ثم يسهل ربطه أيضاً بسياق Windows Service أو الحاويات. إن كنت تنمّيه كتطبيق مقيم، فـ Generic Host أساس طبيعي جدّاً.
عند التحويل إلى Windows Service، البحث عن الملفات بافتراض الدليل الحالي أقلّ أماناً من
الانطلاق من IHostEnvironment.ContentRootPath.
لأن «مسار أساس التطبيق» يُحسَم في سياق الـ host.
flowchart TB
accTitle: أساس worker مقيم
accDescr: في worker مقيم أو تشغيل دوري تركيب Generic Host و BackgroundService أوضح، ويسهل ربطه أيضاً بسياق Windows Service أو الحاويات.
wk1["worker مقيم / تشغيل دوري"] --> cb1["Generic Host و BackgroundService"]
cb1 --> ws1["Windows Service"]
cb1 --> ct1["إقامة في حاوية"]
ws1 -.-> cr1["البحث انطلاقاً من ContentRootPath"]
الشكل 14: العمل المقيم أوضح مع تركيب الـ host و BackgroundService، ويسهل تنميته نحو Windows Service أو الحاويات.
6.3. يوجد أيضاً تحت ASP.NET Core
تطبيقات الويب / API تستخدم WebApplication.CreateBuilder(args)،
فقد تبدو للوهلة الأولى عالماً منفصلاً عن Generic Host.
غير أن الإحساس متّصل بقوّة.
builder.Servicesbuilder.Configurationbuilder.Logging
تشابه طعم الكتابة لهذا السبب.
في ASP.NET Core يدخل بدء خادم HTTP أيضاً داخل عمر الـ host.
أي أن فهم Generic Host يؤثّر أيضاً بمعنى أن قراءة Program.cs في جهة الويب تصير أوضح: «لماذا نلمس هنا DI والإعدادات والسجلات؟».
flowchart TB
accTitle: الويب أيضاً على الـ host نفسه
accDescr: تطبيق ASP.NET Core أيضاً يقوم عبر WebApplication.CreateBuilder على فكرة الـ host نفسها، وبدء خادم HTTP يدخل داخل عمر الـ host.
wa1["تطبيق ASP.NET Core"] --> wb2["WebApplication.CreateBuilder"]
wb2 --> gh2["على فكرة الـ host نفسها"]
gh2 -.-> ht1["بدء خادم HTTP أيضاً داخل العمر"]
الشكل 15: builder جهة الويب أيضاً على فكرة الـ host نفسها، وبدء خادم HTTP يدخل في العمر.
7. الحالات التي يناسبه
مواضع ينسجم فيها Generic Host بسهولة.
- تطبيق console يستخدم إعدادات وسجلات و DI
- worker يشبه مستهلك طابور أو مستطلع أو مراقب أو مجدول
- تطبيق طويل التشغيل تريد تنظيفه عند
Ctrl+Cأو SIGTERM - تطبيق قد ينمو لاحقاً نحو إقامة Windows Service / حاوية
- تطبيق تريد مواءمة أسلوب مجموعات التوسعة نفسها كما في ASP.NET Core
القاسم المشترك: «لا تريد أن يصير مدخل التطبيق وإدارة عمره عشوائيّين».
غير أن هذا وحده يصعّب رسم الخطّ، لذا أضع مقياساً أيضاً. إذا انطبق اثنان أو أكثر ممّا يلي، فغالباً أسهل لاحقاً الركوب على Generic Host من البداية.
flowchart TB
accTitle: مقياس قرار الاعتماد
accDescr: إن انطبق اثنان أو أكثر من بنود المقياس فاركب Generic Host من البداية، وإن لم ينطبق أيّ بند فيمكن اعتباره غير لازم.
qn1["عُدّ ما ينطبق من المقياس"] -->|"اثنان أو أكثر"| ok1["اركب Generic Host من البداية"]
qn1 -->|"لا شيء"| ng1["يمكن اعتبار Generic Host غير لازم"]
الشكل 16: إن انطبق اثنان أو أكثر فاركب من البداية، وإن لم ينطبق أيّ بند فلا حاجة لجلبه.
| المقياس | الخطّ العملي |
|---|---|
| عدد الإعدادات | ثلاثة إعدادات أو أكثر تتغيّر حسب البيئة (جهة الاتّصال، العتبات، جهة الخرج، إلخ) |
| السجلات | يلزم إبقاؤها في ملف أو Event Log. ليست مجرّد كتابة إلى الخرج القياسي والانتهاء |
| شكل التشغيل | مقيم. أو يعمل بفاصل ثابت مرّة أو أكثر في اليوم |
| التبعيات | ثلاثة أطراف أو أكثر تريد تلقّيها في المنشئ. أو طرف تريد استبداله في الاختبار |
| العمر | عند Ctrl+C أو إيقاف الخدمة يلزم تنظيف في منتصف العمل |
| المستقبل | احتمال التشغيل كـ Windows Service أو في حاوية |
8. الحالات التي لا يناسبه / يكون فيها زائداً
في المقابل توجد مواضع لا حاجة لجعل Generic Host البطولة من البداية.
- أداة صغيرة تقرأ الوسائط مرّة وتطبع مرّة وتنتهي
- شيفرة تجريب خشنة تُستخدم عشرات الدقائق فقط
- مشروع مكتبة
- حالة تقرأ إعداداً واحداً ولا تحتاج حتى DI أو سجلات أو إدارة عمر
هنا الكتابة مباشرة في Main أقلّ قراءة وأقلّ ملفات من إقامة host. كمقياس، إن لم ينطبق أيّ بند من جدول الفصل 7، فلا حرج في اعتبار Generic Host غير لازم.
المهم أن قوّة Generic Host لا تعني أنه لازم لكل ملف تنفيذي.
9. مواضع التعثّر
أخيراً، نقاط يسهل الوقوع فيها عند أوّل تعامل مع Generic Host.
- رؤية Generic Host كحاوية DI فقط
- عمليّاً هو أساس يشمل البدء والإيقاف والإعدادات والسجلات و hosted service.
- بدء تطبيق جديد من
Host.CreateDefaultBuilderبالعادة- إن لم يكن هناك سبب لمواءمة كود قائم، فـ
Host.CreateApplicationBuilderأوضح أوّلاً.
- إن لم يكن هناك سبب لمواءمة كود قائم، فـ
- وضع خدمة scoped مباشرة في
BackgroundService- hosted service بلا نطاق افتراضي. صنع نطاق عبر
IServiceScopeFactoryأأمن.
- hosted service بلا نطاق افتراضي. صنع نطاق عبر
- worker ينتهي بمرّة واحدة دون إبلاغ الـ host بالتوقّف
- إن نفّذت «run once» بقالب Worker، فبدون استدعاء
IHostApplicationLifetime.StopApplication()عند انتهاء العمل يستمرّ الـ host في الجري.
- إن نفّذت «run once» بقالب Worker، فبدون استدعاء
- القطع بـ
Environment.Exitرغم إرادة إنهاء سليم- إن كنت تستخدم الـ host، فـ
StopApplication()أوضح حين تريد التوقّف بنظافة.
- إن كنت تستخدم الـ host، فـ
- افتراض current directory في Windows Service
- البحث عن الملفات أثبت إن انطلق من
IHostEnvironment.ContentRootPath.
- البحث عن الملفات أثبت إن انطلق من
- تطويق CLI قصير العمر بـ
BackgroundServiceمن البداية- لعمل لمرّة واحدة يكفي حلّ صنف خدمة عادي وتشغيله.
- وضع مؤقّت callback جزافاً في التشغيل الدوري لـ
BackgroundService- إن كنت تكتب تدفّقاً
asyncفـPeriodicTimerغالباً أسهل قراءة وأقلّ اضطراباً.
- إن كنت تكتب تدفّقاً
مع Generic Host، فصل «هل هو عمل قصير العمر أم عمل مقيم» أوّلاً وحده يقلّل الحيرة كثيراً.
flowchart TB
accTitle: السؤال الذي يُفصَل أوّلاً
accDescr: افصل أوّلاً هل العمل قصير العمر أم مقيم؛ فالقصير يكفي فيه حلّ صنف خدمة عادي وتشغيله، والمقيم يستخدم BackgroundService وإدارة عمر الـ host.
qq1["عمل قصير العمر أم عمل مقيم؟"] -->|"قصير العمر"| sj1["حلّ الخدمة وتشغيلها فقط"]
qq1 -->|"مقيم"| lj1["BackgroundService وإدارة العمر"]
الشكل 17: فصل قصير العمر عن المقيم أوّلاً وحده يقلّل الحيرة حول مدى استخدام أدوات الـ host.
10. الخلاصة
Generic Host في جملة: أساس يجمع مدخل تطبيق .NET وإدارة عمره.
نراجع النقاط التي تستحق النظر.
- Generic Host لا يشمل DI فقط، بل الإعدادات والسجلات ومعالجة الإيقاف و hosted service
- للتطبيق الجديد غير الويب،
Host.CreateApplicationBuilder(args)أوضح أوّلاً - في عمل قصير العمر يكفي البناء والتشغيل دون
BackgroundService - في عمل مقيم،
BackgroundServiceوإدارة عمر الـ host تؤثّران بقوّة BackgroundServiceبلا نطاق افتراضي، فاصنع نطاقاً صريحاً للخدمات scopedWebApplicationBuilderفي ASP.NET Core أيضاً، من حيث الفكرة، على المسار نفسه
Generic Host ليس أداة لطقس ثقيل. إذا نمت الإعدادات والسجلات والتبعيات والإقلاع والإيقاف ولو قليلاً، فهو أداة لإبقائها مجمّعة عند المدخل بدل تسريبها إلى الجدران.
في المقابل، إن كانت الأداة صغيرة لا تحتاج ذلك بعد، فلا حاجة لجلبه. إذا أمكن هذا التمييز، لم يعد 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
- مقال ذو صلة: اختيار مؤقّتات .NET الثلاثة - PeriodicTimer / Timer / DispatcherTimer
- مقال ذو صلة: جدول قرار عملي لـ C# async/await - Task.Run و ConfigureAwait
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
أفضل الممارسات العمليّة لتعدّد مؤشّرات الترابط: نسخة .NET ── ما تقرِّره قبل إضافة مؤشّرات ترابط
قواعد تصميم عمليّة تمنع شيفرة .NET/C# متعدّدة مؤشّرات الترابط من الانهيار أو التجمّد أحياناً: اركب على Task بدل إنشاء المؤشّرات بنفسك، قل...
كيف نفهم عزل الجلسات في Windows ── Session 0 وRDP وتشغيل عدة مستخدمين معاً
نوضح مفهوم «الجلسة» الذي يربك كثيراً من مطوّري تطبيقات Windows. نشرح عملياً سبب عزل Session 0 الذي يمنع الخدمة من عرض واجهة، وسلوك الجلسا...
كيف تختار وسيلة الاتّصال بين عمليّات Windows ── جدول قرار للأنابيب المسمّاة / TCP / gRPC / الذاكرة المشتركة / COM
كيف تختار وسيلة التواصل بين تطبيقات Windows؟ ننظّم في جدول قرار مجالات تفوّق ومطبّات الأنابيب المسمّاة، وTCP المحليّ، وgRPC، والذاكرة الم...
كيفيّة إنشاء خدمات Windows وتشغيلها ── من التمييز عن جدولة المهام إلى تحويل BackgroundService إلى خدمة
هل ينبغي تحويل المعالجة المقيمة إلى خدمة Windows، أم تكفي جدولة المهام؟ نرتّب من منظور عمليّ جدول القرار، وكيفيّة إنشاء الخدمة عبر .NET W...
كيف تختار مكان حفظ بيانات تطبيق Windows محليّاً ── جدول قرار بين SQLite وJSON وRegistry وAccess
أين ينبغي حفظ بيانات تطبيق سطح مكتب Windows، وبأيّ صيغة؟ نرتّب الفرق بين AppData وProgramData، ونقاط قوّة وعيوب كلّ من SQLite وملفّات JSO...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
الموضوع يدور حول بناء تطبيق Windows يشمل المعالجة المقيمة ومعالجة الإنهاء والسجلّات والإعدادات، لذا فهو بوصفه مشروع تنفيذ يتناسب جيّداً مع تطوير تطبيقات Windows.
الاستشارات التقنية ومراجعة التصميم
إن كنت في مرحلة تريد فيها ترتيب حقن التبعيات (DI) ودورة الحياة وتقسيم المسؤوليات قبل التنفيذ، فيمكن البدء بترتيب الخطة ضمن الاستشارة التقنية ومراجعة التصميم.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- ما هو Generic Host؟
- أساس يجمع تشغيل تطبيق .NET وعمره في مكان واحد. داخله DI والإعدادات (Configuration) والسجلات و IHostedService / BackgroundService ومعالجة إيقاف التطبيق. ليس مجرّد غلاف لحاوية DI، بل آلية تجمع نقطة تجميع التطبيق وإدارة عمره. يظهر أثره عندما تبدأ الإعدادات والسجلات والتبعيات والإقلاع والإيقاف بالنمو ولو قليلاً.
- أيّهما نستخدم: Host.CreateApplicationBuilder أم Host.CreateDefaultBuilder؟
- للتطبيق الجديد غير الويب، الدخول من Host.CreateApplicationBuilder(args) أوضح. كلاهما يحمل الوظائف الجوهرية نفسها والسلوك الافتراضي، وليسا علاقة «ميزة جديدة مقابل شيء آخر». الفرق أسلوب الكتابة أساساً: CreateApplicationBuilder يكتب مباشرة إلى builder.Services وما شابه، و CreateDefaultBuilder يسلسل ConfigureServices وما شابه. إن كان هناك سبب لمواءمة كود قائم أو تكوين قديم قائم على توابع التوسعة فاختر CreateDefaultBuilder.
- هل يستحق Generic Host الاستخدام في تطبيق console أيضاً؟
- إذا احتجت DI أو إعدادات أو سجلات، فأداة console تعمل مرّة واحدة كافية لاستخدامه. لست مضطرّاً لصنع BackgroundService حتماً؛ يكفي Build() ثم حلّ الخدمات اللازمة وإنهاء العمل بعد ذلك، وتبقى فائدة Generic Host. في المقابل هو زائد على أداة صغيرة تقرأ الوسائط مرّة وتطبع مرّة وتنتهي، أو على شيفرة تجريب خشنة، فليس شيئاً يُجلَب في كلّ مرّة حتماً.
- كيف نستخدم خدمة scoped داخل BackgroundService؟
- ليس لـ BackgroundService نطاق افتراضي، لذا حقن خدمة scoped مباشرة في المنشئ غير آمن. احقن IServiceScopeFactory، وأنشئ نطاقاً صريحاً داخل ExecuteAsync، وحلّ خدمات جهة العمل داخل ذلك النطاق. إن أردت استخدام خدمة scoped مثل DbContext فالوعي بهذا الشكل لازم خصوصاً.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.