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)
Related
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
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;
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.
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.
i want a thing in JsonResult that they respond after the got request from the browser [client side] and respond them quickly before done the task.
means request come respond before task done and run a thread to done the task.
can anyone show me the code for doing that in asp.net MVC
Isn't AJAX sufficient for your scenario?
$.getJSON('#Url.Action("Foo")', function(result) {
// once the task completes this callback will be executed
});
// flow continues to execute normally
and on the server side:
public ActionResult Foo()
{
// TODO: some task
return Json(someResult, JsonRequestBehavior.AllowGet);
}
If this task is I/O intensive you could take advantage of asynchronous controllers and I/O Completion Ports.
If you have a fire-and-forget scenario you could simply start the task and return immediately from the controller:
public ActionResult StartTask()
{
// Fire the task
Task.Factory.StartNew(() =>
{
// TODO: the task goes here
// Remark: Ensure you handle exceptions
});
// return immediately
return Json(
new { Message = "Task started" },
JsonRequestBehavior.AllowGet
);
}