Task vs Void In Terms Of Controller Disposal - c#

When I use void for the controller's return type, I see that controller is disposed before the action is completed and I get the
"An asynchronous module or handler completed while an asynchronous
operation was still pending."
error.
public async void Test(){
SomeResult result = await GetSomethingAsync();
int a = result.b;
}
But if I use Task instead of void, controller is disposed after the action completes.
Why is this behavior?

In short, because MVC framework cannot await "void" :)
Action of your controller is not the end of the pipeline in fact it is somewhere in the middle. So when you make a return type of your action as "void" the thread that executes the code exists your "Test" method and starts executing what was after the invocation of it. Since it can't await it it assumes that the action has finished executing and that the framework can "kill" your controller.
If you look at the source code you'll find that these two cases are executed in a different way. In particular the one that returns "void" is not awaited. I would recommend having the async/await flow all way, instead of returning "void".
Is there a reason you want to have a return type of "void"?

Related

Async lambda: "a task was cancelled"

Here's the workflow:
Incoming HTTP request to WebApi2 endpoint.
Make synchronous (e.g. not async) call to get some data.
Map response from DB entity to API model.
a. Executes AutoMapper mapping.
b. Includes the following snippet (see below).
c. If operation is "quick", no issue. If operation is "slow", then "a task was cancelled" exception is thrown.
I get lucky in cases when the mapping action is quick. But if I add a Task.Delay(2000), then I get the exception in question. It seems that ASP.NET is not "waiting" for my async lamba to complete?
Here is the body of the mapping expression:
mapping.AfterMap(async (entity, model) => {
var child = await _childRepo.Get(entity.ChildId);
await Task.Delay(2000); // For testing, of course.
if (child != null)
{
// Fill in some properties on model
}
});
Note that this is example code, and I don't intend to make additional DB/repo calls during mapping in "real life".
AfterMap takes an Action, which is a synchronous delegate, not an asynchronous delegate (as I explain on my blog). As such, it does not work as expected with async lambdas.
In this case (since the delegate returns void), the compiler will actually allow an async lambda; however, it will compile to an async void method. (The compiler does this to allow async event handlers). As I describe in my MSDN article on async best practices, you should avoid async void.
One of the reasons to avoid async void is that it is very difficult to detect when an async void method has completed. In fact, (with the exception of WebForm lifetime events), ASP.NET will not even attempt to do so.

Calling async method inside getter throws exception

I've build an API-endpoint to fetch available languages from. In my MVC application, I have a helper to fetch the languages async. The method is defined like:
public static async Task<Languages> GetLanguagesAsync()
{
var apiResponse = await APIHelper.GetContentGetAsync("Languages").ConfigureAwait(false);
return apiResponse.LanguagesDataModel;
}
In my View I want to bind a dropdownlist to the list of available languages the user can select from.
#Html.DropDownListFor(m => m.Language, LanguageHelper.AvailableLanguages)
The getter is defined the following:
public static IEnumerable<SelectListItem<string>> AvailableLanguages
{
get
{
var result = GetLanguagesAsync().Result;
return new List<SelectListItem<string>>(result.Languages.Select(l => new SelectListItem<string> {Value = l.Key, Text = l.Value}));
}
}
However, I always get an error at line var result = GetLanguagesAsync().Result; which is the most upvoted answer from here.
The exception thrown is
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it.
As stated here the called action is marked async.
Razor code today cannot handle asynchronous calls, even if you're wrapping those calls with synchronous blocking (which is a design mistake). The fact that the action is async is immaterial, because this is during the processing of the view, after the action has completed.
Async views have been added to ASP.NET Core; however, it should still be an uncommon use case.
In general, your options are:
Make the helper synchronous "all the way"; i.e., use synchronous API calls instead of asynchronous, and get rid of the sync-over-async code.
Add the data to your view models (possibly as part of a common VM base).
In this specific case, I agree with Panagiotis' comment that this kind of unchanging information should only be loaded once per app, not once per call. Wrapping a synchronous implementation inside a Lazy<T> would be the easiest solution.

Problems And Errors While Implementing Async Controller Action

