I have setup Azure Keyvault on my ASP.Net MVC web application by following the example in Microsoft's Hello Key Vault sample application.
Azure KeyVault (Active Directory) AuthenticationResult by default has a one hour expiry. So after one hour, you must get a new authentication token. KeyVault is working as expected for the first hour after getting my first AuthenticationResult token, but after the 1 hour expiry, it fails to get a new token.
Unfortunately it took a failure on my production environment for me to realize this, as I never tested past one hour in development.
Anyways, after over two days of trying to figure out what was wrong with my keyvault code, I came up with a solution that fixes all of my problems - remove the asynchronous code - but it feels very hacky. I want to find out why it was not working in the first place.
My code looks like this:
public AzureEncryptionProvider() //class constructor
{
_keyVaultClient = new KeyVaultClient(GetAccessToken);
_keyBundle = _keyVaultClient
.GetKeyAsync(_keyVaultUrl, _keyVaultEncryptionKeyName)
.GetAwaiter().GetResult();
}
private static readonly string _keyVaultAuthClientId =
ConfigurationManager.AppSettings["KeyVaultAuthClientId"];
private static readonly string _keyVaultAuthClientSecret =
ConfigurationManager.AppSettings["KeyVaultAuthClientSecret"];
private static readonly string _keyVaultEncryptionKeyName =
ConfigurationManager.AppSettings["KeyVaultEncryptionKeyName"];
private static readonly string _keyVaultUrl =
ConfigurationManager.AppSettings["KeyVaultUrl"];
private readonly KeyBundle _keyBundle;
private readonly KeyVaultClient _keyVaultClient;
private static async Task<string> GetAccessToken(
string authority, string resource, string scope)
{
var clientCredential = new ClientCredential(
_keyVaultAuthClientId,
_keyVaultAuthClientSecret);
var context = new AuthenticationContext(
authority,
TokenCache.DefaultShared);
var result = context.AcquireToken(resource, clientCredential);
return result.AccessToken;
}
The GetAccessToken method signature has to be asynchronous to pass into the new KeyVaultClient constructor, so I left the signature as async, but I removed the await keyword.
With the await keyword in there (the way it should be, and is in the sample):
private static async Task<string> GetAccessToken(string authority, string resource, string scope)
{
var clientCredential = new ClientCredential(_keyVaultAuthClientId, _keyVaultAuthClientSecret);
var context = new AuthenticationContext(authority, null);
var result = await context.AcquireTokenAsync(resource, clientCredential);
return result.AccessToken;
}
The program works fine the first time I run it. And for an hour, AcquireTokenAsync returns the same original authentication token which is great. But once the token expires, AcquiteTokenAsync should get a new token with a new expiry date. And it doesn't - the application just hangs. No error returned, nothing at all.
So calling AcquireToken instead of AcquireTokenAsync solves the problem, but I have no idea why. You'll also notice that I'm passing 'null' instead of 'TokenCache.DefaultShared' into the AuthenticationContext constructor in my sample code with async. This is to force the toke to expire immediately instead of after one hour. Otherwise, you have to wait an hour to reproduce the behavior.
I was able to reproduce this again in a brand new MVC project, so I don't think it has anything to do with my specific project. Any insight would be appreciated. But for now, I'm just not using async.
Problem: deadlock
Your EncryptionProvider() is calling GetAwaiter().GetResult(). This blocks the thread, and on subsequent token requests, causes a deadlock. The following code is the same as yours is but separates things to facilitate explanation.
public AzureEncryptionProvider() // runs in ThreadASP
{
var client = new KeyVaultClient(GetAccessToken);
var task = client.GetKeyAsync(KeyVaultUrl, KeyVaultEncryptionKeyName);
var awaiter = task.GetAwaiter();
// blocks ThreadASP until GetKeyAsync() completes
var keyBundle = awaiter.GetResult();
}
In both token requests, the execution starts in the same way:
AzureEncryptionProvider() runs in what we'll call ThreadASP.
AzureEncryptionProvider() calls GetKeyAsync().
Then things differ. The first token request is multi-threaded:
GetKeyAsync() returns a Task.
We call GetResult() blocking ThreadASP until GetKeyAsync() completes.
GetKeyAsync() calls GetAccessToken() on another thread.
GetAccessToken() and GetKeyAsync() complete, freeing ThreadASP.
Our web page returns to the user. Good.
The second token request uses a single thread:
GetKeyAsync() calls GetAccessToken() on ThreadASP (not on a separate thread.)
GetKeyAsync() returns a Task.
We call GetResult() blocking ThreadASP until GetKeyAsync() completes.
GetAccessToken() must wait until ThreadASP is free, ThreadASP must wait until GetKeyAsync() completes, GetKeyAsync() must wait until GetAccessToken() completes. Uh oh.
Deadlock.
Why? Who knows?!?
There must be some flow control within GetKeyAsync() that relies on the state of our access token cache. The flow control decides whether to run GetAccessToken() on its own thread and at what point to return the Task.
Solution: async all the way down
To avoid a deadlock, it is a best practice "to use async all the way down." This is especially true when we are calling an async method, such as GetKeyAsync(), that is from an external library. It is important not force the method to by synchronous with Wait(), Result, or GetResult(). Instead, use async and await because await pauses the method instead of blocking the whole thread.
Async controller action
public class HomeController : Controller
{
public async Task<ActionResult> Index()
{
var provider = new EncryptionProvider();
await provider.GetKeyBundle();
var x = provider.MyKeyBundle;
return View();
}
}
Async public method
Since a constructor cannot be async (because async methods must return a Task), we can put the async stuff into a separate public method.
public class EncryptionProvider
{
//
// authentication properties omitted
public KeyBundle MyKeyBundle;
public EncryptionProvider() { }
public async Task GetKeyBundle()
{
var keyVaultClient = new KeyVaultClient(GetAccessToken);
var keyBundleTask = await keyVaultClient
.GetKeyAsync(KeyVaultUrl, KeyVaultEncryptionKeyName);
MyKeyBundle = keyBundleTask;
}
private async Task<string> GetAccessToken(
string authority, string resource, string scope)
{
TokenCache.DefaultShared.Clear(); // reproduce issue
var authContext = new AuthenticationContext(authority, TokenCache.DefaultShared);
var clientCredential = new ClientCredential(ClientIdWeb, ClientSecretWeb);
var result = await authContext.AcquireTokenAsync(resource, clientCredential);
var token = result.AccessToken;
return token;
}
}
Mystery solved. :) Here is a final reference that helped my understanding.
Console App
My original answer had this console app. It worked as an initial troubleshooting step. It did not reproduce the problem.
The console app loops every five minutes, repeatedly asking for a new access token. At each loop, it outputs the current time, the expiry time, and the name of the retrieved key.
On my machine, the console app ran for 1.5 hours and successfully retrieved a key after expiration of the original.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace ConsoleApp
{
class Program
{
private static async Task RunSample()
{
var keyVaultClient = new KeyVaultClient(GetAccessToken);
// create a key :)
var keyCreate = await keyVaultClient.CreateKeyAsync(
vault: _keyVaultUrl,
keyName: _keyVaultEncryptionKeyName,
keyType: _keyType,
keyAttributes: new KeyAttributes()
{
Enabled = true,
Expires = UnixEpoch.FromUnixTime(int.MaxValue),
NotBefore = UnixEpoch.FromUnixTime(0),
},
tags: new Dictionary<string, string> {
{ "purpose", "StackOverflow Demo" }
});
Console.WriteLine(string.Format(
"Created {0} ",
keyCreate.KeyIdentifier.Name));
// retrieve the key
var keyRetrieve = await keyVaultClient.GetKeyAsync(
_keyVaultUrl,
_keyVaultEncryptionKeyName);
Console.WriteLine(string.Format(
"Retrieved {0} ",
keyRetrieve.KeyIdentifier.Name));
}
private static async Task<string> GetAccessToken(
string authority, string resource, string scope)
{
var clientCredential = new ClientCredential(
_keyVaultAuthClientId,
_keyVaultAuthClientSecret);
var context = new AuthenticationContext(
authority,
TokenCache.DefaultShared);
var result = await context.AcquireTokenAsync(resource, clientCredential);
_expiresOn = result.ExpiresOn.DateTime;
Console.WriteLine(DateTime.UtcNow.ToShortTimeString());
Console.WriteLine(_expiresOn.ToShortTimeString());
return result.AccessToken;
}
private static DateTime _expiresOn;
private static string
_keyVaultAuthClientId = "xxxxx-xxx-xxxxx-xxx-xxxxx",
_keyVaultAuthClientSecret = "xxxxx-xxx-xxxxx-xxx-xxxxx",
_keyVaultEncryptionKeyName = "MYENCRYPTIONKEY",
_keyVaultUrl = "https://xxxxx.vault.azure.net/",
_keyType = "RSA";
static void Main(string[] args)
{
var keepGoing = true;
while (keepGoing)
{
RunSample().GetAwaiter().GetResult();
// sleep for five minutes
System.Threading.Thread.Sleep(new TimeSpan(0, 5, 0));
if (DateTime.UtcNow > _expiresOn)
{
Console.WriteLine("---Expired---");
Console.ReadLine();
}
}
}
}
}
I have the same challenge you have. I am assuming that you've also seen the sample published at https://azure.microsoft.com/en-us/documentation/articles/key-vault-use-from-web-application/
There is a big difference between what that sample does and what my code does (and I think the intent of your code is). In the sample, they retrieve a secrete and store it in the web application as a static member of their Utils class. Thus, the sample retrieves a secret one time for the entire run time of the application.
In my case, I am retrieving a different key for different purposes at different times during the application's run time.
Additionally, the sample download you linked to uses an X.509 certificate to authenticate the web application to KeyVault, rather than client secret. It's possible there is an issue with that too.
I saw the chat with #shaun-luttin concluding you caused a deadlock, but that's not the whole story I think. I don't use .GetAwaiter().GetResult() or call an async method from a ctor.
Related
(Edited to add more detail about every call I'm making)
I have a Xamarin Forms application connecting to a .Net Core 2.2 web service hosted in Azure App Services.
In my view model I have a call like this:
private async Task GetItems() {
var result = await itemsListFactory.GetItemsAsync()
}
Which calls this:
public async Task<IEnumerable<IItemInfo>> GetItemsAsync() {
return await ItemList.GetItemListAsync();
}
Which calls this (CSLA business object):
public static async Task<ItemList> GetItemListAsync() {
return await DataPortal.FetchAsync<ItemList>();
}
Which calls this:
[Fetch]
private async void DataPortal_Fetch() {
var rlce = RaiseListChangedEvents;
RaiseListChangedEvents = false;
IsReadOnly = false;
using (var ctx = Dal.DalFactory.GetManager()) {
var dal = ctx.GetProvider<IItemDal>();
List<ItemDto> list = null;
list = await dal.FetchAsync();
foreach (var item in list) {
Add(DataPortal.FetchChild<ItemInfo>(item));
}
}
IsReadOnly = true;
RaiseListChangedEvents = rlce;
}
Which calls:
public async Task<List<ItemDto>> FetchAsync() {
var resultSet = new List<ItemDto>();
var connectionManager = ServiceLocator.Current.GetInstance<IAzureConnectionManager>();
using (var conn = await connectionManager.GetOpenConnectionAsync()) {
/* Reading from DB */
}
return resultSet;
}
The implementation of the AzureConnectionManager looks like this:
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
await connection.OpenAsync();
return connection;
}
However, the first time I make this call (e.g. first call of the day, or after not using the service for a while) I get no results back. Any subsequent calls seem to work just fine. My guess is this has something to do with the service having to take a few "extra steps" to return data due to inactivity.
This suspicion seems to be confirmed whenever I debug the web service and set breakpoints in my view model as well as the server-side code. Whenever the service's call returns with no records it's almost as if it's returning early from the server, because it returns to the view model with no data, and then my debugger hops back onto the server after it's received the access token. So, it's as if my code decided not to wait for the GetAccessTokenAsync and OpenAsync to finish what they had to do before returning to the client.
I can fix this by adding a .Result to GetAccessTokenAsync() and .Wait() to OpenAsync() like this:
public async Task<SqlConnection> GetOpenConnectionAsync()
{
var accessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result;
var connection = new SqlConnection(dbconnection) {
AccessToken = accessToken
};
connection.OpenAsync().Wait();
return connection;
}
But this feels like a hack.
I doubt this is the way I'm supposed to fix this, but maybe it is. At the very least I'd like to just understand what's going on here if this is the correct way to handle this situation.
The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any. When the await operator is applied to the operand that represents already completed operation, it returns the result of the operation immediately without suspension of the enclosing method. The await operator doesn't block the thread that evaluates the async method. When the await operator suspends the enclosing async method, the control returns to the caller of the method.
Official Document on this
So if we look at the what the documents say about Async/Await you'll notice that
When the await operator is applied to the operand that represents already completed operation, it returns the result of the operation immediately without suspension of the enclosing method.
More then likely OpenAsync(); Is seen as a operand that's already completed as you might not be awaiting your Returns, So the Operation Runs retrieve's your data but because your not suspending anything in OpenAsync It might assume the operand is already completed on the first instance and just continue then the data is loaded so on your second attempt you have the data to work with, as its already populated on the first try.
So i'd like to see a bit more code actually.
However one thing I will say is that .Wait() is Bad If you have to wait for a result and force that wait the better way to do this is .GetAwaiter().GetResult() I can link you a Seminar that explains in details about this. But in Essence .Wait() Throws exceptions into the void and make them extremly difficult to track(Or at-least far more difficult then you'd want them to be)
"Also note in no way am I anywhere near a Expert in Async/Await so feel free to correct me"
I've been reading examples for a long time now, but unfortunately I've been unable to apply the solutions to the code I'm working with. Some quick Facts/Assorted Info:
1) I'm new to C#
2) The code posted below is modified from Amazon Web Services (mostly stock)
3) Purpose of code is to compare server info to offline already downloaded info and create a list of need to download files. This snip is for the list made from the server side, only option with AWS is to call async, but I need this to finish before moving forward.
public void InitiateSearch()
{
UnityInitializer.AttachToGameObject(this.gameObject);
//these are the access key and secret access key for credentials
BasicAWSCredentials credentials = new BasicAWSCredentials("secret key", "very secret key");
AmazonS3Config S3Config = new AmazonS3Config()
{
ServiceURL = ("url"),
RegionEndpoint = RegionEndpoint.blahblah
};
//Setting the client to be used in the call below
AmazonS3Client Client = new AmazonS3Client(credentials, S3Config);
var request = new ListObjectsRequest()
{
BucketName = "thebucket"
};
Client.ListObjectsAsync(request, (responseObject) =>
{
if (responseObject.Exception == null)
{
responseObject.Response.S3Objects.ForEach((o) =>
{
int StartCut = o.Key.IndexOf(SearchType) - 11;
if (SearchType == o.Key.Substring(o.Key.IndexOf(SearchType), SearchType.Length))
{
if (ZipCode == o.Key.Substring(StartCut + 12 + SearchType.Length, 5))
{
AWSFileList.Add(o.Key + ", " + o.LastModified);
}
}
}
);
}
else
{
Debug.Log(responseObject.Exception);
}
});
}
I have no idea how to apply await to the Client.ListObjectsAsync line, I'm hoping you all can give me some guidance and let me keep my hair for a few more years.
You can either mark your method async and await it, or you can call .Wait() or .Result() on the Task you're given back.
I have no idea how to apply await to the Client.ListObjectsAsync line
You probably just put await in front of it:
await Client.ListObjectsAsync(request, (responseObject) => ...
As soon as you do this, Visual Studio will give you an error. Take a good look at the error message, because it tells you exactly what to do next (mark InitiateSearch with async and change its return type to Task):
public async Task InitiateSearchAsync()
(it's also a good idea to add an Async suffix to follow the common pattern).
Next, you'd add an await everywhere that InitiateSearchAsync is called, and so on.
I'm assuming Client.ListObjectsAsync returns a Task object, so a solution for your specific problem would be this:
public async void InitiateSearch()
{
//code
var collection = await Client.ListObjectsAsync(request, (responseObject) =>
{
//code
});
foreach (var item in collection)
{
//do stuff with item
}
}
the variable result will now be filled with the objects. You may want to set the return type of InitiateSearch() to Task, so you can await it too.
await InitiateSearch(); //like this
If this method is an event handler of some sort (like called by the click of a button), then you can keep using void as return type.
A simple introduction from an unpublished part of the documentation for async-await:
Three things are needed to use async-await:
The Task object: This object is returned by a method which is executed asynchronous. It allows you to control the execution of the method.
The await keyword: "Awaits" a Task. Put this keyword before the Task to asynchronously wait for it to finish
The async keyword: All methods which use the await keyword have to be marked as async
A small example which demonstrates the usage of this keywords
public async Task DoStuffAsync()
{
var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
var task = WriteTextAsync(#"temp.txt", result); //starts saving the string to a file, continues execution right await
Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
await task; //wait for WriteTextAsync to finish execution
}
private async Task<string> DownloadFromWebpageAsync()
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
}
}
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
Some thing to note:
You can specify a return value from an asynchronous operations with Task. The await keyword waits till the execution of the method finishes, and returns the string.
the Task object contains the status of the execution of the method, it can be used as any other variable.
if an exception is thrown (for example by the WebClient) it bubbles up at the first time the await keyword is used (in this example at the line string result (...))
It is recommended to name methods which return the Task object as MethodNameAsync
For more information about this take a look at http://blog.stephencleary.com/2012/02/async-and-await.html.
I am new to Tasks in C#. I am trying to following the example regarding deploying an arm template to azure to start a virtual machine:
https://azure.microsoft.com/en-us/documentation/articles/arm-template-deployment/
One of the first operations given here is to get an authorization token by calling Active Directory. The code given in the link uses AcquireToken api which seems to be deprecated and currently I could find only AcquireTokenAsync. So I modified the code to create a Task and wait for it to complete:
private static string GetAuthorizationHeader()
{
ClientCredential cc = new ClientCredential("{application-id}", "{password}");
var context = new AuthenticationContext("https://login.windows.net/{tenant-id}");
Task<AuthenticationResult> acquireTokenTask = context.AcquireTokenAsync("https://management.azure.com/", cc);
Task.WhenAll(acquireTokenTask);
AuthenticationResult result = acquireTokenTask.Result;
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
I was expecting thatTask.WhenAll blocks until the acquireTokenTask's status becomes "RanToCompletion". But even while the status is "WaitingForActivation" WhenAll is not blocking and the control goes to the next statement trying to get the acquireTokenTask.Result. This is resulting in an exception saying with the exception detail saying there was an error sending the request.
Questions:
1. Is there an error in sending the request that the status is not changing to "RanToCompletion"? I don't think this is the case since 2-3 runs out of 10 runs are succeeding with status "RanToCompletion" and I am able to get the result.
2. I thought Task.WhenAll blocks the thread until it runs to completion. Is this not true? If true, I am wondering how the control is passing to the next statement.
3. How can I resolve this issue and get the successful result on every run by the time the result is extracted?
I thought Task.WhenAll blocks the thread until it runs to completion. Is this not true?
Task.WhenAll returns an awaitable, which you need to await on. Currently, you pass your Task but don't await, which simply makes the code continue execution, until you block it with Task.Result. Although, that isn't nessacery at all here. WhenAll is ment to be used when you have multiple tasks that need to be asynchronously waited on. Here, you can simply await on the single Task:
private static async Task<string> GetAuthorizationHeaderAsync()
{
ClientCredential cc = new ClientCredential("{application-id}", "{password}");
var context = new AuthenticationContext("https://login.windows.net/{tenant-id}");
AuthenticationResult result = await context.AcquireTokenAsync(
"https://management.azure.com/", cc);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken;
return token;
}
I have a webservice that loads up some plugins (dlls) and calls their Process method. One of the plugins takes a list of members and ensures that they are all included in a MailChimp list.
Here is the code that adds the users to the MailChimp group.
private async Task AddMCUsers(List<Member> _memberList)
{
using (var http = new HttpClient())
{
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user:password");
http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", creds);
string memberURI = string.Format(#"{0}lists/{1}/members", _baseURI, _memberGroupId);
var jss = new JavaScriptSerializer();
foreach (var user in _memberlist)
{
var _addStatus = "";
try
{
var content = jss.Serialize(new MCPost()
{
email_address = user.Email,
status = "subscribed",
merge_fields = new MCMergeFields()
{
FNAME = user.Firstname,
LNAME = user.Lastname
}
});
using(var result = await http.PostAsync(memberURI, new StringContent(content,Encoding.UTF8, "application/json")))
{
var resultText = await result.Content.ReadAsStringAsync();
if(result.IsSuccessStatusCode)
{
_addStatus = "Success";
var _returnedUser = jss.Deserialize<MCMember>(resultText);
//Store new user's id
user.ServiceId = _returnedUser.id;
}
else
{
_addStatus = "Fail";
}
}
}
catch {
_addStatus = "Error";
}
LogEvent("Add User - " + _addStatus, string.Format("Id: {0} - {1} {2} (Account: {3}) : {4}", user.Id, user.Firstname, user.Lastname, user.AccountId, user.Email));
}
}
}
In normal procedural code, this wouldn't be a problem. However, the only Post method available on the httpClient was PostAsync. Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
I'm not sure what happens with await when its wrapped in a foreach like I have. Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
My other question is, what is actually going to be returned. IOW, my understanding is that await returns a Task. However, here, I'm looping through the list and making multiple calls to await PostAsync. My method returns a Task. But which task gets returned? If my calling method needs to wait for completion before moving on, what does its call look like?
private void Process()
{
//Get List
var task = AddMCUsers(list);
task.Wait();
//Subsequent processing
}
I've read that you should use Async all the way. Does this mean my calling method should look more like this?
public async Task Process()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
Thanks to whatever help you can offer on this.
In normal procedural code, this wouldn't be a problem.
The whole point of async/await is to write asynchronous code in a way that looks practically identical to "normal" synchronous code.
Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
HttpClient was intended to be reused; in fact, it can be used for any number of calls simultaneously.
I'm not sure what happens with await when its wrapped in a foreach like I have.
One way to think of it is that await "pauses" the method until its operation completes. When the operation completes, then the method continues executing. I have an async intro that goes into more detail.
Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
No, that's fine.
IOW, my understanding is that await returns a Task.
await takes a Task. It "unwraps" that task and returns the result of the task (if any). If the task completed with an exception, then await raises that exception.
My method returns a Task. But which task gets returned?
The Task returned from an async method is created by the async state machine. You don't have to worry about it. See my intro for more details.
If my calling method needs to wait for completion before moving on, what does its call look like? ... I've read that you should use Async all the way. Does this mean my calling method should look more like this?
Yes, it should look like your second snippet:
public async Task ProcessAsync()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
The only thing I changed was the Async suffix, which is recommended by the Task-based Asynchronous Pattern.
in your code you should be fine with reusing the HttpClient. What async / await is allow the code to release the execution thread to prevent locking a cpu thread while waiting for the web response. It also releases control back to the caller. When releasing code back to the caller it means that if your Process function does not await your AddMCUsers, Process could finish before AddMCUsers (useful in fire and forget situations to not await a method).
What async/await do not do is affect the logical flow of an individual method. When you await an async web call the execution is paused and then resumed at the same point once the web call returns. There is also thread context tracking and the code resumes in the same context (ie. UI thread or background thread depending on the parent) by default, but this can be changed if needed.
At some point in your code you may want to have a method that blocks until your async code competes and that is where you will want your Task.Wait() call to block execution. If all you use is awaits then it is possible for your program to end before your task competes. See the code example below.
class Program
{
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
}
static async Task waitAsync()
{
await Task.Delay(5000);
}
}
in the sample with out a Task.Wait call to block the Main method the program will end before the 5 second wait is complete. Having a main method of the following will cause the program to wait for 5 seconds before exiting:
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
waitForMe.Wait();
}
When I call this method, nothing happens and the application crashes. I think it's due the ExecuteAsync method.can someone help me?.This is my code.
CODE1:
public Task<Connection> Connect(string userId, string password)
{
var client = new RestClient(_baseUrl)
{
Authenticator = new SimpleAuthenticator("user", userId,
"password", password)
};
var tcs = new TaskCompletionSource<Connection>();
var request = new RestRequest(AppResources.Authenticating);
client.ExecuteAsync<Connection>(request, response => tcs.SetResult(new
JsonDeserializer().Deserialize<Connection>(response)));
return tcs.Task;
}
I tried this code also but still the same problem exists.
CODE2:
public async Task<Connection> Connect(string userId, string password)
{
var client = new RestClient(_baseUrl)
{
Authenticator = new SimpleAuthenticator("user", userId,
"password", password)
};
var tcs = new TaskCompletionSource<Connection>();
var request = new RestRequest(AppResources.Authenticating);
client.ExecuteAsync<Connection>(request, response => tcs.SetResult(new
JsonDeserializer().Deserialize<Connection>(response)));
Debug.WriteLine(tcs.Task.Result.Data);
return await tcs.Task;
}
You don't want to use Task.Result or Task.Wait in asynchronous code. Those members are only used when you use Task as part of the Task Parallel Library (writing parallel code). They should almost never be used in asynchronous code.
In your case, I suspect that you're calling Result on the Task returned by Connect (or possibly further up the call stack). This can cause a deadlock, as I explain on my blog.
I disagree with #LasseVKarlsen regarding asynchronous code for beginners. I think it is absolutely something you should learn now that the new async/await language features are out.
I recommend you start with my introduction and follow up with the MSDN docs and the TAP pattern. Then review my best practices article to avoid the most common pitfalls.