Asynchronous task in asp .net MVC 5 - c#

I am trying to use async, await and task to implement one of an asynchronous requirement in my web application. I have below code in my controller class. What I want is that after the flow reaches to printing "controller 1", the system starts the business method on a separate thread and without waiting for the response system should reach to print statement in controller and print "controller 2". But when I am executing the below code I am getting output pattern as this-
controller 1
PlandesignBS
controller 2
Whereas I am expecting this-
controller 1
controller 2 (Flow should reach here immediately rather than waiting
for business methods to get executed First)
Contoller Class Code-
public class PlanDetailsController : Controller
{
[HttpPost]
public async Task<ActionResult> Generate(PlanDetailsVM planDetailsVM)
{
System.Diagnostics.Debug.WriteLine("controller 1");
//calls business method to generate
quoteId = await Task.Run(() => _planDesignBS.GenerateBS(planDetailsVM));
System.Diagnostics.Debug.WriteLine("controller 2");
return Json(quoteId , JsonRequestBehavior.AllowGet);
}
}
Business Method Code-
public int GenerateBS(PandetailsDTO plandetails)
{
System.Diagnostics.Debug.WriteLine("PlandesignBS");
//STEP 1-> call to dataLogic Layer to use Entity Framework
// STEP 2-> call to a method (this method is part of dll used
//in our project and we don't have any control on it)
return quoteId
}
EDIT: The reason I am trying to do this is that I want to use few http Posts requests from my client side. Suppose I call the Generate action method first from my client side and then don't want to wait for the response of it and at the same time want to invoke another http post.
EDIT: Including EF code where I am getting execption when I am trying to follow few solutions.This method will be called from my GenerateBS method. I am getting exception on this line inside getSICCode method-
DbContext.Database.ExecuteSqlCommand("spGET_SICCode #industryClasifID,#industrySubClasifID,#SICCode OUTPUT", industryClasifID, industrySubClasifID, SICCode);
EF Code-
public class PlanDesignRepository : Repository<myobject>, IPlanDesignRepository
{
private IDbSet<myobject> _dbSet;
private DbContext _dbContext;
private IDbSet<myobject> DbSet
{
get
{
if (_dbSet == null)
{
_dbSet = base.UnitOfWork.Context.Set<myobject>();
}
return _dbSet;
}
}
private DbContext DbContext
{
get
{
if (_dbContext == null)
{
_dbContext = base.UnitOfWork.Context;
}
return _dbContext;
}
}
public string GetSICCode(IndustryClassficDO industryClassficDO)
{
SqlParameter industryClasifID = new SqlParameter("industryClasifID", SqlDbType.Int);
industryClasifID.Value = industryClassficDO.IndustryClassificationId;
SqlParameter industrySubClasifID = new SqlParameter("industrySubClasifID", SqlDbType.Int);
industrySubClasifID.Value = industryClassficDO.IndustrySubClassificationId;
SqlParameter SICCode = new SqlParameter("SICCode", SqlDbType.VarChar, 10);
SICCode.Direction = ParameterDirection.Output;
DbContext.Database.ExecuteSqlCommand("spGET_SICCode #industryClasifID,#industrySubClasifID,#SICCode OUTPUT", industryClasifID, industrySubClasifID, SICCode);
return (string)(SICCode.Value);
}
}
EDIT: I have few http Post calls as shown below-
AngularJS code for AJAX calls:
$http({
url: key_Url_Generate,
method: Post,
params: $scope.PlanDetails
}).then(function (result) {
//Notify user for success or failure
}

The usage of await means that you are waiting for the task to complete before continuing the code. If you want it to execute simultaneously with the other code you should change it like this
public class PlanDetailsController : Controller
{
[HttpPost]
public async Task<ActionResult> Generate(PlanDetailsVM planDetailsVM)
{
System.Diagnostics.Debug.WriteLine("controller 1");
//calls business method to generate
var task = Task.Run(() => _planDesignBS.GenerateBS(planDetailsVM));
System.Diagnostics.Debug.WriteLine("controller 2");
var quoteId = await task;
return Json(quoteId , JsonRequestBehavior.AllowGet);
}
}
However I wouldn't recommend this as it serves no purpose and can degrade performance via starving the ASP.NET thread pool of threads. Why do you want to do this anyway?

This line:
quoteId = await Task.Run(() => _planDesignBS.GenerateBS(planDetailsVM));
asynchronously waits for the call inside Task.Run to complete. That's why you're seeing "PlandesignBS" printed and only after that you see the call to controller 2.
If you want to issue a "fire and forget" style of execution, you actually don't want to wait at all.
You should definitely not use Task.Run inside ASP.NET at all, as it's dangerous. Instead, if you're on .NET 4.5.2 and above, you can use HostingEnvironment.QueueBackgroundWorkItem or if not, you can use BackgroundTaskManager by Stephan Cleary, which safely registers the background thread with the application pool, so your thread doesn't get terminated once a re-cycle is invoked:
[HttpPost]
public ActionResult Generate(PlanDetailsVM planDetailsVM)
{
System.Diagnostics.Debug.WriteLine("controller 1");
HostingEnvironment.QueueBackgroundWorkItem(ct => _planDesignBS.GenerateBS(planDetailsVM));
System.Diagnostics.Debug.WriteLine("controller 2");
return Json(quoteId , JsonRequestBehavior.AllowGet);
}
As a side note - If you're calling Entity Framework, there's usually no need to involve extra threads. Version 6 and above exposes TAP async based API's which are Task returning which you can use at your disposal for doing these kind of IO based queries.

The problem is, that you simply cannot dispatch asynchronous code that fall out of the asp.net request pipeline per se.
The async feature in asp.net MVC only improves the internal handling of threads inside the IIS process. It does NOT enable you to finish and close the response to the client, before all execution is completed.
If you want to do a real "fire-and-forget" solution, you need more than that.
I recently had the requirement that mails be sent asynchronously after a form in a web application has been submit.
The only way I found to solve this problem was to use hangfire:
http://hangfire.io/

Try this:
public class PlanDetailsController : Controller
{
[HttpPost]
public async Task<ActionResult> Generate(PlanDetailsVM planDetailsVM)
{
System.Diagnostics.Debug.WriteLine("controller 1");
//calls business method to generate
var quoteIdTask = GenerateAsyncBs();
System.Diagnostics.Debug.WriteLine("controller 2");
int id = await quoteIdTask;
return Json(quoteId , JsonRequestBehavior.AllowGet);
}
}
private async Task<int> GenerateBsAsync()
{
//do your planDetails logic here.
}

Related

Run parallel task and return the first completed task and run other in background to save results

I have a WebApi in .NET CORE 3.1 in which I'm trying to get results from a service (other third party). I have created multiple requests in my API for the same service but some parameters of every request are different, the results return from service will be different for every request but structure of result will be same.
As all requests are independent of each other I want to run all that in parallel. And I want to return the first result as soon as received from the service from my API, but I also want to run all other requests in background and save there results in REDIS.
I tried to create a sample code to check if possible:
[HttpPost]
[Route("Test")]
public async Task<SearchResponse> Test(SearchRequest req)
{
List<Task<SearchResponse>> TaskList = new List<Task<SearchResponse>>();
for (int i = 0; i < 10; i++)
{
SearchRequest copyReq = Util.Copy(req); // my util function to copy the request
copyReq.ChangedParameter = i; // This is an example, many param can changed
TaskList.Add(Task.Run(() => DoSomething(copyReq)));
}
var finishedTask = await Task.WhenAny(TaskList);
return await finishedTask;
}
private async Task<SearchResponse> DoSomething(SearchRequest req)
{
// Here calling the third party service
SearchResponse resp = await service.GetResultAsync(req);
// Saving the result in REDIS
RedisManager.Save("KEY",resp);
return resp;
}
Now I'm wondering if this is correct way to dealing with this problem or not. If there is any better way please guide me to that.
EDIT
Use Case scenario
I have created a web app which will fetch results from my webapi and will display the results.
The WebApp searches for list of products (can be anything) by sending a request to my api. Now my api creates different requests as the source (Let's say Site1 and Site2) for results can be different.
Now the third party handles all requests to different sources(Site1 and Site2) and convert there results into my result structure. I have just to provide the parameter from which site i want to get results and then call the service at my end.
Now I want to send the results to my WebApp as soon as any source(site1 or site2) gives me the result, and in background I want to save the result of other source in redis. So that I can fetch that too from my webapp on other request hit.
The code looks pretty good; there's only one adjustment I'd recommend: don't use Task.Run. Task.Run causes a thread switch, which is totally unnecessary here.
[HttpPost]
[Route("Test")]
public async Task<SearchResponse> Test(SearchRequest req)
{
var TaskList = new List<Task<SearchResponse>>();
for (int i = 0; i < 10; i++)
{
SearchRequest copyReq = Util.Copy(req); // my util function to copy the request
copyReq.ChangedParameter = i; // This is an example, many param can changed
TaskList.Add(DoSomething(copyReq));
}
return await await Task.WhenAny(TaskList);
}
private async Task<SearchResponse> DoSomething(SearchRequest req)
{
// Here calling the third party service
SearchResponse resp = await service.GetResultAsync(req);
// Saving the result in REDIS
RedisManager.Save("KEY",resp);
return resp;
}
Note that this is using fire-and-forget. In the general sense, fire-and-forget is dangerous, since it means you don't care if the code fails or if it even completes. In this case, since the code is only updating a cache, fire-and-forget is acceptable.

Keep an async task to continue exeuction after result is returned (ASP.NET)

I am building an ASP.NET application. The application displays result from a third-party WebAPI. If the WebAPI takes more than 2 seconds, it will return the cached result.
The code below creates two async tasks, one for calling the WebAPI and one for retrieving from the cache. It also has a timer task. If the web task completes in less than 2 seconds, its result is returned. If the timer task ends first or the web task has an exception, the cached result is returned. The code runs so far so good.
public async Task<ActionResult> Index()
{
Task<string> readFromCacheTask = ReadFromCacheAsync();
Task<string> readFromWebTask = ReadFromWebAsync();
try
{
Task timerTask = Task.Delay(2000);
Task completedTask = await Task.WhenAny(readFromWebTask, timerTask);
await completedTask;
if (readFromWebTask.Status == TaskStatus.RanToCompletion)
{
return Content(readFromWebTask.Result);
}
}
catch (Exception)
{
// Return cache result if there is an error getting from web.
}
return Content(await readfromCacheTask);
}
Question: What I want in addition is allowing the web task to continue running even after the cached result is returned to the caller so that an updated web result will be cached and will be available for the next caller.
What I have in mind is spawning an async background thread using Task.Run in Application_Start. The thread contains a task collection and runs in a continuous loop. When the controller is called, it creates a readFromWebTask and adds it to the task collection in the background thread.
Is this a good approach? Are there better ways to achieve?
Clarification #2019-02-27 14:46: The readFromWebTask contains the logic for updating the cache. From the run-time observation, ASP.NET does not let readFromWebTask to run past the request completion. The task was aborted and did not run to the end. In following example, only Foo was written in the text file. Bar is not there.
public class HomeController : Controller
{
public async Task DelayAndLog()
{
string path = #"C:\temp\test.txt";
System.IO.File.AppendAllText(path, string.Format("{0}: Foo", DateTime.Now));
await Task.Delay(2000);
System.IO.File.AppendAllText(path, string.Format("{0}: Bar", DateTime.Now));
}
public async Task<ActionResult> Index()
{
Task a = DelayAndLog();
await Task.Delay(1);
return Content("Hello World");
}
}

Error: An asynchronous module or handler completed while an asynchronous operation was still pending

I have a Controller Action method to Save user Details like below.
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
return Json(new { Success = String.IsNullOrEmpty(result) });
}
So far there is no issue in above 4 lines function.
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
new MailController().CreateUser(user.userDetails); //<==This is problem line of code.
}
and Below is my Mail Controller.
public class MailController : MailerBase
{
public void CreateUser(ViewModel.VM_User user)
{
To.Add(user.EmailAddress);
From = System.Configuration.ConfigurationManager.AppSettings["emailSender"];
Subject = "Hi";
Email("CreateUser", user).DeliverAsync();
}
}
in the above code, I am facing problem when I execute the Email Sending code. I get below error message.
An asynchronous module or handler completed while an asynchronous
operation was still pending
Please suggest the corrective action !!
This is because async methods track their completion, even async void. They do this by registering with the SynchronizationContext when starting and marking the operation complete when returning. ASP.NET tracks all created operations and requires them to all be completed before your action returns, otherwise it returns an error to the HTTP client. If you need to run a “fire and forget” method, you must manually avoid launching it on the ASP.NET SynchronizationContext. For example, await Task.Run(() => CallFireAndForget()) (await so that you wait for the synchronous portion to run on the thread pool. This works because CallFireAndForget() is async void. To fire a method which returns a Task as fire-and-forget, you have to explicitly avoid returning the Task to Task.Run() like this: await Task.Run(() => { CallFireAndForgetAsync(); })).
Another way to get the same error message to show up should be to write this in an asynchronous action:
// BAD CODE, DEMONSTRATION ONLY!
AsyncOperationManager.CreateOperation();
If you use the AsyncOperation API, you have to ensure you call AsyncOperation.OperationCompleted() prior to allowing the action method to return.
In your case, if the mail sending is really intended to be a fire-and-forget task, you can do the following inside of CreateUser after changing its signature to async Task CreateUserAsync(ViewModel.VM_User user):
await Task.Run(() => Email("CreateUser", user).DeliverAsync());
and in your action:
await new MailController().CreateUserAsync(user.userDetails);
Ideally, you wouldn’t use Task.Run() for something like this. Instead, you can temporarily replace the current SynchronizationContext instead (NOTE WELL: Never await inside the try{}—instead, if you need to, store the Task in a local and await it after restoring the original SynchronizationContext (it may be safer to use a primitive such as SynchronizationContextSwitcher.NoContext() (source) which does the right thing)):
var originalSynchronizationContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
new MailController().CreateUser(user.userDetails);
}
finally
{
SynchronizationContext.SetSynchronizationContext(originalSynchronizationContext);
}
Does DeliverAsync return a Task? If it does, try this.
public class MailController : MailerBase
{
public async Task CreateUserAsync(ViewModel.VM_User user)
{
To.Add(user.EmailAddress);
From = System.Configuration.ConfigurationManager.AppSettings["emailSender"];
Subject = "Hi";
await (Email("CreateUser", user).DeliverAsync());
}
}
And then in your controller, await the task returned by CreateUserAsync.
public async Task<ActionResult> SaveUser(ViewModel.VM_CreateUser user)
{
var result = await _user.Save(userDetails);
await (new MailController().CreateUserAsync(user.userDetails));
return Json(new { Success = String.IsNullOrEmpty(result) });
}
note: If your goal is to make sending the email a background fire and forget operation, this is not it.

