您提到(在您对另一篇文章的评论中)您的问题在调用 Response.Redirect() 时会表现出来,因为它会引发 ThreadAbortException,这会导致您的 OnPreRender() 事件未被调用。那么为什么不使用它呢?:
Response.Redirect("~/SomePage.aspx", false);
您在此处看到的“false”表示页面的执行是否应该立即终止。默认情况下,Response.Redirect() 使用“true”。如果您需要运行 OnPreRender() 事件以便 OnLoad() 事件拥有所需的一切,则将其设置为“false”并确保在调用 Response.Redirect 后跳转到 Page_Load() 的末尾() 或者在它可以运行之后执行的代码。
也许你不喜欢使用重载的 Response.Redirect() 方法传递“false”的想法,所以这就是你没有走那条路的原因。以下是一些可能有助于改变您想法的文档:
Microsoft 声明“建议为 endResponse 参数传递 false ”,因为指定“true”会调用 HttpResponse。原始请求的End() 方法,然后在完成时抛出 ThreadAbortException。微软接着说“这个例外对 Web 应用程序的性能有不利影响”。请参阅此处的“备注”部分:http: //msdn.microsoft.com/en-us/library/a8wa7sdt.aspx
这是去年在 MSDN 上发布的:
End 方法也在我的“从不使用”列表中。停止请求的最好方法是调用 HttpApplication.CompleteRequest。End 方法之所以存在,是因为我们在 1.0 发布时尝试与经典 ASP 兼容。经典 ASP 具有终止 ASP 脚本处理的 Response.End 方法。为了模仿这种行为,ASP.NET 的 End 方法尝试引发 ThreadAbortException。如果这成功了,调用线程将被中止(非常昂贵,对性能不利)并且管道将跳转到 EndRequest 事件。ThreadAbortException,如果成功,当然意味着线程在它可以调用更多代码之前展开,所以调用 End 意味着你不会在此之后调用任何代码。如果 End 方法不能引发 ThreadAbortException,相反,它会将响应字节刷新到客户端,但它会同步执行此操作,这对性能非常不利,并且当 End 之后的用户代码执行完毕时,管道会跳转到 EndRequest 通知。向客户端写入字节是一项非常昂贵的操作,特别是如果客户端在地球的另一端并使用 56k 调制解调器,因此最好异步发送字节,这是我们在请求结束时正常方式所做的事情。同步刷新真的很糟糕。总而言之,您不应该使用 End,但使用 CompleteRequest 非常好。End 的文档应该说明 CompleteRequest 是跳到 EndRequest 通知并完成请求的更好方法。但它是同步执行的,这对性能非常不利,并且当 End 之后的用户代码执行完毕时,管道会跳转到 EndRequest 通知。向客户端写入字节是一项非常昂贵的操作,特别是如果客户端在地球的另一端并使用 56k 调制解调器,因此最好异步发送字节,这是我们在请求结束时正常方式所做的事情。同步刷新真的很糟糕。总而言之,您不应该使用 End,但使用 CompleteRequest 非常好。End 的文档应该说明 CompleteRequest 是跳到 EndRequest 通知并完成请求的更好方法。但它是同步执行的,这对性能非常不利,并且当 End 之后的用户代码执行完毕时,管道会跳转到 EndRequest 通知。向客户端写入字节是一项非常昂贵的操作,特别是如果客户端在地球的另一端并使用 56k 调制解调器,因此最好异步发送字节,这是我们在请求结束时正常方式所做的事情。同步刷新真的很糟糕。总而言之,您不应该使用 End,但使用 CompleteRequest 非常好。End 的文档应该说明 CompleteRequest 是跳到 EndRequest 通知并完成请求的更好方法。管道跳转到 EndRequest 通知。向客户端写入字节是一项非常昂贵的操作,特别是如果客户端在地球的另一端并使用 56k 调制解调器,因此最好异步发送字节,这是我们在请求结束时正常方式所做的事情。同步刷新真的很糟糕。总而言之,您不应该使用 End,但使用 CompleteRequest 非常好。End 的文档应该说明 CompleteRequest 是跳到 EndRequest 通知并完成请求的更好方法。管道跳转到 EndRequest 通知。向客户端写入字节是一项非常昂贵的操作,特别是如果客户端在地球的另一端并且使用 56k 调制解调器,因此最好异步发送字节,这是我们在请求结束时以正常方式执行的操作。同步刷新真的很糟糕。总而言之,您不应该使用 End,但使用 CompleteRequest 非常好。End 的文档应该说明 CompleteRequest 是跳到 EndRequest 通知并完成请求的更好方法。这是我们在请求以正常方式结束时所做的。同步刷新真的很糟糕。总而言之,您不应该使用 End,但使用 CompleteRequest 非常好。End 的文档应该说明 CompleteRequest 是跳到 EndRequest 通知并完成请求的更好方法。这是我们在请求以正常方式结束时所做的。同步刷新真的很糟糕。总而言之,您不应该使用 End,但使用 CompleteRequest 非常好。End 的文档应该说明 CompleteRequest 是跳到 EndRequest 通知并完成请求的更好方法。
正如 MSDN 建议的那样,我在调用 Response.Redirect() 后添加了这一行,并注意到一切似乎都运行相同。不确定 4.0 是否需要它,但我认为这不会造成伤害:
HttpContext.Current.ApplicationInstance.CompleteRequest();
更新 1
在对 Response.Redirect() 的调用中使用“false”可以避免 ThreadAbortException,但是可能在您的页面上抛出的其他未处理异常呢?这些异常仍然会导致您在没有 OnPreRender() 的情况下调用 OnUnload() 的问题。您可以按照每个人的建议在 OnPreRender() 中使用一个标志来避免这种情况,但是如果您抛出未处理的异常,您就会遇到更大的问题,并且无论如何都应该重定向到错误页面。由于未处理的异常不是您计划在每次回发时抛出的东西,因此如果您将 OnUnload() 逻辑包装在 Try-Catch 中会更好。如果您正在记录和监视您的异常,您将看到在 OnUnload() 事件中记录 NullReference 异常之前引发了一个未处理的异常,并且会知道要忽略哪个异常。
更新 2
您仍然应该将 OnUnload() 包装在 Try-Catch 中,但我认为这就是您真正想要的(记住 IsRequestBeingRedirected 将在调用 Response.Redirect 或在未处理异常后重定向到错误页面时为真) .:
if (HttpContext.Current.Response.IsRequestBeingRedirected != true)
{
//You're custom OnUnload() logic here.
}
有了这个,您将知道在 OnUnload() 事件中处理您的自定义逻辑是否安全(甚至值得)。我意识到我可能应该开始这样做,但我认为我们今天学到了很多。;)
注意:使用 Server.Transfer() 也会调用可怕的 Response.End()。为避免这种情况,请改用将preserveForm属性设置为“false”的 Server.Execute():
Server.Execute("~/SomePage.aspx", false);
return;
注意:关于 Server.Execute("~/SomePage.aspx", false); 的事情 是 IsRequestBeingRedirected 将是错误的,但您的 OnPreRender() 仍将执行,所以不用担心。