How to get result from async Task<> when I cannot Run()? [duplicate] - c#

This question already has answers here:
An async/await example that causes a deadlock
(5 answers)
Closed 5 years ago.
I'm trying to make a call to another API using POST methods through Asynctask.
Everything works fine (the request is correctly executed because it launches my SQL request) but I can't get any response from the external server.
I can see that I don't need to run the task to make it execute but I don't have any result.
[HttpPost]
public string Post([FromBody]JObject value)
{
if (MesureController.CheckJsonIntegrity<Mesure>(value))
{
var task = MesureAsync(value);
return task.Result;
}
return null;
}
static async Task<string> MesureAsync(JObject value)
{
using (client)
{
string url = ConfigurationManager.AppSettings["internalserver:url"];
var json_string = new Dictionary<string, string>
{
{"json_string", value.ToString() }
};
var content = new FormUrlEncodedContent(json_string);
var response = await client.PostAsync(url + "Mesure/Index", content);
string resultContent = await response.Content.ReadAsStringAsync();
return resultContent;
}
}

You're seeing a common deadlock that I describe in more detail on my blog. In summary, ASP.NET (full, not Core) code runs within a "request context" that only allows one thread to work on a request at a time. When MesureAsync sends the POST to the other API, it returns an incomplete task. Your Post method then blocks the current thread, waiting for that task. Later, when the POST to the other API completes, MeasureAsync attempts to resume executing within that same request context, but it can't because Post has blocked a thread within that request context, and the request context only allows one thread at a time.
So, you end up with Post taking up the request context waiting for MeasureAsync to complete, and MeasureAsync waiting for Post to give up the request context so that it can complete. Classic deadlock.
The best solution is to go "async all the way", i.e., don't block on async code. In this case, replace Result with await:
[HttpPost]
public string Post([FromBody]JObject value)
{
if (MesureController.CheckJsonIntegrity<Mesure>(value))
{
return await MesureAsync(value);
}
return null;
}
If you try to compile this now, it will give you a compiler error that tells you exactly what to do with Post to get it to work:
[HttpPost]
public async Task<string> Post([FromBody]JObject value)
{
if (MesureController.CheckJsonIntegrity<Mesure>(value))
{
return await MesureAsync(value);
}
return null;
}
...and you're done!

No result was return in the original code because of the mixing of blocking and async code.
The actions syntax should be updated to be async all the way and also to allow better content negotiation.
Assuming this code is for Asp.Net-Core, the actions would be updated to
[HttpPost]
public async Task<IActionResult> Post([FromBody]JObject value) {
if (MesureController.CheckJsonIntegrity<Mesure>(value)) {
var measure = await MesureAsync(value);
return Ok(measure);
}
return BadRequest();
}
If using WebAPI 2.* then change IActionResult to IHttpActionResult and it will work the same way.

You have to ensure Task will run.
Use this snippet:
Task.Run(()=>MesureAsync(value)).Result;

Related

Debugging async controller with async service inside

It's MVC project. I've started making some heavy calls to remote api async, but I am having problems debugging this code, because it is not entering breakpoint after await. I am still learning the async/await. GetSearchResults method in used in ajax requests on frontend. Application is hosted in IIS.
Here is my controller:
[HttpGet]
public async Task<ActionResult> GetSearchResults(SearchRequest searchModel)
{
var result = await _apiSearchService.SearchAsync(searchModel);
return Json(result, JsonRequestBehavior.AllowGet);
}
And here is my method inside service:
public async Task<Search> SearchAsync(SearchRequest searchRequest)
{
var requestUrl = "api/search";
var request = new NewtonsoftRestRequest(requestUrl, Method.GET);
/*
...assigning params
*/
var response = await _apiClient.ExecuteTaskAsync(request);
var searchResults = _apiClient.ResolveResponse<Search>(response);
return searchResults;
}
When I hit line
await _apiSearchService.SearchAsync(searchModel);
I enter SearchAsync method and after line with ExecuteTaskAsync I am leaving debugging at all and get my page hit.
var response = await _apiClient.ExecuteTaskAsync(request);
Could someone tell me how can debugging be achieved here? Also, could someone do a quick code review, if I am using async/await well? Also maybe I should use Task.Run() ? Going deeper is really getting more and more confusing.
Thanks!
The problem was unhandled exception in my method. Thanks #bommelding

Async Web Api Action Returning Prematurely

