Trying to use asp.net async page - c#

I have an ASP.NET (webforms) page that needs to call a stored procedure which may take up to a minute to return. I realise that this is not ideal but the the database side of this project is out of my hands and hence I must live with this problem.
Basically I am looking for some method which will allow the page to render without the stored procedure hanging the page - with the results from the database call being displayed when available.
So, I am looking at an async page. I have added "ASYNC=TRUE" to the top of the page and so far, I have the following:
private async void GetCampaignCounts(int CampaignID)
{
Task t = new Task
(
() =>
{
CampaignService cs = new CampaignService();
FilterSet.TargetCounts f = cs.GetCampaignDetails(CampaignID); //LONG RUNNING DB CALL
if (f.Total > 0)
{
panelStatsLeft.Visible = true;
//DO STUFF IN HERE
}
else
panelStatsLeft.Visible = false;
}
);
t.Start();
await t;
}
However, this still hangs the page whilst the database query is running!
Am I doing something totally wrong?!

The asynchronous requests in web applications are not intended to not hang the page. The request will still take the same time as the synchronous version. The benefits are in scalability (i.e. if your page could handle 100 simultaneous users before it would be able to handle 1000 if it is async on the same hardware assuming that the bottleneck was the request pipeline and not the database).
If you want to make the page load and update itself when the operation completes I am afraid you will need significantly more complex architecture. Your best bet is load the page and do an ajax request that runs the query and update the page when the request returns. It is a best practice to use async/await for this ajax request (again for scalability).

Personally i wouldn't bother tying the two together. A better operation is to offload the query elsewhere and return later to get the results.
So use a signalling framework such as signalr. Submit your report parameters, pass them to msmq where they can be handled on a different server or use a one way wcf request. When the request is received optionally store the result in the database and use signalling to notify client (either passing them the actual result or telling them the url where they can download the report from (pick it up from the database in that other url)). Async that into the users current browser page which presumably has a spinner saying "hey we are generating your report".
Consider signalling too.

As I describe in my MSDN article on the topic, async on ASP.NET is not a silver bullet:
When some developers learn about async and await, they believe it’s a way for the server code to “yield” to the client (for example, the browser). However, async and await on ASP.NET only “yield” to the ASP.NET runtime; the HTTP protocol remains unchanged, and you still have only one response per request. If you needed SignalR or AJAX or UpdatePanel before async/await, you’ll still need SignalR or AJAX or UpdatePanel after async/await.
This makes sense if you think about the HTTP protocol: there can be only one response for each request, and async doesn't change the HTTP protocol (more detail on my blog). So, if you return a response to the client (allowing it to render the page), then that request is done. You can't send a followup response later to change the page you already sent.
The proper solution is fairly complex, because ASP.NET wasn't designed to track "background" operations without a request. ASP.NET will recycle your application periodically, and if there are no active requests, it assumes that it's a good time to do so. So, "background" operations are in danger of being terminated without notice.
For this reason, the best solution is to have an independent worker process that actually executes the background operation using a basic distributed architecture (requests are placed into a reliable queue by the ASP.NET handler; requests will re-enter the queue automatically if the worker process fails). This also means your requests should be idempotent.
If you don't want this level of complexity, you can trade-off reliability for complexity. An intermediate step is Hangfire, which requires a database backend at least (which it uses for reliability instead of a queue). If you want the least reliability (and least complexity), you can just use HostingEnvironment.QueueBackgroundWorkItem or my ASP.NET Background Tasks library. I have a longer overview of these options on my blog.

Related

How to cancel ServiceStack async request?

