Understanding the ASP.NET Architecture


ASP.NET is built with an extensible architecture that provides excellent performance and a logical approach to servicing client requests. The ASP.NET runtime engine delegates client requests to a wide variety of handler classes. Client requests are not serviced by a monolithic process but are instead routed to targeted classes designed to service a specific kind of request. For example, when a client calls up an *.aspx page in their browser, the request gets routed from the ASP.NET runtime engine to a specialized factory class that receives the request and returns a Page object. This object can be manipulated on the server as needed before it is rendered on the browser as HTML. Like a corporate chief executive officer (CEO), the HttpRuntime instance is ultimately accountable for servicing a client request, but it does so by delegating the work to handler classes.

Figure 2-1 provides a schematic view of ASP.NET architecture. The role of IIS is relatively diminished in ASP.NET compared to classic ASP. Specifically, the role of IIS (in ASP.NET) is primarily relegated to that of a request/response broker. IIS is the point of first contact for incoming client requests, and it will run authentication checks before allowing requests to proceed. At a minimum, IIS simply accepts an anonymous client request and assigns it to the default local machine account that has been created for IIS ( IUSR_[Machine Name ] ). Alternatively, you can configure the Web site to use Windows-based authentication, in which case IIS will demand specific credentials from the client. Either way, once IIS has performed its authentication checks, it forwards the client request to a dedicated process called the ASP.NET worker process ( aspnet_wp.exe ). This process executes a number of steps that culminate in passing the client request to the ASP.NET runtime engine.

click to expand
Figure 2-1: ASP.NET Web site architecture

The ASP.NET runtime engine contains a set of classes for handling client requests and serving responses. These classes provide the basic infrastructure and the core capabilities for supporting Web applications. The gateway to the runtime engine is the HttpRuntime class. This class initially accepts a client request and then delegates it to any number of HTTP handler classes, depending on the nature of the request. Figure 2-1 depicts incoming HTTP requests from Web clients that get filtered down to the HTTP runtime engine (shown as solid lines). The HTTP request then gets delegated to several HTTP handler classes, including the PageHandlerFactory class, which is responsible for generating a Page object. The Page object receives a reference to the HTTP request object and begins processing the request. Finally, the Page object renders an HTML response that gets filtered back to the Web clients (shown as dashed lines). The figure also represents the close ties between the HTTP runtime engine and the underlying .NET Framework on which it is built.

HTTP Handlers

HTTP handlers implement a common interface called IHttpHandler (a member of the System.Web namespace). There are two kinds of handler classes:

  • Handler processing classes: These are classes that implement the interface ( IHttpHandler ) that allows them to process HTTP requests. For example, the Page class is a handler that represents an *.aspx Web page.

  • Handler factory classes: These are classes that dynamically manufacture new handler classes. These classes implement the IHttpHandlerFactory interface. For example, the PageHandlerFactory class generates a Page handler class for every HTTP request that calls an *.aspx Web page.

The IHttpHandler interface defines a method called ProcessRequest() , which accepts an HttpContext instance as its argument. The interface also provides a Boolean property called IsReusable() , which dictates whether an HTTP handler can pass a request to another handler:

 Sub ProcessRequest(ByVal context As HttpContext) ReadOnly Property IsReusable As Boolean 

The HttpContext object encapsulates all of the HTTP-specific details for an individual HTTP request. Clearly, the ProcessRequest() method provides a clean way of passing a client's request details between different objects on the Web server.

HTTP handlers must be registered in the ASP.NET configuration files (either Machine.config or Web.config ). This is an excerpt from the Machine.config file:

 <httpHandlers>     <add verb="*" path=" trace.axd" type=" System.Web.Handlers.TraceHandler"/>     <add verb="*" path="*.aspx" type=" System.Web.UI.PageHandlerFactory"/> </httpHandlers> 

The <httpHandlers> section registers HTTP handlers on the server using <add> child elements. A handler class can process an individual Uniform Resource Identifier (URI) or a group of URIs that share a common file extension. The Machine.config file excerpt shown previously illustrates both options. The attributes for the <add> element are as follows :

  • Verb: This is a comma-separated list of HTTP verbs, including GET, POST, PUT, or the wildcard asterisk (*).

  • Path: This is a single URI path or a wildcard path, for example, *.aspx .

  • Type: This is a class/assembly combination that contains the handler class. The excerpt only shows the class name, but you can append the assembly name as well, separated by a comma.

We discuss ASP.NET configuration files in greater detail in the "Understanding the ASP.NET Configuration System" section.

