Search code examples
c#asp.net-corecqrsmediator

How to use Mediator inside Background Service in C# ASP NET Core?


I am trying to implement a background service, QueuedBackground service with Mediator.

So far I am able to implement the queues but I am unable to execute Mediator.

Interface

public interface IBackgroundTaskQueueService
    {
        void QueueBackgroundWorkItem(object workItem, CancellationToken token);

        Task<object> DequeueAsync(
            CancellationToken cancellationToken);
    }

Implementation

public class BackgroundTaskQueueService : IBackgroundTaskQueueService
    {
        private readonly ConcurrentQueue<(object,CancellationToken)> _workItems =
            new ConcurrentQueue<(object,CancellationToken)>();

        private SemaphoreSlim _signal = new SemaphoreSlim(0);

        public void QueueBackgroundWorkItem(object workItem, CancellationToken token)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            _workItems.Enqueue((workItem,token));
            _signal.Release();
        }

        public async Task<object> DequeueAsync( CancellationToken cancellationToken)
        {
            await _signal.WaitAsync(cancellationToken);
            _workItems.TryDequeue(out var workItem);
            return workItem.Item1;
        }
    }

Background Service

public class QueuedHostedService : BackgroundService
    {
   
        private readonly ILogger _logger;
        private readonly IMediator _mediator;
        public QueuedHostedService(IBackgroundTaskQueueService taskQueue, ILoggerFactory loggerFactory, IMediator mediator)
        {
            TaskQueue = taskQueue;
            _mediator = mediator;
            _logger = loggerFactory.CreateLogger<QueuedHostedService>();
        }

        public IBackgroundTaskQueueService TaskQueue { get; }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (false == stoppingToken.IsCancellationRequested)
            {
                try
                {
                    var workItem = await TaskQueue.DequeueAsync(stoppingToken);
                    await _mediator.Send(workItem, stoppingToken);
                    // await _mediator.Send(new UpdateProductCostByMaterialRequestModel()
                    // {
                    //     Id = 1
                    // }, stoppingToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"Error occurred executing Work item.");
                }
            }
        }
    }

Usage

_queueService.QueueBackgroundWorkItem(new UpdateProductCostByMaterialRequestModel()
            {
                Id = request.ProductId
            }, CancellationToken.None);

Now with the above code I am able to receive the class object but when I pass it in the Mediator I get InvalidOperation Handler not registered.

I am confused.


Solution

  • Okay I found the issue

    Instead of passing it from the Constructor I had to use the ServiceFactory Interface

    My Solution BackgroundService is a Singleton. You cannot inject a Scoped into a Singleton.

    public class QueuedHostedService : BackgroundService
        {
       
            private readonly ILogger _logger;
            private readonly IServiceScopeFactory _serviceScopeFactory;
    
            public QueuedHostedService(IBackgroundTaskQueueService taskQueue, ILoggerFactory loggerFactory, IServiceScopeFactory serviceScopeFactory)
            {
                TaskQueue = taskQueue;
                _serviceScopeFactory = serviceScopeFactory;
                _logger = loggerFactory.CreateLogger<QueuedHostedService>();
            }
    
            public IBackgroundTaskQueueService TaskQueue { get; }
    
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                using var scope = _serviceScopeFactory.CreateScope();
    
                var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
                while (false == stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        
                        var workItem = await TaskQueue.DequeueAsync(stoppingToken);
                        if (workItem is IRequest<object> item)
                        {
                            await mediator.Send(workItem, stoppingToken);
                        }
                        // await _mediator.Send(new UpdateProductCostByMaterialRequestModel()
                        // {
                        //     Id = 1
                        // }, stoppingToken);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"Error occurred executing Work item.");
                    }
                }
            }
        }