0

My searching hasn't been very fruitful with my exact problem; I can't seem to get asynchronous REST calls to work properly in my Xamarin Forms app. Take this example:

public async Task<GPIOFunctions> GetGPIOFunction(int gpioNumber)
{
    var response = await _client.GetAsync(GetFullUrl($"/GPIO/{gpioNumber}/function"));
    response.EnsureSuccessStatusCode();
    return GetFunctionFromString(await response.Content.ReadAsStringAsync());
}

My unit tests call this function, and always work fine. When my app calls it however, it never returns from the first await. If I add a .GetAwaiter().GetResult() to my GetAsync, then it returns, and even properly executes the last ReadAsStringAsync() properly, returns to the caller, and everything continues.

Now I can get my GetAsync() to work if I add a .ConfigureAwait(false) to the end, but then the second await never completes, and adding .ConfigureAwait(false) to that does not rectify the issue.

I have enabled breaking on all exceptions, have watched the debug output, but neither have given any additional information. Additionally I've debugged the app with Fiddler running and never seen the request being made, so I don't think it's an issue of my emulator/device not being able to access the API (and yes, the INTERNET permission is enabled in my manifest).

Is this a known limitation of Xamarin Forms?

Edit:

function call hierarchy below, starting in my view model's constructor:

public MainViewModel()
{
    _client = new HttpEndpoint(Url, User, Pass);
    TriggerDoorCommand = new Command(async () => await ExecuteTriggerDoorCommand());
    _currentDoorFunc = GetCurrentDoorFunction().Result;
}

private async Task<GPIOFunctions> GetCurrentDoorFunction()
{
    return await _client.GetGPIOFunction(DOOR_TOGGLE_PIN);
}

Additional info, the class HttpEndpoint is in a separate class library and wraps the HttpClient and all the API calls. The shared project and this class library are both .NET Standard 2.0.

helrich
  • 1,300
  • 1
  • 15
  • 34
  • 1
    Can you post how and where *GetGPIOFunction* is invoked – L.B Feb 01 '18 at 20:33
  • @L.B I've added that and some additional info on the structure of the projects. – helrich Feb 01 '18 at 20:43
  • 1
    Don't mix `async/await` with blocking calls like `.Result` which can lead to deadlocks. You can create an event and event handler as a work around – Nkosi Feb 01 '18 at 20:43

1 Answers1

2

Don't mix async/await with blocking calls like .Result which can lead to deadlocks.

You can create an event and event handler as a workaround

public MainViewModel() {
    _client = new HttpEndpoint(Url, User, Pass);
    TriggerDoorCommand = new Command(async () => await ExecuteTriggerDoorCommand());
    //Subscribe to event
    GetData += GetDataHandler;
    //Raise event
    GetData(this, EventArgs.Empty);
}

private event EventHandler GetData = delegate { }; 

private async void GetDataHandler(object sender, EventArgs args) {
    _currentDoorFunc = await GetCurrentDoorFunction();
}

private async Task<GPIOFunctions> GetCurrentDoorFunction() {
    return await _client.GetGPIOFunction(DOOR_TOGGLE_PIN);
}

Reference Async/Await - Best Practices in Asynchronous Programming

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • That works. I did just submit an edit request to clean up a couple syntax things but this got me pointed in the right direction. I was hung up on the one call failing with the other working, but I've decided to learn Xamarin as well as async/await at the same time so I got some wires crossed :) – helrich Feb 01 '18 at 21:10
  • The provided link should provide some insight with the async await. – Nkosi Feb 01 '18 at 21:12