By now you can begin to appreciate how extensible the ASP.NET runtime engine is. You can route HTTP requests to any handler you set up. Few readers will ever need to create a custom handler because you can almost always tackle a specific HTTP request through the Page object (discussed next ). But in other cases, HTTP handlers are the most efficient way to handle an HTTP request because they can service a request without going to the expense of loading up a Page object.

Let's look at an interesting example. Consider a Web application that logs the Internet Protocol (IP) address and a timestamp of all clients when they first access the application. Let's say that the application provides an entry page, called ap_gateway.aspx , that provides no user interface but that records the client's IP address with a timestamp and then redirects the client on to a formal login page. You could create a standard *.aspx Web page that performs this function, but this approach unnecessarily creates an instance of the Page object ( assuming you have not altered the standard HTTP handler for .aspx pages). A better approach would be to create a custom HTTP handler that processes the ap_gateway.aspx page directly. If you want even more distinction, you could create a custom file extension, such as .xyz , for the gateway page.

Listing 2-1 contains the code for this custom HTTP handler.

Listing 2-1: A Custom HTTP Handler
start example
 Namespace Apress     Public Class apXYZHandler         Implements IHttpHandler         Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) _             Implements System.Web.IHttpHandler.ProcessRequest             ' Instance an EventLog object             Dim objEvt As EventLog = New EventLog()             Try             ' Write the client's IP address to the event log, with a timestamp             Dim strClientIP As String = context.Request.UserHostAddress             objEvt.Source = "ASP.NET 1.0.3705.0" ' Event source is ASP.NET             objEvt.WriteEntry("Client IP: " & strClientIP & " logged at: " & Now)             ' Redirect the client to the login page             context.Response.Redirect("ap_login.aspx")             Catch err As Exception             ' No action taken. Prevents unhandled errors if .WriteEntry fails.             End Try         End Sub         Public ReadOnly Property IsReusable() As Boolean Implements _             System.Web.IHttpHandler.IsReusable             Get                 Return (True)             End Get         End Property     End Class 
end example
 

Listing 2-1 is very simple. The client's IP address is extracted using the Request object's UserHostAddress property. Next, the handler writes a record into the system event log. Finally, the handler redirects the user to the formal login page, called ap_login.aspx . Clearly, the HttpContext object is critical for an HTTP handler class to work!

Notice that Listing 2-1 includes exception handling around the WriteEntry() method in case this method fails. The exception handler does not do anything per se in that it does not take a specific action when an exception occurs. However, it does prevent unhandled exceptions, which could potentially bring down the Web site. This may happen if the site administrator fails to reconfigure the event logs from the default overwrite after 7 days to overwrite as needed . If the event logs fill up completely, then a write failure will cause an exception in the code, with potentially adverse effects. This is of particular concern in high-volume sites that write a lot of information to the event logs.

Next, you must register the HTTP handler class in the Web.config (or Machine.config ) file:

 <httpHandlers>          <add verb="*" path=" ap_gateway.aspx"              type=" AspNetChap2.Apress.apXYZHandler, AspNetChap2"/> </httpHandlers> 

The syntax for the class name and the assembly is important to get right; otherwise the Web application will generate runtime errors when you attempt to load it in the browser.

We have compiled the HTTP handler class directly into the AspNetChap2 sample project for this chapter. In this case, AspNetChap2 is equivalent to the assembly name, so you see it included in the type description. We experimented with omitting the assembly name from the type definition, but the Web application failed to load.

Note

You can download the source code for the sample projects from the Downloads section of the Apress Web site at www.apress.com . The sample projects are an integral part of this book and an excellent learning and reference tool.

Finally, you can test the HTTP handler by opening a new browser and typing the path to the gateway page ”in this case, http://localhost/AspNetChap2/ap_gateway.aspx . The page will load, then after a couple of seconds you should be redirected to the ap_login.aspx page. Open the application log in the Event Viewer, and you will see a recent information record with something like the following contents:

 Client IP: 127.0.0.1 signed in at: 4/15/2002 7:19:45 PM. 

The most interesting aspect of this example is that the page ap_gateway.aspx does not exist. We never added one to the ASP.NET project, nor do we need to add one. The HTTP handler recognizes the path name and takes that as an indication to start working. The actual page does not need to exist, and this is what makes HTTP handler classes so efficient under certain circumstances.

The Page Class

A Page object is instanced every time an *.aspx page is requested . The Page object is responsible for processing a client request and rendering HTML in response. The Page object provides programmatic access to a Web form, plus access to the HTTP intrinsic objects such as HttpRequest and HttpResponse. Every *.aspx page is associated with an instance of the Page class. Specifically, the Web page inherits from a code-behind class, which in turn inherits from the Page class. For example, the ap_login.aspx page contains the following directive at the top of the file:

 <%@ Page Language=" vb" Codebehind=" ap_login.aspx.vb"     Inherits=" AspNetChap2.ap_login"%> 

