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;
}
Related
I have tried to simulate a scenario that when I request my api service once, I have to do 4 IO bounded request to different external API's or databases etc. to get response on my example.
I want to start these requests at the same time as asynchronously and If I get negative response (null for my example) one of these requests, stop all request as soon as I get negative response one of them.
According to my example, the requests was started at the same time and even if I get negative response one of requests I have to wait until long request(task4) finishes. It means I was able to get result at least in 40 second. How can I stop working unnecessarily 20 second more, when I get null response from second request (task2) on 20th second?
If It's possible how I should handle this scenario?
public class HomeController : Controller
{
public async Task<string> SampleAsyncAction()
{
var task1 = DoSomeIOAsync(10000);
var task2 = DoSomeIOAsync(20000);
var task3 = DoSomeIOAsync(30000);
var task4 = DoSomeIOAsync(40000);
var result = await Task.WhenAll(task1, task2, task3, task4);
if (result.Any(x=> x == null))
return "Error occurred";
return "Operation was done successfully in " + result.Sum().ToString() + "millisecond";
}
private int? DoSomeIO(int millisecond)
{
Thread.Sleep(millisecond);
if (millisecond == 20000)
return null;
return (millisecond);
}
private async Task<int?> DoSomeIOAsync(int millisecond)
{
return await Task.Run(() => DoSomeIO(millisecond));
}
}
First, only async work can be cancelled, and only if that async work supports cancellation. A sync command like Thread.Sleep is not cancelable, so what you're trying to do here is fundamentally impossible to begin with.
Task.Run is not some magic wand that makes sync suddenly async. All you're doing with that is running the sync code on a new thread. It still must run to completion, and that thread will be tied up until that happens.
Assuming you are actually dealing with async code, and that async code supports cancellation (generally, does it accept a CancellationToken param), then the way to do what you want is with a custom CancellationTokenSource. You create an instance of that, and pass it as the cancellation token to each async method. Then you await each operation and if one of them fails, you send a cancellation to the CTS, which will then cause any operations using that cancellation token to cancel as well.
(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 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.
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();
}
So I'm trying to create a proof of concept for server-side asynch using HTTP 202 codes (where the server accepts the task, immediately returns an endpoint to poll and then creates/updates the resource)
Rick Strahl has a description of how to do this in ordinary ASP.NET. That technique depends on being able to Response.End and then continue to execute code. The Response object doesn't even seem to be available in a Web API controller's context.
If the following worked as planned, it would not block returning the http 202 and still guarantee that the database task will run to completion.
//Insert or Update Asych.
public Task<HttpResponseMessage> Post(bool asynch, [FromBody]DatedValue value) //Insert Value
{
Guid key = Guid.NewGuid();
//Want this to run to completion, even if response associated with parent thread is done.
Task toWait = Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
//Return address to resource just created.
Task<HttpResponseMessage> sender = Task.Factory.StartNew(() =>
{
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
});
Task.WaitAll((new[] { toWait, sender }));
return sender;
}
Task.WaitAll blocks execution and response is not returned until both the tasks are completed. If you change your code something like below, you should be able to return the response while the task is being run.
public HttpResponseMessage Post(bool asynch, [FromBody]DatedValue value)
{
Guid key = Guid.NewGuid();
Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
}
However, you should be aware of a problem with an approach like this. If you are hosting Web API on IIS, your worker process can get recycled while your task is running. Since you have already returned the response, as far as ASP.NET is concerned, it has completed its work. So, if for some reason IIS decides to recycle the worker process, it will go ahead regardless of where your task is in terms of execution and hence you can end up with corrupt data.
With .NET 4.5.2, you can use QueueBackgroundWorkItem. Read this - http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx.