将迭代转换为异步

时间:2014-11-10 12:22:07

标签: asp.net .net vb.net asynchronous asp.net-4.5

我返回大量的DataTable行,迭代它们并将每行的值推送到webservice,然后返回响应代码(字符串)。如果Web服务中发生任何错误,整个过程将停止并显示错误:

Protected Sub DoStuff(sender As Object, e As EventArgs) Handles lnk_orderCards.Click
    Dim dt as DataTable = GetDataBaseData()
    For each dr as DataRow in dt.Rows()
        Dim f as String = dr.Item("firstname").ToString()
        Dim m as String = dr.Item("middleName").ToString()
        Dim s as String = dr.Item("surname").ToString()
        Dim err as String = String.Empty
        Dim result as String = XYZService.DoIt(f, m, s)
        Select Case result
            Case "ok"
                ' OK - allow For Loop Next '
            Case "e"
                err = "Some error"
            Case "e2"
                err = "Another error"
        End Select
        If Not String.IsNullOrWhiteSpace(err) Then
            ShowError(err)
            Exit Sub
        End If
    Next
    XYZService.Complete()
    ltl_status.Text = "Success!"
End Sub

我认为以上是Async方法的一个很好的候选者,特别是如果数据表有1000行,因为每个webservice请求可以并行发送。但是,从MSDN中我找不到足够的示例来说明如何最好地实现Async。

有人可以推荐更彻底的方法吗?我已阅读MSDN关于Task.WaitAllTask.Factory.StartNew的内容,但这些示例并不简单。如果使用Task.WaitAll like this,如果一个(或多个)任务失败,如何停止流程?

重要的是,所有任务在调用XYZService.Complete()之前都会返回成功。

基于Stephen的输入的最终代码

Protected Async Sub DoStuff(sender As Object, e As EventArgs) Handles lnk_orderCards.Click
    Dim cts As New CancellationTokenSource
    Dim dt As DataTable = GetDataBaseData()
    Dim rows As IEnumerable(Of Task(Of String)) = (From dr As DataRow In dt.Rows Select DoServiceCall(cts, dr))
    Dim results() As String = Await Task.WhenAll(rows)
    Dim errors As List(Of String) = (From s As String In results Where s <> String.Empty).ToList()
    If errors.Count > 0 Then
        ShowError(String.Join("<br/>", errors))
        Exit Sub
    Else
        Console.WriteLine("Success")
    End If
End Sub

Protected Async Function DoServiceCall(t As CancellationTokenSource, dr As DataRow) As Task(Of String)
    If t.IsCancellationRequested Then
        t.Token.ThrowIfCancellationRequested()
    End If
    Dim f As String = dr.Item("firstname").ToString()
    Dim m As String = dr.Item("middleName").ToString()
    Dim s As String = dr.Item("surname").ToString()
    Dim returnResult As XYZService.ServiceReturnResult = Await XYZService.DoItAsync(f, s, s)
    Select Case returnResult.return
        Case "ok"
            ' OK - allow For Loop Next '
        Case Else
            t.Cancel(False)
            Throw New Exception("Web service error: " & returnResult.return)
    End Select
    Return returnResult.return
End Function

1 个答案:

答案 0 :(得分:2)

最简单的解决方案是使用Task.WhenAll。具体来说,您可以将每个项目(LINQ的Select)投影到Task (of T),然后执行Await Task.WhenAll

但是,这种简单的方法将同时执行所有请求,并且如果其中一个请求失败,则不会停止其他请求。这有点复杂;我建议使用CancellationTokenSource来表示“紧急停止”,每个请求都会收到CancellationToken,如果失败则取消源。

请勿使用Task.Run或更差的表兄StartNew。由于您的操作本质上是异步的(I / O绑定的网络请求),因此您需要异步并发(Task.WhenAll),而不是并行并发(StartNew)。