Could someone explain this interesting ASP.NET HTTP pipeline events behavior? - c#

It is clear that member variables of an ASP.NET page should lose scope every time the page is refreshed.
I have the following code to display a grid of "Alerts" with edit capabilities:
public partial class Alerts : System.Web.UI.Page
{
private _WikiSyntaxEngine;
protected void Page_Load(object sender, EventArgs e)
{
_WikiSyntaxEngine = new WikiSyntaxEngine();
}
protected void gvAlerts_RowDataBound(object sender, GridViewRowEventArgs e)
{
var alert = (DataModel.Alert)(e.Row.DataItem);
// Below is the only line of interest, because only when published
// to the server would it throw an exception...
string html = _WikiSyntaxEngine.ConvertWikiTextToHTML(alert.Description);
}
}
It was only on the development server to which I published did this code fail (i.e. throws a NullReferenceException because _WikiSyntaxEngine was null) during an update to the row.
However, on my local machine the code runs just fine - the row is updated and the grid refreshes as expected.
I have since fixed the code to make the WikiSyntaxEngine a singleton, or the way it should have been designed from the beginning because it shouldn't have to be instantiated everywhere it is used.
My question then, is how can the Page_Load event be called before gvAlerts_Databound() on my local machine (as expected), but not called on the development machine resulting in the NullReferenceException for _WikiSyntaxEngine?

I'm assuming that your gvAlerts GridView is defined in the ASP.NET markup of the page. If this is the case, it is probably being DataBound in the OnInit() event, not on Page_Load(), where you're instantiating WikiSyntaxEngine.
You probably should use OnInit() to instantiate your WikiSyntaxEngine if your GridView is getting bound before Page_Load.
As for why you're getting different behavior in development and production is still a mystery to me.

Related

ASPX Page Life Cycle when calling [WebMethod]s

I'm calling a number of methods that have been decorated with [WebMethod] via jQuery ajax.
These require a database connection to be set up in an external library that will be the same for each method.
My original code looked like this:
public partial class Server : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// code to set up DB connections
ExternalLibrary.SetupDB();
}
[WebMethod]
public static string AjaxAccessibleMethod()
{
try
{
// get some data from the database via the external library
ExternalLibrary.CallDatabase();
}
catch(Exception ex)
{
// handle errors
}
}
}
This was working, but then started throwing exceptions claiming that the ExternalLibrary's database hadn't been initialized.
Placing breakpoints in my code I found that the Page_Load event wasn't being called when calling my AjaxAccessibleMethod, I also tried moving the DB setup stuff into the Page_Init event but likewise that wasn't called.
Can anyone explain to me the aspx page life cycle when using WebMethods? The fact that this worked initially seems to imply that Page_Load was called, but it no longer is.
Notice that the method you are using as WebMethod is static, this should be the first hint to the fact that Page object is not created at all.
Page Methods is a simple alternative to full blown web services, and as such, its life cycle is more similar to web service than to page. That is, request goes through the general ASP.NET pipeline, with objects like HttpContext, Request and such. But then the difference happens: for page requests and postbacks page object is created and the whole series of page events happens, whereas for page methods page object is not created, and method is simply called as Server.AjaxAccessibleMethod().
There is really no way to mix the two, because this would unnecessarily complicate processing of calls to page methods. So the only path forward for you here is duplicate necessary code:
protected void Page_Load(object sender, EventArgs e)
{
// code to set up DB connections
ExternalLibrary.SetupDB();
}
[WebMethod]
public static string AjaxAccessibleMethod()
{
ExternalLibrary.SetupDB();
...
}

Detecting refresh/postback conditions working in debug but not production

