11

Is there any way to redirect from Page_Load (or any other ASP.NET event) when using async-await? Of course Redirect throws ThreadAbortException but even if I catch it with try-catch it ends up with an error page. If I call Response("url", false) it doesn't crash but I need to stop execution of the page (rendering the page etc.) so it's not the solution. And as I noticed these two methods act differently:

This ends up with ThreadAbortException (I assume the task ends synchronously):

protected async void Page_Load()
{
    await Task.Run(() => { });
    Response.Redirect("http://www.google.com/");
}

This one continues after Response.Redirect:

protected async void Page_Load()
{
    await Task.Delay(1000);
    Response.Redirect("http://www.google.com/");
}

I must wait for the response but I was experimenting and even if I remove the await keyword (so Task runs in background and the method continues) it ends up the same. Only thing that helps is to remove the async keyword - I thought async ONLY enables await and nothing more?!

JakubRi
  • 819
  • 9
  • 11
  • Are you trying to create a "fire and forget" task here, where you don't care about the response? – Andrew Barber Oct 30 '12 at 09:25
  • Nope, I was just trying different scenarios but I must wait for the response. – JakubRi Oct 30 '12 at 09:37
  • 1
    possible duplicate of [Response.Redirect issue with Asp.net async](http://stackoverflow.com/questions/20147825/response-redirect-issue-with-asp-net-async) – tne Apr 14 '15 at 08:51

3 Answers3

3

OK, I found the answer how to deal with it.

I've created a Redirect method wrapper in my base Page class:

protected void Redirect(string url)
{
    this.isRedirecting = true;

    Response.Redirect(url, false);

    if (Context.ApplicationInstance != null)
        Context.ApplicationInstance.CompleteRequest();
}

And override Render and RaisePostBackEvent:

protected override void Render(HtmlTextWriter writer)
{
    if (this.isRedirecting)
        return;
}

protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
{
    if (!this.isRedirecting)
        base.RaisePostBackEvent(sourceControl, eventArgument);
}

That does the trick. ASP.NET 4.5 won't fire the PreRenderComplete event (= won't continue in the life-cycle) until all awaited tasks are completed.

JakubRi
  • 819
  • 9
  • 11
  • 1
    I understand the problem a bit better now. I think I hadn't twigged what was wrong until your last comment to me on my answer. One thing I will say though is that your answer should contain more than just the link to the page that helped you. If that link dies in the future then this answer becomes useless. Instead copy a summary of the bitsyou used (eg the local value determining whether or not to run the render step or skip it). Its also worth noting that if the async tasdk takes a long time you may well still run a lot of stuff in the meantime because it hasn't set a local variable yet. :) – Chris Nov 05 '12 at 14:19
  • This code worked nicely except in the Render method, there needs to be an else statement calling base.Render(writer). Otherwise you get a blank page. – xcr Jul 20 '23 at 05:23
1

Diclaimer: I've not used async/await but the following is based on my reading of the docs.

As I understand it whenever you have an await in the code it will call the task provided aynchronously and the rest of the method is in effect converted into a callback method. So in this case because Page_Load is async when it runs it and it comes to an await it will return control to the caller while waiting for its aynchronous task to complete.

In the case of the Page_Load event this will, I think, then appear as if it has completed sopresumably the page life cycle will continue onto its next step.

The reason for the differences is that in your first example although it will continue on with the life cycle the callback is almost immediate so the Response.Redirect will run almost immediately, before the rest of the page has a chance to do anything.

In the case of the second one you have that delay of 1000 and during that time the page will continue running other things and its only when that delay is over that the respone.redirect fires. This might be after a response (from an error) has already been fired.

So in essence async does enable await but what await does sounds like it is a bit more than you expect.

Chris
  • 27,210
  • 6
  • 71
  • 92
  • 1
    Thanks for very detailed answer but ASP.NET 4.5 waits for Tasks to complete before PreRender and doesn't continue in the pipeline. See **RegisterAsyncTask Notes**: http://www.asp.net/web-forms/tutorials/aspnet-45/using-asynchronous-methods-in-aspnet-45#CreatingAsynchGizmos – JakubRi Oct 30 '12 at 12:43
  • More details: http://evolpin.wordpress.com/2011/05/02/c-5-await-and-async-in-asp-net/ – JakubRi Oct 30 '12 at 13:01
  • @JakubRi: that page actually says that PreRender is run, it just won't fire the PreRenderComplete event until after all the async tasks return. It still seems likely to me that code after the page load but before the render is throwing an error somewhere. What is the error that you actually get back in the instance it doesn't work? – Chris Oct 30 '12 at 13:46
  • OK, sorry, it waits for `PreRenderComplete`, my mistake. But anyway the problem is that after calling `Response.Redirect` it continues in the pipeline (it goes into .aspx so the `Render` event is called) which is the thing I want to avoid (it's the standard behavior and it's done by aborting the thread but as it's async it doesn't work when there is an async operation). – JakubRi Oct 31 '12 at 08:46
1

Changing the Response.Redirect final parameter to "false" solves this error as the last parameter determines whether the execution of the page should terminate which, in this case you do not want to occur as you are attempting to complete an async task.

protected async void Page_Load()
{
    await Task.Run(() => { });
    Response.Redirect("http://www.google.com/",false);
}
Matt
  • 149
  • 3
  • 14