In ASP.Net, I want to run some code (logging, other cleanup) after the page has already been sent to the user. I don't want this code to interfere with the amount of time it takes the client to receive a response. I tried placing this code in the OnUnload part of the page, but through testing (using breakpoints, or busy waiting loops) the client does not actually display the page until the code in the OnUnload is finished executing. Even though the response object is no longer available at this point, and therefore I would assume that the buffered response has been sent to the client, the client still does not display the page until the OnUnload is finished executing. The only way that seems to work at the moment is to start a new thread to do the work, and allow the OnUnload to finish immediately. However, I don't know if this is safe or not. Will the server kill the thread if it executes too long after the page is already sent out? Is there a more correct way to accomplish this?
Kibbee,
Try overriding the Page.Render method and flushing the response like so:
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
MyBase.Render(writer)
Response.Flush() ' Sends all buffered output to the client
' Run some code after user gets their content
End Sub
I think at this point the response still isn't complete, but the user will get what the page has rendered before you finish running that final code.
HTH,
Mike
Unload is the last part of the ASP.NET page life cycle. See link below:
http://msdn.microsoft.com/en-us/library/ms178472.aspx
Is this something you could do with javascript or AJAX?
How about hooking into the page.Disposed event instead of the Unload event?
Depending on what you need to do an ISAPI filter can be created to do stuff at the end of the response.
You could try this solution - Best ASP.NET Background Service Implementation
Using a windows service and communicating with it through MSMQ might be a far more reliable solution and can scale up better. This will help you separate the concerns and let asp.net front end just focus on the user while the windows service focusses on the background tasks.
If you are moving to the cloud with azure, you can simply replace the asp.net front end with a web role and the windows service with a worker role and ur solution can scale seamlessly!
Here's a redneck way to do it. Have a second aspx page that does all your "clean up" post-rendering logic. Call that one 'cleanup.aspx' for example. Have your clean up code run in Page_Load of cleanup.aspx:
public partial class cleanup: System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// do logging blah blah here....
}
}
In your main aspx page, have a JavaScript function that makes an AJAX call to 'cleanup.aspx'. Make the AJAX function fire after page load. I recommend jquery, if so your code will look like this:
$(function(){
yourAJAXYFunctionName();
});
function yourAJAXYFunctionName()() {
// ajax code here
//
$.ajax({
url: "cleanup.aspx",
});
}
This way, your first page's code runs, then the page is sent to the browser. As the page renders to the client, the 2nd ASPX page is called via AJAX, which really doesn't matter to cleanup.aspx, it doesn't care how its called and its page load event is executed. Your cleanup/logging code is then ran.
Caveats: client side JavaScript is required. But really who the hell doesn't have JS running anyway? Also, your cleanup.aspx page is totally abstracted from your main page, so if you want to use any object in cleanup.aspx that originated in your first page, then you'll have to store them in session or cookies, or you can pass them as parameters in the AJAX call. That will require dynamic manipulation of the AJAX script itself, but that's not too hard.
Better i think try try{Response.End()}catch{} runcode();
Related
I am working on an older ASP.Net Web Forms application. I am looking to introduce async code to help modernise it. However have encountered an impase.
The application make uses of Response.Redirect(true) in the Master Page to end a session when doing things like checking whether the user is logged in. This ends the response and redirects user back to the login page.
The trouble is when async code is used on the page. Eg:
protected async void Page_Load(object sender, EventArgs e)
{
await _userService.GetUser();
if (!IsPostBack)
{
...
}
}
The page blows up when the redirect is hit in the master page with: System.Threading.ThreadAbortException: Thread was being aborted.
I understand why this happens from this question: Why Response.Redirect causes System.Threading.ThreadAbortException? but I am unsure how to get around this issue. I would like to use async code in the page and also allow the Response.Redirect to end the response without processing the rest of code.
I tried to use:
Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();
But then the page execution continues. Is there any way around this issue on WebForms? Or do I need to revert to non async code? There does not seem to be a way like in MVC to return RedirectToAction which stops page execution?
From my understanding it seems like async support is available for WebForms - how are other projects getting around this kind of issue? especially in scenarios where we need to stop page execution (for instance if user is logged out?)
Well, since that code call is to run and go off and do its own thing?
Then of course that code can't update the page, since the page will render, and be send off and down to the client side.
Your code behind as a general rule can't wait.
So, start that process outside of the web form - after all, that async code has nothing really to do with the page, right? Since the page will have LONG been rendered and send down to the browser side while that code goes off and runs and does whatever it supposed to do, right? of course the code can't wait, and you can't hold up the page being sent down to the browser, since if you do that, then your code not async anymore, is it?????
so, do this:
{
Thread mypthread = new Thread(_userService.GetUser(););
mypthread.Start();
}
After all, that routine has nothing to do with the current page, and the current page can't wait, since if it waits, then the page will be held up until all that processing is done and THEN MAKE the trip down to the browser side.
So, you can't block or hold up the round trip.
Page post back - browser travels up to server.
Code behind runs, new page is rendered, maybe code behind changes things on that page.
Page travels down back to browser and is now just sitting there.
So, if you going to fire off some other routine that you don't want to wait for? Sure, just start it in a new thread OUT SIDE of that all important round trip. But you certainly can't fire off some async code, but THEN wait for it to complete, and THEN let the page continue being rendered. As noted, to do so would defeat the whole purpose of async code.
I have an object, let's call it objK that has an event Message that accepts a string. I have a event handler in my code behind page called HandleObjKMessage(string s). that method looks like this currently :
void HandleObjKMessage(string s)
{
TextBox1.ReadOnly = false;
TextBox1.Text += s+ Environment.NewLine;
UpdateP.Update();
}
objK is an object from an external .dll(assembly) that is running an iterative process that I need to be keeping the user update from. That is the purpose of using an event.
Am i barking up the wrong tree? I'm typically a winforms guy and this is frustrating the hell out of me.
Thanks.
I might have misunderstood you, but you have an object that you instantiate, and then add an event handler to this object's event, and when the event fires you want the user to get the updated data?
If this is the case, it is not possible. This is simply because the web does not have state. There is no persistent connection between the server and the client.
When a request is received for your page, the server will create an instance of your page class, and run through the life-cycle (init, load etc.) Once the page has been rendered into HTML markup, the server disposes the instance of the page, and thus your attached event handler.
You can read more about the WebForms Page Lifecycle at MSDN
If you really want to go all the way and actually push the notification to your user, you could use SignalR, but this adds quite some complexity.
I'm new to web development, and I'm currently using ASP.net. I wonder what would I need to do to let the browser wait for 3 seconds so my users can read the text "Customer Successfully Added" before turning to another page? I have attached my code as follows.
Protected Sub btnAdd_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAdd.Click
Dim db As New DatabaseClass
db.addProfile(txtLN.Text, txtFN.Text, txtUsername.Text, txtPassword.Text, txtAddress.Text, txtZip.Text, txtPhone.Text, txtEmail.Text)
lblMessage.Text = "Customer Successfully Added"
End Sub
In addition, I'm not sure how to utilize MSDN. For me, its information overload, I'm wondering how to go about finding the solution on MSDN so i would be able to solve my problems in the future. Thank you!
You can't do it in the code behind of the page because of how asp.net works - the label text would not update until after the timeout occurred if you did it in the code-behind.
The server-side processing spits all the html back to the browser only after it has completely processed any server-side code unless you're using Ajax. Since you're new, I won't even bother going into how to do it with Ajax as there is a MUCH simpler option for accomplishing what you want.
A simple method to accomplish what you're looking for would be to have a simple HTML page that just has a message that says "Customer successfully added" and use javascript (client-side code) to pause and then redirect using the Javascript "SetTimeout" function.
There's an example here: http://www.bloggingdeveloper.com/post/JavaScript-Url-Redirect-with-Delay.aspx
The logic flow wshould work like this:
The original page should add the record (in code-behind) then redirect to this simple html page (in code-behind). The html page should have the "Customer Added" message and use the SetTimeout and Redirect to go to whatever page you want the user to see after viewing the message.
For stuff like this you need the code to run client side rather than on the server. The easiest way to do this is to return some javascript with your page (in the .aspx part rather than the code behind)
Take a look here for an idea of what to do :)
The page is displayed for a few seconds and then the javascript triggers a redirect to a url of your choosing. Just add something like this into your html.
You can emit javascript to redirect to the other page, using the setTimeout function.
This is best accomplished using the ScriptManager to register any javascript on the page.
I have noticed that window.onunload event fires off AFTER page_load event which makes no sense.
This behaviour is creating an issue for me - in my unonload I clear the session, so if the Page_Load first BEFORE onunload, there are errors on the page displayed.
I would expect the javascript onunload to fire BEFORE Page_Load....is that the correct assumption?
TO CLARIFY:
Let's assume I am on page test.aspx, then I click on the link that goes to the same page (say I click on a menu), what I observe is that Page_Load fires first, then onunload fires off.
Makes no sense at all.
It's a browser-specific behaviour. Chrome and FF will send a GET requst BEFORE onunload is fired, IE8 will execute onunload first. Not sure, how the other browser handle it. Better not rely on this functionality.
Have you considered using a common base class for your pages, and clearing the session in there if the request isn't a postback (I assume that you're using session for postbacks)?
public class BasePage : System.Web.UI.WebControls.Page {
protected override OnPreInit (EventArgs e) {
// Get in nice and early, however you could use OnInit if you prefer
if (!Page.IsPostBack) {
Session.Clear();
}
}
Then your pages that need to clear session can be declared as:
public class SpecialPage : BasePage {
// Your page logic goes here.
// Note that if you need to do work in OnPreInit here you should call
// base.OnPreInit(e) first.
}
I would guess that window.unload is actually firing only when you're going to have to RENDER the new page you navigated to (aka the old DOM is being torn down in place of some new HTML). The browser doesn't know what to render until the response comes back from the server with the HTML to display. That HTML isn't generated until the page lifecycle completes, which includes Page_Load. Hence the page_load before the window.unload?
In any case, if you can clear the session during window.unload, why not just clear it in response to some user interaction and be a bit more explicit about it?
Edit: Could you also try window.onbeforeunload?
The onunload event does fire before the request for the new page is fired off to the server, so it definitely fires before the Page_Load method runs on the server.
The problem is most likely that you are sending another request to the server from the onunload event. As the IIS only handles one request at a time from each user, this request will be queued and executed after the request for the new page.
You can write an utility function which will handle removing of the session variables and then call that function in the respective menu click events. That should be simpler to use since window unload will fire after page load only.
It sounds like you are using the Session to save temporary variables that change from page to page. I would say the Session is not really suitable for this kind of scenario. A better solution would be to use the Httpcontext's Item collection which is scoped only on a per request basis. It's works just the same as the Session when storing data.
Context.Items["myvariable"] = "some data";
As it's only scoped on a per request basis, there is no need to use javascript to clear the items you have stored on each page request.
How do you call a Javascript function from an ASPX control event?
Specifically, I want to call the function from the SelectedIndexChanged event of a DropDownList.
I get a little nervous whenever I see this kind of question, because nine times out of ten it means the asker doesn't really understand what's going on.
When your SelectedIndexChanged event fires on the server, it fires as part of a full postback. That means that for that code to run, the entire rest of your page's load code also had to run.
More than that, the code runs as the result of a new http request from the browser. As far as the browser is concerned, an entirely new page is coming back in the result. The old page, and the old DOM, are discarded. So at the time your SelectedIndexChanged event code is running, the javascript function you want to call doesn't even exist in the browser.
So what to do instead? You have a few options:
Change the page so the control doesn't post back to the server at all. Detect the change entirely in javascript at the client. This is my preferred option because it avoids odd onload scripts in the browser page and it saves work for your server. The down side is that it makes your page dependent on javascript, but that's not really a big deal because if javascript is disabled this was doomed from the beginning.
Set your desired javascript to run onload in the SelectedIndexChanged event using the ClientScript.SetStartupScript().
Apply the expected results of your javascript to the server-model of the page. This has the advantage of working even when javascript is turned off (accessibility), but at the cost of doing much more work on the server, spending more time reasoning about the logical state of your page, and possibly needing to duplicate client-side and server-side logic. Also, this event depends on javascript anyway: if javascript is disabled it won't fire.
Some combination of the first and third options are also possible, such that it uses javascript locally if available, but posts back to the server if not. Personally I'd like to see better, more intuitive, support for that built into ASP.Net. But again: I'm talking about the general case. This specific event requires javascript to work at all.
As Muerte said you have to just put the javascript, or a call to it on the page from the code behind. Personally I use this:
ClientScript.RegisterClientScriptBlock("customscript", "<script>simple script here</script>")
Of you can call the function if you already have a more complex one on the page instead of the stuff I have.
You can't do it directly from an event, because ASPX control event is server side.
What you can do is emit a Javascript in the ASPX event which will call the JavaScript function when the page reloads.
For example, if in your ASPX page you have a Javascript function called "DoSomething()", in you ASPX control event, add the following:
protected void btnSubmit_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterStartupScript(this.GetType(), "myEvent", "DoSomething()", true);
}
The last boolean parameter defines that tags are added automatically.
In the code behind, attach some markup to the server side control via its attributes collection. This assumes that the function is already in a client script file that is already available to the page.
MyServerDDLControl.Attributes.Add("SelectedIndexChanged", "MyClientSideFunction();");