将传统方法转换为异步方法

时间:2018-05-01 19:17:18

标签: c# asp.net .net asp.net-web-api async-await

我有一个API,负责在数据库中插入短信详细信息。 它通过对存储库进行同步调用来实现,我认为可以通过异步方式实现。如何实现这一点?或者什么是处理这种情况的最佳方式。代码片段示例非常受欢迎,因为我仍然围绕着.NET。

API:

public IHttpActionResult SendSMSNotification([FromBody] SMSNotification smsNotification)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }            
        _service.SendSMS(smsNotification);

        return Ok();
    }

服务:

internal void SendSMS(SMSNotification smsNotification)
    {
        _repository.Notify(_mapperService.GetSMSNotification(smsNotification));
    }

mapper:

public SMSNotification GetSMSNotification(SMSNotification message)
    {
        return AutoMapper.Mapper.Map<SMSNotification>(message); 
    }
回复:

public virtual bool Notify(SMSNotification request)
    {
        using (var sql = _sqlMapper.CreateCommand('Database', 'Stored proc'))
        {
            sql.AddParam("@fMessage", request.Message);
           //..............
           //.............. more params
            var retvalParamOutput = sql.AddOutputParam("@fRetVal", System.Data.SqlDbType.Int);
            sql.Execute();
            return retvalParamOutput.GetSafeValue<int>() == 1;
        }
    }

这里的sql是一个自定义的东西,它有以下方法:

  public static int Execute(this IDataCommand @this);
        [AsyncStateMachine(typeof(<ExecuteAsync>d__1))]
        public static Task<int> ExecuteAsync(this IDataCommand @this);

1 个答案:

答案 0 :(得分:2)

将阻止(通常是IO绑定的调用)(例如数据库,网络或文件系统工作)更改为异步可以使您的应用程序更好地扩展。

这确实会通过您的API产生影响。也就是说,你需要等待异步调用一直到最顶层的调用,否则,某个地方会阻塞,你只是失去了调用异步API的好处。

为了证明这一点,让我们从存储库调用的底部开始,因为这可能是非常昂贵的阻塞操作。我们改变sql.Execute以使用异步版本ExecutAsync版本:

回购:

public virtual async Task<bool> Notify(SMSNotification request)
{
    using (var sql = _sqlMapper.CreateCommand('Database', 'Stored proc'))
    {
        sql.AddParam("@fMessage", request.Message);
       //..............
       //.............. more params
        var retvalParamOutput = sql.AddOutputParam("@fRetVal", System.Data.SqlDbType.Int);
        await sql.ExecuteAsync();
        return retvalParamOutput.GetSafeValue<int>() == 1;
    }
}

现在我们必须更改方法的签名,以返回包含bool结果的Task。

我们还将该方法标记为异步,因此我们可以进一步使用“await”运算符。如果不这样做,我们必须做更多的重构来操作并自己返回Task结果,但是“async”修饰符和“await”关键字让编译器为我们做了那个魔术,其余代码大部分看起来像正常。

映射器调用实际上不需要更改:

映射器:

public SMSNotification GetSMSNotification(SMSNotification message)
{
    return AutoMapper.Mapper.Map<SMSNotification>(message); 
}

服务调用现在正在调用异步方法,因此我们要等待而不是阻塞该异步调用,我们还必须将此前的void方法更改为异步方法。注意我们将它从“void”更改为“async Task”;您可以将void方法标记为“async void”,但这是作为Windows窗体和WPF应用程序中事件处理程序的解决方法;在其他每种情况下,您都希望在将其设置为异步时将“void”方法更改为“async Task”。

服务:

internal async Task SendSMS(SMSNotification smsNotification)
{
    await _repository.Notify(_mapperService.GetSMSNotification(smsNotification));
}

最后,我们的API调用可以异步,等待我们的服务调用:

API:

public async Task<IHttpActionResult> SendSMSNotification([FromBody] SMSNotification smsNotification)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }            
    await _service.SendSMS(smsNotification);

    return Ok();
}

有时候建议你在执行这样的重构之后,按照惯例将方法重命名为“Async”;但是我认为这不是必须的,因为.NET API表面的大部分变得异步,现在几乎是多余的。

值得深入了解异步/等待的东西;我试图让这个例子保持相对简短。但我希望这至少可以让你开始。