I am writing a nuget package that will consume a REST API I wrote. Im having some difficulty in calling it asynchronously using RestSharp.
I have the following controller method:
[HttpGet("{id}")]
public async Task<IActionResult> GetFeatureByIdAsync(string id)
{
if (string.IsNullOrWhiteSpace(id))
{
ModelState.AddModelError("id-Null", "The id parameter cannot be null.");
return BadRequest(ModelState);
}
var feature = await _director.GetFeatureById(id);
if (feature == null)
return NotFound();
var model = new ViewModel<Feature>(feature);
return Ok(model);
}
I have the follow class library in a seperate solution:
public async Task<bool> IsSwitchEnabledAsync(string name)
{
string fullyFormedUrl = $"{_url}/{name}";
var client = new RestClient(fullyFormedUrl) { Encoding = Encoding.UTF8 };
var request = new RestRequest(Method.GET) { RequestFormat = DataFormat.Json };
var result = await client.ExecuteTaskAsync<FeatureViewModel>(request);
var message = $"Response: {result.StatusCode}, {result.ResponseStatus}, " +
$"\n\nError: {result.ErrorException}, {result.ErrorMessage} " +
$"\n\nData: {result.Content}";
Debug.WriteLine(message);
return result.Data != null;
}
I have the following basic integration test sandbox:
[TestFixture]
public class Sandbox
{
private FeatureSwitch _sut;
[OneTimeSetUp]
public void Setup()
{
const string machineName = "localhost";
const int port = 5000;
_sut = new FeatureSwitch(machineName, port);
}
[Test]
public async Task ShouldReturnTrue()
{
var result = await _sut.IsSwitchEnabledAsync("Release_US501_AddUser");
result.Should().BeTrue();
}
}
I am looking for a little education on the best approach on how to call the API correctly? As my sandbox code is failing with an Internal Server 500 Error. This is because the api method is waiting for the nested async call to complete. So the initial request returns back to the sandbox code before the nested api call has a chance to complete.
The sandbox test code needs to wait for the API to return a result. At the moment it is not doing this.
So what is the correct approach here?
UPDATE: My issue was down to incorrect usage of a Mongo library method.
My issue came from a .chained method call on a Mongo library method:
I was trying to do the following:
Collection.Find(x => x.Name.ToLower().Equals(name.ToLower())
The code compiled but it was throwing an exception that VS studio 17 was not breaking on so i had no idea where the issue was occurring. With code lens turned on, I noticed that the failed requests highlighted in red. This opened an App Insights window which highlighted a break down of 4 InvalidOperationException() that had been thrown. That's how i discovered what the Internal Server Error 500 was about.
Anyways that's how i solved Async Web API problem - it was my first time using it on a web service so I had no idea what the expected default behaviour ought to be.

ASP.NET MVC workaround for ajax.abort()

I have a rest endpoint on asp.net mvc:
[HttpGet]
public List<SomeEntity> Get(){
// Lets imagine that this operation lasts for 1 minute
var someSlowOperationResult = SomeWhere.GetDataSlow();
return someSlowOperationResult;
}
On the frontEnd I have a next javascript:
var promise = $.get("/SomeEntities");
setTimeout(function(){promise.abort()}, 100);
How to force Thread to die after abort call, to prevent slow calculation to be done?
Thanks in advance.
I found that Response have isClientConnected property. So we can use next approach:
[HttpGet]
public List<SomeEntity> Get(){
var gotResult = false;
var result = new List<SomeEntity>();
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
Task.Factory.StartNew(() =>
{
// Do something with cancelation token to break current operation
result = SomeWhere.GetSomethingReallySlow();
gotResult = true;
}, ct);
while (!gotResult)
{
if (!Response.IsClientConnected)
{
tokenSource2.Cancel();
return result;
}
Thread.Sleep(100);
}
return result;
}
Can we? Or I miss something?
UPDATE:
Yes, it works
The backend has no idea that you have called abort() and if the request has already been sent then the server-side logic will run until it completes. In order to stop it from running you will have to send another request to your controller which notifies that controller that you've aborted the request and the controller will have to access the instance that is currently running you slow operation and this instance should have a method which forces the calculations to cancel.

How do I wrap a Web Api's async call and results as a synchronous method?

I need to integrate a third party's Web API methods into a WCF service.
The WCF Service will be called by another external party, and they expect the call and return to be synchronous.
I am able to get results from the API with the usual RestClient.ExecuteAsync.
I put together the following for synchronous calls:
public static List<Books> GetSyncBooks(int companyId)
{
var response = GetSynchronousBooks(companyId);
var content = response.Result.Content;
List<Books> result = new List<Books>();
return Helpers.JSONSerialiser.Deserialize<BookList>(content);
}
async private static Task<IRestResponse> GetSynchronousBooks(int companyId)
{
var request = BuildGETRequest("Book", companyId);
var response = await RestSharpHelper.ExecuteSynchronousRequest(request);
return response;
}
public static Task<IRestResponse> ExecuteSynchronousRequest(RestRequest request)
{
var client = new RestClient(BaseUrl);
client.AddHandler("application/json", new RestSharpJsonDotNetDeserializers());
var tcs = new TaskCompletionSource<IRestResponse>(TaskCreationOptions.AttachedToParent);
client.ExecuteAsync(request, (restResponse, asyncHandle) =>
{
if (restResponse.ResponseStatus == ResponseStatus.Error)
tcs.SetException(restResponse.ErrorException);
else
tcs.SetResult(restResponse);
});
return tcs.Task;
// BREAKPOINT here shows TASK value as
// Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
}
The problem, however, is that I never get a result using this. The response content is always null. What am I doing wrong?
EDIT: Thanks Stephen. I have seen your name on some of the questions here on this subject: your score seems to indicate you know your way around this. I have indeed implemented the wcf service calls as you indicated based on your answer at another question. Can I ask you a related follow-up question? How scalable are async WCF service calls like this example? Would it "just work" for multiple simultaneous calls in the range of 10 to 100 per second, ignoring the processing overhead downstream?
I assume that your WCF service is hosted in ASP.NET.
Your problem is here: response.Result. I explain this deadlock situation on my blog. In summary, await will capture the current "context" (in this case, an ASP.NET request context) and will use that to resume the async method. However, the ASP.NET request context only allows one thread at a time, so if the request thread is blocked (calling response.Result), then the async method can never continue and you get a deadlock.
The solution is to correct this misunderstanding:
The WCF Service will be called by another external party, and they expect the call and return to be synchronous.
Since you're dealing with a client/server scenario, you don't have to make it synchronous. The asynchrony of the client is completely independent from the asynchrony of the server.
So, just implement your WCF service asynchronously:
public static Task<List<Books>> GetBooksAsync(int companyId)
{
var response = await GetBooksAsync(companyId);
var content = response.Content;
List<Books> result = new List<Books>();
return Helpers.JSONSerialiser.Deserialize<BookList>(content);
}
The client can still call it synchronously if they wish to.

Asp MVC and Web Api Async Controllers

I was working with Asp MVC 3, and in my application was created Async controller with some methods like:
public void ActionAsync()
{
AsyncManager.OutstandingOperations.Increment();
AsyncManager.Parameters["someResult"] = GetSomeResult();
AsyncManager.OutstandingOperations.Decrement();
}
public JsonResult ActionCompleted(SometResultModel someResult)
{
return Json(someResult, JsonRequestBehavior.AllowGet);
}
And now, when I'm working with MVC4 and Web Api I need to create controller with async actions like in mvc 3. At current time it's looks like:
public Task<HttpResponseMessage> PostActionAsync()
{
return Task<HttpResponseMessage>.Factory.StartNew( () =>
{
var result = GetSomeResult();
return Request.CreateResponse(HttpStatusCode.Created, result);
});
}
Is it a good idea to make async actions in web api like this or there some better way is exist?
UPD. Also, if I will use
public async Task<HttpResponseMessage> ActionAsync()
{
var result = await GetSomeResult();
return Request.CreateResponse(HttpStatusCode.Created, result);
}
will this full action work in background thread? And how to make my GetSomeResult() function be awaitable? It's return that Task<HttpResponseMessage> is not awaitable.
There is a big difference with your original action in MVC 3 where you are basically releasing the client after the ActionAsync method is invoked (The client thread is released, and it has to call the ActionCompleted action afterward to get the results). If that's what you are looking for, you need to implement async code with tasks on the client side.
Your second version is to make the server code async but the client thread will still wait for a response synchronously. await GetResult will make the server thread to return to the ASP.NET thread pool until the GetResult method returns something, so that thread can be reused with another request. It does not have anything to do with background work. If you want to use a fire and forget approach, you need to use the Task.Factory.StartNew(() => your code) or ThreadPool.QueueUserWorkItem(() => your code)

Categories

Resources