我正在尝试调用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
答案 0 :(得分:3)
在这种情况下,最简单的转换可能是最容易的。首先,您需要在服务上定义自己的TAP友好扩展方法。如果您正在进行桌面开发,VS将为您生成这些,但不幸的是,它不会为Silverlight执行此操作。
MSDN文档描述how to wrap EAP(EAP是使用*Async
方法匹配*Completed
事件的模式。如果你有APM方法,wrap those into TAP更容易(APM是使用Begin*
/ End*
方法对的模式。
如果您有一个包装器,例如LoadUnitsTaskAsync
,请更改您的方法,而不是LoadUnitsAsync
和Await
调用结果。这将要求您的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
确保使用Visual Studio 2012或更高版本。 (早期版本不支持Async
/ Await
语言关键字。)
转到Silverlight应用程序项目,让Visual Studio通过添加服务引用... 对话框为服务创建代理类型。 (以下假设您为代理类型选择了SomeServiceReference
命名空间。)
验证Visual Studio是否在服务代理类型Begin…
,End…
中为DoWork
创建了一对ISomeService
/ SomeServiceClient
方法;即应该有BeginDoWork
方法和EndDoWork
方法。
确保已将 Microsoft.Bcl.Async NuGet包安装到Silverlight应用程序项目中。如果您还没有这样做,请将NuGet包管理器Microsoft.Bcl.Async
安装到您的Silverlight应用程序项目中。
创建一个使用DoWorkTaskAsync
将Task.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
你已经完成了。您可以将Async
/ Await
与您的WCF服务一起使用:
Async Function Foo() As Task
…
Dim someService As New SomeServiceClient()
Dim output As String = Await someService.DoWorkTaskAsync("input")
…
End Function
您需要为每个服务方法执行此操作(创建扩展/包装方法)。