It seems that I have massive problems understanding the topic regarding async-operations in C# and especially ASP.NET MVC in Controller.
I have a Controller for all of my AJAX-Requests from my Webpage. for each I have an action. Now I try to implement like a 'notification-system'. I created a class which handles the notification in a Queue, which are selected through a dictionary using the SessionID.
Because I am using Reverse-AJAX, the Thread working on the AJAX-Response needs to be hold at the Server. Therefore, I used Thread.Sleep in combination with a while to check if the queue has elements or not. Here is the part of the controller:
public class AJAXController : AsyncController
{
public async void polling()
{
if (Session["init"] == null) //so the sessionID is not changing on every request
Session.Add("init", 0);
NotificationQueue queue =
NotificationQueue.getInstance(HttpContext.Session.SessionID);
object responseObj = null;
responseObj = await Task.Run(() =>
{
while (queue.getSize() == 0)
Thread.Sleep(200);
return queue.getNextQueueElement(); //behind this is queue.Dequeue();
});
Response.Write(new JavaScriptSerializer().Serialize(responseObj));
}
}
Basically, I don't now what is incorrect with that code - neither I know with is correct.
The syntax is correct, but when I try to use the website, the Server answers with: 500 (internal Server error), Message: >>An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it.<<
Do I need an AsyncController? The other methods aren't Asynchronous because these are just simple responses.
I tried to use public async void pollingAsync() in Combination with public async string pollingCompleted(string response), but the parameter was null every time.
My Questions are the above and how I can solve the problem. Is there a better solution, and when yes, how could I implement this?
I appreciate any help!
Don't use async void, use async Task instead. async void operations are generally fire and forget, as you have no way of asynchronously waiting on them. Also, there's no need to use AsyncController when using async-await. You can read more about that here
You need:
public async Task PollingAsync()
{
if (Session["init"] == null) //so the sessionID is not changing on every request
Session.Add("init", 0);
NotificationQueue queue =
NotificationQueue.getInstance(HttpContext.Session.SessionID);
while (queue.GetSize() == 0)
await Task.Delay(200);
var responseObj = queue.getNextQueueElement();
Response.Write(new JavaScriptSerializer().Serialize(responseObj));
}
Generally speaking, as a side note, you can get around the "polling" experience by using websockets, with a technology such as SignalR this is even made quite easy and friendly. I'd recommend looking into that.

Asynchronous web service in ASP. NET MVC

