0

I have an asynchronous method that consumes a Web API action. It appears to be stuck in a loop. My reasoning for this is because if I put a break point on line 1 of the catch block, and step into, it never actually hits the second line.

I was initially returning a data set with 100,000+ (~30mb) rows and thought that it could just be slow due to the size of the request, but after changing my Web API action to return only 1 row the problem still persisted. The data is definitely being returned as when I browse to the URI of the Web API solution I'm getting JSON returned in my browser.

public async Task<IEnumerable<Document>> GetAll()
{
 try
 {
 var response = await _client.GetAsync(
 string.Format("{0}/api/document", _baseURI));
 // never hits line below
 return await response.Content.ReadAsAsync<Document>() 
 as IEnumerable<Document>;
 }
 catch (Exception ex)
 {
 // handle exception
 }
}

I'm not sure if I'm missing something here? Some help would be appreciated.

EDIT 1

In response to some questions, I have a Web API project that's referenced by a MVC project. I've had to make some changes from original question for JSON deserialization.

Repository:

public async Task<IEnumerable<Document>> GetAll()
{
 try
 {
 string json;
 var response = await _client.GetAsync(string.Format(
 "{0}/api/document", _baseURI)).ConfigureAwait(false);
 var resource = await response.Content.ReadAsAsync<Document>();
 using(var reader = new StreamReader(resource.ToString()))
 {
 json = reader.ReadToEndAsync().ToString();
 }
 return JsonConvert.DeserializeObjectAsync<Document>(json) 
 as IEnumerable<Document>;
 }
 catch (Exception)
 {
 throw;
 }
 }

Controller:

public async Task<ActionResult> GetAll()
{
 return PartialView("_GetAllDocumentsPartial", await _docRepo.GetAll());
}

With changes described in answers below, the same problem still occurs when debugging as above. However I get "A task was canceled." exception on the catch in the method in the repository.

Stack trace.

asked Jul 12, 2013 at 15:47
8
  • 3
    Never write throw ex. stackoverflow.com/a/2999314/34397 Commented Jul 12, 2013 at 15:49
  • 1
    Are you sure its never hitting the second line? there are issues with async and debugging... you might try putting a break point on the second line as well just to be sure. Commented Jul 12, 2013 at 15:50
  • Yes, just placed a break point there and it doesn't hit it. Chrome just continues to load/page is blank. Commented Jul 12, 2013 at 15:52
  • To further solidify your assertion about the last sentence before the example, have you tried using fiddler to make sure that the the data is being returned to your actual application? That is, assuming you're running this on a machine where you can get fiddler in between the two... Commented Jul 12, 2013 at 15:55
  • There's no JSON being returned in Fiddler. The GET never actually completes. The service is definitely available though as browsing to the URI directly returns the JSON. Commented Jul 12, 2013 at 16:05

3 Answers 3

5

Are you calling GetAll().Result from an ASP.NET application? Since you have tagged MVC to this question, I assume so. If you do, then you are deadlocking yourself.

Say you calling web API like this.

public ActionResult Index()
{
 var result = GetAll().Result;
 return View();
}

The problem here is when the async call completes, it needs to continue on the original thread where it jumped out but you are blocking that thread by calling Result(). Call to Result blocks and waits for async call to return and the async continuation waits for the ASP.NET thread to be available to continue. That is a dead lock.

Change your GetAll() like this.

public async Task<IEnumerable<Document>> GetAll()
{
 try
 {
 var response = await _client.GetAsync(
 string.Format("{0}/api/document", _baseURI))
 .ConfigureAwait(false);
 // never hits line below
 return await response.Content.ReadAsAsync<Document>() 
 as IEnumerable<Document>;
 }
 catch (Exception ex)
 {
 // handle exception
 }
}

This indicates the context need not be preserved when the async call is complete and hence the continuation happens in a thread pool thread (not ASP.NET).

Better yet change the action method to public async Task<ActionResult> Index() and await GetAll().

If you find this answer helpful, you must only thank Stephen Cleary.

answered Jul 12, 2013 at 16:48
Sign up to request clarification or add additional context in comments.

3 Comments

Apologies in delay in replying. I never actually did as you described in your answer ie var result = GetAll().Result;. Please see updated question.
When I add .ConfigureAwait(false) and debug it steps onto the next line, but, with that in place respsonse doesn't contain a definition for content ie I can't go respsonse.content.... How can I access the Result of the response when I use .ConfigureAwait(false)?
Something like var x = await response.GetAwaiter().GetResult()... works. Some other minor issues stopping me getting to where I want to be, maybe for another question though.
1

Are you working in a external library? If so try adding ConfigureAwait(false);

var response = await _client.GetAsync(string.Format("{0}/api/document", _baseURI))
 .ConfigureAwait(false);

Its to do with telling await not to capture the current context. Calling ConfigureAwait will ensure the rest of the method is executed using the ThreadPool. Some good reading on the subject can be found here on Stephen Cleary's blog.

answered Jul 12, 2013 at 16:48

1 Comment

Yes I have an MVC project that consumes a Web API project. I've made changes as per your answer. Please see updated question.
0

If it works with a web browser but not using code then you probably need to send an accept header. Add the following line

_client.DefaultRequestHeaders.Accept = new MediaTypeWithQualityHeaderValue("application/json");
answered Jul 12, 2013 at 17:18

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.