在不返回结果的情况下在其他方法中调用多个异步方法

Calling multiple async methods within other method without returning result

我正在使用 Dapper 开发 ASP.NET Core 2.2 应用程序。几乎所有方法都遵循 async/await 模式 return 结果是某种 Task

由于我们在 Dapper 中遇到的问题(我没有亲自调查过,这部分代码我按原样使用)但基本上可以归结为这样一个事实,如果你想在 Transaction 多个异步方法中执行,这些方法在内部调用其他异步方法,并且您可能以这种方式嵌套多个级别,您需要将所有这些方法调用包装在一个将在事务中执行的方法中。

处理方法如下:

public async Task<TOut> ExecuteInTransactionAsync<TOut>(
    Delegate function, 
    params object[] parameters)
{
    using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        var result = await ((Task<TOut>)function.DynamicInvoke(parameters))
            .ConfigureAwait(false);

        scope.Complete();

        return result;
    }
}

所以我有一个非常复杂的实体,它通过调用 Save 到许多较小的实体来保存。这部分代码运行正常,如下所示:

public async SaveEntireEntity(EntityDTO entityDTO)
{
  return await _transactionProvider.ExecuteInTransactionAsync<dynamic>(
     new Func<object, Task<dynamic>>(async dto => await SaveInTransaction(dto)),
     new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age });
}

SaveInTransaction 方法如下所示:

private async Task<dynamic> SaveInTransaction(dynamic dto)
{
  var entityId = await nameService.Add(dto.Name);
  await addressService.Add(dto.Address);
  await ageService.Add(dto.Age);

  return entityId;
}

所以这是简化的,但实际上我在这里调用了多个服务,它们调用了多个存储库,这工作正常。

问题 我遇到的问题是在事务中更新同一实体。显示 Save 逻辑的全部目的是在最后指出,因为我有这个 return entityId; 我能够毫无问题地将所有东西链接在一起。但是,就像现在一样,默认情况下我们的 Update 方法没有 returning 任何东西,这就是我无法弄清楚如何实现 Update 逻辑的地方。

目前我有这个:

public async Task UpdateEntireEntity(UpdateEntityDTO, entityDTO)
{
  await _transactionProvider.ExecuteInTransactionAsync<dynamic>(
    new Func<object, Task<dynamic>>(async dto => await UpdateInTransaction(dto)),
    new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age });
}

UpdateInTransaction 看起来像这样:

private async Task<dynamic> UpdateInTransaction(dynamic dto)
{
  await UpdateName(dto.Name);
  await UpdateAddress(dto.Address);
  await UpdateAge(dto.Age);

  return await Task.FromResult<dynamic>(null);
}

这似乎至少基于我所做的几次测试,但我真的不喜欢这部分:

return await Task.FromResult<dynamic>(null);

对我来说,这似乎是一个丑陋的 hack。 Update 方法被认为不会 return 任何值,这太人为了。

甚至最糟糕的是,我不知道如何在不需要 return 的情况下实现更新方法。

我试过的一件事是将 UpdateInTransaction 的声明更改为

private async Task UpdateInTransaction(dynamic dto)

当我调用该方法时,我将其更改为:

await _transactionProvider.ExecuteInTransactionAsync<dynamic>(
new Func<object, Task>( async dto => await UpdateInTransaction(dto)..

但是我遇到了以下异常:

AsyncStateMachineBox1[System.Threading.Tasks.VoidTaskResult, <fully-qualified- name>.<<UpdateEntireEntity>b__0>d] to type 'System.Threading.Tasks.Task1[System.Threading.Tasks.Task]'

。 所以基本上就是这样。很抱歉 post。我真的很感激一些解释得很好的答案。

你可以改变

private async Task<dynamic> UpdateInTransaction(dynamic dto)
{
  await UpdateName(dto.Name);
  await UpdateAddress(dto.Address);
  await UpdateAge(dto.Age);

  return await Task.FromResult<dynamic>(null);
}

private async Task<dynamic> UpdateInTransaction(dynamic dto)
{
  await UpdateName(dto.Name);
  await UpdateAddress(dto.Address);
  await UpdateAge(dto.Age);

  return null;
}

我会避免使用 Delegate 因为它不是输入的:

public async Task<TOut> ExecuteInTransactionAsync<TOut>(Func<Task<TOut>> function)
{
    using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        var result = await (function()).ConfigureAwait(false);

        scope.Complete();

        return result;
    }
}

此签名意味着您需要捕获参数而不是传递它们:

public async Task<dynamic> SaveEntireEntity(EntityDTO entityDTO)
{
  return await _transactionProvider.ExecuteInTransactionAsync(
     async () => await SaveInTransaction(
         new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age }));
}

一旦您在方法签名中使用强类型 Func<Task<T>> 而不是 Delegate,您就可以像这样为 ExecuteInTransactionAsync 创建重载:

public async Task ExecuteInTransactionAsync(Func<Task> function)
{
    using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        await (function()).ConfigureAwait(false);
        scope.Complete();
    }
}

可以这样使用:

public async Task UpdateEntireEntity(UpdateEntityDTO entityDTO)
{
  await _transactionProvider.ExecuteInTransactionAsync(
    async () => await UpdateInTransaction(
        new { Name = entityDTO.Name, Address = entityDTO.Address, Age = entityDTO.Age }));
}

private async Task UpdateInTransaction(dynamic dto)
{
  await UpdateName(dto.Name);
  await UpdateAddress(dto.Address);
  await UpdateAge(dto.Age);
}