نکته تکمیلی: معادل HostingEnvironment.QueueBackgroundWorkItem در ASP.NET Core
با تزریق این IBackgroundTaskQueue و استفاده از متد QueueBackgoundWorkItem، امکان در صف قرار دادن یک وظیفه جدید را خواهید داشت.
در زمان ثبت و معرفی یک کار پسزمینه، داخل صفی با رعایت مباحث همزمانی و تحت عنوان _workItems قرار خواهد گرفت. متد DequeueAsync نیز توسط HostedService پیاده سازی شده در ادامه، استفاده شده و به ترتیب وظایف ثبت شده را اجرا خواهد کرد.
این امکان قرار است به صورت آزمایشی به نسخه ASP.NET Core 3.0 اضافه شود. برای استفاده از آن کافی است QueuedHostedService را به سیستم DI معرفی کرده به شکل زیر عمل کنید:
public interface IBackgroundTaskQueue : ISingletonDependency { void QueueBackgroundWorkItem(Func<CancellationToken, IServiceProvider, Task> workItem); Task<Func<CancellationToken, IServiceProvider, Task>> DequeueAsync( CancellationToken cancellationToken); }
پیاده سازی واسط IBackgroundTaskQueue
internal class BackgroundTaskQueue : IBackgroundTaskQueue { private readonly ConcurrentQueue<Func<CancellationToken, IServiceProvider, Task>> _workItems = new ConcurrentQueue<Func<CancellationToken, IServiceProvider, Task>>(); private readonly SemaphoreSlim _signal = new SemaphoreSlim(0); public void QueueBackgroundWorkItem( Func<CancellationToken, IServiceProvider, Task> workItem) { if (workItem == null) { throw new ArgumentNullException(nameof(workItem)); } _workItems.Enqueue(workItem); _signal.Release(); } public async Task<Func<CancellationToken, IServiceProvider, Task>> DequeueAsync( CancellationToken cancellationToken) { await _signal.WaitAsync(cancellationToken); _workItems.TryDequeue(out var workItem); return workItem; } }
پیاده سازی یک QueuedHostedService
public class QueuedHostedService : BackgroundService { private readonly IServiceScopeFactory _factory; private readonly ILogger _logger; private readonly IBackgroundTaskQueue _queue; public QueuedHostedService( IBackgroundTaskQueue queue, IServiceScopeFactory factory, ILoggerFactory loggerFactory) { _factory = factory ?? throw new ArgumentNullException(nameof(factory)); _queue = queue ?? throw new ArgumentNullException(nameof(queue)); _logger = loggerFactory.CreateLogger<QueuedHostedService>(); } protected override async Task ExecuteAsync(CancellationToken cancellationToken) { _logger.LogInformation("Queued Hosted Service is starting."); while (!cancellationToken.IsCancellationRequested) { var workItem = await _queue.DequeueAsync(cancellationToken); try { using (var scope = _factory.CreateScope()) { await workItem(cancellationToken, scope.ServiceProvider); } } catch (Exception ex) { _logger.LogError(ex, $"Error occurred executing {nameof(workItem)}."); } } _logger.LogInformation("Queued Hosted Service is stopping."); } }
public class InvoiceService : IInvoiceService { private readonly IBackgroundTaskQueue _queue; public InvoiceService(IBackgroundTaskQueue queue) { _queue = queue ?? throw new ArgumentNullException(nameof(queue)); } public Print(InvoiceModel model) { _queue.QueueBackgroundWorkItem((token, provider)=> { //todo: print return Task.Task.CompletedTask; }) } }