I am writing an ASP.NET MVC 5 application which among others uses web services to get/process some the data.
The data flow of the app is following: MVC Action -> Service B -> ExtSvc which is async wrapper of a web service
Here are some examples:
public class ExtSvc
{
//Convert Event based async pattern to task based async pattern:
private Task<Response> ProcessExtRequestAsync(Request request)
{
TaskCompletionSource<Response> taskCompletionSource =
AsyncServiceClientHelpers.CreateSource<Response>(request);
ProcessRequestCompletedEventHandler handler = null;
handler =
(sender, e) =>
AsyncServiceClientHelpers.TransferCompletion(
taskCompletionSource,
e,
() => e.Result,
() => this.Service.ProcessRequestCompleted -= handler);
this.Service.ProcessRequestCompleted += handler;
try
{
this.Service.ProcessRequestAsync(request, taskCompletionSource);
}
catch (Exception)
{
this.Service.ProcessRequestCompleted -= handler;
taskCompletionSource.TrySetCanceled();
throw;
}
return taskCompletionSource.Task;
}
//Usage:
public async Task<Response> UpdateRequest(some arguments)
{
//Validate arguments and create a Request object
var response = await this.ProcessExtRequestAsync(request)
.ConfigureAwait(false);
return response;
}
}
Class B is the one that uses ExtSvc in a synchronous way
public class B
{
public ExtSvc service {get; set;}
public Response Update(arguments)
{
//some logic
var result = this.ExtSvc.UpdateRequest(arguments).Result;
//some logic
return result
}
}
Finally the MVC action (also synchronous)
public ActionResult GetResponse(int id)
{
//some logic
B.Update(id);
//some logic
return View(...);
}
The described flow throws an error
A first chance exception of type 'System.InvalidOperationException'
occurred in System.Web.dll
Additional information: An asynchronous operation cannot be started at
this time. Asynchronous operations may only be started within an
asynchronous handler or module or during certain events in the Page
lifecycle. If this exception occurred while executing a Page, ensure
that the Page is marked <%# Page Async="true" %>. This exception may
also indicate an attempt to call an "async void" method, which is
generally unsupported within ASP.NET request processing. Instead, the
asynchronous method should return a Task, and the caller should await
it.
on the following line of ExtSvc : this.Service.ProcessRequestAsync(request, taskCompletionSource); ProcessRequestAsync is a void method
So it corresponds to:
This exception may
also indicate an attempt to call an "async void" method, which is
generally unsupported within ASP.NET request processing
I know that converting GetResponse MVC action to asynchronous (by using async/await) and also converting the B class that actually uses ExtSvc to be asynchronous resolves the issue.
BUT my questions is:
If I can't change the signature of B class (because of an interface it implements) to return Task<Response> instead of Response it basically means that I can't use async/await on it so how this issue could be resolved?
ProcessRequestAsync is void but it's not async void. It looks like it's an EBAP API. EBAP components generally use AsyncOperationManager/AsyncOperation, which in turn do use SynchronizationContext to notify the underlying platform of the asynchronous operation (the last link is to my MSDN article on SynchronizationContext).
The exception you're seeing is because ASP.NET sees that notification (of the asynchronous operation starting) and says "whoa, there, fella. You're a synchronous handler! No async for you!"
Hands-down, the best approach is to make all methods asynchronous that should be asynchronous. This means B.Update should be B.UpdateAsync. OK, so there's an interface IB.Update - just change the interface to IB.UpdateAsync too. Then you're async all the way, and the code is clean.
Otherwise, you'll have to consider hacks. You could use Task.Run as #neleus suggested - that's a way of avoiding the ASP.NET SynchronizationContext so it doesn't "see" the asynchronous operation starting - but note that "ambient context" such as HttpContext.Current and page culture is lost. Or, you could (temporarily) install a new SynchronizationContext() onto the request thread - which also avoids the ASP.NET SynchronizationContext while staying on the same thread - but some ASP.NET calls assume the presence of the ASP.NET SynchronizationContext and will fail.
There's another hack you could try; it might work but I've never done it. Just make your handler return a Task<ActionResult> and use Task.FromResult to return the view: return Task.FromResult<ActionResult>(View(...)); This hack will tell ASP.NET that your handler is asynchronous (even though it's not).
Of course, all of these hacks have the primary disadvantage that you're doing sync-over-async (this.ExtSvc.UpdateRequest(arguments).Result), which means you'll be using one extra unnecessary thread for the duration of each request (or two threads, if you use the Task.Run hack). So you will be missing all the benefits of using asynchronous handlers in the first place - namely, scalability.
I think the error occurs because your code
this.Service.ProcessRequestAsync(request, taskCompletionSource);
actually calls SynchronizationContext's OperationStarted method that results in error as described here.
As a possible solution you can call your action on ThreadPoolSynchronizationContext
public async Task<ActionResult> GetResponse(int id)
{
//some logic
await Task.Run(() => { B.Update(id); });
//some logic
return View(...);
}
but it adds some overhead of utilizing a thread from the pool.

Asynchronous method appears to not be fully executing

I have a process where an incoming user request to our system is being handled. I also want to add some metadata about the request to a database table without impacting the responsiveness of the main process. To achieve this I added a call to an asynchronous method like this:
public static ReturnObject ResponsiveMethod(string ip, string code)
{
// ... some reasonably quick code
IPDetail.InsertAsync(ip); // <-- call to the async method
return new ReturnObject(code);
}
The InsertAsync() method looks like this:
public static void InsertAsync(string ipAddress)
{
Action action = () => IPDetail.Insert(ipAddress);
action.BeginInvoke(aResult => Log.Debug("Completed Insert"), null);
}
And finally, the normally non-asynchronous method called Insert():
private static void Insert(string ipAddress)
{
ApplicationContextHelper.LoadApplicationContext();
var helper = new GeoLocationHelper();
var result = helper.GetDetailsFromIP(ipAddress);
Log.InfoFormat("Succesfully retreived IP data {0}.", ipAddress);
result.Save();
}
In my unit tests the InsertAsync() call works perfectly. Inside the method calls in Insert() there are many operations occuring which are detailed by logging, and all the expected log messages are there, as well as the final result of the result.Save() method.
However, we have a webservice which utilizes something like the ResponsiveMethod() method above and for some reason the asynchronous calls do not complete. All of the logging in the LoadApplicationContext() method gets fired, but after that there is no log activity related to the Insert() and the result.Save() is never getting executed.
Revised summary question to be more concise
My current thinking is that the webservice has completed its task and the thread which called the asynchronous no longer exists. Would this stop the async call from completing?
I've never used BeginInvoke before, but usually where there's a Begin*, you also need the coresponding End*. Please add one, along with correct exception handling.
My first thought is that you may be throwing an exception on your async call in the web service scenario for some reason. I know you've probably pared it down to post it on the web, but is there any "more-or-less unfailable" error handling code in there?
Are you relying on the identity of the caller in the Async method call? The identity may be lost when called from the web service.

Categories