locked
Async With WCF In A Silverlight App RRS feed

  • Question

  • Hello,

      I am really impressed with the Async CTP - this should make things a lot easier.  I'm trying to figure out a way to use the Async CTP within a Silverlight App that calls a WCF service.

      Just to learn, I've created a simple WCF service that has 1 method that returns "Hello World"


    <ServiceContract()>
    Public Interface IService1

        <OperationContract()> _
        Function GetNameData() As String

    End Interface

    '---- Here is the implementation ----
    Public Class Service1
        Implements IService1

        Public Function GetNameData() As String Implements IService1.GetNameData
            Return "Hello World"
        End Function

    End Class

     

      From my Silverlight App - I've referenced the Async CTP library for Silverlight and attempted to call the WCF service from a button click.

    Private Async Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
      Dim proxy As New ServiceReference1.Service1Client()
      Dim sReturnString As String = Await proxy.GetNameDataAsync()
      Windows.MessageBox.Show(sReturnString)
    End Sub


    The problem is that the GetNameDataAsync function doesn't return a value from that method within Silverlight - it will if done within a Windows Form App for example.  Is there any way to make this work?  (This would make developing Silverlight apps that call WCF services about 50% easier to do).

    Thanks.

     

    Saturday, November 20, 2010 11:10 PM

Answers

  • Hi Ryan-

    The plan is for the WCF client proxy generator to automatically generate Task-based methods that you can call and await from Silverlight just as you want.  In fact, in the CTP, for full .NET Framework applications, there's a sample that augments the WCF client proxy generator to do this (look at the WCF sample included in the CTP for an example).  However, we didn't have time before the Async CTP was released to make that generator work with Silverlight as well.  The *Async method you're trying to use is likely the one the WCF client proxy generator currently outputs, which is conforming to the Event-based Async Pattern rather than to the Task-based Async Pattern.

    In the meantime, to get this working you can write your own small wrapper for the Begin/End methods generated on the client for your method, e.g.

    private static Task<string> GetNameDataTaskAsync(this Service1Client client)
    {
        var tcs = new TaskCompletionSource<string>();
        client.BeginGetNameData(iar =>
        {
            try { tcs.SetResult(client.EndGetNameData(iar)); }
            catch(Exception exc) { tcs.SetException(exc); }
        }, null);
        return tcs.Task;
    }

    and then call this GetNameDataTaskAsync extension method instead of the one you're currently trying to use.

    I hope that helps.

    Sunday, November 21, 2010 10:28 PM
    Moderator

All replies

  • Hi Ryan-

    The plan is for the WCF client proxy generator to automatically generate Task-based methods that you can call and await from Silverlight just as you want.  In fact, in the CTP, for full .NET Framework applications, there's a sample that augments the WCF client proxy generator to do this (look at the WCF sample included in the CTP for an example).  However, we didn't have time before the Async CTP was released to make that generator work with Silverlight as well.  The *Async method you're trying to use is likely the one the WCF client proxy generator currently outputs, which is conforming to the Event-based Async Pattern rather than to the Task-based Async Pattern.

    In the meantime, to get this working you can write your own small wrapper for the Begin/End methods generated on the client for your method, e.g.

    private static Task<string> GetNameDataTaskAsync(this Service1Client client)
    {
        var tcs = new TaskCompletionSource<string>();
        client.BeginGetNameData(iar =>
        {
            try { tcs.SetResult(client.EndGetNameData(iar)); }
            catch(Exception exc) { tcs.SetException(exc); }
        }, null);
        return tcs.Task;
    }

    and then call this GetNameDataTaskAsync extension method instead of the one you're currently trying to use.

    I hope that helps.

    Sunday, November 21, 2010 10:28 PM
    Moderator
  • Thanks Stephen - yeah this should work - I'll give it a shot - I understand what you mean though.  This should make calling WCF services so much easier from Silverlight - especially to keep code in line and to be able to port other code.

    Thanks so much.

    Sunday, November 21, 2010 11:22 PM
  • If anyone else needs this in VB - I converted Stephen's example to VB.NET.  This works for me.

        Private Function GetNameDataTaskAsync(ByVal client As ServiceReference1.Service1Client) As System.Threading.Tasks.Task(Of String)
            Dim tcs = New System.Threading.Tasks.TaskCompletionSource(Of String)()

            client.ServiceReference1_IService1_BeginGetNameData(Function(iar)
                                                                    Try
                                                                        tcs.TrySetResult(client.ServiceReference1_IService1_EndGetNameData(iar))
                                                                    Catch exc As Exception
                                                                        tcs.TrySetException(exc)
                                                                    End Try
                                                                End Function, Nothing)
            Return tcs.Task
        End Function

    Monday, November 22, 2010 12:49 PM
  • Does this code really work?

    I have tried every permutation of this in my own application and can't get it to work. Please see my thread here:

    http://social.msdn.microsoft.com/Forums/en/async/thread/c4787a55-247f-4880-86a2-b251ceebb301

    This is my code (which is modeled after the above as closely as possible):

    public static Task<Run> DoRunAsync( AssetValuationUtilityServiceClient client, AssetValuationSchema schema, string transactionId, int startRecord, int maxRecords, DateTime periodStartDate, DateTime periodEndDate)
    {
      var tcs = new TaskCompletionSource<Run>();
      client.BeginDoRun(schema, transactionId, startRecord, maxRecords, periodStartDate, periodEndDate, callbackResult =>
      {
        try
        {
          tcs.TrySetResult(client.EndDoRun(callbackResult));
        }
        catch (Exception exc)
        {
          tcs.TrySetException(exc);
        }
      }, null);
      return tcs.Task;
    }
    

    It just hangs.

    Wednesday, February 2, 2011 4:26 AM
  • Is anyone maintaining these threads? I really need to get this code working but nobody is replying.
    Friday, February 18, 2011 4:23 AM
  • Hi Stephen,

     

    In silverlight WCF generated proxy, we have public XxxxCompleted event and public XxxxAsync method (EAP) and private BeginXxxx and EndXxxx methods (APM).

    Do you know why APM methods are private?

    APM is much more Task aware  thanks to TaskFactory.FromAsync.

    Monday, May 2, 2011 5:01 PM
  • Hi StoneFactory-

    An Event-based implementation can be wrapped with code something like the following:

    public static Task<string> GetNameDataTaskAsync(this Client client)
    {
        var tcs = new TaskCompletionSource<string>();
        
        GetNameDataCompletedEventHandler handler = null;
        handler = (s, e) =>
        {
            if (e.UserState == tcs)
            {
                client.Completed -= handler;
                if (e.Error != null) tcs.TrySetException(e.Error);
                else if (e.Cancelled) tcs.TrySetCanceled();
                else tcs.TrySetResult(e.Result);
            }
        };
        client.Completed += handler;

        try { client.GetNameDataAsync(tcs); }
        catch
        {
            client.Completed -= handler;
            throw;
        }

        return tcs.Task;
    }

     

     

     

    Tuesday, May 3, 2011 1:18 AM
    Moderator