如何在Silverlight 5(VB)中使用Async / Await

时间:2013-05-01 00:14:53

标签: vb.net web-services silverlight async-await

我正在尝试调用Web服务并让我的代码等待该服务返回结果(或超时)。我的项目是使用.NET 4.0的Web服务的Silverlight 5,我在VS 2012下使用Microsoft.Bcl.Async.1.0.16 \ lib \ sl4 \ Microsoft.Threading.Tasks.dll ...任务运行该项目。 Extensions.dll ...和Extensions.Silverlight.dll。

这就是我一直在做的并且它正在工作,但我正在试图找出如何更改我的代码以便我可以使用Async / Await进程。 Web服务引用配置为在所有引用的程序集中返回带有重用类型的ObservableCollection和Generic.Dictionary。

我需要将一些代码转换为Async / Await:

    Private _Units As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
Public Property Units() As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
    Get
        Return _Units
    End Get
    Set(ByVal value As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
        _Units = value
        OnPropertyChanged(New PropertyChangedEventArgs("Units"))
    End Set
End Property


   Public Sub ReadUnits()

    Try

        ' Client is required
        If Not Me.Client Is Nothing Then

            ' User is required
            If Not Me.User Is Nothing Then

                ' Must be a real Client
                If Me.Client.ClientID > 0 Then

                    ' My have a sites
                    If Not Me.Site Is Nothing Then

                        ' Call the web service relative to where this application is running
                        Dim webServiceURI As New Uri("../WebServices/Unit.svc", UriKind.RelativeOrAbsolute)
                        Dim webServiceAddress As New EndpointAddress(webServiceURI)

                        ' Setup web Service proxy
                        Dim wsUnits As New DC.SL.Services.WebServiceUnit.UnitClient
                        wsUnits.Endpoint.Address = webServiceAddress

                        ' Add event handler so we can trap for web service completion
                        AddHandler wsUnits.LoadsCompleted, AddressOf LoadUnitsCompleted

                        ' Call web service to get Sites the user has access to
                        wsUnits.LoadsAsync(Me.Client, Me.Site.SiteID, Me.Size.SizeID, Me.RentalType.RentalTypeID, Me.UnitState)

                    End If

                End If

            End If

        End If

    Catch ex As Exception

        Dim Problem As New DC.SL.Tools.Errors(ex)

    End Try

End Sub

Private Sub LoadUnitsCompleted(ByVal sender As Object, ByVal e As DC.SL.Services.WebServiceUnit.LoadsCompletedEventArgs)

    Try

        If Not IsNothing(e.Result) Then

            Me.Units = e.Result

            If Me.Units.Count > 0 Then
                Me.Unit = Me.Units.Item(0)
            End If

        End If

    Catch ex As Exception

        Dim Problem As New DC.SL.Tools.Errors(ex)

    End Try

End Sub

仍然没有让这个工作...这是我现在的,但问题仍然是... UI线程执行继续,并不等待Web服务调用完成。

致电代码:

ReadUnitsAsync().Wait(3000)

以下是更新后的代码:

    Public Async Function ReadUnitsAsync() As Task(Of Boolean)

    Dim Results As Object = Await LoadReadUnitsAsync()

    Return True

End Function

Public Function LoadReadUnitsAsync() As Task(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))

    LoadReadUnitsAsync = Nothing

    Dim tcs = New TaskCompletionSource(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))

    ' Client is required
    If Not Me.Client Is Nothing Then

        ' User is required
        If Not Me.User Is Nothing Then

            ' Must be a real Client associated
            If Me.Client.ClientID > 0 Then

                ' Only get associated sites IF we don't have any defined
                If Not Me.Site Is Nothing Then

                    ' Call the web service relative to where this application is running
                    Dim webServiceURI As New Uri("../WebServices/Unit.svc", UriKind.RelativeOrAbsolute)
                    Dim webServiceAddress As New EndpointAddress(webServiceURI)

                    ' Setup Site web Service proxy
                    Dim wsUnits As New DC.SL.Services.WebServiceUnit.UnitClient
                    wsUnits.Endpoint.Address = webServiceAddress

                    ' Add event handler so we can trap for web service completion
                    AddHandler wsUnits.LoadUnitsCompleted, Sub(s, e)
                                                               If e.Error IsNot Nothing Then
                                                                   tcs.TrySetException(e.Error)
                                                               ElseIf e.Cancelled Then
                                                                   tcs.TrySetCanceled()
                                                               Else
                                                                   tcs.TrySetResult(e.Result)
                                                               End If
                                                           End Sub

                    '' Set Busy Status
                    'BusyStack.Manage(ProcessManager.StackAction.Add, "ReadUnits", Me.IsWorking, Me.IsWorkingMessage)

                    ' Call web service to get Sites the user has access to
                    wsUnits.LoadUnitsAsync(Me.Client, Me.Site.SiteID, Me.Size.SizeID, Me.RentalType.RentalTypeID, Me.UnitState)

                    Return tcs.Task

                End If

            End If

        End If

    End If

End Function

所以这里的最终代码(缩写)似乎符合我的目标(也就是等待Web服务在继续之前完成)。

Public Class UIUnits
Implements INotifyPropertyChanged, IDataErrorInfo

Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