Are there any benefits of using a task if we synchronously wait for the result?

I have a Partial Action that needs to call an async method that returns Task from a DB Call. Since I can't have an async Partial Action I need to wait for the task to complete:
public ActionResult TopMenu()
{
var topMenuTask = Task.Run<IEnumerable<TopMenuItem>>( () =>
{ return _menuService.GetTopMenu(); });
topMenuTask.Wait();
//view model population code goes here
return PartialView(viewModel);
}
It is my understanding the benefit of doing an async DB (IO) call is we are requeueing the thread to the thread pool so it can serve more requests while the IO is finishing, but wouldn't this be a moo point in this case as we are synchronously waiting for it?
I assume from your problem description that GetTopMenu returns a Task, and thus should actually be called GetTopMenuAsync.
In this case, your best option is probably what you are already doing:
public ActionResult TopMenu()
{
var topMenuTask = Task.Run(() => _menuService.GetTopMenuAsync());
var topMenu = topMenuTask.Result;
//view model population code goes here
return PartialView(viewModel);
}
It is my understanding the benefit of doing an async DB (IO) call is we are requeueing the thread to the thread pool so it can serve more requests while the IO is finishing, but wouldn't this be a moo point in this case as we are synchronously waiting for it?
Correct. By wrapping the (asynchronous) DB access within a (synchronous) partial action, the code is nullifying the benefits of async.
This is a limitation of the platform (ASP.NET MVC). Please vote on the issue and on uservoice.
You will summon a thread from the thread pool that will execute that logic but the web request thread will be blocked waiting for the result. So no, there is no benefit.
Why can´t you do this?:
public async Task<ActionResult> TopMenu()
{
var topMenu = await Task.Run<IEnumerable<TopMenuItem>>( () => { return _menuService.GetTopMenu(); });
//view model population code goes here
return PartialView(viewModel);
}
Another option is to use the classic async MVC way, but you have to derive your controller from AsyncController rather than Controller:
public void TopMenuAsync()
{
AsyncManager.OutstandingOperations.Increment();
Task.Run<IEnumerable<TopMenuItem>>(() => { return _menuService.GetTopMenu(); })
.ContinueWith(t =>
{
AsyncManager.Parameters["topMenu"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult TopMenuCompleted(TopMenuItem topMenu)
{
//view model population code goes here
return PartialView(viewModel);
}
You should not be using Task.Run in ASP.NET in the fist place. You're still limited by a single HTTP request/response time frame, it won't speed up the content delivery and will just introduce an extra thread switch, check this.
So, your action should look like this:
public ActionResult TopMenu()
{
var topMenu = return _menuService.GetTopMenu();
//view model population code goes here
return PartialView(viewModel);
}
If your partial views may take a while to render, consider using a technique like this:
http://blog.michaelckennedy.net/2012/11/13/improve-perceived-performance-of-asp-net-mvc-websites-with-async-partialviews/
You are correct.. and it is generally the reason why you defer the decision of using a Task up to the caller, or provide async and non-async versions of the call (e.g: GetTopMenu and GetTopMenuAsync). If you force the use of an async call then you run into situations just like this.
Your alternative is to load the view and asynchronously make the call via AJAX. You can also give the user visual feedback in this scenario. Utilising await in this scenario will free up the worker process for IIS to continue serving requests.

Asynchronous begin/end in same method

(I'm using .Net 4.0)
I want to call a WCF service asynchronously, from my service layer. This service layer is used by and MVC.Net controller. I've read that it's good practice to call a WCF service asynchronously. So I'm using begin/end (apm). I want to double check if I'm doing it richt:
public byte[] GetSomeData()
{
IAsyncResult result = myServiceClient.BeginDoSomething(someInputValue, null, null);
var data = _pdfCreatieService.EndCreateForPreview(result);
return data;
}
I'm not totally sure about the code above, because I've seen constructions like the code below, which seem a bit more complex and unnecessary in my case:
public byte[] GetSomeData()
{
var myState = new MyState();
IAsyncResult result = _myServiceClient.BeginDoSomething(someInputValue, CreateForPreviewCallback, myState);
result.AsyncWaitHandle.WaitOne();
return myState.Bytes;
}
private void DoSomethingCallback(IAsyncResult result)
{
var myState = (MyState)result.AsyncState;
myState.Bytes = _myServiceClient.EndDoSomething(result);
}
Thanks Avner Shahar-Kashtan, Ned Stoyanov and Noseratio. Your answers are really insightfull!
What your code will do is, in effect, take an asynchronous method and call it synchronously. When you call the EndDoSomething method, you are effectively blocking your thread until the asynchronous method has completed, which is exactly the opposite of an async call.
Of course, your second code block also calls the code synchronously, by blocking executing explicitly, using the waithandle.
What you want to do is, instead of returning the byte[] from your initial method, have your DoSomethingCallback do something active with the bytes - either store them in some class member that can be checked by the controller, raise an event, or do something else. If you're waiting for your async call, you're not getting any benefit.
What you can also do if you're using .NET 4.5 or higher (or .NET 4.0 in VS2012, using the BCL Async Package) is to use async/await, which is a nice wrapper which will allow you to consolidate the calling method and the callback method into a single, more coherent method.
But regardless of the syntax or libraries you choose, your first step is to understand that async programming necessarily breaks your code's control flow into the invocation and the result callback, or continuation of the asynchronous operation.
In ASP.NET MVC, you only benefit from asynchronous calls if your controller is asynchronous too. Apparently, this is not the case with your code, because you're blocking with WaitOne inside your controller's method.
Implementing asynchronous controllers is really easy with .NET 4.5, check "Using Asynchronous Methods in ASP.NET MVC 4" for more info.
With .NET 4.0, it's a bit more tedious, check "Using an Asynchronous Controller in ASP.NET MVC". Your controller should derive from AsyncController and use AsyncManager to notify the ASP.NET stack about the pending asynchronous operations.
Here's an example from there for .NET 4. 0, adapted for your case(untested). Note the use of Task.Factory.FromAsync and Task.ContinueWith:
public static class WcfExt
{
public static Task<byte[]> DoSomethingAsync(
this IMyService service,
string someInputValue)
{
return Task.Factory.FromAsync(
(asyncCallback, asyncState) =>
service.BeginDoSomething(someInputValue, asyncCallback, asyncState),
(asyncResult) =>
service.EndDoSomething(asyncResult);
}
}
public class PortalController : AsyncController
{
public void NewsAsync(string someInputValue) {
AsyncManager.OutstandingOperations.Increment();
var myService = new MyService();
myService.DoSomethingAsync(someInputValue).ContinueWith((task) =>
{
AsyncManager.Parameters["data"] = task.Result;
AsyncManager.OutstandingOperations.Decrement();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
public ActionResult NewsCompleted(byte[] data)
{
return View("News", new ViewStringModel
{
NewsData = data
});
}
}
Both of your approaches are doing what is called sync over async. That is executing an asynchronous method in synchronous fashion. A better approach would be to build your own asynchronous method to rerieve the data with TaskCompletionSource. I haven't tested this, but you should be able to do something like this:
public Task<byte[]> GetSomeDataAsync()
{
var tcs = new TaskCompletionSource();
IAsyncResult result = myServiceClient.BeginDoSomething(someInputValue, x =>
{
try
{
var data = _pdfCreatieService.EndCreateForPreview(result);
tcs.SetResult(data);
}
catch(Exception ex)
{
tcs.SetException(ex);
}
}, null);
return tcs.Task;
}
Then to use it simply do this
GetSomeDataAsync().ContinueWith(t => /*<Use retrieved data from t.Result>*/);
This code will return straight away and once the asynchronous operation is complete the ContinueWith part will execute.

Categories