After searching the web, there seems to be a strong consensus that a good way to prevent a refresh from triggering a database access is to use a ViewState variable and Session variable to detect the condition. Here's the code in my base page class:
protected override void OnLoad( EventArgs e )
{
base.OnLoad( e );
if ( IsPostBack && ViewState["REFRESH_CHECK"] != Session["REFRESH_CHECK"] )
{
IsRefresh = true;
}
Session["REFRESH_CHECK"] = System.Guid.NewGuid().ToString();
ViewState["REFRESH_CHECK"] = Session["REFRESH_CHECK"];
}
public virtual bool IsRefresh
{
get;
private set;
}
So in my pages I have some code that looks like this:
protected void Page_Load( object sender, EventArgs e )
{
if ( !IsPostBack )
{
if ( !IsRefresh )
{
doStuffThatShouldOnlyBeDoneOnce();
}
}
}
This works perfectly while debugging, however when I run on a production system, I always get two invocations of the doStuff...() method. Of course when I debug it, there is only one call ever.
It may or may not be relevant, but I am using nested Master pages too.
Any ideas?
Oh man please do not use this code, this can break your web page. For a simple test, try to open same page with two different tabs and you will see that the solution has failed.
The only solution for the F5 refresh problem is Response.Redirect.
I have tried this soltion so I am telling you from my experience.
Half-answer
I was unable to figure out why Page_Load() is getting invoked twice in a production environment and only once in debug mode.
None of the techniques regarding refresh hitting the DB twice were working for me either.
So the approach I took was to change where the work was being done. Instead of executing the code when a new page was being loaded, I executed the code in a delegate for the button control when it was pressed prior to the redirect.
delegate( Object o, EventArgs eventArgs )
{
doStuffHere();
HttpContext.Current.Response.Redirect( "admin/Admin.aspx", false );
}
In this case, the next page needs some setup done based on the previous page, so long as I precede all redirects with this, everything is ok. This does not work if a user goes straight to a URL, but for this particular situation thats ok - the page won't be fully populated, but it won't cause an error and the user can still do work.

Refresh page repeat database transaction?

