hkguile hkguile - 4 years ago 89
C# Question

sharing class variable between async method and normal return method

here is my class, have a async method and get method

class Webservice
{
public string token;
public async void login (string url)
{
Console.WriteLine(url);
var client = new HttpClient();

// Create the HttpContent for the form to be posted.
string username = ConfigurationSettings.AppSettings["email"];
string password = ConfigurationSettings.AppSettings["password"];

var requestContent = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("email", username),
new KeyValuePair<string, string>("password", password),
});

// Get the response.
HttpResponseMessage response = await client.PostAsync(url, requestContent);

// Get the response content.
HttpContent responseContent = response.Content;

// Get the stream of the content.
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
// Write the output.
//Console.WriteLine(await reader.ReadToEndAsync());
token = await reader.ReadToEndAsync();

}
}


public string getToken (string url)
{
this.login(url);
Console.WriteLine(token);
return token+"abc";
}


the token = await reader.ReadToEndAsync(); can't set the class variable or maybe set after getToken was returned, anyone know how to deal with this situation?

Answer Source

By calling:

this.login(url);

You are firing and forgetting the async call.

You need to make the containing function async and await the login call to finish

public async Task<string> getToken (string url)
{
    await this.login(url);
    Console.WriteLine(token);
    return token+"abc";
}

Do not use this.login(url).Wait().

Finally

public async void login (string url)

async void is used for event handlers, it should be this:

public async Task login (string url)

IMHO

I believe this class has a too many responsibilities. It shouldn't be used to retrieve and store the token. One would assume you have some kind of caching layer in your application (it could just be memory).

Therefore, I would prefer logic like:

if (string.IsNullOrWhiteSpace(this.cache[TOKEN_KEY])) {
   this.cache[TOKEN_KEY] = await this.webservice.login(url);
}

// use the this.cache[TOKEN_KEY] here...
await this.anotherService.MakeRequest(this.cache[TOKEN_KEY]);

The cache could just be a singleton class with a dictionary...

The new Task<string> login(string url) method would now return the token at the bottom, rather than just setting a private field:

return await responseContent.ReadAsStringAsync();

This logic would then make it easier for you to add layers in and around the login if needs be without making the code hard to reason about.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download