Asynchronous Web Methods - c#

I have a client/service.
The service has a method that takes a long time to do (it interacts with a database).
I'm call this method via a AJAX request from the page to the client, then to the service and back.
My service code:
[WebMethod]
public static string LookupUPC(string sessionId, string upc) {
string response = "";
var client = new SmartShopService.SmartShopInterfaceClient();
try {
response = client.LookupUPC(sessionId, upc);
}
catch (Exception e) {
throw e;
}
finally {
if (client.State == System.ServiceModel.CommunicationState.Faulted)
client.Abort();
else
client.Close();
}
return response;
}
It is called from the page by an AJAX request
for(var i = 0;i<10; i++){
$.ajax({
type: "POST",
url: "SmartShopGUI.aspx/LookupUPC",
contentType: "application/json; charset=utf-8",
data: DataCreator(allData),
dataType: "json",
success: function (result) {
$(upcName).html(result.d);
},
error: AjaxFailed
});
}
Now, this is done asynchronously on the page, but the client is sending the requests synchronously. I want to change it so that if it asks for 10 all at once, it'll send 10 different requests to the service.
http://www.screencast-o-matic.com/watch/cX1Qo8qV2
Here is a video that might help.

Remove the reliance on Session in the webmethod, you'll probably find that session access is serial and that is what the block is.
http://msdn.microsoft.com/en-us/library/ms178581.aspx
Concurrent Requests and Session State
Access to ASP.NET session state is
exclusive per session, which means
that if two different users make
concurrent requests, access to each
separate session is granted
concurrently. However, if two
concurrent requests are made for the
same session (by using the same
SessionID value), the first request
gets exclusive access to the session
information. The second request
executes only after the first request
is finished. (The second session can
also get access if the exclusive lock
on the information is freed because
the first request exceeds the lock
time-out.) If the EnableSessionState
value in the # Page directive is set
to ReadOnly, a request for the
read-only session information does not
result in an exclusive lock on the
session data. However, read-only
requests for session data might still
have to wait for a lock set by a
read-write request for session data to
clear.

You would do better to create a BatchLookup API that can handle a block of requests all in one go. Any connection between a browser and a server will be limited as to how many simultaneous requests you can make and each round trip is a slow operation itself.
I suspect that this isn't a server-side issue at all but the browser connection limitation that you are hitting. Even if you fix the server-side to handle two simultaneous requests the browser isn't going to fire off all 10 of them at once. See for example this article on the topic: http://www.ajaxperformance.com/2006/12/18/circumventing-browser-connection-limits-for-fun-and-profit/
BTW, how can looking up a single UPC code in a database take so long? Do you have it indexed properly?

Related

Async MVC.NET action method blocks any other HTTP requests

