使用后台服务进行完全依赖访问

问题描述 投票:0回答:1

给定一个 ASP.NET Core API (dotnet8),使用 MediatR,我有一个控制器并希望存档以下内容:

    [HttpPost]
    public async Task<IActionResult> CreateAndPublish([FromBody] CreateAndPublishRequest request)
    {
       // create configuration (like metadata for a sth. and store to db)

       // do not wait for this, run it in a background task 
       await Mediator.Send(new CollectAndZipDataCommand());
       await Mediator.Send(new SendDownloadLinkPerMailCommand());

       return Ok();
    }

因此这两个Mediator命令应该被执行,但需要更长的时间,这样它们就可以在后台继续执行,并且控制器将立即返回Ok()。

查看 https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-8.0&tabs=visual-studio 并像这样实现它

_taskQueue.QueueBackgroundWorkItemAsync(async (cancellationToken, serviceProvider) => {

var mediator = serviceProvider.GetRequiredService<Mediator>();
await mediator.Send(new CollectAndZipDataCommand());
await mediator.Send(new SendDownloadLinkPerMailCommand());
});

允许我成功且正确地调用Mediator,但CommandHandlers的服务为空。我很确定这是范围,但无论我尝试什么,我都无法获得一个干净的解决方案来解决后台任务中 DI 容器的所有依赖项。

asp.net .net scope
1个回答
0
投票

问题可能在范围内引起,我建议你尝试这样的代码。

   public class PublishController : Controller
    {
        private readonly BackgroundTaskQueue _backgroundTaskQueue;
        private readonly IServiceScopeFactory _serviceScopeFactory;

        public PublishController(BackgroundTaskQueue backgroundTaskQueue, IServiceScopeFactory serviceScopeFactory)
        {
            _backgroundTaskQueue = backgroundTaskQueue ?? throw new ArgumentNullException(nameof(backgroundTaskQueue));
            _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
        }

        [HttpPost]
        public IActionResult CreateAndPublish([FromBody] CreateAndPublishRequest request)
        {
            _backgroundTaskQueue.QueueBackgroundWorkItem(async token =>
            {
                using (var scope = _serviceScopeFactory.CreateScope())
                {
                    var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
                    await mediator.Send(new CollectAndZipDataCommand(), token);
                    await mediator.Send(new SendDownloadLinkPerMailCommand(), token);
                }
            });

            return Ok();
        }
    }

它对我有用,你可能需要定义队列 例如

 public class BackgroundTaskQueue
    {
        private readonly ConcurrentQueue<Func<CancellationToken, Task>> _workItems = new ConcurrentQueue<Func<CancellationToken, Task>>();
        private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);

        public void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

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

        public async Task<Func<CancellationToken, Task>> DequeueAsync(CancellationToken cancellationToken)
        {
            await _signal.WaitAsync(cancellationToken);
            _workItems.TryDequeue(out var workItem);

            return workItem;
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.