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.
Related
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"?
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.
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.
Every time I try to use the new Async and Await operators and return a collection of objects from a database I get an Invalid Operation exception. When I use it to only return a single Item it works fine.
Controller Code:
public async Task<ActionResult> EnvironmentList()
{
EfEnvironmentDataAccess dataAccess = new EfEnvironmentDataAccess();
ICollection<Environment> environments = await dataAccess.GetAllEnvironmentsAsync();
return PartialView(environments);
}
View Code:
<div class="ECURightCol">
<h3>Table Dumps</h3>
#Html.Action("EnvironmentList", "Environment")
#Html.Action("ComputerList", "Computer")
#Html.Action("ProductList", "Product")
#Html.Action("InstanceList", "Instance")
#Html.Action("ProfileList", "Profile")
The Data Access Code:
public ICollection<Environment> GetAllEnvironments()
{
using (EcuWebDataContext db = new EcuWebDataContext())
{
return db.Environments.OrderBy(e => e.Name).ToList();
}
}
public async Task<ICollection<Environment>> GetAllEnvironmentsAsync()
{
return await Task.Run(() => GetAllEnvironments());
}
The Error I get is:
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: HttpServerUtility.Execute blocked while waiting for an asynchronous operation to complete.
First of all, you cannot use asynchronous processing with child actions and I suppose this is what you are trying to do.
Secondly, you are not doing any asynchronous processing here by spinning up another thread to execute your code with the below line of code:
Task.Run(() => GetAllEnvironments());
It will block a thread at the end of the day and you will have nothing but a context switch overhead. EF6 will have support for asynchronous processing. For asynchronous queries with pure ADO.NET, have a look:
Asynchronous Database Calls With Task-based Asynchronous Programming Model (TAP) in ASP.NET MVC 4
It's been a while since this was answered but another way around is as follows:
Call your method from an action
#Html.Action("YourSyncMethod", "YourController")
Define it as a normal sync action
public ActionResult YourSyncMethod()
Then inside it call your async method
var taskResponse = YourAsyncMethod();
which will return a model with whatever you need
private async Task<YourModel> YourAsyncMethod()
It seems simpler than tampering with config options or creating more complex code
It's been some time since this question was answered. But I was having a similar situation with MVC 5 and I was able to make a [ChildActionOnly] work asynchronously just by commenting out the following line under <system.web> section of web.config file.
<system.web>
<!--<httpRuntime targetFramework="4.5" />-->
EDIT:
Consider this a workaround while you find a real solution for your situation. Please see Leri's comments below.
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.