Public Async Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)

    Dim propertyEventHandler As PropertyChangedEventHandler = PropertyChangedEvent

    Try

        If propertyEventHandler IsNot Nothing Then

            RaiseEvent PropertyChanged(Me, e)

            Select Case e.PropertyName

                Case "Size"

                    Await ReadUnitsAsync()

        DoSomethingElseAfterWebServiceCallCompletes()

                Case Else

            End Select

        End If

    Catch ex As Exception

        Dim problem As New DC.SL.Tools.Errors(ex)

    End Try

End Sub

...

Private _Units As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
Public Property Units() As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
    Get
        Return _Units
    End Get
    Set(ByVal value As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
        _Units = value
        OnPropertyChanged(New PropertyChangedEventArgs("Units"))
    End Set
End Property

...

Public Async Function ReadUnitsAsync() As Task(Of Boolean)

    Me.Units = Await LoadReadUnitsAsync()
    Return True

End Function

...

Public Function LoadReadUnitsAsync() As Task(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))

    LoadReadUnitsAsync = Nothing

    Dim tcs = New TaskCompletionSource(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))

    ' Client is required
    If Not Me.Client Is Nothing Then

        ' User is required
        If Not Me.User Is Nothing Then

            ' Must be a real Client associated
            If Me.Client.ClientID > 0 Then

                ' Only get associated sites IF we don't have any defined
                If Not Me.Site Is Nothing Then

                    ' Call the web service relative to where this application is running
                    Dim webServiceURI As New Uri("../WebServices/Unit.svc", UriKind.RelativeOrAbsolute)
                    Dim webServiceAddress As New EndpointAddress(webServiceURI)

                    ' Setup web Service proxy
                    Dim wsUnits As New DC.SL.Services.WebServiceUnit.UnitClient
                    wsUnits.Endpoint.Address = webServiceAddress

                    ' Add event handler so we can trap for web service completion
                    AddHandler wsUnits.LoadUnitsCompleted, Sub(s, e)
                                                               If e.Error IsNot Nothing Then
                                                                   tcs.TrySetException(e.Error)
                                                               ElseIf e.Cancelled Then
                                                                   tcs.TrySetCanceled()
                                                               Else
                                                                   tcs.TrySetResult(e.Result)
                                                               End If
                                                           End Sub

                    ' Call web service 
                    wsUnits.LoadUnitsAsync(Me.Client, Me.Site.SiteID, Me.Size.SizeID, Me.RentalType.RentalTypeID, Me.UnitState)

                    Return tcs.Task

                End If

            End If

        End If

    End If

End Function

3 个答案:

答案 0 :(得分:3)

在这种情况下,最简单的转换可能是最容易的。首先,您需要在服务上定义自己的TAP友好扩展方法。如果您正在进行桌面开发,VS将为您生成这些,但不幸的是,它不会为Silverlight执行此操作。

MSDN文档描述how to wrap EAPEAP是使用*Async方法匹配*Completed事件的模式。如果你有APM方法,wrap those into TAP更容易(APM是使用Begin* / End*方法对的模式。

如果您有一个包装器,例如LoadUnitsTaskAsync,请更改您的方法,而不是LoadUnitsAsyncAwait调用结果。这将要求您的ReadUnits方法为Async,因此请将其更改为Task - 返回Function(并将其名称从ReadUnits更改为{{1} }})。接下来更改ReadUnitsAsync的所有来电者,以便ReadUnitsAsync显示其结果。重复,直到到达实际的事件处理程序,可能是Await(不要对任何中间方法使用Async Sub;请改用Async Sub

答案 1 :(得分:2)

我使用this库在我的Silverlight 5项目中实现异步等待

答案 2 :(得分:0)

这个答案演示了that answer中描述的技术。

为简单起见,我们假设我们有一些WCF服务,其中包含以下合同:

<ServiceContract>
Public Interface ISomeService
    <OperationContract>
    Function DoWork(input As String) As String
End Interface
  1. 确保使用Visual Studio 2012或更高版本。 (早期版本不支持Async / Await语言关键字。)

  2. 转到Silverlight应用程序项目,让Visual Studio通过添加服务引用... 对话框为服务创建代理类型。 (以下假设您为代理类型选择了SomeServiceReference命名空间。)

  3. 验证Visual Studio是否在服务代理类型Begin…End…中为DoWork创建了一对ISomeService / SomeServiceClient方法;即应该有BeginDoWork方法和EndDoWork方法。

  4. 确保已将 Microsoft.Bcl.Async NuGet包安装到Silverlight应用程序项目中。如果您还没有这样做,请将NuGet包管理器Microsoft.Bcl.Async安装到您的Silverlight应用程序项目中。

  5. 创建一个使用DoWorkTaskAsyncTask.Factory.FromAsync / Begin…方法对转换为End…的扩展方法Task<string>。将扩展方法类放在服务代理类型所在的同一名称空间中:

    Namespace SomeServiceReference
        Friend Module SomeServiceExtensions
            Public Function DoWorkTaskAsync(someService As ISomeService,
                                            input As String) As Task(Of String)
                Return Task.Factory.FromAsync(Of String, String)(
                           beginMethod:=AddressOf someService.BeginDoWork,
                           endMethod:=AddressOf someService.EndDoWork, 
                           arg1:=input, 
                           state:=Nothing)
            End Function
        End Module
    End Namespace
    
  6. 你已经完成了。您可以将Async / Await与您的WCF服务一起使用:

    Async Function Foo() As Task
        …
        Dim someService As New SomeServiceClient()
        Dim output As String = Await someService.DoWorkTaskAsync("input")
        …
    End Function
    

    您需要为每个服务方法执行此操作(创建扩展/包装方法)。