(I apologize for changing the question)
The following snippet is from a MVC.NET controller (.NET: v4.5; AspNet.MVC: v5.2.3) .
After LongOperation is called, it:
Spawn a process
Waits for its completion
Monitors a few LOG files
Uses SignalR to notify browser of the progress from the LOG files
(I have omitted the code for simplicity)
All this works, only while LongOperation is running, no other HTTP requests are handled by the controllers.
They get handled after the LongOperation completes and the action method returns result to the AJAX call.
What am I messing up?
Thank you in advance.
Update (for #angelsix comment):
Here is a simplified setup:
I have removed async/await as advised
Added breakpoints as advised
Verified they are hit as explained in the above
Basically: same result, see the console.log-ed text and timestamps
Will appreciate any help from the community.
Thank you in advance!
GUI and log
Action methods in the Controller
[AjaxOnly]
public ActionResult _RunLongOperation(string hubId)
{
try
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
ProgressNotifierHub.Notify(hubId, string.Format("Notification from _RunLongOperation {0}", i));
}
return new HttpStatusCodeResult(HttpStatusCode.OK, "_RunLongOperation : OK");
}
catch (Exception)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "_RunLongOperation : NOK");
}
}
[AjaxOnly]
public ActionResult _RunAnotherOperation(string hubId)
{
return new HttpStatusCodeResult(HttpStatusCode.OK, "_RunAnotherOperation : OK");
}
Razor View (partial) and javascript with SignalR hub setup Ajax calls
<script src="~/signalr/hubs"></script>
#{
Layout = null;
}
<button id="longOperationBtn" type="button" class="t-button" style='width: 155px'>Long Operation</button>
<button id="anotherOperationBtn" type="button" class="t-button" style='width: 155px'>Another Operation</button>
<script type="text/javascript">
$(function () {
setupEventHandlers();
setupProgressNorificator();
});
function setupEventHandlers() {
$('#longOperationBtn').click(function (event) {
requestOperation('_RunLongOperation')
});
$('#anotherOperationBtn').click(function (event) {
requestOperation('_RunAnotherOperation')
});
}
function requestOperation(method) {
trace(method + ' requested');
$.ajax({
url: '/Profiles/Validate/' + method,
type: 'GET',
data: { hubId: $.connection.hub.id },
contentType: 'application/json; charset=utf-8',
success: function () {
trace(method + ' completed');
},
error: function () {
trace(method + ' failed');
}
});
}
function setupProgressNorificator(profileId) {
var hub = $.connection.progressNotifierHub;
hub.client.notify = function (notification) {
console.log(notification);
};
$.connection.hub.start();
}
function trace(s) {
console.log('[' + new Date().toUTCString() + '] ' + s);
}
</script>
It looks like you are running the client "test" on Chrome. I'm not sure what the 2018 limitation is (it seems to change from release to release), but browsers do limit the number of concurrent connections allowed to the same host. I believe Chrome's limit is 6 or 8.
The log you posted appears to be from the client side. If it is, the behavior you are seeing could actually be the client waiting for a free connection to connect to the server - the problem may not be with ASP.NET at all, but rather with how you are testing.
It would be easy to validate - write another test that you can run concurrently with your current test in a separate browser window that just calls the "short" operation. If it has latency, I'm wrong. If it doesn't, well hopefully I've helped out!
The issue
Your response does not mention anything about caching. So I suspect the browser is caching the response and using that.
Verify
To verify press F12 in the browser to open developer tools and then see if the second response shows a status of 304 or states Cached
Solve
To prevent caching on the action, inside the action you want to do this
Prevent Caching in ASP.NET MVC for specific actions using an attribute
According to this:
Some ... protocols like HTTP Polling (XHR) use up to two simultaneous connections per ... client. It is important to understand that the maximum number of connections is per browser and not per browser tab. Attempting to run multiple clients within the same browser might cause this limit to be reached.
In my case, SignalR client was using long polling. A fried reported that when Web sockets are used, there was no blocking issue. Closing the subject.
Thanks for the help Joe.
You need to set your controller to have read-only session state behavior. Then you can do ajax requests to the controller during running a long controller method. In other case (as the one that you are complaining about) the requests will be queued and call all at once after finishing the controller action call. Just put this before controller class definition
[SessionState(SessionStateBehavior.ReadOnly)]

Two parallel ajax requests to Action methods are queued, why?

