I've a flow: WebApi > ServiceFramework > DBLayer > MongoDB.
Since its a new application, I ensured to have async from the ground up in all layers. However, when my DB Layer has async code, webapi never gets a response back.
API CONTROLLER
[HttpGet]
public IHttpActionResult GetAllRecords()
{
var result = FrameworkApi.GetRecords().Result;
return Ok(result);
}
above calls > FRAMEWORK API
public async Task<List<Record>> GetRecords()
{
return await FrameworkDbApi.GetRecords();
}
above calls > DB FRAMEWORK API (which Calls MongoDB)
public async Task<List<Record>> GetRecords()
{
return await Task.Run(() =>
NoSqlDocumentClient.GetDefaultDatabase().Result.
GetCollection<Record>("record").AsQueryable().ToList());
//following Synchronous version works..but defeats the purpose
//return NoSqlDocumentClient.GetDefaultDatabase().Result
// .GetCollection<Record>("record").AsQueryable().ToList();
}
However, when the operations in either DBLayer or Framework are invoked via test case, I do get result. But when invoked via WebApi controller, the asynchronous version never returns a response while synchronous version works fine.
But when invoked via WebApi controller, the asynchronous version never
returns a response while synchronous version works fine.
That's because your actual request is deadlocking. When you invoke the method via WebAPI, which has a SynchronizationContext, you're seeing the deadlock, in contrary to your test which hasn't got one, when the test runs fine. This is the reason why you shouldn't block on async code.
Your call-chain should look like this in order to avoid deadlocking (this is what it means to go "async all the way":
[HttpGet]
public async Task<IHttpActionResult> GetAllRecordsAsync()
{
var result = await FrameworkApi.GetRecordsAsync();
return Ok(result);
}
public Task<List<Record>> GetRecordsAsync()
{
return FrameworkDbApi.GetRecordsAsync();
}
public async Task<List<Record>> GetRecordsAsync()
{
var result = await NoSqlDocumentClient.GetDefaultDatabase();
return result.GetCollection<Record>("record").AsQueryable().ToList();
}
Related
I've got a class library for talking to a logging api server, the method "chain" is this:
Entry point ->
private static bool SendChecksumToServer(Checksum checksum)
{
var res = _api.GetAsync($"Checksum?assemblyName={checksum.CurrentAssembly}&checkSum={checksum.LogFileChecksum}&fileName={checksum.FileName}");
return _api.Deserialize<bool>(res.Result.Content.ReadAsStringAsync().Result);
}
Which calls this:
public async Task<HttpResponseMessage> GetAsync(string apiCall)
{
ApiGet get = new ApiGet();
return await get.GetAsync(apiCall, client);
}
Which calls this:
public async Task<HttpResponseMessage> GetAsync(string apiCall, HttpClient client)
{
var response = await client.GetAsync(apiCall);
return response;
}
This works completely fine when I use the class library within a console app, but as soon as I move it to an actual application (MVC) it stops working, it doesn't even hit the controller action at all, I've tried everything I can think of like checking firewalls, making sure the async is correct (although I'm sure it still isn't because the api not responding freezes the app, but I can't bubble the async any higher)
Most likely experiencing a deadlock because of .Result blocking call.
Don’t mix blocking and async code
Reference Async/Await - Best Practices in Asynchronous Programming
Refactor the code to be async all the way
private static async Task<bool> SendChecksumToServer(Checksum checksum) {
var res = await _api.GetAsync($"Checksum?assemblyName={checksum.CurrentAssembly}&checkSum={checksum.LogFileChecksum}&fileName={checksum.FileName}");
String data = await res.Result.Content.ReadAsStringAsync();
return _api.Deserialize<bool>(data);
}
Ensure what ever is calling SendChecksumToServer also awaits the task,
and also not using async-await in the other calls if nothing needs to be awaited.
public Task<HttpResponseMessage> GetAsync(string apiCall) {
ApiGet get = new ApiGet();
return get.GetAsync(apiCall, client);
}
ApiGet
public Task<HttpResponseMessage> GetAsync(string apiCall, HttpClient client) {
return client.GetAsync(apiCall);
}
I'm building a RESTful web api that is going to fire off several tasks, wait for them to complete then return some data. But I keep having problems with it seeming to hang when waiting. I scaled it down the the most basic:
[Authorize]
public class ValuesController : ApiController
{
public IEnumerable<string> Get([FromUri]bool enableWait)
{
Task t = Silly(enableWait);
t.Wait();
return new string[] { "value1", "value2" };
}
private async Task Silly(bool delay)
{
if (delay)
await Task.Delay(1);
}
}
If I pass false it returns without any issue. If I return true, it should wait for 1 millisecond (plus Task/Asnyc overhead) then return. However it just hangs there.
I'm running this on the IIS Express that comes with VS 2017. Any clues?
When implementing async/await you need to make sure you are implementing it all the way through. Your controller action needs to be async, and it needs to await your Silly() method:
public async Task<IEnumerable<string>> Get()
{
return await Silly();
}
You also have to make sure your Silly() method is calling the proper async methods it needs, making sure you are await-ing them.
I'm trying to return a list of followed users from the Instagram API. I'm on a sandbox account using the InstaSharp wrapper for .NET.
The action method is being called after user is authenticated.
public ActionResult Following()
{
var oAuthResponse = Session["InstaSharp.AuthInfo"] as OAuthResponse;
if (oAuthResponse == null)
{
return RedirectToAction("Login");
}
var info = new InstaSharp.Endpoints.Relationships(config_, oAuthResponse);
var following = info.Follows("10").Result;
return View(following.Data);
}
Try making the method async all the way through instead of making the blocking call .Result which runs the risk of causing a deadlock
public async Task<ActionResult> Following() {
var oAuthResponse = Session["InstaSharp.AuthInfo"] as OAuthResponse;
if (oAuthResponse == null) {
return RedirectToAction("Login");
}
var info = new InstaSharp.Endpoints.Relationships(config_, oAuthResponse);
var following = await info.Follows("10");
return View(following.Data);
}
depending on how info.Follows was implemented.
Looking at the Github repo, the API internally makes a call to a method defined like this
public static async Task<T> ExecuteAsync<T>(this HttpClient client, HttpRequestMessage request)
Which looks like your smoking gun as calling .Result higher up the call stack on this task would result in your experienced deadlock.
Reference Async/Await - Best Practices in Asynchronous Programming
I had set up my controller and service layer to not use async and Task originally. I updated my code (below). Is this the correct way to use async, Task, and await? Will my code benefit long term as number of users grow?
Controller:
public async Task<IHttpActionResult> All()
{
var warmup = await _warmupService.GetAllAsync();
if (warmup != null)
return Ok(warmup);
return NotFound();
}
The Controller calls this method in my service layer:
public async Task<Warmup> GetAllAsync()
{
return await Task.Run(() => GetWarmup());
}
GetWarmup() (also in Service) makes a DB call using EF6:
private Warmup GetWarmup()
{
var warmup = new Warmup();
var factoryTools = _db.FactoryTools.Select(tool => new { tool.Factory });
}
It is not the best way to do it. If you can, avoid using Task.Run when your underlying code provides a Task or Task<T> you can await.
Instead of using Task.Run in this piece of code
public async Task<Warmup> GetAllAsync()
{
return await Task.Run(() => GetWarmup());
}
and using this:
private Warmup GetWarmup()
{
var warmup = new Warmup();
var factoryTools = _db.FactoryTools.Select(tool => new { tool.Factory });
}
you should use the native async methods provided by the Entity Framework like demoed here: https://msdn.microsoft.com/en-us/library/jj819165(v=vs.113).aspx
So your GetWarmUp() should be async Task<Warmup>() and call one of the native async methods of the Entity Framework.
See https://msdn.microsoft.com/en-us/library/system.data.entity.queryableextensions(v=vs.113).aspx for a list of available async extension methods.
I'm using Visual Studio 2012 RC with .Net 4.5 and ASP MVC 4 RC. It hangs whenever I use async at all. The controller action method uses async but is not itself an async controller method.
There are no errors logged or exceptions thrown, but the browser shows "Waiting for www.myweb.local" forever.
// Simplest possible async
public class Waiter
{
public async Task<int> GetValue()
{
await Task.Yield();
return await Task.Factory.StartNew(() => 42);
}
}
// simplest possible controller that uses the async
public class HomeController : Controller
public ActionResult Index()
{
var waiter = new Waiter();
var resultTask = waiter.GetValue();
int result = resultTask.Result;
// it never gets here
return View();
}
}
I have done the things that are noted in this answer, and it still does not work.
ie. The web.config contains
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
And the magic words await Task.Yield(); are in the async method.
The .Net framework version is 4.5.50501. I have observed this behaviour on IIS Express and on IIS 6.0.
I tried applying the "July 2012 update" to VS2012, but that did not fix it.
This answer suggests that it may be because the task is already completed when I wait for it, however if that was the case, this should work and it does not:
public class Waiter
{
public async Task<int> GetValue()
{
await Task.Yield();
return await Task.Factory.StartNew(() =>
{
Thread.Sleep(1500);
return 42;
});
}
}
A couple of people have suggested that ConfigureAwait(false) is needed, but this code does not work either:
public async Task<int> GetValue()
{
var task = new Task<int>(() => 42);
return await task.ConfigureAwait(false);
}
The following does work with the razor view engine, but not with spark. Surely there should be a way to get the other scenario working as well? Can one not use the async Tasks inside synchronous code?
public class Waiter
{
public async Task<int> GetValue()
{
return await Task.Factory.StartNew(() => 42);
}
}
public class HomeController : Controller
{
public async Task<ActionResult> IndexAsync()
{
await Task.Yield();
var waiter = new Waiter();
int result = await waiter.GetValue();
return View();
}
}
I know that is this isn't released software, but Microsoft's RCs are usually quite stable, so I'm surprised that it fails, and fails in an unhelpful way.
You are causing a deadlock, just like this question.
James Manning's suggestion is correct, but you have to await the result of ConfigureAwait, like this:
public async Task<int> GetValue()
{
var task = new Task<int> (() => 42);
return await task.ConfigureAwait(false);
}
In general, mixing synchronous and asynchronous code is a Really Bad Idea unless you Really Know what you're doing. Making the controller action asynchronous would be much better.
I have had async working in the beta just fine. I've not tested it, but I'm guessing it's because your controller method is not async. Change it to this:
public async Task<ActionResult> IndexAsync()
{
var waiter = new Waiter();
int result = await waiter.GetValue();
// it never gets here
return View();
}