2

I call a WCF and the Completed event of method is call after the awaiting method.

   public void my_method()
    {
        Task task = Task.Run(async () => await DoWorkAsync());       
        task.Wait();            
    }

    public async Task DoWorkAsync()
    {
        await Task.Run(() =>
         {
             wcf1.ServiceAnnonces s = new wcf1.ServiceAnnonces();
             s.DoWorkCompleted += S_DoWorkCompleted;
             s.DoWorkAsync();
         });
    }

    private string retour;
    public string Retour { get => retour; set => retour = value; }

    private void S_DoWorkCompleted(object sender, wcf1.DoWorkCompletedEventArgs e)
    { 
            Retour = e.Result;   
    }

I call method with this code :

    Droid.Resources.ISvAnnoncesSoap s = new Droid.Resources.ISvAnnoncesSoap();
        s.ma_methode();

        LbResulat.Text = s.Retour;

My service :

 [ServiceContract]
    public interface IServiceAnnonces
    {
        [OperationContract]
        string DoWork();
    }

  public class ServiceAnnonces : IServiceAnnonces
    {

        public string DoWork()
        {
            return "coucou";
        }
    }
}

But le Completed event is throw after LbResulat.Text = s.Retour;

Thank for your help.

EDIT :

I delete some line code. My code now :

  public void ma_methode()
    {
        wcf1.ServiceAnnonces s = new wcf1.ServiceAnnonces();
        s.DoWorkCompleted += S_DoWorkCompleted;
        s.DoWorkAsync();         
    }

    private string retour;
    public string Retour { get => retour; set => retour = value; }

    private void S_DoWorkCompleted(object sender, wcf1.DoWorkCompletedEventArgs e)
    { 
            Retour = e.Result;   
    }

And to call directly :

 Droid.Resources.ISvAnnoncesSoap s = new Droid.Resources.ISvAnnoncesSoap();
            s.ma_methode();

        LbResulat.Text = s.Retour;

But same, But the Completed event is throw after LbResulat.Text = s.Retour; So my Retour is null.

  • At a guess, `s.DoWorkAsync()` is also async, and should be awaited. I'm pretty sure you don't need all those `Task.Run's` though - [async doesn't need Threads](https://blog.stephencleary.com/2013/11/there-is-no-thread.html) Can you provide the code, or the method signature for `ServiceAnnonces.DoWorkAsync();` – StuartLC Feb 03 '18 at 13:21
  • Thank i edit my post – Guillaume Durupt Feb 03 '18 at 13:33

1 Answers1

1

Assuming DoWorkAsync() and DoWorkCompleted are the generated client proxies for your WCF service contract, then the "issue" is that you aren't blocking / awaiting for the completion of the DoWorkCompleted callback - you are invoking DoWorkAsync() and then your code continues. At some point later, the WCF call completes and the callback is invoked (this is how the asynchronous client method proxy pairs in WCF were intended to work - they predate .Net 4.5's await).

You can 'resynchronize' the code, e.g. using a TaskCompletionSource to allow your code to wait for the completion of the callback and signal that the Task has completed:

public async Task DoWorkAsync()
{
     var tcsWorkDone = new TaskCompletionSource<bool>();
     wcf1.ServiceAnnonces s = new wcf1.ServiceAnnonces();
     s.DoWorkCompleted += (sender, e) =>
     {
        S_DoWorkCompleted(sender, e);
        tcsWorkDone.SetResult(true);
     };
     s.DoWorkAsync();
     await tcsWorkDone.Task;
 }

(Again, if there isn't any work done after the callback, you could also drop the async, and just return the Task from the method for the caller to wait on)

It could be pointed out that this is all rather silly - we're re-synchronizing the asynchronous WCF client proxy.

Instead, you could fall back to the synchronous client WCF proxy (which would generate a synchronous method, DoWork() which you can bind to your LbResulat.Text (WPF / WinForms / Web Forms Label?).

Better still, you could do the binding asynchronously in the callback

private void S_DoWorkCompleted(object sender, wcf1.DoWorkCompletedEventArgs e)
{ 
   Retour = e.Result;
   // TODO - Use this to trigger code to set LbResulat.Text = s.Retour;
}

Note however that the callback can be on a different thread, so you may need appropriate (re)invoke code to ensure that any UI artifact like a Label / TextBox is updated on the UI thread.

Re: Don't use Task.Run

Preferable to using Task.Run would be to

public async Task my_method()
{
    return await DoWorkAsync();
}

Or if there's no other work, just return the task for the caller to await:

public Task my_method()
{
    return DoWorkAsync();
}

But if you really can't make my_method async, then instead of spinning up a second thread with Task.Run and then blocking on the the first, then you can synchronize with:

public void my_method()
{
    DoWorkAsync()
        .GetAwaiter()
        .GetResult();
}
StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • 1
    Woh ! Thank a lot for explanation, i try to understand and i tell you if it's ok. – Guillaume Durupt Feb 03 '18 at 14:18
  • I haven't used WCF for ages, but it seems that you can now generate a [net 4.5 async / await Task based asyncronous proxy](https://stackoverflow.com/a/13778263) - that would probably be the proper async way to approach your code. – StuartLC Feb 03 '18 at 14:22