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.
Related
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.
I am having unexplained behavior when I post from jquery using ajax to C#.
1) The main page is called not the method I am requesting in jQuery.
To work around this I simply put an if in the page load so that if a particular item is in the querystring it will trigger a series of commands. It does hit that if statement and runs the code perfectly fine. There are some methods that do things like change a color on the map. These never actually happen. I can set a label and it will pass right over it but the label remains unset.
2) strangely enough.... my page has a timer with a refresh on it. It refreshes the page and now the changes are processed.
Here is the way I am calling my method in jQUery:
function mycmethod(param)
{
//alert(precinct);
$.ajax({
url: "myPage.aspx/someMethod",
type: 'POST',
data: "params=" + param,
success: function iGotData(responseJSON) {
// alert("Worked");
},
error: function (xhr, status, errorThrown) {
console.log("Error: " + errorThrown);
console.log("Status: " + status);
console.log(xhr);
alert("Didnt work:" + errorThrown);
},
})
};
It was originally set to async: true but that didn't make a difference.
The method its not calling on load is:
[WebMethod][ScriptMethod]
public Boolean someMethod(string param)
{
setFeatures();
GenerateMap();
return true;
}
I doubt its relevant but I am calling a jquery call with over mouse over of a specific element. That jquery calls a function which calls a asmx web service that returns some jSON. I am calling the mycmethod after the JSON is returned.
Why is my UI elements not responding until the page refreshes. If not, is there a way I can force a refresh like the timer does?
[WebMethods] methods should be declared as static.
I've also found that you might need to specify the content type in your ajax call:
contentType: "application/json; charset=utf-8"
Also, your data option looks suspicious. Maybe you should append it to the url option:
url: "myPage.aspx/someMethod?params" + parm
or, more ideally, send it as either a JSON object or a JSON string:
data: {
params: param
}
or
data: JSON.stringify({
params: param
})
If I understand you correctly, you're loading the page, then calling the server via ajax and expecting the server to change UI elements of the currently loaded page.
It doesn't work like that, unfortunately. Once you've served the page, the server itself cannot manipulate that page without doing a refresh/post back (or something along those lines).
If you want to update the UI without doing a refresh/post back you can have your WebMethod return HTML, and your jQuery success method can update the relevant controls.
Alternatively you could use jQuery's .get() to retrieve a fresh copy of the page via ajax, and update your current page like that. (although it's less efficient)
I am using a WCF service, it is not an asynchronous service.
I do database operations in them. the Database operations return me huge result.
I want to provide a way to cancel the operation to my client.
Can anyone please suggest to achieve the same. I googled a lot
also could find out some soluions, some are saying to make the service operation as asynchronous.This is what I got from msdn
http://msdn.microsoft.com/en-us/library/ms731177(v=vs.110).aspx
But how do I stop it.
Also as I am new to this, I am not able to figure out the thing.
Can anyone please help me with this.
Thanks in advance.
Regards
You can keep your service synchronous but make async calls inside of your methods, around database queries for example. In this case you can use class Task which supports CancellationToken. But you should be on .NET 4 and up.
I you only want to cancel Service call (There is no session Locking (Session Creation/updation/deletion), you can make ajax call to service in javascript like :
var xhr;
var serviceUrl = "Service1.svc/GetData";
xhr= $.ajax({
type: "POST",
url: serviceUrl,
data: "{\"value\":\"request\"}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
//anything you need to do on success
}
});
For aborting request, you can do :
if(xhr && xhr.readystate != 4){
xhr.abort();
}
Please not that will abort the handler waiting for request & not request itself and if you are playing with Session(except for read), it will not work & wait for request to complete.
If you have above case, its better to use Task as mentioned by vzayko.
Also if you're using MVC 4, you can also use TAP(Task-based Asynchronous Pattern), which is specifically optimised for using tasks in ASP.net MVC.
Is there any way to detect all the possible user activities within a WebBrowser and return a custom Event? For example: User clicks on "search" button, return "SearchButtonClicked" custom Event.
It would be something like, logging of all the activity that user does, stored in a sequence and could be automated once he wanted.
Edit: I do not own the webpage. I am trying to make an application to automate some searching on google.
After some research, I discovered the HtmlElementEventHandler.
Example:
webBrowser1.Document.GetElementById("MainContent_LoginButton").Click += new HtmlElementEventHandler(test);
// some code...
public void test(object sender, HtmlElementEventArgs e)
{
MessageBox.Show("Clicked login button");
}
Wow, it's going to be quite a bandwidth intensive application... :) You might consider a framework like jQuery to attach events to all anchors and button-type inputs to perform AJAX calls to your server, for example. So you might have something along the lines of the following process:
Include a JS file on all your pages to do something like the following:
$(document).ready(function () {
var trackUserActivity = function(elementId, elementText) {
$.ajax({
type: "POST",
url: "url/TrackUserActivity",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({
ElementId: elementId,
ElementText: elementText
}),
success: function (result) {
// do something if call was successful
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
// do something if an error occurred
}
});
};
$("a, input[type=\"button\"], input[type=\"submit\"]").click(function() {
trackUserActivity($(this).attr("id"), $(this).text());
});
});
Create a Web Method that can be called via AJAX to track the user activity:
[WebMethod]
public static void TrackUserActivity(string ElementId, string ElementText)
{
// Implement your user activity tracking logic, like saving it in a database
}
After your OP edit, you don't own the application, so this won't work. I'm keeping the answer here for someone in the future who might have a similar need.
See types of HtmlElementEventHandler:
http://msdn.microsoft.com/en-us/library/system.windows.forms.htmlelement_events%28v=vs.110%29.aspx
Use this event handler for knowing event occured. On event occured, get the element details, tag details and log in the file with the type of event.
See usage of downcasting required when using HtmlElementEventHandler
http://www.hanselman.com/blog/BackToBasicsThisIsNotTheObjectYoureLookingwaitOhItIsTheObject.aspx.
When you want to replay action, you may run logged events in sequence, after extracting tag name and value fields.
I think that you can set like on post and get methods in jquery timeouts but I am wondering does jquery have a global timeone like it has with ajax start and stop.
Like I would like to set it that if say a post or get or some sort of ajax request is running and it runs more then X amount of seconds. That a popup or something in that nature comes up saying that the "server is slow and the request has timed out" and kills that request.
Then the user can either try again or do something else.
Does jquery have something like this?
Thanks
The jQuery.ajax function is what you want. It gives you access to a pretty large set of options for making AJAX calls.
Specifically, you're going to want to make a call similar to
$.ajax({'complete': callbackFunction, 'url': 'foo/bar/', 'timeout': 5000, 'error': errorCallback});
The two options you're interested in are timeout and error. The entire documentation for the function is here. It's a bit more work than using the more standard get/post functions, but much more flexible.
The error function is very similar to the standard callback you use with any jQuery AJAX request. While the callback is called if the request succeeds, the error function is called when it fails (such as 404 errors, or when the timeout is hit). You'd use the error function to display your message to the user that their request has timed out. Full documentation on the function's arguments (which practically speaking, you probably won't need to use) is available on the $.ajax doc page (linked earlier).
Alternatively, you can set the timout globally on every AJAX call by using the jQuery.ajaxSetup function. Its arguments are exactly the same as the jQuery.ajax function, so you'd do something like this:
$.ajaxSetup({'timeout': 5000, 'error': errorCallback});
The upside is that you can continue using jQuery.get/jQuery.post, but the downside is that you have to do extra work to make AJAX calls without a timeout.
Yep, jQuery has a 'timeout' property (in milliseconds) you send the $.ajax() event:
http://www.bennadel.com/blog/1500-Catching-Timeout-Errors-With-jQuery-Powered-AJAX.htm
$.ajax(
{
method: "get",
url: "yourpage.php",
dataType: "json",
timeout: (3 * 1000), // 3 seconds
success: function(){
//success code here
},
error: function( request, strError ){
//error code here
}
}
);