I have over 30 aspx pages, i have discovered a problem recently that if i did any kind of database transaction like insert, update, delete and then after the transaction is complete i pressed F5 or refreshed the page in anyway the same is transaction occur.
I searched for a solution but all i could found is that i have to check for viewstate on each button which is impossible, cause that means there will be a lot of work. There got to be generic solution, please help me in this problem.
Edit:
Here is the code on one of the buttons which change a value in data base to either true or false:
protected void btn_Publish_Click(object sender, EventArgs e)
{
if (Convert.ToBoolean(int.Parse(hf_Published.Value.ToString())))
{
publish(false);
}
else
{
publish(true);
}
}
After the execution of the code if refreshed the page the same code is executed, i noticed that since a break point was placed on this method.
You can try this. I used this in several project and working successfully.
public bool IsRefreshed
{
get
{
if (Convert.ToString(Session["RefreshTimeStamp"]) == Convert.ToString(ViewState["RefreshTimeStamp"]))
{
Session["RefreshTimeStamp"] = HttpContext.Current.Server.UrlDecode(System.DateTime.Now.ToString());
return false;
}
else
{
return true;
}
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
ViewState["RefreshTimeStamp"] = Session["RefreshTimeStamp"];
}
protected override void OnLoad(EventArgs e)
{
if (!Page.IsPostBack)
{
Session["RefreshTimeStamp"] = HttpContext.Current.Server.UrlDecode(System.DateTime.Now.ToString());
}
base.OnLoad(e);
}
There IS a generic solution, used for years by thousands of developers.
And the solution is: each time you perform an intrusive process at the server (insert/update/delete) you don't just render the page but rather you redirect the response with 302 to a fresh page with "your transaction succeeded" message.
This way, pressing the F5 will just refresh this message page, not the original page which triggers the transaction.
It is up to you whether or not this is directly applicable in your scenario.
http://en.wikipedia.org/wiki/Post/Redirect/Get
To fix this issue you could check the following:
Disable the submit button when necessary
Add some validation to your code and check for double entries
Redirect the user to another page after submit
Unless it's a real 'transaction' like payments etc. (which others already explained, do redirect etc.),
You could also try defining caching on your pages that interact with the Db or are bottlenecks for your app.
If you wanna have always live info (and it's that 'alive' type of app) then no luck with that solution (but even then), but usually, you can put some reasonable time expiration on how 'fresh' you want your data to be.
Caching ASP.NET Pages

ASP.NET Updating a UpdatePanel on another client

Okay.
So basically i am working on a message system on a webpage.
Users on my webpage is able to send each other messages, but now i want the messages to "pop up" on the receivers screen when sent. Exactly like when somebody on facebook sends you a message while your online, the message thing goes red. To solve my problem i need every client to know which other clients are online at the moment. I have solved this by coding an Observer-like pattern in my Global.asax:
public static void AddObserver(Observer o)
{
if(!observers.Contains(o))
observers.Add(o);
System.Diagnostics.Debug.WriteLine("Observer tilføjet : " + observers.Count);
}
public static void RemoveObserver(Observer o)
{
if (observers.Contains(o))
observers.Remove(o);
System.Diagnostics.Debug.WriteLine("Observer fjernet : " + observers.Count);
}
public static void NotifyObserversNewMail(Observer observer)
{
foreach (Observer o in observers)
if(!o.Equals(observer))
o.UpdateNewMail();
}
And the observer in this case i simply the Site.Master, which i have made extend the Observer class :
public partial class SiteMaster : System.Web.UI.MasterPage, Observer
{
protected void Page_Unload(object sender, EventArgs e)
{
Session["observer"] = this;
Global.AddObserver(this);
}
protected void Page_Load(object sender, EventArgs e)
{
//ADD OBSERVER TO GLOBAL.ASAX
if (Session["observer"] != null)
Global.RemoveObserver((Observer)Session["observer"]);
public void Update()
{
DLMessages.DataSource = ServiceMessages.GetInstance().GetMessages();
DLMessages.DataBind();
UPMessages.Update();
}
Where DLMessages is a DataList inside the UpdatePanel UPMessages.
So we have a "sender" client, and a "receiver" client.
When the sender creates a new message this method gets called:
protected void MessageSend(object source, EventArgs args)
{
Page.Validate("ValGroupMessageTo");
if (Page.IsValid)
{
ServiceMessages.GetInstance().SendMessage(ServiceCommunity.GetInstance().GetUser(MessageTo.Text).Id, ((User)Session["user"]).Id, MessageMessage.Text);
Global.NotifyObserversNewMail((Observer)Session["observer"]);
ClosePopups(new object(), new EventArgs());
Update();
}
}
As you can might notice it calls the Notify on global.asax, and the update() directly on itself. The UpdatePanel on the "sender" side updates perfectly, but on the receiver side nothing happens. Not in the UpdatePanel anyways.
Cause if i alter the code in the Update() to run through the messages from the DB, i can see that the message gets called fine, and the new message is loaded. Just not updated to the UpdatePanel.
So i have been thinking a lot about why the updatepanel doesnt get updated on the "receiver" side when the data gets updated, and my conclusion is it is because theres no partial postback on the "receiver" side. Yeah sure, the Update() method gets called, but theres no postback. So my question is this:
Is it possible to "force" a partial post back from the code-behind? Or is there another solution that might work better?
Hope it makes sense :-)
Do yourself a favour and build the whole thing using SignalR. It is a library for real time communication between the server and the browser for .NET (includes client-side libraries).
If you insist on doing it the hard way you should use a timer to trigger the update panel update
I'm not a big fan of working with updatepanels, but I think you can use the timer-control to force a partial postback.
Have a look at: http://mattberseth.com/blog/2007/08/using_the_ajax_timer_control_a.html
Those solutions, comet etc will work but really your best bet is to use socket.io
Real Time Browser Applications
You could also combined it with backbone.js and have a really nice app. I helped make a real time web messenger based on these 2.
I'm not an expert but you could try the Javascript/Jquery SetTimeout function associated with a WebService (.svc) to make periodic request from the client and retrieve data from the server.

My Visual Studio 2008 web application keeps throwing a .Net error when I first run it, but refreshing fixes it

As stated in the title my web application builds successfully, although every time I run it in debug mode I get the following .Net error:
If I hit refresh then the application gets no more errors until I next start it up again, any ideas?
Here is my global.asax file:
<%# Application Language="C#" Inherits="MyCompany.Web.MyApp.Shell.CustomWebClientApplication" %>
<script runat="server">
void Session_End(Object Sender, EventArgs e)
{
FormsAuthentication.SignOut();
}
protected void Session_Start(Object sender, EventArgs e)
{
if (Session.IsNewSession)
{
Response.Redirect(System.Web.Configuration.WebConfigurationManager.AppSettings["HomePage"]);
}
}
protected void Application_Error(Object sender, EventArgs e)
{
System.Exception oops = Server.GetLastError();
//Injection attack error handling
if (oops.GetBaseException() is System.Web.HttpRequestValidationException)
{
Response.Redirect("ErrorPage.aspx");
}
}
</script>
You have something which is trying to access a variable which is set to null (or hasn't been initialized). Do you have anything in the Global.asax or anything that fires on the start of the application? Do you have any asynchronous operations that fire on the start of the application?
Check on your Home.aspx page to see what is happening there. It looks like your application redirects to that page, so I would guess that on either the init or page_load event there is something which is executing that it causing the problem.
System.Exception oops
I think that this is source of problems. When there is no object returned from
Server.GetLastError();
then you will get NullReferenceException on line
oops.GetBaseException()
It makes perfect sense. On first run, oops is null (because no error occured before), thus throwing NullReferenceException. On second page refresh, GetLastError() returns object reffering previous errror (NullReferenceException) and page is displayed. Always check objects for null before accessing them.
Anyway, you can always try catching all runtime exceptions (Debug->Exceptions->Common Language runtime) and see where a problem is.
Sounds like an initialization issue. You're probably trying to use a resource before it's initialized, but by the time you refresh it's had time to be initialized.

Categories

Resources