I'm developing a video website using ASP.NET MVC.
One functionality I want to have in my application is transocding video. But as the transcoding process could be very time-consuming, I want to show the progress of that process to the client user.
So, my schema is to use one controller action to handle the whole transcoding process and write its progress into a file stored on the server. Meanwhile I use Ajax to call another controller action to read the specified file, retrieve the progress information and send it back to the client for display every 2 seconds during the transcoding process.
To fulfill my plan, I have written the following code:
Server Side:
public class VideoController : Controller
{
//Other action methods
....
//Action method for transcoding a video given by its id
[HttpPost]
public async Task<ActionResult> Transcode(int vid=0)
{
VideoModel VideoModel = new VideoModel();
Video video = VideoModel.GetVideo(vid);
string src = Server.MapPath("~/videos/")+video.Path;
string trg = Server.MapPath("~/videos/") + +video.Id+".mp4";
//The file that stores the progress information
string logPath = Server.MapPath("~/videos/") + "transcode.txt";
string pathHeader=Server.MapPath("../");
if (await VideoModel.ConvertVideo(src.Trim(), trg.Trim(), logPath))
{
return Json(new { result = "" });
}
else
{
return Json(new { result = "Transcoding failed, please try again." });
}
}
//Action method for retrieving the progress value from the specified log file
public ActionResult GetProgress()
{
string logPath = Server.MapPath("~/videos/") + "transcode.txt";
//Retrive the progress from the specified log file.
...
return Json(new { progress = progress });
}
}
Client Side:
var progressTimer = null;
var TranscodeProgress = null;
// The function that requests server for handling the transcoding process
function Transcode(vid) {
// Calls the Transcode action in VideoController
var htmlobj = $.ajax({
url: "/Video/Transcode",
type: "POST",
//dataType: 'JSON',
data: { 'vid': vid },
success: function(data)
{
if(data.result!="")
alert(data.result);
}
else
{
//finalization works
....
}
}
});
//Wait for 1 seconds to start retrieving transcoding progress
progressTimer=setTimeout(function ()
{
//Display progress bar
...
//Set up the procedure of retrieving progress every 2 seconds
TranscodeProgress = setInterval(Transcoding, 2000);
}, 1000);
}
//The function that requests the server for retrieving the progress information every 2 seconds.
function Transcoding()
{
//Calls the GetProgress action in VideoController
$.ajax({
url: "/Video/GetProgress",
type: "POST",
//dataType: 'JSON',
success: function (data)
{
if (data.progress == undefined || data.progress == null)
return;
progressPerc = parseFloat(data.progress);
//Update progress bar
...
}
});
}
Now the Client-side code and the Transcode action method all work fine. The problem is that the GetProgress method will never get called until the Transcode action finishes its whole procedure. So what's wrong with my code? How can I modify it to make those two actions work spontaneously so as to achieve my goal?
Update
Based on Alex's answer, I found that my problem is caused by the session lock mechanism of Asp.Net framework. So disabling the SessionState of my VideoController or setting it as read-only does make the controller responses to the request for retrieving transcoding progress when the action method of transcoding video is being executed. But because I use Session in my VideoController to store some variables for use across multiple requests, this way couldn't be a suitable solution for my problem. Is there a better way to solve it?
You misunderstood the whole point about async/await. It doesn't change the fact that for each single request, there is a single response that is returned. When you call await in your action, nothing is returned to client yet. The only thing it does (in a very high level abstraction) is to release the current thread that handles this request to a thread pool so it could be used for processing other requests. So basically it allows you to use your server resources more efficiently since there are no threads that are wasted waiting for long I/O operations to complete. Once the I/O operation is completed the the execution of the action (that called for await) continued. Only at the end of an action the response is sent to the client.
As for you scenario, if it is a long running task, I would use some kind of background processing solution such as Hangfire and use SignalR to push updates from server.Here is an example
You can also implement something similar on your own (example).
UPDATE:
As #Menahem stated in his comment I maybe misinterpreted part of your question.
Request queuing issue may be caused by incorrect configuration of SessionStateBehavior. Since MvcHandler handler which is used by ASP.NET MVC is marked with IRequiresSessionState interface, only one request at time could be processed per session. In order to change that, make you controller sessionless (or ar least make sure that you are not writing into session in this controller) and mark it with
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] attribute.
File creation is blocking call. In other words, until first thread will not close file, second one which makes report will not be able to read contents of that file. As workaround you can create files with percentage of progress. For example movie1-5-percent.txt, movie1-10-percent.txt, movie1-15-percent.txt etc, in order to avoid blocking calls to file system. Then you can check, if for movie1 there is file movie1-15-percent.txt, then you can report to ajax call, that 15 percent of movie was converted. Or choose another non blocking storage. For example you can report result to db in first thread, and read results from db in another.

Check is aspx session expired via jquery ajax request