Then, when you switch to the code-behind file, you see the following:

 Public Class ap_login     Inherits System.Web.UI.Page     Private Sub Page_Load(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles MyBase.Load         ' Code goes here     End Sub End Class 

Note that the Codebehind attribute simply indicates the location of the code-behind file. The Inherits attribute is what actually binds the Web form to a specific class. The @ Page directive supports a long list of attributes that control the Web form's behavior at runtime. Several of these attributes override application-level settings in the Web.config file. For example, if the Web application has session state enabled, you can prevent an individual page from participating by setting its EnableSessionState directive to "False."

The @ Page Directive

The @ Page directive provides the three required attributes shown in the previous listing: Language, Codebehind, and Inherits. In addition, many others are set to "True" by default, which means they apply to the page even if they are missing from the @ Page directive. So, the @ Page directive is as much about disabling what you do not want as it is about enabling what you need. You should always explicitly set three attributes:

  • AutoEventWireUp: This is a Boolean attribute that indicates whether Page events are wired into specific delegate functions ("True") or whether they can be wired into user-defined functions ("False"). If the attribute value is "True" (or, by default, if the attribute is missing), then the Page_Init() and Page_Load() event handlers will always be called, and they must be declared in a standard way. If the attribute value is "False," then the events are handled only if the user chooses, and they can be delegated to any function that supports the right interface for the event. Remember, Page events will always raise, but this does not mean you have to devote code and processing time to responding to the events. We always set this attribute value to "False."

  • EnableViewState: View state allows server controls to persist their contents and selected values between postings. View state is enabled by default for an entire page, but because it can have performance implications, you should set the Boolean attribute explicitly, even if you plan to keep view state enabled ("True"). View state is convenient, but it is not always needed. Typically, it is most convenient to keep view state intact for the overall page and disable it for individual controls. The "View State" section discusses this in further detail.

  • EnableViewStateMac: This Boolean attribute indicates whether the view state contents should be encrypted and whether the server should inspect them for evidence of tampering on the client. The Mac portion of the attribute name stands for Machine Authentication Check. Many users ( ourselves included) have run into page loading problems when this attribute is omitted (which sets the attribute to "True," by default). Many users set this attribute value to "False." Microsoft hastens to point out that MAC encryption is not a replacement for a certificate-based encryption system such as Secure Socket Layer (SSL). If tampering is an issue for you, consider implementing a certificate-based encryption system rather than relying on MAC encoding.

Page Class Members

The Page class members roughly fall into three groups. The first group includes the properties and methods that manipulate the Web form controls. The second group includes properties that access the ASP Intrinsic objects. The third group includes the Page lifecycle events (which are discussed in the next section). Table 2-1 describes important members of the Page class that fall into the first two groups.

Table 2-1: The Page Class Members

CLASS MEMBER

DESCRIPTION

Controls

[Property] A collection of Control objects hosted on the page. You can iterate through the collection using for- each-next syntax. For example, to print out control type details, use this code:

 Dim objItem As Control For Each objItem In Page.Controls       Console.WriteLine(objItem.GetType) Next 

FindControl

[Method] Retrieves an object reference for a specific control on the page, using its ID. For example:

 Dim MyCtl As TextBox = Page.FindControl("ap_txt1") 

HasControls

[Method] Determines if a server control contains child controls. For example, a DataGrid control may contain embedded (child) controls, such as textboxes and buttons .

IsPostBack

[Property] A Boolean value that indicates whether the current GET or POST request results from the current page posting back to itself. This property is typically checked in the Page_Load() event. If view state is enabled, this property often indicates that the page should be processed but not re-rendered. Because view state preserves the original contents of the Page controls, you will get duplicate items in the controls if they are re-rendered without first clearing the existing items. (This generalization may not apply to your page.)

Request

[Property] Gets a reference to the current HttpRequest instance, which encapsulates the client's request details.

Response

[Property] Gets a reference to the current HttpResponse instance, which encapsulates the server response details.

Application

[Property] Gets a reference to the Application object for the current request (which wraps the HttpApplicationState class). This class enables global application information to be shared across multiple requests and sessions.

Session

[Property] Gets a reference to the Session object for the current request (which wraps the HttpSessionState class). This class provides access to session-specific settings and values. ASP.NET provides several modes for storing session state.

Page Lifecycle Events

Recall that the Page object is responsible for processing a client request and rendering HTML in response. The Page object runs through a specific set of lifecycle stages as it fulfills its responsibilities. Several of these stages are associated with events that you can capture and code behind.

The Page class itself inherits from the Control and TemplateControl classes, both of which are members of the System.Web.UI namespace. The Control class provides a common set of properties, methods, and events that are shared by all server controls. The TemplateControl class provides additional base functionality for the Page class. Together, these two classes provide the base events that the Page class raises, as well as base methods that the Page class can override.

Table 2-2 summarizes the more important lifecycle stages and their associated events, or the methods that can be overridden, for adding code to specific stages.

Table 2-2: The Page Lifecycle Stages

STAGE

DESCRIPTION

EVENT OR METHOD?

Initialize

Initializes settings.

The Page_Init event

Load View State

Loads view state information from the _VIEWSTATE hidden form field and assigns the contents to a StateBag object. This object is exposed by the Page object's ViewState property.

The LoadViewState() method

Load Post Data

Process incoming form data. If state changes occur between postbacks, the RaisePostDataChangedEvent() method will be raised immediately following the _Load() event.

The LoadPostData() method

Load

Performs actions for all requests, including initializing server controls and restoring theirstate. The Page object provides an .Is PostBack property that will fire for posts (value is "False") and reposts (value is "True"). The .IsPostBack property allows you to set up conditional logic in the Load() event handler for handling the first post vs. subsequent reposts. You can check for postback data and can view state information for the Page's child controls.

The Page_Load() event

Handle Postback Events

Handles the client-side events that trigger a postback to the server. For example,the _Click() event of a Button server control or the _OnPageIndexChanged() event of a DataGrid with paging enabled.

The RaisePostBackEvent() method

Pre-Render

The stage where remaining updates are performed prior to saving view state and rendering the form. You can add custom values to view state at this stage.

The Page_PreRender() event

Save View State

The stage where view state information is persisted to a hidden field on the form.

The SaveViewState() method

Render

Generates HTML output to send to the client.

The Render() method

Dispose

Releases resources and performs final cleanup, prior to unloading the Page object.

The Page_Disposed() event

Unload

This stage is where the Page object is unloaded from server memory. You can perform cleanupand release resources in this stage, butdevelopers generally perform these tasks in the Page_Disposed() event.

The Page_Unload() event

The descriptions in Table 2-2 are specific to the Page object, but many of the events and methods apply equally to any server control. This should be of no surprise, given that the Page class inherits from the Control class, which is common to all server controls. In addition, the Page object acts as a container for a collection of server controls, all of which run through their own processing stages. There is a complicated interplay between the controls' execution orders and the Page execution order. This sequencing is of particular concern when you are developing a custom server control that handles postbacks and participates in view state.

View State

View state is an ASP.NET feature that allows a Web page to retain all of its state values between requests to the Web server. In classic ASP, developers are forced to handle this task manually, which is a tedious and time-consuming process. Consider one example, where the user fills out a form with several HTML input text controls. Once the form is submitted to the server, the ASP engine retrieves the control contents from the HTTP headers and processes the business logic. When the page returns to the client, the control values (including user input) are not retained, unless the developer has manually rehydrated the control values using embedded server-side code within the HTML form. This issue is a regular headache for form-based applications because the server commonly needs to return control values to the client for a second look ”for example, if the user input fails server-side validation.

Input controls represent one of the simpler problems. Drop-down list controls pose a more complicated problem because they contain multiple values and include a user-selected value. Typically, drop-down lists need to be hydrated just once, and they must then retain the same values and user selections. Classic ASP requires you to manually repopulate the select box contents between postings to the server. This requirement is not just tedious for the developer, but it can also be expensive for the server, especially if additional database lookups are required.

View state enables all controls on a Web page to retain their state values between successive postbacks to the server. Furthermore, view state preserves a control's properties between postbacks. For example, if a control's Visible property is set to "False," then the control will remain invisible between successive postbacks. In short, view state saves developers time by reducing the amount of coding they have to do. In addition, view state improves application performance by eliminating the database calls that would otherwise be needed to rehydrate controls between postbacks.

How View State Works

View state is stored using encoded key-value pairs within a hidden form field. Every control on the page is represented using one or more pairs. For view state to work, a Web page must contain a server-side form (with the runat =server attribute) so that a hidden _VIEWSTATE field may be added directly below the opening <Form> tag. (A server-side form is provided by default when you create a new Web page). View state only works for server-side controls contained within the server-side form. In fact, server-side controls will generate compilation errors if they are added to a page outside of their server-side form.

For a Web page with view state enabled, the view state process works as follows:

  1. The client requests a Web page.

  2. The server renders the page, including a _VIEWSTATE hidden field that contains encoded key-value pairs for the properties and values of every control on the page.

  3. The client enters information into the rendered HTML controls and then posts the page back to the server.

  4. The server initializes the page and then tracks the client's changes to control state. The server executes the business logic on the page and then renders the page. The server controls get rehydrated using the latest values stored in the _VIEWSTATE hidden field, which is updated to include the new, client-specified values and selections.

You manage view state using the StateBag class, which is a member of the System.Web.UI namespace. The Page object provides access to a StateBag object through its ViewState property. The StateBag class implements a number of interfaces, including the IDictionary and IEnumerable interfaces, which allow you to enumerate and modify the view state contents. Table 2-3 describes important members of the StateBag class.

Table 2-3: The StateBag Class Members

CLASS MEMBER

DESCRIPTION

Add

This method adds a new StateItem object to the StateBag object or updates an existing StateItem object value if it is already included in the StateBag class. The StateItem object represents a view state name-value pair.

Remove

This method removes an object from the StateBag object.

Keys

This property gets a collection of (enumerable) keys that represent the items in the StateBag object.

Item

This property gets or sets the value of an object in the StateBag object.

You can add or modify view state items from the Web form code-behind file up until the Pre-Render stage of the page's lifecycle, which is just before the page starts rendering HTML. For example, you can append custom view state values that are distinct from any of the page controls:

 Private Sub Page_PreRender(ByVal sender As Object, _     ByVal e As System.EventArgs) Handles MyBase.PreRender     ' Store custom ViewState values     ViewState("Key1") = "MyValue1"     ViewState.Item("Key2") = "MyValue2" ' Alternate notation End Sub 

In fact, you can add any object to view state, as long as it supports binary serialization. Serialization is the process by which binary data is converted into a stream of bytes in order to be saved to a storage medium. Serializable objects, such as the DataView object, implement the ISerializable interface. Non-serializable objects, such as the ListItem object, do not implement this interface. As you can see, you can add serializable objects to view state easily:

 ' ViewState will store any serializable object Dim sqlDV As DataView ViewState.Add("MyDataView", sqlDV) 

Retrieving objects from view state is just as simple:

 If Page.IsPostBack Then      ' Retrieve the DataView object from ViewState        sqlDV = ViewState.Item("MyDataView") End If 

Persisting View State Across Multiple Pages

In special cases, view state may persist values across multiple pages, not just across postbacks for the same page. Consider an application with two Web pages, where each page contains a TextBox server control named TextBox1. If Page 1 submits to Page 2, then the TextBox control on Page 2 will pick up the view state for the Page 1 TextBox control. You can use this behavior to your advantage if you want to persist common information across multiple pages, as long as the Web application uses posting between pages, rather than hyperlinks . For example, consider an application where every page contains a Label server control for persisting the client's login name. This is the design view for Page 1:

 <form id="Form1" method="post" runat="server">          <asp:Label id="lblUserName" runat="server" Width="126px"                   Height="28px"></asp:Label> </form> 

In the code-behind file, you would assign the login name to the Label control:

 Public Class ap_ViewState     Inherits System.Web.UI.Page     Protected lblUserName As System.Web.UI.WebControls.Label     Sub Page_Load([Arguments]) Handles MyBase.Load         If Not Page.IsPostBack Then             ' Assign Username to label (Username is hardcoded for demo purposes)             lblUserName.Text = "AEinstein"         End If     End Sub End Class 

Then on subsequent Pages 2, 3, and so forth, you can automatically pick up the Label value from view state by doing just two things:

  1. Add a Label server control named "lblUserName."

  2. Add a variable declaration for the label in the code-behind file for the server control.

The only "coding" you need to do is to ensure that every page declares the server control variable:

 Protected lblUserName As System.Web.UI.WebControls.Label 

The inverse behavior also applies: The view state will clear for all server controls that are not repeated between pages. Custom view state values will only persist for as long as the client continues to post back the same page.

Disabling View State

ASP.NET server controls have their view state enabled by default, but you can disable it for those controls that do not need to retain their values between postbacks. For example, DataGrid controls that are rehydrated on every postback donot need to retain their view state.

You can disable view state for a server control in two ways:

  • At design-time, by setting its EnableViewState attribute equal to "False":

     <asp:Label id=" lblUserName" runat=" server" EnableViewState=False></asp:Label> 
  • At runtime, although make sure you do so the first time the page loads:

     lblUserName.EnableViewState = False 

You can also disable view state for an entire page using the @ Page directive:

 <%@ Page Language=" vb" EnableViewState=" false" Codebehind="  [Page]  .aspx.vb"     Inherits=" AspNetChap2.  [Page]  " %> 

Finally, you can disable view state for an entire application using the Web.config configuration file. To do so, simply modify the <pages> configuration element's enableViewState attribute value:

 <pages buffer=" true" enableViewState=" false"> 

Keep in mind that when view state is completely disabled, you will have to do a lot of work to manage state manually.

Performance Considerations with View State

View state always comes with a performance price for three reasons:

  • View state can add a significant number of bytes to the overall page size , depending on the amount and complexity of the persisted data. Larger pages take longer to render, and they deliver more slowly back to the client.

  • View state is only optimized to work with a small set of simple object types, including strings, integers, Booleans, ArrayLists, arrays, and Hashtable objects. All other objects (including server controls) are less efficient to serialize. They take longer to serialize because they generate larger byte streams.

  • Server control states must be deserialized from view state (or rehydrated ) every time the server renders a reposted page back to the client. It is expensive and time-consuming to deserialize pages that contain rich server controls (for example, DataGrids) or controls that hold large numbers of values (for example, DropDownList controls).

You cannot modify how a control's view state is persisted, so as the developer, you have no way to optimize the process. A server control will always be entered into the _VIEWSTATE hidden field, even if the control is empty. For all of these reasons, you should always disable view state for controls that do not require it. Also, not all browsers are equally capable of handling view state. For example, a Web application that targets the Pocket PC can only persist a limited amount of information in the _VIEWSTATE hidden field before the contents become corrupted.

Typically, you will want to keep view state enabled for the overall page and then selectively disable view state for certain controls. However, you can disable view state at the page level if you are sure the page will only be reposted once. For example, Figure 2-2 shows a voluntary sign-in page, which reposts to itself and appends the sign-in name to a redirect URL to another page.

click to expand
Figure 2-2: The ap_login screen

Listing 2-2 shows the code-behind file for the page.

Listing 2-2: The Code-Behind File for ap_login.aspx
start example
 Public Class ap_login     Inherits System.Web.UI.Page     Protected TextBox1 As System.Web.UI.WebControls.TextBox     Protected WithEvents Button1 As System.Web.UI.WebControls.Button     Private Sub Page_Load(ByVal sender As Object, _         ByVal e As System.EventArgs) Handles MyBase.Load         If Not Page.IsPostBack Then             ' Initialize the Textbox             Me.TextBox1.Text = "AEinstein"         End If     End Sub     Private Sub Button1_Click(ByVal sender As System.Object, _         ByVal e As System.EventArgs) Handles Button1.Click         Page.Response.Redirect("ap_ViewState.aspx?User=AEinstein")     End Sub End Class 
end example
 

Let's assume that any input is acceptable, including blank inputs, so no errors will arise that need to be reported back to the user. This kind of page does not require view state, so it should be disabled at the page level.

Finally, you can always disable view state for a specific control under four scenarios:

  • The control contains no dynamic values, only hard-coded values or fixed properties in the *.aspx file. (An example is a Label control with fixed dimensions and font properties.)

  • The control contains dynamic values that are rebound on every page request.

  • The control does not raise custom-handled events. For example, if a page contains a simple DataGrid, then you can disable view state. But if the DataGrid provides paging, then you cannot disable view state because the code-behind file needs to handle the OnPageIndexChanged() event.

  • The control's host page will not be reposted to the server. In this case, avoid the cost of saving and retrieving view state that will never actually be used by the server.

Measuring View State Performance Cost

View state management always exacts a performance price, but as we noted earlier, it can also reduce the need for redundant database calls. View state management may be faster and more secure than manually handling control state on your own. Without view state, a Select box will need to be repopulated from the database on every postback. With view state, the Select box only needs to be populated once, when the page first loads. The cost of view state is measured by the extra bytes it adds to the page, plus the processing cost of serializing the server control (including encryption and decryption). On the other hand, the cost of no view state is the processing cost of making multiple, redundant database calls, as well as the performance and productivity cost of having to create your own state management solution if required.

The point is that you often do not know, a priori , whether view state is a good thing for your application. You will have better insight as you become more experienced with using view state. However, barring this, the only way to know is to test your application both with view state and without it, and then you can decide. Without view state, you may be forced to make redundant database calls to rehydrate a posted page. If you implement caching, then redundant database calls no longer become an issue, and view state may lose out on the performance measure. But it may still win over on the convenience measure because you will not need to invest time in creating code to manage state manually. You will have to decide whether you want to support the more complex code that is required for manually persisting server control states. You will also have to determine, through testing and experimenting, whether the manual code will even perform better than view state.

Finally, you will also want to evaluate how many additional bytes view state adds to your pages and whether they are becoming unreasonably large compared to your target page sizes. Visual Studio .NET provides a trace log that will tell you the view state size, in bytes, for every control on a form. To enable tracing on a page, set the @ Page directive's Trace attribute to "True":

 <%@ Page Language=" vb" Trace=" True" AutoEventWireup=" false"     Codebehind=" ap_ViewState.aspx.vb" Inherits=" AspNetChap2.ap_ViewState" %> 

Figure 2-3 shows a simple application that provides two server controls, a submit button, and a label that displays a timestamp for when the button was clicked.

click to expand
Figure 2-3: The ap_ViewState screen

The Trace log for this screen includes a Control Tree section, which summarizes the render sizes and the view state sizes of the server controls in the page, as shown in Figure 2-4.

click to expand
Figure 2-4: Trace log for the ap_ViewState screen

In summary, view state management exacts a performance price that may be acceptable if it reduces database calls and code complexity.

Security Considerations with View State

The __VIEWSTATE hidden field is essentially a long, encoded text string that provides no protection for sensitive information such as connection strings or credit card numbers. Encoding is not the same as encryption. View state uses what's called a base64-encoded string , which simply means the string will remain unaltered by whatever HTTP encoding scheme the application uses for transmitting bytes during requests and responses between the client and the Web server.

View state contents may be encrypted directly from ASP.NET using machine key-based encryption. However, be aware that this step will add to the view state processing time. You can add view state encryption in two steps:

  1. Set the @ Page directive's EnableViewStateMac attribute value to "True":

     <%@ Page EnableViewStateMAC=" True" %> 
  2. Open the Web.config configuration file and set the <machineKey> element as follows:

     <machineKey validationKey="AutoGenerate" decryptionKey="AutoGenerate"          validation="3DES"/> 

ASP.NET supports other encryption algorithms, but view state only works with the Triple DES encryption algorithm.

The @ Page directive's EnableViewStateMac attribute appends a hashcode to the end of the __VIEWSTATE hidden field. The purpose of this hashcode is to help the ASP.NET runtime verify the integrity of the view state contents. That is, it checks whether the contents may have been tampered with during posting. If ASP.NET detects a problem, then the view state contents are discarded, and the controls are restored to their initial values. The EnableViewStateMac attribute is really of little use; you should only use it if you decide to implement 3DES encryption. Even then, we recommend you consider using HTTPS over 3DES encryption. HTTPS is a proven, secure encryption technology that is widely known and trusted by users of Internet-based applications.

Implementing View State in Server Farms

You can implement view state in a server farm using a simple modification to the <machineKey> element. By default, view state works with a random validation key that indicates the integrity of the view state data. For example, this is a <machineKey> element with default attribute values:

 <machineKey validationKey="AutoGenerate" decryptionKey="AutoGenerate"          validation="SHA1"/> 

In a server farm, you must manually set the validationKey attribute to a cryptographic key that is consistent for all servers in the farm. Otherwise, one server (in the farm) will assume that the view state for another server is corrupted, and the view state data will not be restored. The validation key must be between 20 and 64 bytes and is represented as a hexadecimal string, from 40 to 128 characters in length. The recommended key length is 128 hexadecimal characters (or 64 bytes). You can create the key value using a specialized .NET Framework class called RNGCryptoServiceProvider, which resides in the System.Security.Cryptography namespace. The sample project contains a page called ap_crypto.aspx , which demonstrates how to generate a 128-character cryptographic key. Listing 2-3 shows the simple code.

Listing 2-3: Generating a 128-Character Cryptographic Key
start example
 Imports System.Text Imports System.Security.Cryptography Private Sub Page_Load(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles MyBase.Load     Response.Write("The 128-character cryptographic key is: " & _         GenerateCryptoKey()) End Sub Private Function GenerateCryptoKey() As String     ' Step 1: Initialize a byte array     Dim buff(63) As Byte     ' Step 2: Declare the class     Dim rngKey As RNGCryptoServiceProvider = _         New RNGCryptoServiceProvider()     ' Step 3: Populate the buffer with key bytes     rngKey.GetBytes(buff)     ' Step 4: Transfer the key to a string     Dim i As Integer     Dim sb As StringBuilder = New StringBuilder(128)     For i = 0 To buff.Length - 1         sb.Append(String.Format("{0:X2}", buff(i)))     Next     Return (sb.ToString) End Function 
end example
 

This listing initializes a byte array and then populates it with a cryptographic key using the RNGCryptoServiceProvider class. Notice the use of the Format() function to map the generated bytes to a hexadecimal character representation. This is one example of a generated key:

 0CB9F8A3F24CBB5C724AD939CE7AC075D6266224CBF92D88A46BD09E7397735ADFF161 D9309B7353E82DB7FFCEE74D73412A68830543BD6FCACDE303C98F4500 

Simply copy this key value to the Web.config file of every server in the Web farm to share view state across the farm.

View State vs. Session State

Observant readers will notice the similarity between view state and session state when it comes to storing session-specific, custom values. Both approaches use a straightforward, key-based system for getting and setting values:

 Dim MyObject As System.Object ' Generic object Session("MyData") = MyObject ' Add an object to session state ViewState("MyData") = MyObject ' Add an object to view state Dim MyObject As Object = Session("MyData")  ' Get an object from session state Dim MyObject As Object = ViewState("MyData")  ' Get an object from view state 

Classic ASP users are trained to be wary of using Session variables because they incur a high performance and scalability cost. Session variables have been overhauled in ASP.NET and are now a viable choice for persisting session-specific information. Some of the same limitations that affected Session variables in classic ASP still apply in ASP.NET, but the technology is better. Chapter 4, "Optimizing Application and Session State Management," discusses this topic in greater detail. For now, the discussion focuses on whether view state or session state is a better choice for storing session-specific custom objects.

Perhaps surprisingly, Session variables are often the better choice, especially on stand-alone Web servers, for the following reasons:

  • Session variables can hold a wide range of object types, as long as they are thread-safe. View state can only hold simple object types, or objects that support binary serialization. Session variables have no such limitation, as long as the session state mode is set to InProc (in-memory) or SQLServer. The StateServer mode employs binary serialization, so it is not suitable for all object types.

  • Session variables are more efficient at storing large and complex objects, especially when the Session State mode is set to "InProc." View state must serialize all objects to bytes, and large objects require storing a large number of bytes.

  • Session variables are persistent for the entire duration of the client's session. View state is only persistent for the current page, except for server controls that are exactly duplicated across multiple pages.

  • Session variables are more efficient to serialize. An object serializes more efficiently to an InProc Session variable than it does to a view state byte stream.

  • Session variables provide more privacy for sensitive information compared to view state because Session data is stored on the server, whereas view state data is marshaled between the client and server.

Still, there are times when you will want to use view state over Session variables. The reasons are as follows:

  • View state is efficient at storing simple object types.

  • View state does not consume server memory resources for storing objects in the same way that InProc (in-memory) session variables do.

  • View state does not time out, whereas Session variables will time out by default. Sessions will also be destroyed if changes are made to the Web.config file or if IIS gets reset.

  • View state only requires a minor special configuration for a server farm environment (implementing a consistent validation key). However, session state management in this environment requires special considerations. For example, it must exclude InProc session state, which is only valid if the request returns to the same server. Instead, the SQLServer or StateServer modes must be used, which will likely not perform as fast as view state.

In summary, Session variables may be a better choice than view state for storing complex or large, session-specific custom objects. This is especially true on stand-alone Web servers that support low to moderate traffic volumes . In a server farm environment, view state is usually a better choice compared to session state management. In addition, view state is most suitable for storing simple object types.

Keep in mind, however, that view state offers one significant convenience over Session variables. Namely, view state automatically rehydrates Web page controls, restoring both their properties and their data. Session variables will store data, but it is up to the developer to manually rehydrate the Web page controls using this data. Convenience often comes with a price, and you should always consider alternatives to view state if your Web application is focused on persisting data rather than control states.

Further Alternatives to View State

You can use caching in place of view state or session state, depending on what a specific Web page needs to do. Page-level output caching is a convenient way to store rendered content and is suitable for non-interactive, read-only pages. Alternatively, fragment caching allows you to mix cached and non-cached content. Finally, the Cache API allows you to cache objects and access them with a high level of control. Chapter 5, "Caching ASP.NET Applications," covers caching, along with session state management, and it discusses the similarities and contrasts between each technology.

Cookies are another technology you can use in conjunction with caching to replace view state, albeit with some limitations. Cookies are useful for storing limited amounts of information on the client, and they are suitable for persisting simple information, such as combo box selections. Cookies may be a good alternative for Web pages that do not cache content but that need to persist user input, selections, and preferences. You could use caching to persist control contents, and you could use cookies persist user selections and preferences. In combination, these two technologies may be a suitable and better-performing alternative to view state. Keep in mind, however, that the developer is then responsible for manually hydrating the Web page controls with cached data.




Performance Tuning and Optimizing ASP. NET Applications
Performance Tuning and Optimizing ASP.NET Applications
ISBN: 1590590724
EAN: 2147483647
Year: 2005
Pages: 91

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net