I'm using ServiceStack v4, and making an async request like:
var client = new JsonServiceClient(url);
Task reqTask = client.GetAsync(request);
I'll be making a lot of requests simultaneously to different servers, and need to support cancellation to immediately cancel all outstanding calls. The underlying AsyncServiceClient has a method CancelAsync, but it doesn't seem to do anything.
How can I cancel the request such that it aborts the web request immediately?
Short version, it seems that CancelAsync does nothing indeed.
Looking at the code, it seems that ServiceStack uses the old way (v3) of asynchronously executing requests and notifying you with with callbacks, all built on top of WebRequest and the APM-style calls (BeginXXX,EndXXX).
The GetAsync, SendAsync methods are actually wrappers that use a TaskCompletionSource to provide a Task based interface over the actual asynchronous operation.
CancelAsync actually calls a CancelAsyncFn delegate that isn't set by the classes in JsonServiceClient's hierarchy, so calling it actually does nothing. I suspect this is provided to create custom clients and actions.
In any case, cancelling an HTTP request isn't so easy as it sounds. You need to understand what it is that you are trying to cancel.
You can't actually cancel the call to the server once it's left your application.
You can't tell the server or the intervening devices to stop processing, as HTTP doesn't provide such a mechanism. The server has to provide such an API, which is actually a new HTTP call.
You can't prevent the server from sending a response
You can cancel sending the entire request to the server. This will only work for requests that take a detectable amount of time to serialize
You can ignore the server's response and refuse to open a stream to read the body
You can stop reading the response from the server
You can simply ignore the results of the operation
Looking at the code, it seems that you can't do #4, #5 or #6, because AsyncServiceClient doesn't expose the IAsyncResult used internally. The only thing you can do is simply ignore the response and pay the price of deserialization. Note, the same applies if you use HttpClient's GetStringAsync methods instead of executing each individual step.
So, if cancel means abandoning the request, you can do this by simply ignoring the return value. If it means stop server processing, you can't.
Cancellable requests is a feature.
http://www.github.com/ServiceStack/ServiceStack/wiki/Cancellable-Requests
You basically tag your requests and the feature adds service calls to cancel using the tag. You also have to wrap logic in your service to handle this feature, much like the caching feature.

Run asynchronous operation in one controller method and get that operation's result in another

I have a long running operation you might read in couple of my another questions (for your reference here is first and second).
In the beginning of whole deal, project expose a form in which user should specify all necessary information about XML file and upload XML file itself. In that method all user input data caught and went to an WCF service that handles such king of files. Controller got only task id of such processing.
Then user got redirected to progress bar page and periodically retrieves status of task completeness, refreshes the progress bar.
So here is my issue comes. When processing of XML file if over, how can I get results back and show them to user?
I know that HTTP is stateless protocol but there is cookie mechanism that could help in this situation. Of course, I may just save processing results to some temporary place, like a static class in WCF server, but there is a high load on service, so it will eat all of supplied memory.
In other words, I would like to pass task to WCF service (using netNamedPipeBinding) and receive results back as fast as it really possible. I want to escape temporary saving result to some buffer and wait until client will gather it back.
As far as I go is using temporary buffer not on service side but at client's:
using (XmlProcessingServiceClient client = new XmlProcessingServiceClient())
{
client.AnalyzeXmlAsync(new Task { fileName = filePath, id = tid });
client.AnalyzeXmlCompleted += (sender, e) =>
{
System.Web.HttpContext.Current.Application.Lock();
// here is I just use single place for all clients. I know it is not right, it is just for illustrating purposes.
System.Web.HttpContext.Current.Application["Result"] = e;
System.Web.HttpContext.Current.Application.UnLock();
};
}
I suggest you to use a SignalR hub to address your problem. You have a way to call a method on the client directly to notify the operation completed. And this happen without having to deal with the actual infrastructure trouble there is in implementing such strategies. Plus SignalR plugs easily in an asp.net MVC application.
To be honest I didn't really get the part about the wcf server and stuff, but I think I can give you more of an abstract answer. To be sure:
You have a form with some fields + file upload
The user fills in the form and supplies an XML file
You send the XML file to an WFC services which procress it
Show in the mean time a progress bar which updates
After completion show the results
If this is not want you want or this is not what your question is about you can skip my answer, otherwise read on.
Before we begin: Step 3 is a bit ambiguous: It could mean that we send the data to the service and wait for it to return the result or that we send the data to the service and we don´t wait for it to return the result.
Situation 1:
Create in a view the form with all the required fields
Create an action in your controller which handles the postback.
The action will send the data to the service and when the service returns the result, your action will render a view with the result.
On the submit button you add an javascript on click event. This will trigger an ajax call to some server side code which will return the progress.
The javascript shows some sort of status bar with the correct progress and repeats itself every x seconds
When the controller finishes it will show the result
Situation 2:
-
-
After sending the data to the service the controller shows a view with the progress bar.
We add an javascript event on document ready which checks the status of the xml file and updates a progressbar. (same as the onclick event in step 4 in situation 1)
When the progressbar reaches 100% it will redirect to a different page which shows the results
Does this answer your question?
Best regards,
BHD
netNamedPipeBinding will not work for cross-machine communication if this is what you have in mind.
If you want to host our service on IIS then you will need one of the bindings that use HTTP as their transport protocol. Have a look at the duplex services that allow both endpoints to send messages. This way the server can send messages to the client anytime it wishes to. You could created a callback interface for progress reporting. If the task is going to take a considerable amount of time to complete, then the overhead of the progress reporting through HTTP might be ok.
Also have a look at Building and Accessing Duplex Services if you want to use a duplex communication over HTTP with Silverlight (PollingDuplexHttpBinding).
Finally you could look for a Comet implementation for ASP.NET. In CodeProject you will at least a couple (CometAsync and PokeIn).
I'm not sure if this is the best solution but I was able to do something similar. This was the general setup:
Controller A initialized a new class with the parameters for the action to be performed and passed the user's session object
The new class called a method in a background thread which updated the user's session as it progressed
Controller B had json methods that when called by client side javascript, checked the user's session data and returned the latest progress.
This thread states that using the session object in such a way is bad but I'm sure you can do something similar with a thread safe storage method like sql or a temp file.

