Asynchronous web service in ASP. NET MVC - c#

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.

Related

.NET MVC Fire and Forget Method also return View immediately

I'm looking on best solution for Fire & Forget a method at the Action and return View immediately. So far as i can see if i make the Action's return type Task<ActionResult> and await the asynchronous method it will work but Action is also waiting that asynchronous method to done before return View as expected.
In the other hand if i don't await the asynchronous method, method's execution will be cut after the View returns.
I'm asking for what's the best solution for situation like that ? My code is like that :
public class DefaultController : Controller
{
// GET: Default
public async Task<ActionResult> Index()
{
await Asynchronous();
return View();
}
public async Task Asynchronous()
{
var FilePath = ControllerContext.HttpContext.Server.MapPath("~/HelloWorld.txt");
for(int i = 0; i <= 100; i++)
{
await Task.Delay(15000); // Wait 15 Seconds.
System.IO.File.AppendAllLines(FilePath, new string[] { i.ToString() });
}
}
}
I'm looking on best solution for Fire & Forget a method at the Action and return View immediately
That depends on what "best" means. Fire and Forget on ASP.NET is inherently unsafe, so there are varying degrees of how safe you want your code to be.
If your app must continue execution, then the only safe system is to have your action handler write what it wants to do into a safe storage mechanism (e.g., Azure Queue, MSMQ, or SQL Server). Once it has been safely stored, then your action method can return. Then you'll also have an independent background process (e.g., Azure Function, Win32 Service, or possibly a thread in your ASP.NET process only if you're very careful about how it's hosted). That background process will read from the safe storage and do the actual work.
If your app is fine with occasionally "losing" work (after returning success to the client), then you can use a less-safe mechanism such as HostingEnvironment.QueueBackgroundWorkItem for .NET 4.5.2, or my AspNetBackgroundTasks library for earlier versions.
Other alternatives are listed in my blog post on the subject.

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.

Exception when using await on async function

I'm trying to use LinqToTwitter to post newsarticles to Twitter from an ASP.NET CMS. I've created a static class that will handle it for me as the outcome will be logged anyway and won't affect the rest of the website. However, when I try to run it and post to Twitter, an exception occurs:
System.InvalidOperationException: An asynchronous module or handler completed while an asynchronous operation was still pending
The full class:
public static class SocialTwitter
{
internal async static void PostTwitter(string message)
{
await Post(message);
}
private async static Task Post(string message)
{
var auth = new SingleUserAuthorizer
{
CredentialStore = new SingleUserInMemoryCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["TwitterConsumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["TwitterConsumerSecret"],
AccessToken = ConfigurationManager.AppSettings["TwitterToken"],
AccessTokenSecret = ConfigurationManager.AppSettings["TwitterTokenSecret"]
}
};
await auth.AuthorizeAsync();
TwitterContext t = new TwitterContext(auth);
//Exception occurs here
Status tweet = await t.TweetAsync(Uri.EscapeDataString(message));
if (tweet != null)
{
Util.Log.Info(string.Format("Status posted: ({0}) {1}, {2}", tweet.StatusID, tweet.User.Name, tweet.Text));
}
else
{
Util.Log.Info("Error occurred posting status to Twitter");
}
}
}
The weird part (for me at least) is that I use the function as described in the tooltip and documentation of LinqToTwitter. I get that some function is completed while another was still running but isn't that what the "await" keyword is for?
I tried cutting out the PostTwitter function or the code after the point of failure but still the same result. I've searched a lot for a solution but I might just be terrible at searching or searching in the wrong direction. I even registered on SO just to ask this question! :D
The culprit here is the fact that you're invoking Post with an async void method:
internal static async void PostTwitter
Async void functions exists solely for event handling, and have very odd and unpredictable error handling when used outside of this context. See this MSDN article for information.
Switch your async void method to an async Task method and some actual errors may start to bubble to the surface:
internal static async Task PostTwitter
Stephen Cleary actually goes into this problem in great detail in another SO post. To quote from him directly regarding the behavior of asynchronous methods:
Historically, ASP.NET has supported clean asynchronous operations since .NET 2.0 via the Event-based Asynchronous Pattern (EAP), in which asynchronous components notify the SynchronizationContext of their starting and completing
Whenever your code calls into SocialTwitter.PostTwitter, the PostTwitter method notifies the SynchronizationContext that it's started, bypassing whichever framework you're using completely and going straight to the ASP.NET core. The PostTwitter method than starts the async Post method, which also notifies the SynchronizationContext that it has started.
The ASP.NET frameworks understand how to wait for Tasks, but, excluding WinForms and WebForms in very specific situations, know little about how to handle async void methods. Thus the framework believes the async void to be complete and it attempts to notify the SychronizationContext that the async void method has finished, even though the async void method spawned an async Task method that's still running. As part of a series of safety nets designed specifically to catch errors like this, the SynchronizationContext throws the InvalidOperationException that you've been seeing.

Categories