In an existing ASP.NET application, I have a base class containing a Page_Load method:
public class PageBaseClass : System.Web.UI.Page {
protected virtual void Page_Load(object sender, EventArgs e) {
// do stuff...
}
}
I have an actual page which inherits from this base class. However, it doesn't override the existing Page_Load method, but declares a new one like this:
public class ActualPage : PageBaseClass {
protected void Page_Load(object sender, EventArgs e) {
// do other stuff...
}
}
The compiler gives me a warning that the Page_Load method in the actual page is hiding the existing Page_Load method. So effectively, there are two seperate Page_Load methods, since the old one wasn't overridden, but hidden.
Now my question is, what does the ASP.NET architecture do in it's lifecycle in such a situation? Which one is being called? Or are they both being called?
Note: I know this is bad design, I'm not sure what the original author had in mind, I'm just trying to understand what's happening and how this will affect the logic of the system.
After some digging around, I've found out what's happening.
Short answer: only the top-most method is called!
At first I put down some breakpoints like MelanciaUK suggested in a comment. This showed me that only the top-most method was being called.
Digging deeper, I looked at what volpav suggested. It was true that AutoEventWireup was set to true, which was automatically hooking up the method to the event handler (since this indeed wasn't done manually). However, unlike he claimed, only the top Page_Load method was being called.
Reflecting over the type gave me a bit of a clue what was going on:
var pageloads = this.GetType().GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).Where(m => m.Name == "Page_Load");
var pageload = this.GetType().GetMethod("Page_Load", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
The pageloads value ended up containing two results: the two Page_Load methods, one with declaring type ActualPage and one with declaring type PageBaseClass.
I though that would cause problems for the second call, since having two results, I expected it to throw an AmbiguousMatchException. However, it did not. It contained the MethodInfo for the method in the ActualPage.
Digging deeper, I stumbled upon an article by K. Scott Allen: Inside AutoEventWireup
http://odetocode.com/blogs/scott/archive/2006/02/16/inside-autoeventwireup.aspx
According to his article, wiring up events is happening as follows:
Delegate.CreateDelegate(typeof(EventHandler), this, "Page_Load", true, false);
Executing this myself, I found that this call creates a delegate pointing at the Page_Load method for the ActualPage, the same way the GetMethod call returned that method.
So it seems that in case an event-method is being wired up automatically, it will not wire up any methods that are being hidden.
The code in both methods will be called since these are essentially an event handlers (one which is attached by the base class, another one - by your "ActualPage" class). This is due to "AutoEventWireup" property set to "true" (it's set by default in your markup).
Related question on "AutoEventWireup": What does AutoEventWireUp page property mean?
UPDATE
Turned out I was wrong stating that both methods will be called and in fact, only the one declared on the "ActualPage" gets called. See Rene's answer below.
Related
a quick question for an ASP.NET expert. It is an arguable moment for us in the company at the moment...
We have built a nice (no bugs in there) CMS framework that we use for our sites...
It goes something like this:
MyCms.Content.Channels channels = new MyCms.Content.Channels();
where at the moment of instantiating the Channels class, it loads a bunch of XML files and converts them to a List<MyCms.Content.Channels.Channel> that is held within the Channels class, and cached using System.Web.HttpRuntime.Cache (until there are any changes to the folder holding the XML files)
The Channels class is basically a hierarchical structure for web pages...
We normally use it like this in our ASP.NET pages (code behind):
public partial class Default : System.Web.UI.Page
{
public MyCms.Content.Channels channels;
public MyCms.Content.Images images;
public MyCms.Content.Channels.Channel CurrentChannel;
public List<MyCms.Content.Channels.Channel> latestItems;
public MyCms.Content.GameVotes votes;
public MyCms.Content.GameVotes.Vote vote;
protected void Page_Load(object sender, EventArgs e)
{
channels = new MyCms.Content.Channels();
images = new MyCms.Content.Images();
..
}
as you can see, the public variable 'channels' is instantiated at Page_Load()... where at the moment it has loaded a bunch of XML files either from a file system, or from cache...
Our colleague though, is sometimes instantiating this class outside of Page_Load() - right next to the public declaration of the 'channels' variable like this:
public partial class Default : System.Web.UI.Page
{
public MyCms.Content.Channels channels = new MyCms.Content.Channels();
protected void Page_Load(object sender, EventArgs e)
{
... he does the same in various User Controls...
Now thing is.. I need your opinion on whether it's ok to instantiate a very active class like this - outside of Page_Load() event... ? The site that was built by our colleague was hanging the entire IIS from time to time, and I just suspect that this could be one reason... - what do you think, please ? :)
The same CMS Framework is being used on other sites, on other servers with no issues at all... So the only difference I could find between the good working sites and the one that is hanging - is this .. instantiation of 'channels' class outside the scope of Page_Load()...
It shouldn't make a difference. That's not to say it doesn't.
All that's happening is you're changing the point at which you create the object from load event, which happens about half way through the lifecycle to the constructor, right at the very start. At both points the cache should be available as it's part of the context, although you really ought to check that.
I would say though, instantiating such an important class should happen at a specific point, like page initialise or load, rather than at constructor.
Simon
The only difference I'm aware of is that if you initialize the objects outside of the Page_Load they will be created as soon as your page's class is created (i.e. before all the Page_XXX events), and if you initialize them inside the Page_Load they will be created only when the event is called.
That means if your application crashes, redirects or for whatever reason does not enter Page_Load, you have created the object uselessly.
In building custom controls, I've seen two patterns for using the viewstate. One is to use properties to disguise the viewstate access as persistent data.
public bool AllowStuff
{
get
{
return (ViewState[constKeyAllowStuff] != null) ?
(bool)ViewState[constKeyAllowStuff] : false;
}
set { ViewState[constKeyAllowStuff] = value; }
}
The other is to use private member fields and to override the Load/SaveViewState methods on the control and handle it all explicitly:
protected override object SaveViewState()
{
object[] myViewState = new object[2];
myViewState[0] = base.SaveViewState();
myViewState[1] = _allowStuff;
return myViewState;
}
protected override void LoadViewState(object savedState)
{
object[] stateArray = (object[])savedState;
base.LoadViewState(stateArray[0]);
_allowStuff = (bool)stateArray[1];
}
(I cut out a lot of safety checking for clarity, so just ignore that.)
Is there are particular advantage to one method over the other? I can't see how they'd differ much performance wise. Version 1 is lazy, so I guess you save a bit if you don't need that particular value during a pass. Version 1 is also more abstract, hides the details better. Version 2 is clearer about when the data is actually valid and ok to read or modify (between the load and save) because it more clearly works within the ASP.NET lifecycle.
Version 2 does tend to require more boilerplate code though (a property, a backing private field, and viewstate handling in two places) as opposed to Version 1 which combines all that into one place.
Thoughts then?
The private member field approach is often used for objects who do not directly have access to the ViewState state bag. So in a sense, I'd use option one for custom controls, user controls, or pages, or anything that has a ViewState or similar property, but use the other option for an object that does not directly have access to ViewState (like a class you want to be able to "serialize" and store in viewstate). For instance, custom controls would use that approach to store state for child objects that do not directly reference viewstate.
HTH.
Fist of all I would use ControlState and not viewstate so it works correctly if in a container that has view state turned off.
Then i would override init, savecontrolstate, loadcontrolstate and databind.
and make sure to register that the control uses the control state i.e. Page.RegisterRequiresControlState(this)
oh and the advantage is that your control is more robust (user can't screw it up as easily) and will work when dynamically loaded and across postbacks "better"
I recently wrote an implementation of a VirtualizingWrapPanel that recycles containers as they scroll into and out of view.
On occasion I've noticed that the content rendered by the control is actually the previously contained data, not the current data. Performing any action on the control that forces a new render call updates the control such that it displays the correct data.
Could this a bug in the ItemContainerGenerator recycling or is it more likely in my recycling code? Is there a way I can force all my bindings to update (after updating the control with new content) without explicitly writing each binding expression in code behind?
I've seen problems very like this in the past when using virtualization when we were using custom controls that really weren't expecting their DataContexts to be changed once they were displayed.
If your panel is correctly (as it sounds) handing new DataContexts to the reused objects then it does sound like the reused objects aren't processing that DataContext change correctly. (This 'render' call you talk about would then pick up the new DataContext and display that.)
If you're using plain data binding in your control then I'm slightly stumped. (Is your panel re-Measure/Arranging the controls after they've got their new DataContext?)
The fix for us was to have our controls listen out for when their DataContext changed. (This is also useful for debugging virtualizing panels to test that the DataContext is coming in correctly.)
Sadly the OnDataContextChanged method isn't public in Silverlight but you can still find out about DC changes by binding to them.
public MyClass()
{
InitializeComponent();
SetBinding(MyDataContextProperty, new Binding());
}
private static readonly DependencyProperty MyDataContextProperty =
DependencyProperty.Register("MyDataContext",
typeof(object),
typeof(MyClass),
new PropertyMetadata(DataContextChanged));
private static void DataContextChanged(
object sender,
DependencyPropertyChangedEventArgs e)
{
MyClass source = (MyClass)sender;
source.OnDataContextChanged();
}
private void OnDataContextChanged()
{
// My DataContext has changed; do whatever is needed.
// re 'render' in your case?
}
Why is "_requestValueCollection" empty on PostBack?
I have a really strange problem with post backs. In some cases on post backs (this.Request.RequestType == "POST") have null "_requestValueCollection" member. And for ASP.NET that means this.IsPostBack == false.
So I have modified the Page_Load in the following way:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack && this.Request.RequestType != "POST")
{
//REGULAR INIT STUFF
}
else
{
//REGULAR SITE POSTBACK STUFF
}
}
What is possible danger of this approach? So far everything is doing OK (and is pretty rich and complicated page).
It isn't clear from your example what you are attempting to do with this code, so this is mostly a short in the dark.
You probably don't need the second part of the if statement. Checking IsPostBack alone should be sufficient.
_requestValueCollection is not a property, it is a field and probably isn't a good place to get at the data submitted by the client. I suggest instead that you consider using the Form property (this.Request.Form) or the Headers property (this.Request.Headers) depending on what you are looking for. Keep in mind that most of the time you can just get form values from the asp.net controls on the form directly.
You may also want to look at the Request.HttpMethod property if you need to determine the exact http method used to invoke the page.
Edit: Adding info about _requestValueCollection
The mechanics behind the _requestValueCollection being loaded are quite complex, but I took a look at the MS source and from what I can determine the page calls on every control on the page that implements the IPostBackDataHandler interface. For each of these it will call the LoadPostData method which adds the data for that control to the collection.
The main things that I can think of off the top of my head that might cause the collection to be null would be:
no server controls on the page implement IPostBackDataHandler
there is no server form, or the contents of the form weren't sent by the client
Alternately, the page may be using query strings to convey the data to the server, and the query string doesn't contain anything
As I said, this is a bit fuzzy. The Page class is very complex internally and so there could be other ways data gets put into that collection too, but this was all I could find on a casual examination.
I'm using an ObjectDataSource to bind data to a GridView; it works fine except that it always creates a new object to use as a data source. I can do all the setup just fine but I cannot use an instance of an existing object to specify as the "data source" for it. Is it possible to do this? If so, how?
If it's not possible, why?
EDIT: Here's the gist of what's going on (object types changed): On the first page you are editting the attributes for a dog. One of the attributes is "has puppies" and if it's true, the next page you specify the names of those puppies. What's happening in my case is that those puppies are not getting linked to the original dog but to a "new" dog. (The implication that my problem is a "female dog" was coincidental. ;-) )
Create an event handler for the ObjectCreating event on the ObjectDataSource.
You can assign the instance to using the ObjectDataSourceEventArgs property
protected void ObjectDataSource1_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
e.ObjectInstance = myObject;
}
Wire this event up in the markup too
<asp:ObjectDataSource OnObjectCreating="ObjectDataSource1_ObjectCreating" />
As I just discovered in my own question here, items stored in the Application Cache are going to pass themselves as a reference for use. You may consider storing your data there (or potentially in the Session as well) and pass items that way.