0

I have a class:

public class User
{
    public string id, name, email, image;

    public User (IFBGraphUser user)
    {
        id = user.GetId ();
        name = user.GetName ();
        GetEmail ();
    }


    private void GetEmail()
    {
        FBRequestConnection.StartWithGraphPath ("/me", null, "GET", ConnectionReturn);
    }

    private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
    {
        var me = (FBGraphObject)result;
        Console.WriteLine("this is a test");
        this.email = me["email"].ToString();
    }
}

With a async method: StartWithGraphPath

When the constructor is called I want to wait for StartWithGraphPath to finish before GetEmail returns.

How Can I accomplish this?

StartWithGraphPath does not return an IAsyncResult so I can't use AsyncWaitHandle.

Edit

When the code is called:

User u = new User(user);
Console.WriteLine("hello");

My application output:

hello
this is a test

Which is what leads me to believe StartWithGraphPath is being called async. Is there another explanation?

Whats odd is there is also a method called StartWithGraphPathAsync So wouldn't this one Im using be synchronous by deduction? It has a synchronous feel while in debugger but not when simply running the app

Deekor
  • 9,144
  • 16
  • 69
  • 121

2 Answers2

2

I wouldn't wait for it to complete in the constructor. Make the asynchrony visible (in a friendlier form) to the consuming code, and it can choose when or if to actually perform a wait for the email value:

public class User
{
    public string id, name, image;

    public User (IFBGraphUser user)
    {
        id = user.GetId ();
        name = user.GetName ();
        GetEmail ();
    }

    public Task<string> Email {
      get{
        return emailTask.Task;
      }
    }

    private TaskCompletionSource<string> emailTask =
         new TaskCompletionSource<string>();

    private void GetEmail()
    {
        FBRequestConnection.StartWithGraphPath ("/me", null, "GET", ConnectionReturn);
    }

    private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
    {
        var me = (FBGraphObject)result;
        Console.WriteLine("this is a test");
        emailTask.SetResult(me["email"].ToString());
    }

}
Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • I just edited my question a bit.. Not sure if StartWithGraphPath is really async or not now – Deekor Aug 06 '14 at 09:18
-1

A quick solution would be to use a ManualResetEvent or ManualResetEventSlim, wait for it in the constructor and set it in the callback method:

private readonly ManualResetEventSlim myEvent;

public User(IFBGraphUser user)
{
    myEvent = new ManualResetEventSlim();
    id = user.GetId();
    name = user.GetName();
    GetEmail();

    myEvent.Wait();
}

private void ConnectionReturn(FBRequestConnection connection, NSObject result, NSError error)
{
    var me = (FBGraphObject)result;
    Console.WriteLine("this is a test");
    this.email = me["email"].ToString();

    myEvent.Set();
}

Please be aware that this solution only works if the StartWithGraphPath method is not using the thread that was used to call it to invoke the callback. If it does - e.g. classes close to the user interface often will execute callbacks on the UI thread - then a deadlock will occur. (Thanks to @L.B for pointing this out)

If you can modify your class design I would suggest removing the expensive call from the constructor. Constructing an object should usually be cheap, and using an asynchronous method in it suggests that it might take some time.

Dirk
  • 10,668
  • 2
  • 35
  • 49
  • I just edited my question a bit.. Not sure if `StartWithGraphPath` is really async or not now based off trying this answer. – Deekor Aug 06 '14 at 09:12
  • @Deekor I just don't see why this would lock up the code forver, unless the call to Set is never executed. – Dirk Aug 06 '14 at 09:16
  • any idea as to why my output is the way it is then? – Deekor Aug 06 '14 at 09:17
  • @Deekor The call to StartWithGraphPath appears to be asynchronous. Why don't you simply use a debugger and set a breakpoint in the callback and constructor at the Wait and Set calls to see what happens. – Dirk Aug 06 '14 at 09:20
  • it hits the wait. I click resume and it never hits the set. – Deekor Aug 06 '14 at 09:24
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/58761/discussion-between-dirk-and-deekor). – Dirk Aug 06 '14 at 09:25
  • @Dirk you block the UI thread. `StartWithGraphPath` might be trying to call the callback function in UI context. Since it is blocked, callback will never be executed. A classical deadlock problem... – L.B Aug 06 '14 at 09:25
  • @L.B True, this could happen as I don't know that API at all. Thanks for pointing this out and I'll add some information about it in my answer. – Dirk Aug 06 '14 at 09:28