تكرار السؤال: …
الإجابة المختصرة
تنفذ بالوراثة من BackgroundService وكتابة المنطق داخل ExecuteAsync()؛ ولا يمكن حقن الخدمات الـ Scoped فيها مباشرة لأنها Singleton ويجب إنشاء نطاق يدوياً عبر IServiceScopeFactory.
الشرح
فئة BackgroundService هي الطريقة القياسية لتشغيل مهام طويلة الأمد (مثل سحب رسائل من طابور أو معالجة دورية). نظراً لأن الـ BackgroundService تسجل كـ Singleton طوال عمر التطبيق، فإن حقن DbContext (وهو Scoped) في الباني سيرمي خطأ InvalidOperationException. الحل الصحيح هو حقن IServiceScopeFactory وإنشاء نطاق يدوي using (var scope = _scopeFactory.CreateScope()) داخل كل دورة.
مثال بالكود
public class QueueConsumer(IServiceScopeFactory scopeFactory) : BackgroundService {
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
while (!stoppingToken.IsCancellationRequested) {
using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.ProcessPendingOrdersAsync(stoppingToken);
await Task.Delay(5000, stoppingToken);
}
}
}أخطاء شائعة
- تجاهل CancellationToken stoppingToken مما يمنع التطبيق من إتمام عملية الإغلاق النظيف (Graceful Shutdown) عند استلام إشارة التوقف.