Waiting for asynchronous result in a different controller method than the one that starts it

How would one go about waiting for an asynchronous result in a controller method? It seems like it would be trivial to implement, but so far I have not found a good example that is clean and elegant.
Here is the problem. I have two controller methods. Method A and Method B. Method A starts a long running process via TaskFactory, and uses ContinueWith to update a cached value when the process is finished. There are some intermediate steps between A and B. Now when I get to Method B, I need to check the value in the cache. If the value in the cache has not been updated yet, I need to wait for x amount of seconds and periodically check to see if the value has been updated.
I would prefer to handle all of this in the controller, so the client doesn't have to poll for the result (and having to rewrite the controller to handle the polling). But I haven't been able to figure out a way to implement the polling inside the controller method that doesn't block until the timeout expires(I'm worried about thread pool starvation). Maybe there isn't a clean implementation that I can use here. And if polling is the right or only answer, I will just have to accept it.
HTTP is a stateless protocol. You can't implement such polling on the server without blocking. You have 2 possibilities:
Polling on the client - have the client hammer your server at regular intervals with AJAX requests
Use push and have the server notify the client(s) when some task completes - checkout SignalR
You can wait for mvc 4 to get async controllers or get beta of mvc 4 and .net 4.5.

MVC3 action return EmptyResult after timeout?

I'm currently calling the Facebook graph API from one of my actions which returns a partial view with a model. The view is returning some data which is hidden to most website users (it is mostly used for SEO).
We have some software which monitors the time taken to call external APIs from our site and every now and then we're noticing that the Facebook Graph API is taking a long time to respond, resulting in our users waiting forever for a page to load (even when they likely won't even see the content we're trying to pull back from the API). Seeing as this call is not business-critcial, I need a way in which to get the aforementioned action to simply return an EmptyResult() and just not bother waiting for a response from the Facebook API after a given timeout period.
Does anyone know of a way in which I can set a timeout on the action, and then return an EmptyResult if the timeout is exceeded?
Unfortunately, there is nothing (out of the box) in MVC that will allow you to do such a thing.
But, if you're using MVC2 or 3 you can use asynchronous action methods and hand off your work to a CLR thread and simultaneously start a Timer that will signal to ASP.NET (using AsyncManager.Sync) to return an EmptyResult (if the CLR thread work has not completed).

Server cannot modify cookies after HTTP headers have been sent

I am creating a web application in C#.
When my page loads I fire an asynchronous thread to process some data. Part of this processing is the updating of a cookie. However when I save the cookie to the response by
System.Web.HttpContext.Current.Response.Cookies.Add(cookie), I get the following exception:
HttpException: Server cannot modify cookies after HTTP headers have been sent.
Any way I can work around or fix this?
Unless you have a very good reason to, you shouldn't be spinning up background worker threads in an ASP.NET request. Ultimately you still have to wait for this thread to finish its work before you send the response back to the browser.
It sounds like the response stream has already been partially written to and then your thread is trying to add the cookie.
I'd rethink your strategy here and take a read of the following guidelines:
Chapter 6 — Improving ASP.NET Performance - Threading Guidelines
It looks like a dated document but the principles still stand. If the reason for making your call to the data processor is to prevent the ASP.NET worker thread from blocking and using up resources because the data processor is long running, then consider making the page an Asynchronous page instead.
Yes, Cookies are part of the http response and in a async operation you cannot change anything after response is generated and sent to browser.
To workaround this i recommend to build a ajax loop on browser to get async operation result. When operation completed you can return a cookie with ajax response.
What if it is in preinit or init? not sure if this will help though.
http://msdn.microsoft.com/en-us/library/ms178472.aspx#lifecycle_events

Categories

Resources