给定一个 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()。
_taskQueue.QueueBackgroundWorkItemAsync(async (cancellationToken, serviceProvider) => {
var mediator = serviceProvider.GetRequiredService<Mediator>();
await mediator.Send(new CollectAndZipDataCommand());
await mediator.Send(new SendDownloadLinkPerMailCommand());
});
允许我成功且正确地调用Mediator,但CommandHandlers的服务为空。我很确定这是范围,但无论我尝试什么,我都无法获得一个干净的解决方案来解决后台任务中 DI 容器的所有依赖项。
问题可能在范围内引起,我建议你尝试这样的代码。
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;
}
}