I use asp.net to manage the session state of my site. I also use jquery and $.ajax(...) for synchronous and asynchronous requests.
Normally, in asp.net if a users session times out, it can be detected via a full or partial post-back. However, suppose a partial or full-post-back does not occur because I am using jquery ajax calls to a static c# web method. Whats the best what to know if the session timeout?
i would create a javascript interval to make a ajax request from time to time, something like this:
<script>
$(document).ready(function () {
setInterval(function (){
$.ajax({
url: 'WebMethodhere',
type: 'GET',
success: function (result) {
if(result!='true')
{
//It means that the Session expired, so do somethig
}
}
})
},3000);
});
</script>
I used 3000ms in this example, but the time it's up to you.
And your webmethod could be really simple:
if(Session["WhateverSession"]==null)
{
return false;
}
else
{
return true;
}
There are couple of approaches to achieve it.
Approach #1: Create a generic handler and implement the IReadOnlySessionStateinterface to access session variables from this handler. Use a ajax get request and access this generic handler which provides whether session is active or not.
Approach #2: Decorate a public method of your page with [WebMethod] (using System.Web.Services) and access this method through ajax get request which provides whether session is active or not.
But all these approaches should be used just to check session is active or expired. But if the session is active, it'll renew your session - it may not be desirable. Please check that option also.
The easiest solution was to force a post back at certain intervals.
<meta http-equiv="refresh" content="1800">

What happens to an ASP.NET MVC controller when the user navigates away before a response is received?

I have an AJAX action that can take a couple of minutes to complete depending upon the amount of data involved. If a user gets frustrated and navigates away while this action is still running, what happens to the controller? Does it complete anyway? Does it know the request should be abandoned and dispose of the controller object?
It will not cancel the request to the server as the act of navigating away does not send any information back to the server regarding that request. The client (browser), however, will stop listening for it. After the request finishes, regardless if the client was listening for it or not, the controller will dispose as it typically would.
With that said, you could get fancy and use a combination of listening for a page change on the client side and calling abort on the AJAX request to the server.
This SO question discusses how to abort a request. I could imagine setting a variable when you first start the AJAX request and then unsetting it when it finished.
Warning - Pseudo code below
var isProcessing = false;
var xhr = $.ajax({
type: "POST",
url: "myUrl",
beforeSend: function(){
isProcessing = true;
}
complete: function(){
isProcessing = false;
}
});
window.onbeforeunload = function(){
if(isProcessing){
xhr.abort();
}
}
The above is very basic idea of the concept, but there should probably be some checks around if the xhr object exists, perhaps also bind/unbind the window.onbeforeunload in the beforeSend and complete object handlers for the .ajax() item.

Update label while a method is running

How do I update a label in a aspx page while a method is running? Perhaps using AJAX (update panel)?
private void button_Click(object sender, EventArgs e)
{
doThings1();
label.Text = "Status1";
doThings2();
label.Text = "Status2";
doThings3();
label.Text = "Done";
}
I want to show step by step. While the method is running when the doThings1() is done, shows "Status1", doThings2() is done, shows "Status2"... In this way, the label doesn't show "Status1" and "Status2", just "Done" when the process is finished. I'd like to show step by step.
This is not an easy thing to do, the way it is in a desktop application. You need to start an asynchronous operation that will continue after the request ends, you'll need to have the client continually poll the server for updates as to the progress, and the server side asynchronous code will need to update some sort of share state (i.e. session, a database, view state, etc.) that the polling method can read the progress from. All around it's quite inefficient (especially if you have a lot of users doing this) and takes some time to write. Here is an example on MSDN that does this, to give you an idea of what's involved.
The rule is: 1 request --> one response.
Different approach:
You can these methods execute with 3 asyncron javascript call and set the labels' text at the success callback.
http://api.jquery.com/jQuery.ajax/
Example:
$.ajax({
type: "POST",
url: "URL.asmx/doThings1",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(result) {
// result will be "done" from the function of webservice below.
// set the first label text
},
error: function(xmlHttpRequest, status, err) {
alert(xmlHttpRequest.statusText + " " + xmlHttpRequest.status + " : " + xmlHttpRequest.responseText);
}
});
Repeat these calls 3 times and do your modifications in different functions.
You can handle your buttonclick at client side with jquery or pure javascript.
You can use a webservice or generic handler to execute server side methods.
How to create webservice
[WebMethod]
public string doThings1()
{
return "done";
}
It sounds like you want to show the progress of some task that is running on the server. The signalr library will allow you to send real time updates to the client from the server. So anytime the task completed a stage (Status1, Status2, etc) of the task, it would send an update to the listening clients with the new status.
You could also have some javascript request the task status from the server every few seconds and display it to the user.

Categories