Calling Flurl from winforms app never returns a result

944 Views Asked by At

Quick background. Used Flurl inside a class library I built to simplify my code for communicating with a cloud storage api. Works beautifully when calling the library from a console app used to test all of the methods. When attempting to use the exact same class library with a simple winform, the same method that returns very quickly using the console app now seems to never return a result. When debugging, the code below gets to the ".GetAsync()" line and then never returns a result and also prevents the debug session from continuing. No error message is ever thrown.

I found a comment on the Flurl site that someone seemed to be having this same issue but, it doesn't seem like they posted the question here as was recommended. Anything that could point me in the right direction would be greatly appreciated.

Flurl code wrapped in async method

public async Task<AccountInfo> Authorize()
    {
        string credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(Utils.ToNonSecureString(accountId) + ":" + Utils.ToNonSecureString(applicationKey)));
        var result = await B2UrlType.Authorize
            .WithHeader("Authorization", "Basic " + credentials)
            .GetAsync()
            .ReceiveJson<AccountInfo>();
        return result;
    }

Console app calling code that works perfectly

if (client == null)
        {
            var vault = new Vault();
            Console.WriteLine("Retrieving account keys");
            client = new Client(vault.GetAccountId(), vault.GetApiKey());
            Console.WriteLine("Successfully retrieved account keys");
            Console.WriteLine("Created new client");
            client.Authorize().GetAwaiter().GetResult();
        }

Winform calling code that does not return

private Client client;
    public MainWindow()
    {
        InitializeComponent();
        var vault = new Vault();
        client = new Client(vault.GetAccountId(), vault.GetApiKey());
        client.Authorize().GetAwaiter().GetResult();

    }
2

There are 2 best solutions below

1
On BEST ANSWER

Your original code hangs because you're blocking the UI thread with your call to GetResult(). This is not a Flurl-specific problem; this is async 101.

Your fix works because you're no longer blocking, but you're also not awaiting your call to Auth(), which is really the equivalent of just calling client.Authorize() without await or GetResult() directly from your MainWindow() constructor. You're no longer blocking, but you are fire-and-forgetting, meaning any exceptions that might occur in client.Authorize will go unobserved, causing bugs that are hard to track down.

Rule of thumb: Like with any async library, call Flurl's async methods from other async methods and await them whenever possible. In console apps, you have to block the main thread somewhere or the application will simply exit before tasks are complete. (I like to do it at the very top - define a MainAsync method containing all the work and call MainAsync().Wait() from Main.) But with WinForms apps, there's a good place to put async code where you never have to block or fire-and-forget: Event handlers.

I haven't done WinForms in a long while, but based on other answers it seems like a decent event for initialization code is Window.Load. Moving your authorization call there would be a good solution. Something like this:

private async void MainWindow_Load(object sender, System.EventArgs e)
{
    await client.Authorize();
}
0
On

This is what worked but I'm still not sure why...

    private Client client;
    public MainWindow()
    {
        InitializeComponent();
        var vault = new Vault();
        client = new Client(vault.GetAccountId(), vault.GetApiKey());
        Auth();

    }

    private async void Auth()
    {
        await client.Authorize();
    }

Wrapping the authorization call in an async method allowed the httpPost to complete and return results.