Using Rx to consume a Task based WCF service
Among the many changes that Dev 11 brings is the new default when adding a service reference to generate Task based proxy methods rather than using the APM flavor (using the BeginXXX – EndXXX model). In this post, we’ll look at creating a simple service and then consuming it using the Reactive Extensions. Let’s start by defining the service interface and implementation:
Imports System.ServiceModel Imports System.Threading <ServiceContract()> Public Interface ISimpleServicesvc <OperationContract()> Function DoSomethingCool(input As String) As String End Interface Public Class SimpleServicesvc Implements ISimpleServicesvc Public Function DoSomethingCool(input As String) As String Implements ISimpleServicesvc.DoSomethingCool Return (String.Join("", From letter In input.ToCharArray() Order By letter Distinct)) End Function End Class
Essentially here we are just taking a string input and returning the distinct characters sorted. The details of the service in this case are trivial. Our focus here is how to implement the service client. We start by adding a service reference in our client application by right clicking on the project and selecting Add Service Reference. (Alternatively, you can now press Ctrl-Q and request to “Add Service Reference” from there. From the dialog, you can still use the “Discover” button to locate the service as long as it is in your solution.
One thing to note is that the proxy classes are now by default generated using Task based methods rather than the previous IAsyncResult AMP method.
As a result, the definition of the proxy class is as follows:
Public Function DoSomethingCoolAsync(ByVal input As String) As System.Threading.Tasks.Task(Of String) Implements SimpleService.ISimpleServicesvc.DoSomethingCoolAsync Return MyBase.Channel.DoSomethingCoolAsync(input) End Function
If we wanted to consume this using the new Async/Await, we could do it as follows:
Private Async Sub SubmitClicked() Handles SubmitButton.Click Dim svc = New SimpleService.SimpleServicesvcClient() Dim req = Await svc.DoSomethingCoolAsync(InputText.Text) OutputText.Text = req End Sub
Of course, to put the LINQ spin on this, let’s see the Rx version to do the same thing:
Private Async Sub SubmitClicked() Handles SubmitButton.Click Dim svc = New SimpleService.SimpleServicesvcClient() Dim req = svc.DoSomethingCoolAsync(InputText.Text).ToObservable() req.ObserveOnDispatcher().Subscribe(Sub(val) OutputText.Text = val) End Sub
We start by turning the Task into an Observable producer using the ToObservable extension method. We then subscribe to the observable making sure to return back to the dispatcher thread because the task based service is run on a taskpool thread. Of course in this case, we are subscribing on every button click. With Rx, we could wire the button click and service request up on form navigate and unwire it when navigating from the form as follows:
Private requestDisposable As IDisposable Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) Dim svc = New SimpleService.SimpleServicesvcClient requestDisposable = (From click In Observable.FromEventPattern(Of RoutedEventArgs)(SubmitButton, "Click") From req In svc.DoSomethingCoolAsync(InputText.Text).ToObservable() Select req). ObserveOnDispatcher(). Subscribe(Sub(val) OutputText.Text = val) End Sub Protected Overrides Sub OnNavigatedFrom(e As Navigation.NavigationEventArgs) MyBase.OnNavigatedFrom(e) requestDisposable.Dispose() requestDisposable = Nothing End Sub