如何将参数传递给排队后台任务(.NET Core)

Nik*_*hou 10 .net self-hosting background-process .net-core

在我的网络应用程序中,我对长时间运行的任务执行操作,并且我想在后台调用此任务。因此,根据文档 .NET Core 3.1排队后台任务,我使用这样的代码:

public interface IBackgroundTaskQueue
{
    ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken, ValueTask> workItem);

    ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(CancellationToken cancellationToken);
}

public class BackgroundTaskQueue : IBackgroundTaskQueue
{
    private readonly Channel<Func<CancellationToken, ValueTask>> _queue;

    public BackgroundTaskQueue(int capacity)
    {
        var options = new BoundedChannelOptions(capacity){FullMode = BoundedChannelFullMode.Wait};
        _queue = Channel.CreateBounded<Func<CancellationToken, ValueTask>>(options);
    }

    public async ValueTask QueueBackgroundWorkItemAsync(Func<CancellationToken, ValueTask> workItem)
    {
        if (workItem == null)throw new ArgumentNullException(nameof(workItem));
        await _queue.Writer.WriteAsync(workItem);
    }

    public async ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(CancellationToken cancellationToken)
    {
        var workItem = await _queue.Reader.ReadAsync(cancellationToken);
        return workItem;
    }
}
Run Code Online (Sandbox Code Playgroud)

和托管服务

public class QueuedHostedService : BackgroundService
{
    private readonly ILogger<QueuedHostedService> _logger;

    public QueuedHostedService(IBackgroundTaskQueue taskQueue, ILogger<QueuedHostedService> logger)
    {
        TaskQueue = taskQueue;
        _logger = logger;
    }

    public IBackgroundTaskQueue TaskQueue { get; }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await BackgroundProcessing(stoppingToken);
    }

    private async Task BackgroundProcessing(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var workItem = await TaskQueue.DequeueAsync(stoppingToken);

            try
            {
                await workItem(stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred executing {WorkItem}.", nameof(workItem));
            }
        }
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Queued Hosted Service is stopping.");
        await base.StopAsync(stoppingToken);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我注册所有服务

services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue>(new BackgroundTaskQueue(queueCapacity));
Run Code Online (Sandbox Code Playgroud)

然后我可以通过不带参数的调用来成功使用它,就像在这样的示例中一样

public async Task<TenantBo> RegisterCompanyAsync(AddTenantBo addTenantBo)
{
  var tenantBo = new TenantBo();

  try
  {
    _companyRegistrationLogHelper.SetInfoLog(GetTenantId(tenantBo), 
      "Start create company: " + JsonConvert.SerializeObject(addTenantBo));

      InitOnCreateCompanyTasks(tenantBo);

      //skip if already create tenant 
      tenantBo = await CreateTenantAsync(tenantBo, addTenantBo);

      //run in background
      _companyRegistationQueue.QueueBackgroundWorkItemAsync(RunRegistrationCompanyMainAsync);

      return tenantBo;
  }
  catch (Exception e)
  {
    //some logs
    return tenantBo;
  }
}

private async ValueTask RunRegistrationCompanyMainAsync(CancellationToken cancellationToken)
{
  //some await Tasks
}

private async ValueTask RunRegistrationCompanyMainAsync(string tenantId, CancellationToken cancellationToken)
{
  //some await Tasks
}
Run Code Online (Sandbox Code Playgroud)

所以我只能使用一个参数调用 RunRegistrationCompanyMainAsync(CancellationToken CancellationToken) ,而不能使用两个参数调用 RunRegistrationCompanyMainAsync(stringtenantId, CancellationToken CancellationToken)

你能帮我传递字符串参数作为此任务的参数吗?

Gur*_*ron 21

QueueBackgroundWorkItemAsync(RunRegistrationCompanyMainAsync)调用中,编译器实际上执行从方法组到委托的转换。但要提供委托实例,Func您不限于方法组,您可以提供lambda 表达式,例如:

 var someTenantId = ....
 .....
_companyRegistationQueue.QueueBackgroundWorkItemAsync(ct => RunRegistrationCompanyMainAsync(someTenantId, ct));
Run Code Online (Sandbox Code Playgroud)

  • @NikitaSychou 在我的解决方案中,您不需要 `Func&lt;string, CancellationToken, ValueTask&gt;`,您可以使用与无参数作业相同的 `Func&lt;CancellationToken, ValueTask&gt;` 。您的“Tuple”解决方案几乎与编译器在我的解决方案中对闭包所做的实际操作相同,但麻烦更多。 (2认同)
  • @GuruStron 对我来说 100% 有效 (2认同)