0

I'm currently seeing a problem whereby my await method is just hanging, and causing the response to just hang, not doing anything until I kill the request. This is evident in both Chrome debug tools and Fiddler.

I have the following API action defined:

[Route("state/{stateCode}")]
[LogApiCallFilter]
public async Task<IList<MapPlaceDTO>> GetWithinState(string stateCode)
{

    //    
    // Additional code truncated for SO
    // Via debugging I know that the 'state' variable below is correct
    //

    IList<Place> places = await _placeManager.GetPlacesInState(state);

    // Instantiate the list of places.
    IList<MapPlaceDTO> mapPlaces = new List<MapPlaceDTO>();

    // Iterate through the places and add to the map place list
    foreach (Place place in places)
    {
        mapPlaces.Add(MapPlaceDTO.FromPlace(place));
    }

    return mapPlaces;

}

When I step through that code in debug mode for a unit test for the GetWithinState action, the IList<Place> places = await _placeManager.GetPlacesInState(state); method runs without exception, however I am not able to hover over the places variable to inspect it, nothing happens. Nor can I add it to the watch list, I get the following message: error CS0103: The name 'places' does not exist in the current context

Interestingly however, if I run the exact same code within a "PlaceManager" unit test, outside of the Web API project, the test runs fine, and I can inspect the places variable.

[Fact(DisplayName = "Can_Get_All_Places_Within_State")]
[Trait("Category", "Place Manager")]
public async Task Can_Get_All_Places_Within_State()
{

    State state = new State()
    {
        ShortName = "VIC",
        Name = "Victora",
        CountryCode = "AU"
    };

    IList<Place> places = await _placeManager.GetPlacesInState(state);

    Assert.NotNull(places);
    Assert.True(places.Count > 0);

}

This is the code that runs within the PlaceManager.GetPlacesInState method:

public async Task<IList<Place>> GetPlacesInState(State state)
{
    if (state == null)
    {
        throw new ArgumentNullException("state", "The 'state' parameter cannot be null.");
    }

    // Build the cache key
    string cacheKey = String.Format("places_state_{0}", state.Id);

    // Get the places from the cache (if they exist)
    IList<Place> places = CacheManager.GetItem<IList<Place>>(cacheKey);

    // Get the places from the database.
    if (places == null)
    {
        // Get the places from the database
        places = await _repository.Find(i => i.State.ToLower() == state.ShortName.ToLower() && i.Country.ToLower() == state.CountryCode.ToLower());

        // If there are places, then add to the cache for next time
        if (places != null && places.Count > 0)
        {
            CacheManager.AddItem(cacheKey, places);
        }
    }

    // return the places
    return (places != null ? places : new List<Place>());
}

Does anyone have any idea why this may be occurring within the API method, but is working fine in unit tests?

svick
  • 236,525
  • 50
  • 385
  • 514
Juzzbott
  • 1,737
  • 2
  • 25
  • 44
  • 2
    Break when it hangs and inspect all threads. 93% that `Find` actually not real async method and deadlocks on some sort of `getDataAsync().Result;` code – Alexei Levenkov May 06 '15 at 23:58
  • That was exactly the cause. My Place object had a get only property that would get a place type from an async function in the following way: `get { return getPlaceType().Result; }`. I think I need to go and do some further reading on async/await and the .Result property. Cheers, Justin. – Juzzbott May 07 '15 at 04:26
  • Here is a starting point for reading - http://stackoverflow.com/questions/13140523/await-vs-task-wait-deadlock – Alexei Levenkov May 07 '15 at 04:47

1 Answers1

1

As mentioned by Alexei Levenkov above, I was causing a deadlock using the getDataAsync().Result code.

While mapping my Place object to a MapPlaceDTO object, the Place object has a get property to load the place type, which would call an async function in the following way:

public PlaceType PlaceType
{
    get
    {
        return getPlaceType().Result;
    }
}

Once I removed that property and just called the GetPlaceType method directly using the await keyword, everything started working correctly. Thanks Alexei!

svick
  • 236,525
  • 50
  • 385
  • 514
Juzzbott
  • 1,737
  • 2
  • 25
  • 44