15 September 2006

update/delete/insert nodes to an xml document


Load an xml document to memory. Nevigate the DOM object, using xpath to query nodes. To update the node: create an new one, and replace the existing one.
Default namespace in xml doc still needs to declare in namespace mamager- and use in the xpath query. Although it is not present in the xml data:
Sample Data

C# Code to do this:
private void SetTestData(int index, int itemsPerPage, int totalResults)
{
XmlTextReader reader = new XmlTextReader("..\\TestData_V1.xml");
XmlDocument doc = new XmlDocument();
doc.Load(reader);
reader.Close();

string osUri = "http://a9.com/-/spec/opensearch/1.1/";

XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("os", osUri);
nsMgr.AddNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
nsMgr.AddNamespace("dc", "http://purl.org/dc/elements/1.1/");
nsMgr.AddNamespace("rss", "http://purl.org/rss/1.0/");

XmlNode oStartIndex, oItermsPerPage, oTotalResults;
XmlElement root = doc.DocumentElement;

oStartIndex = root.SelectSingleNode("/rdf:RDF/rss:channel/os:totalResults", nsMgr);
oItermsPerPage = root.SelectSingleNode("/rdf:RDF/rss:channel/os:itemsPerPage", nsMgr);
oTotalResults = root.SelectSingleNode("/rdf:RDF/rss:channel/os:startIndex", nsMgr);

XmlElement nStartIndex = doc.CreateElement("os", "startIndex", osUri);
XmlElement nItermsPerPage = doc.CreateElement("os", "itemsPerPage", osUri);
XmlElement nTotalResults = doc.CreateElement("os", "totalResults", osUri);

nStartIndex.InnerXml = index.ToString();
nItermsPerPage.InnerXml = itemsPerPage.ToString();
nTotalResults.InnerXml = totalResults.ToString();

root.SelectSingleNode("/rdf:RDF/rss:channel", nsMgr).ReplaceChild(nStartIndex, oStartIndex);
root.SelectSingleNode("/rdf:RDF/rss:channel", nsMgr).ReplaceChild(nItermsPerPage, oItermsPerPage);
root.SelectSingleNode("/rdf:RDF/rss:channel", nsMgr).ReplaceChild(nTotalResults, oTotalResults);

doc.Save("..\\tempTestData.xml");
}

02 September 2006

The ASP.NET Page Object Model

Revisit and take away from Dino Esposito's classic article The ASP.NET Page Object Model

Code Behind

The code of a page is the set of event handlers and helper methods that actually create the behavior of the page. This code can be defined inline using the tag or placed in an external class—the code-behind class.

  1. Think it this way make it easier to break away sequnential mentally on reading/writing code.
  2. Code behind is totally optional.
    1. You can have 'orphan' aspx page derived from web.UI.Page directly. In situations, say that you are testing good and feel of a web control
    2. You can have all aspx derived from a common code-behind class

AutoEventWireup

VSNET IDE wizard generates boiler-plate aspx contains @page like this

<%@ Page language="c#" Codebehind="MyPage.aspx.cs" AutoEventWireup="false" Inherits="MyNameSpace.MyPage" %>

For backward compatibility with the earlier VB programming style, ASP.NET also supports a form of implicit event hooking. By default, the page tries to match special method names with events; if a match is found, the method is considered a handler for the event. ASP.NET provides special recognition of six method names. They are Page_Init Page_Load, Page_DataBind, Page_PreRender, and Page_Unload. These methods are treated as handlers for the corresponding events exposed by the Page class. The HTTP run time will automatically bind these methods to page events saving developers from having to write the necessary glue code. For example, the method named Page_Load is wired to the page's Load event as if the following code was written.

this.Load += new EventHandler(this.Page_Load);

The automatic recognition of special names is a behavior under the control of the AutoEventWireup attribute of the @Page directive. If the attribute is set to false, any applications that wish to handle an event need to connect explicitly to the page event. Pages that don't use automatic event wire-up will get a slight performance boost by not having to do the extra work of matching names and events. Although VSNET creates aspx with the AutoEventWireup attribute disabled, the default setting for the attribute is true, meaning that methods such as Page_Load are recognized and bound to the associated event.

You should always explicitly register an appropriate handler instead of relying on AutoEventWireup.

Another interesting experitment. set AutoEventWireup="true" and also keep the following line in the InitializeComponent of code-behind. Put a Response.Write to it. It is called twice!

this.Load += new System.EventHandler(this.Page_Load);

The Page Lifecycle

Stage Page Event Overridable method
  • Page initialization
Init


  • View state loading

LoadViewState Restores view-state information from a previous page request that was saved by the SaveViewState method

  • Postback data processing
(implicitly by Page base class -ProcessPostData (private))
Call to LoadPostData methods in the controls tree that implements the System.Web.UI.IPostBackDataHandler

interface (e.g. TextBox)
  • Page loading
Load

  • Postback change notification

RaisePostDataChangedEvent method in any control that implements the IPostBackDataHandler interface

  • Postback event handling
Any postback event defined by controls RaisePostBackEvent method in any control that implements the IPostBackEventHandler interface

  • Page pre-rendering phase
PreRender


  • View state saving

SaveViewState

  • Page rendering

Render

  • Page unloading
Unload

The ProcessPostData is a private method implemeted at Page class which contains code like this:

IPostBackDataHandler handler1 = (IPostBackDataHandler) control1;
if (handler1.LoadPostData(text1, this._requestValueCollection))
{
this._changedPostDataConsumers.Add(handler1);
}
if (this._controlsRequiringPostBack != null)
{
this._controlsRequiringPostBack.Remove(text1);
}

A simple page to demo this cycle: PageLifeCycle.zip (contains PageLifeCycle.cs and .aspx, unzip and add them to a web proj. to see it in action)