0

I am trying to clean up my code and put things in classes (something I should have done from the start, I know). But I am running into a problem with an async Task.

I have the following async method, which was originally in the same class as the call to that method, i.e no instantiation needed, I just called the method, it ran asynchronously, and all worked fine.

However, I have now moved this async method to a new class, where I intend to put all methods relating to the database. So, now I create an instance of the new class and call the method, but it seems to get hung up and not go anywhere.

Here is the new class with the asnyc method:

public class ParseDBQuery
{
    public string DBcompanyName { get; set; }
    public string DBofferTitle { get; set; }
    public string DBlatitude { get; set; }
    public string DBlongitude { get; set; }
    public string DBofferDescription { get; set; }
    public string DBlogoURL { get; set; }

    public async Task getUserCoupons()
    {
        try{

            var query = ParseObject.GetQuery("userCoupons").WhereEqualTo("userObjectID", ParseUser.CurrentUser).Include("couponsObjectID");

            IEnumerable<ParseObject> MyResults =  await query.FindAsync();

            foreach (var result in MyResults)
            {
                var couponObject = result.Get<ParseObject>("couponsObjectID");

                DBcompanyName = couponObject.Get<string>("entityName");
                Console.WriteLine ("The company name is......... " + DBcompanyName);

                DBofferTitle = couponObject.Get<string>("offerTitle");
                Console.WriteLine ("The offer title is......... " + DBofferTitle);

                DBofferDescription = couponObject.Get<string>("offerDescription");
                Console.WriteLine ("The offer title is......... " + DBofferDescription);

                DBlogoURL = couponObject.Get<string>("logoURL");
                Console.WriteLine ("The logo URL is......... " + DBlogoURL);

                DBlatitude = couponObject.Get<string>("latitude");
                Console.WriteLine ("The latitude is......... " + DBlatitude);

                DBlongitude = couponObject.Get<string>("longitude");
                Console.WriteLine ("The longitude is......... " + DBlongitude);

            }

        }
        catch (ParseException e) {
            Console.WriteLine ("There was a problem fetching getUserCoupon data from parse - ParseDBQuery class");

        }

    }
}

Here is the instantiation and call to the method:

ParseDBQuery myQuery = new ParseDBQuery ();
Task myTask = myQuery.getUserCoupons ();
//myTask.Wait ();

Here you can see i've tried the Wait() method, and this is where it stops. If i remove the Wait() method, the code carries on before getting values from the async method (obviously because of await). The problem seems to be in the new class where the method now lives. If I put a breakpoint on one of the queries such as

DBcompanyName = couponObject.Get<string>("entityName");

... it never hits the breakpoint. So the problem lies here somewhere but I don't know why.

The odd thing is, if i just put the whole method back into the same class as the method call, without instantiation, it works perfectly! Any ideas?

Cheesebaron
  • 24,131
  • 15
  • 66
  • 118
Can'tCodeWon'tCode
  • 543
  • 1
  • 11
  • 36
  • 1
    Carefree mixing of sync & async leads to deadlocks. Go `async` all the way down or use `ConfigureAwait(false)` down the chain so that the continuation does not have to resume on the 'main' thread, or preferably: both, when applicable. – Patryk Ćwiek Sep 18 '14 at 13:16
  • As far as i'm aware, using async will return control back to the main thread and carry on. This causes me problems as i rely on the data returned in order for the code to continue. As far as i know, using wait, will actually halt the program until the async task has finished - which is what i need to able to do. So what are my options here? The DB qury needs the async method, so therefore i need to use 'await'. But then i also need the program to stop until this method finishes?!! Help! :-/ – Can'tCodeWon'tCode Sep 18 '14 at 13:47
  • The most natural solution is to use `await`. What exactly do you mean "i also need the program to stop"? – Stephen Cleary Sep 18 '14 at 14:06
  • using await (as far as i'm aware) means that the query is done in the background while the main thread continues. However, i need the result from the query before the program can continue, so i need it to wait for the query to finish. However, the Parse query i'm doing must be done on an async task. But none of this really explains why it works when the code is in the Main class, but when i move it out into its own class it doesn't. Also @StephenCleary I don't think this is a duplicate question. I have removed the 'wait' entirely and the code still doesn't execute properly. – Can'tCodeWon'tCode Sep 18 '14 at 14:13
  • @user3753146: I have reopened the question and will post an answer shortly. – Stephen Cleary Sep 18 '14 at 14:42

1 Answers1

1

This is the common deadlock situation that I describe on my blog.

When an async method awaits a task, by default the await will capture a "current context" and use that to resume the async method. In this case, that context is the UI context, which is associated with a single UI thread. When your code blocks on the task (by calling Wait), it is blocking the UI thread. When the FindAsync task completes, the async getUserCoupons method attempts to resume on the UI thread, but it can't because the UI thread is blocked.

As an aside, it works when the code is in the Main class (when your code presumably called Wait on the task returned from FindAsync) because FindAsync doesn't resume on the captured context.

The ideal solution is to use await instead of Wait. This means that the method calling getUserCoupons must become async, and its caller should use await and become async, etc., etc. This growth of async is perfectly natural and should be embraced. Ideally, you want to go "async all the way"; I describe this concept more in an MSDN article.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • It doesn't seem to matter where or how many times i use 'await', the problem persists due to the fact that the methods are running async. And therefore by design, handing control back to the caller and continuing before it's returned the results i need. It seems that i absolutely need to be able to wait for the results before allowing the program to continue. I just don't know how to do that without blocking / locking the thread using 'wait'. – Can'tCodeWon'tCode Sep 18 '14 at 15:32
  • `await` will pause the current method until the task completes. Just use `await` all the way (and use `async Task` instead of `async void`) and it should work fine; what "problem" persists? – Stephen Cleary Sep 18 '14 at 15:45
  • Thanks for your patience Steven. I appreciate the help. From what i can see, await is not "pausing the current method" as you suggest. Instead of pausing it, it seems to in fact return control to the caller of the method and continue on, whilst the async task runs in the background. And to be honest i thought that was exactly what it was meant to do. I seem to have worked around it by having the async task return a bool value when it finishes, and in the method call, i just loop until the value is true. It seems dirty though and i'm sure not how it's meant to be done. – Can'tCodeWon'tCode Sep 18 '14 at 15:53
  • `await` will certainly pause the `async` method that it's in, and it will also return to its caller. If its caller uses `await`, then it also pauses and returns to *its* caller, etc. It's not clear to me at all what could be causing your problem; could you post a minimal repro in a new question? – Stephen Cleary Sep 18 '14 at 16:54