Programming Microsoft ASP.NET 4 - PDF Free Download (2024)

The actual body of the resulting table is determined by the item and alternating item templates.

Definition of the Item Template In a tabular layout, created using an HTML table, the item template can’t be anything but a sequence of

The first row contains two cells: one for the company name, and one for the country/region. The second row shows the phone number on a single-cell row. Both rows are rendered for each record bound to the ListView control. Figure 11-2 demonstrates the markup you obtain in this way.

FIGURE 11-2  A tabular layout built with the ListView control.

As you can see in the figure, some extremely simple styles have been applied to the table items. In particular, the

When comparing this sort of flexibility with the GridView control, the GridView control ­provides a number of free facilities, but it doesn’t offer as much flexibility in design, as seen

in this example. To choose, you have to first evaluate your requirements and make a choice between flexibility of rendering and functions to implement.

Using Alternate Rendering for Data Items ItemTemplate is mandatory in a ListView control and indicates the template to use for each bound item. The AlternatingItemTemplate property can be used to differentiate every other item, as shown in Figure 11-3.

FIGURE 11-3  A tabular layout built with the ListView control using an alternating item template.

Most of the time, the alternating item template just features the same layout as regular items but styles it differently. However, changes to the template are allowed to any extent that can keep your users happy. The following code uses a small indentation for alternating rows:

Figure 11-4 shows the result.

FIGURE 11-4  Using a slightly different layout for alternating items.

Reflecting On the Table Layout HTML tables are an essential, but too often abused, piece of the Web jigsaw puzzle. HTML tables were designed for presenting tabular information. And they are still great at doing

this. So any developer of a Web page that needs to incorporate a matrix of data is correct in using HTML tables. The problem with tables is that they are often used to define the page layout—a task they weren’t designed for. To illustrate, a grid control that uses HTML tables to output its content is more than acceptable. A tree-view control that uses HTML tables to list its contents is less desirable. It’s not by mere chance that the ASP.NET team released the CSS adapter toolkit to allow you to change the rendering engine of some controls to make them inherently more CSS-friendly. And the TreeView is just one of the controls whose rendering style can be modified by using the toolkit. Note  Using tables for creating multicolumn layouts—which is still common these days in most

Web sites—has a number of significant drawbacks. Tables require a lot of code (or tags if you create tables declaratively) that has little to do with the data you intend to display through them. This code is tedious to write, verbose, and difficult to maintain. Worse yet, it makes for longer downloads and slower rendering in the browsers (a factor of growing importance for wireless devices). In addition, tables tend to mix up information and layout instead of forcing you to keep them neatly separated, and they result in less accessible content.

Building a Flow Layout Visual Studio provides some facilities to work with ListView controls. Specifically, once you have bound the control to a data source, Visual Studio queries the data source and offers to generate some templates for you.

Definition of the Overall Layout A flow layout is the simplest layout you can get. It requires only that you define a c­ ontainer— typically, a —and then the markup for each record. The ListView control simply composes the resulting markup by concatenating the markup in a unique flow of HTML tags. Needless to say, the resulting markup can flow horizontally or vertically, depending on the tags you use (block or inline) and the CSS styles you apply. If you’re looking for a block flow layout, your LayoutTemplate property will probably always look as simple as the one shown here:

If you opt for a tag, instead of getting a new block you get a piece of markup that flows inline with the rest of the ASP.NET page. Note that in ASP.NET 4 the LayoutTemplate is optional. You can get the same results if you simply wrap the markup directly in an ItemTemplate element, as shown here:

This approach simplifies the definition of a ListView without loss of programming power and generality.

Definition of the Item Layout A good example of a flowing template is shown in Figure 11-1. Here’s another example: ID: CompanyName: ContactName: ContactTitle:

The tag normally creates a new block of markup and breaks the current flow of HTML. However, if you give it the float:left CSS style, it will float in the specified direction. As a result, the block of markup forms a horizontal sequence that wraps to the next line when the border of the browser’s window is met. Figure 11-5 offers a preview. Note  In the previous chunk of HTML markup, I used and tags with styles applied and also mixed CSS styles with HTML tags used for controlling the appearance of the page, such as and. This approach is clearly arguable. The reason why I haven’t opted for a niftier, pure CSS-based code in the snippet is clarity. By reading which CSS styles are applied to which tag, you can more easily make sense of the output depicted in Figure 11-5.

FIGURE 11-5  Using the float CSS attribute to display tags as a horizontal sequence.

Building a Tiled Layout Admittedly, the output of Figure 11-5 is not really attractive, even though it contains a few elements that, if improved a bit, might lead to more compelling results. The output of Figure11-5 shows blocks of markup that flow horizontally and wrap to the next row. However, they share no common surrounding layout. In other words, those blocks are not tiled. To build a perfectly tiled output, you need to leverage group templates.

Grouping Items So far we’ve used the ListView control to repeat the item template for each bound record. The GroupTemplate property adds an intermediate (and optional) step in this rendering ­process. When you specify a group template, the total number of bound records is partitioned in groups and the item template is applied to the records in each group. When a group has been rendered, the control moves to the next one. Each group of records can have its own particular template—the group template—and a separator can be inserted between items and groups. How is the size of each determined? That has to be a fixed value that you

set, ­either declaratively or programmatically, through the GroupItemCount property. Let’s consider the following layout and group templates:

It indicates that the final output will be an HTML table where a new row is created for each group of items. Each table row contains as many cells as the value of GroupItemCount sets. The default value is 1. Note that in the preceding code snippet we’re using the default names for group and item containers—that is, groupPlaceholder and itemPlaceholder. When these names are used, there’s no need to set corresponding GroupPlaceholderID and ItemPlaceholderID properties on the ListView markup. Here’s the top-level markup for a tiled layout: ...

As an example, if you set GroupItemCount to 4, you’ll have rows of 4 cells each until there are less than 4 records left. And after that? What if the number of bound records is not a perfect multiple of the group item count? That’s where the EmptyItemTemplate property fits in:

This template is used to complete the group when no more data items are available. Figure11-6 shows a typical tiled output you obtain by employing a ListView control.

FIGURE 11-6  A four-cell tiled layout built with the ListView control.

Using the Group Separator Template Each group of items can be separated by a custom block of markup defined through the GroupSeparatorTemplate property. Here’s an example:

If you add this markup to the preceding example, you’ll display a blank row in between rows with data-bound cells. It’s a kind of vertical spacing. The same can be done horizontally to separate data-bound cells within the same table row. To do so, you use the ItemSeparatorTemplate property instead. In both cases, the markup you put in must be consistent with the overall markup being created for the whole ListView control.

Modifying the Group Item Count Dynamically The GroupItemCount property is read-write, meaning that you can change the size of each group programmatically based on some user actions. The following code snippet shows a pair of event handlers associated with the Click event of two Button controls: protected void Button1_Click(object sender, EventArgs e) { // There's no upper limit to the value of the property ListView1.GroupItemCount += 1; }

Part II  ASP.NET Pages and Server Controls protected void Button2_Click(object sender, EventArgs e) { // The property can't be 2 or less if (ListView1.GroupItemCount >2) ListView1.GroupItemCount -= 1; }

The GroupItemCount property itself can’t take any value less than 1, but it has no upper limit. However, it should not accept any value larger than the actual number of data items currently bound. As you assign a new value, the set modifier of the property resets the internal data-binding flag and orders a new binding operation. If you change the value of GroupItemCount over a postback, the ListView control automatically renders the updated markup back to the client. (See Figure 11-7.)

FIGURE 11-7  Changing the size of ListView groups dynamically.

The ListView control doesn’t natively support more advanced capabilities—such as uneven groups of items where, for example, the association between an item and a group is based on a logical condition and not merely determined by an index. In this scenario, you could have a list where the first group contains customers whose name begins with A and the second group contains those beginning with B, and so on. You would have to provide the logic for this yourself. Let’s look at this next.

Data-Driven Group Templates The support for groups built into the ListView control is not data driven. In other words, the layout (groups and items) is first created and it is then bound to data. When the binding step occurs, the group template is not directly involved and you won’t receive any event that tells you that a group has been either created or bound to its data. However, this doesn’t mean that your group templates must have a fixed layout and can’t be dynamically populated using data from its contained items. The ListView’s ItemDataBound event is the key to obtaining output such as that shown in Figure 11-8.

FIGURE 11-8  The header of each group is determined dynamically by looking at the bound contents.

To start out, let’s take a look at the overall layout template of the ListView control:

The group template is made of a Label control followed by an tag and the list of data items. Each bound item is expressed through an

tag. Let’s see how to change the Text property of the groupHeader control for each group being created. Here’s the structure of the ItemDataBound event handler: private int lastGroup = -1; protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e) { // To assign the group a data-bound title, retrieve the data item first if (e.Item.ItemType == ListViewItemType.DataItem) { var currentItem = (ListViewDataItem) e.Item; CustomizeGroupHeader((ListView) sender, currentItem); } }

The ListViewItemEventArgs argument contains an Item property that refers to the item being bound to data. This item can be of a few types—InsertItem, EmptyItem, or DataItem. The list of feasible values is in the ListViewItemType enumerated type. In this case, we’re interested only in data items—that is, regular items showing some bound data. To put your hands on the real data being bound to the item, you need to cast the ListView item to the ListViewDataItem type, from which you can access a number of data-related properties: private void CustomizeGroupHeader(ListView root, ListViewDataItem currentItem) { // The type of the data item depends on the data you bound--in this case, // a collection of Customer objects var cust = (DAL.Customer) currentItem.DataItem; // Get a ListViewContainer object--the container of the group template Control container = currentItem.NamingContainer; if (container == null) return;

Chapter 11  The ListView Control

493

// Look up for a particular control in the group template--the Label Label groupHeader = (Label)container.FindControl("groupHeader"); if (groupHeader == null) return; // Figure out the 0-based index of current group. Note that the display index // refers to the index of the item being bound, not the group int groupIndex = currentItem.DisplayIndex / root.GroupItemCount; if (groupIndex != lastGroup) { // This is a new group lastGroup = groupIndex; // Update the UI groupHeader.Text = String.Format("Group {0} starting with {1}", groupIndex + 1, cust.CompanyName.Substring(0, 1).ToUpper()); } }

You first get a reference to the naming container of the item. This container is the wrapper control for the group template. By using the FindControl method, you gain access to the Label control in the group template. The final step entails determining the value for the Text property of the Label control. As mentioned, the ListView control doesn’t provide any readymade information about groups. So you don’t know about the index of the current group. The DisplayIndex property tells you only the index of the item being processed. Because the size of each group is fixed—and is based on the GroupItemCount property—you can easily obtain the 0-based index of the current group. You track the index of the current group in a global variable, and whenever a new group is found, you update the header.

Styling the List Unlike other view controls, the ListView control doesn’t feature the usual long list of style properties such as HeaderStyle, ItemStyle, SelectedItemStyle, and so forth. After a few years of industry use, Microsoft downsized the importance of style properties and their role. Today, as evidenced by the ListView control, in ASP.NET, CSS styles are emerging as the most effective and efficient way to modify the appearance of the markup.

Style Properties ASP.NET controls let you set style attributes in two nonexclusive ways—using the CssClass property and using style properties. The CssClass property takes the name of a CSS class and passes it on to the class attribute of the root HTML tag generated for the control. More often than not, though, ASP.NET controls produce a complex markup where multiple HTML tags are rendered together but yet need to be styled differently. Although this is far from

494

Part II  ASP.NET Pages and Server Controls

being an impossible goal to achieve with CSS styles, for a few years Microsoft pushed style ­properties as the way to go. Developers are probably more inclined to use style properties than CSS styles, which r­ equire some specific skills. Anyway, style properties let you specify CSS attributes to apply to particular regions of the markup being generated. For example, the ItemStyle property in a GridView control allows you to define the colors, font, and borders of each displayed item. In the end, the value of these properties are translated to CSS attributes and assigned to the HTML tags via the style attribute. The developers don’t have to build up any CSS skills and can leverage the Visual Studio editors and designers to get a preview. Is there anything wrong with this approach? The problem is that style attributes are defined as part of the page’s code, and there’s no clear separation between layout and style. ASP.NET themes are helpful and certainly mitigate the problem. All in all, for view controls with a relatively fixed layout, style properties— which are better if used through the intermediation of themes—are still a good option. The ListView control, though, is kind of an exception.

Using Cascading Style Sheets The ListView control provides an unprecedented level of flexibility when it comes to ­generating the markup for the browser. The item that for, say, a GridView control can be safely identified with a table row, can be virtually anything with a ListView control. The CSS designer in Visual Studio allows you to style controls and save everything back to a CSS class. So, as a developer, you always work with properties and scalar values but have them saved back to the CSS class instead of the view state. This is another important factor that leads developers to prefer cascading style sheets over style properties. The CSS is a separate file that is downloaded only once. Style properties, on the other hand, are saved to the view state and continually uploaded and downloaded with the page. Cool cascading style sheets are usually developed by designers and assign a style to the vast majority of HTML tags. Often cascading style sheets incorporate layout information and influence the structure of the page they are applied to. A common trick used by cascading style sheets consists of assigning a particular ID to tags and treating them in a special way. Let’s see how to radically improve the user interface of a previous ListView-based page with a cool CSS. First, you explicitly link any relevant CSS file to the page (or the master page) by using the tag. The HtmlHead control also allows you to load CSS files programmatically. Note

Chapter 11  The ListView Control

495

that most realistic CSS files have an auxiliary folder of images that you have to set up on the production server too. The CSS file I’m using in the next example assigns a special role to tags with the following IDs: header, footer, page, and content. The alternative is to explicitly assign a CSS class using the class attribute. Both ways are widely accepted. The class approach makes more obvious that something has been styled and what class it has been assigned to. But, in the end, it’s a matter of preference. If you opt for styling via IDs, you are totally free to choose any names you want. (Note, however, that IDs must be unique to allow them to be used with client scripts. This can be hard to achieve with multiple controls in one page, so, class names are really preferred.) Customer List

Company Country

The result is shown in Figure 11-9.

496

Part II  ASP.NET Pages and Server Controls

FIGURE 11-9  Using cascading style sheets to style the markup of a ListView control.

Working with the ListView Control The ListView control makes it easy to handle common data-based operations, such as insert, update, delete, or sorting. All that you have to do is place buttons in the layout template and associate buttons with command names. Buttons can be global to the list (such as insert, sort, and page buttons) or specific to a particular item (such as update and delete buttons). Command names are just strings that are assigned to the CommandName property of the Button control. So far, we have considered only scenarios with relatively static and noninteractive templates. It is definitely possible, though, to use the ListView control to create rich user interfaces that allow in-place editing, selection of items, paging, and updates back to the data source. Let’s start with in-place editing.

In-Place Editing Unlike the GridView control, the ListView control doesn’t automatically generate an Edit ­button; nor does it automatically adapt the edit mode user interface from the item template.

Chapter 11  The ListView Control

497

This responsibility falls to the developer by design. The developer is required to define an edit template that will be used to edit the contents of the selected item, in keeping with the flexible nature of the control.

Defining the Edit Item Template The edit template is any piece of markup you intend to display to your users when they click to edit a record. It can have any layout you like and can handle data access in a variety of ways. If you’ve bound the ListView control to a data source control—for example, an ObjectDataSource control—you can take advantage of the ASP.NET built-in support for ­t wo-way data binding. Simply put, you use data binding expressions to bind to data, the Eval method for read-only operations, and the Bind method for full I/O operations. The following markup defines a classic two-column table for editing some fields of a ­customer record:

ID
Name
Country
Street
City

Only one displayed item at a time can be in edit mode; the EditIndex property is used to get or set this 0-based index. If an item is being edited and the user clicks on a button to edit another one, the last-win policy applies. As a result, editing on the previous item is canceled and it’s enabled on the last-clicked item.

498

Part II  ASP.NET Pages and Server Controls

To turn the ListView user interface into edit mode, you need an ad hoc button control with a command name of Edit:

When this button is clicked, the ItemEditing event fires on the server. By handling this event, you can run your own checks to ensure that the operation is legitimate. If something comes up to invalidate the call, you set the Cancel property of the event data structure to cancel the operation, like so: protected void ListView1_ItemEditing(object sender, ListViewEditEventArgs e) { // Just deny the edit operation e.Cancel = true; }

Adding Predefined Command Buttons An edit item template wouldn’t be very helpful without at least a couple of predefined ­buttons to save and cancel changes. You can define buttons using a variety of controls, ­including Button, LinkButton, ImageButton, and any kind of custom control that implements the IButtonControl interface. Command names are plain strings that can be assigned to the CommandName property of button controls. The ListView (and other view controls) recognizes a number of predefined command names, as listed in Table 11-5. TABLE 11-5  Supported

Command Names

Command

Description

Cancel

Cancels the current operation (edit, insert), and returns to the default view (item template)

Delete

Deletes the current record from the data source

Edit

Turns the ListView control into edit mode (edit item template)

Insert

Inserts a new record into the data source

Page

Moves to the next or previous page

Select

Selects the clicked item, and switches to the selected item template

Sort

Sorts the bound data source

Update

Saves the current status of the record back to the data source

The following code shows how to add a pair of Save/Cancel buttons:

Download from Wow! eBook

Chapter 11  The ListView Control

499

Any button clicking done within the context of a ListView control originates a server-side event—the ItemCommand event: protected void ListView1_ItemCommand(object sender, ListViewCommandEventArgs e) { // Use e.CommandName to check the command requested }

Clicking buttons associated with predefined command buttons can result in subsequent, and more specific, events. For example, ItemUpdating and ItemUpdated are fired before and after, respectively, a record is updated. You can use the ItemUpdating event to make any last-­ minute check on the typed data before this data is sent to the database. Note that before the update is made, ListView checks the validity of any data typed by ­calling the IsValid method on the Page class. If any validator is defined in the template, it is ­evaluated at this time.

Adding Custom Command Buttons In the edit mode user interface, you can have custom buttons too. A custom button differs from a regular Save or Cancel button only in terms of the command name. The command name of a custom button is any name not listed in Table 11-5. Here’s an example:

To execute any code in response to the user’s clicking on this button, all you can do is add an ItemCommand event handler and check for the proper (custom) command name and react accordingly: protected void ListView1_ItemCommand(object sender, ListViewCommandEventArgs e) { // Check the command requested if (e.CommandName == "MyCommand") { ... } }

Conducting the Update When the ListView control is used in two-way binding mode, any update operation is ­conducted through the connected data source control. You define select and save methods on the data source, configure their parameters (either declaratively or programmatically), and delegate to the ListView control all remaining chores.

500

Part II  ASP.NET Pages and Server Controls

For update and delete operations, though, you need to identify the record uniquely. This is where the DataKeyNames property gets into the game. You use this property to define a ­collection of fields that form the primary key on the data source: ...

In this case, the DataKeyNames tells the underlying data source control that the ID field on the bound record has to be used as the key. Figure 11-10 shows a sample page in action that edits the contents of the currently displayed record.

FIGURE 11-10  In-place editing in action with the ListView control.

Deleting an Existing Record As you can see, Figure 11-10 also contains a Delete button side by side with the aforementioned Edit button. Here’s the full markup for ListView’s item template:

, ,

Chapter 11  The ListView Control

501

The Delete operation is even more crucial than an update. For this reason, you might want to be sure that deleting the record is exactly what the user wants. For example, you can pop up a client-side message box in which you ask the user to confirm the operation. It is a little piece of JavaScript code that you attach to the OnClientClick property of a Button control or to the onclick attribute of the corresponding HTML tag. It can save you a lot of trouble.

Showing a Message Box upon Completion Wouldn’t it be nice if your application displays a message box upon the completion of an update operation? It doesn’t change the effect of the operation, but it would make users feel more comfortable. In a Web scenario, you can use only JavaScript for this purpose. The trick is that you register a piece of startup script code with the postback event where you execute the update operation. In this way, the script will be executed as soon as the page is served back to the browser. From the user’s perspective, this means right after the completion of the operation. Here’s what you need: protected void ListView1_ItemUpdated(object sender, ListViewUpdatedEventArgs e) { // Display a client message box at the end of the operation Page.ClientScript.RegisterStartupScript( this.GetType(), "update_Script", "alert('You successfully updated the system.');", true); }

Inserting New Data Items The ListView control allows you to define a made-to-measure interface for adding new ­records to the underlying data source. You do this through the InsertItemTemplate property. More often than not, the insert template is nearly identical to the edit item template, except for the fields that form the primary key of the data source. These fields are normally rendered as read-only text in the edit template. Clearly they have to be editable in an insert item scenario.

Setting Up the Insert Item Template So let’s assume you have the following insert item template. As you can easily verify, it is the same edit item template we used in the previous example, except that a TextBox control is used for entering the ID of the new customer.

502

Part II  ASP.NET Pages and Server Controls

ID
Name
Country
Street
City

How would you display this template? The edit item template shows up when the user clicks a button decorated with the Edit command name. Unfortunately, there’s no equivalent New command name to automatically bring up the insert item template. Instead, with the ListView the New command name is considered a custom command, handled by code you provide to activate the insert item template, unless it’s active by default. We’ll look at the details next. The insert item template is displayed by position. The InsertItemPosition property determines where the template is displayed. There are three possibilities, as shown in Table 11-6. TABLE 11-6  Feasible

Positions for the Insert Item Template

Position

Description

FirstItem

The insert item template is displayed as the first item in the list and precedes all items in the bound data source.

LastItem

The insert item template is displayed as the last item in the list and trails all items in the bound data source.

None

The insert item template is not automatically displayed. The developer is ­responsible for showing and hiding the template programmatically. This is the default value for the InsertItemPosition property.

Chapter 11  The ListView Control

503

If you leave the InsertItemPosition property set to its default value, no insert template is ­displayed, but you won’t have a predefined button to bring it up. If you use any of the other two values, the template is always visible and displayed at the beginning or the end of the list. This might not be desirable in most cases. Let’s see how to take programmatic control over the display of the insert template.

Taking Full Control of the Insert Template In the layout template, you add a custom button and capture any user’s click event. You can give the button any command name not listed in Table 11-5:

To handle the click on the button, you write an ItemCommand handler. In the event handler, you simply change the value of the InsertItemPosition property, as shown here: protected void ListView1_ItemCommand(object sender, ListViewCommandEventArgs e) { if (e.CommandName.Equals("New", StringComparison.OrdinalIgnoreCase)) { ListView me = (ListView) sender; me.InsertItemPosition = InsertItemPosition.FirstItem; } }

Changing the value of InsertItemPosition to anything but None brings up the insert item template, if any. In the insert template, you need to have a couple of predefined buttons with command names of Insert (to add) and Cancel (to abort). It should be noted, though, that the insert item template is not automatically dismissed by the ListView control itself. As mentioned, this is because of the lack of built-in support for the New command name. In the end, this requires that you add a couple more handlers to ­dismiss the template when the user cancels or confirms the insertion. The ItemCanceling event fires when the user hits a button associated with the Cancel ­command name. This can happen from either the edit or insert template. The event data ­object passed to the handler has the CancelMode property, which is designed to help you figure out what mode is active (insert or edit) and allow you to tailor your application’s response. protected void ListView1_ItemCanceling(object sender, ListViewCancelEventArgs e) { ListView me = (ListView) sender; // Dismissing the insert item template if (e.CancelMode == ListViewCancelMode.CancelingInsert) me.InsertItemPosition = InsertItemPosition.None; }

504

Part II  ASP.NET Pages and Server Controls

To hide the insert item template after the new data item has been successfully appended to the data source, you use the ItemInserted event: protected void ListView1_ItemInserted(object sender, ListViewInsertedEventArgs e) { ListView me = (ListView) sender; me.InsertItemPosition = InsertItemPosition.None; }

Adding a Bit of Validation When you’re going to add a new record to an existing data source, a bit of validation—much more than is generally desirable—is mandatory. Being responsible for the insert template, you can add as many validators as necessary to the markup. The ListView control’s internal facilities then ensure that the operation is finalized only if no validator raised an error. In particular, you might want to check whether the ID being inserted already exists in the data source. You can use a CustomValidator control attached to the text box:

The CustomValidator control fires a server-side event in which you can run code to validate the text in the input field. The server event is fired via a postback and has the following prototype: protected void CustomValidator1_CheckID(object source, ServerValidateEventArgs e) { string proposedCustomerID = e.Value; e.IsValid = CheckIfUsed(proposedCustomerID); } private bool CheckIfUsed(string proposedCustomerID) { var c = CustomerRepository.Load(proposedCustomerID); // The object is of type NoCustomer if no matching customer exists if (c is DAL.NoCustomer) return true; return false; }

The Load method in the sample data access layer (DAL) used in this example supports the Special Case pattern. In other words, the method always returns a valid Customer object regardless of the value of the input proposedCustomerID parameter. If a customer with a

Chapter 11  The ListView Control

505

matching ID can’t be found, the return object is an instance of the NoCustomer class. Of course, NoCustomer is a class that derives from Customer. How is this different from returning a plain null value or perhaps an error code? In both cases, the caller can figure out whether a matching customer exists or not. However, returning a special-case Customer object is inherently more informative and doesn’t violate the consistency of the method—a class that inherits from Customer is always returned, whereas an error code is a number and null is a non-value.

Selecting an Item The SelectedItemTemplate property allows you to assign a different template to the currently selected item in the ListView control. Note that only one displayed item at a time can be given the special selected template. But how do you select an item?

Triggering the Selection The selected item template is a special case of the item template. The two templates are ­similar and differ mostly in terms of visual settings—for example, a different background color. The switch between the regular and selected item template occurs when the user clicks on a button with the Select command name. If you intend to support the selection item feature, you place a Select button in the item template. When this button gets clicked, the ListView automatically applies the new template to the clicked item. Here are some sample item and selected item templates:

, ,

, ,

506

Part II  ASP.NET Pages and Server Controls

In addition to changing some visual settings, the selected item template can contain buttons to trigger operations on the particular item. In Figure 11-10 shown earlier, each item features its own set of operational buttons, such as Edit and Delete. This layout can be reworked to display buttons only on the selected item. To do so, you just move the buttons to the SelectedItemTemplate property. In the item template, though, you need to insert a button control to trigger the selection process. You can use a push button or attach any significant text in the template to a link button:

Figure 11-11 shows the result.

FIGURE 11-11  A selected item in a ListView control.

Releasing the Selection When you click the link button, the ListView switches the template and sets the SelectedIndex property accordingly. As soon as the user clicks on a different item, the selection is moved and the previously selected item regains the regular template. Is there a way to programmatically reset the selection? You bet. All that you have to do is add a new custom button and handle its click event. In the event handler, you assign the –1 value to the SelectedIndex property. A value of –1 means that no items are selected. Here’s the related code snippet: protected void ListView1_ItemCommand(object sender, ListViewCommandEventArgs e) { ListView me = (ListView) sender; if (e.CommandName.Equals("Unselect", StringComparison.OrdinalIgnoreCase)) me.SelectedIndex = -1; }

Chapter 11  The ListView Control

507

Note that the index of the currently selected item and the index of the item being edited are saved to the view state and persisted across postbacks. This means that if the user changes the country/region selection (shown in Figure 11-11), both the edit and selection indexes are retained, which might not be desirable. For example, imagine that you selected (or are editing) the second customer from Argentina. Next, the user changes to Brazil while the selected (or edit) template is on. The result is that the second customer from Brazil is displayed in the selected (or edit) mode. If this behavior works for you, there’s nothing to modify in the code. Otherwise, you need to reset SelectedIndex and EditIndex in any postback event outside the ListView control. Here’s an example: protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e) { // The sender argument here indicates the DropDownList or any other // control responsible for the postback. You reference the ListView by // name or via a custom global member in the code-behind class of the page ListView1.SelectedIndex = -1; ListView1.EditIndex = -1; }

Paging the List of Items In ASP.NET, grid controls support data paging natively. Purely iterative controls such as Repeater and DataList, though, leave the burden of pagination entirely on the developer’s capable shoulders. The ListView control falls somewhere in the middle of these two extreme positions. The ListView control doesn’t have built-in paging capabilities, but it knows how to work with a new control specifically designed to enable data paging on a variety of ­data-bound controls. This control is the DataPager.

The DataPager Control The DataPager control is designed to add paging capabilities to a family of data-bound ­controls and not just the ListView. The support that the DataPager control offers to databound pageable controls such as the ListView is limited to the user interface of the pager. You configure the DataPager to obtain the pager bar you like best, and then you instruct theDataPager control to fall back to the paged control to display the specified number of data items starting at the specified position. In no case does the DataPager expose itself to the data source or a data source control. All that it does is communicate to the paged controlthe next set of records to select and display. Table 11-7 lists the properties of the DataPager control.

508

Part II  ASP.NET Pages and Server Controls TABLE 11-7  Properties

of the DataPager Control

Property

Description

Fields

Gets the collection of DataPagerField elements that form the pager bar. Elements in this collection belong to classes such as NumericPagerField, TemplatePagerField, and NextPreviousPagerField.

MaximumRows

Gets the maximum number of rows that the page can support.

PagedControlID

Gets and sets the ID of the control to page. This control must implement the IPageableItemContainer interface.

PageSize

Gets and sets the size of the page. The default value is 10.

QueryStringField

The name of the query string field for the current page index. The pager uses the query string when this property is set.

StartRowIndex

Gets the index of the first item in the data source to display.

TotalRowCount

Gets the total number of rows to page through.

Only a few of these properties can be set declaratively. They are Fields, PagedControlID, PageSize, and QueryStringField. The other properties are read-only and owe their value to the paged control and the size of the bound data source.

Using the DataPager Control The following code snippet shows the typical code you use to embed a data pager in an ASP.NET page that hosts a ListView control:

The DataPager control heralds a new model for paging data-bound controls that is quite a bit different from the model employed by GridView controls. The user interface for paging is not part of the control, but it can be placed anywhere in the page and even driven through the query string. The DataPager control is linked to the data-bound control being paged and lets this control know about the user selection. Subsequently, the paged control adjusts its row properties and passes the information back to the data pager. Figure 11-12 shows a data pager inaction.

Chapter 11  The ListView Control

509

FIGURE 11-12  A data pager in action—the pager can be placed anywhere in the page.

Configuring the Data Pager Fields The user interface of the data pager control is largely customizable. You do that through the Fields property—a collection of DataPagerField objects. The property allows you to add ­multiple pagers of different styles. Table 11-8 lists the various options you have. TABLE 11-8  Types

of Data Pagers

Type

Description

NextPreviousPagerField

Displays a fully customizable Next/Previous user interface for the pager. You can use images or text for Next/Previous buttons and also add a First/Last pair of buttons.

NumericPagerField

Displays a fully customizable list of numeric links, one for each page. The number of pages is calculated on the page size and the total number of bound rows.

TemplatePagerField

Allows you to use a user-defined template for the pager.

All classes in Table 11-8 inherit from the same common class—DataPagerField. If you’re OK with the default user interface of the pagers, you don’t need to set any of the pager’s ­properties. The following markup, therefore, is perfectly functional:

Pager fields, though, have a number of visual properties to set the CSS style of buttons, the companion text, or perhaps the images to use instead of text.

510

Part II  ASP.NET Pages and Server Controls

Pageable Containers As mentioned, the data pager control doesn’t handle data itself. Rather, the control is the manager of the paging user interface. For this reason, it needs to communicate with the paged control. Whenever a button in the pager is clicked to move to a given page, the pager control fires a message to the paged control and has it refresh the user interface properly. Not all data-bound controls can be paged using a data pager. In ASP.NET, this privilege is limited to controls that implement the IPageableItemContainer interface. Currently, the sole control to support this interface is the ListView control. You can create your own custom ­controls to implement the interface, however. Here’s the definition of the interface: public interface IPageableItemContainer { // Events event EventHandler TotalRowCountAvailable; // Methods void SetPageProperties(int startRowIndex, int maximumRows, bool databind); // Properties int MaximumRows { get; } int StartRowIndex { get; } }

The PagedControlID property on the DataPager control defines the linked data-bound ­control. Whenever the pager is acted on, it invokes the SetPageProperties method on the paged control through the contracted interface. In doing so, it lets the ListView control (or the paged control) know about the new start row to display and the size of the page. Here’s the pseudocode used by the ListView control to support paging: void SetPageProperties(int startRowIndex, int maximumRows, bool databind) { if ((this._startRowIndex != startRowIndex) || (this._maximumRows != maximumRows)) { PagePropertiesChangingEventArgs e; e = new PagePropertiesChangingEventArgs(startRowIndex, maximumRows); if (databind) { this.OnPagePropertiesChanging(e); } this._startRowIndex = e.StartRowIndex; this._maximumRows = e.MaximumRows; if (databind) { this.OnPagePropertiesChanged(EventArgs.Empty); } } if (databind) { base.RequiresDataBinding = true; } }

Chapter 11  The ListView Control

511

PagePropertiesChanging and PagePropertiesChanged events are fired before and after, ­respectively, each paging operation. The data pager control is normally placed outside the ListView’s layout. In this case, you use the PagedControlID property of the data pager to specify the paged control. However, if the PagedControlID property is not specified, the data pager assumes that its naming container is the paged control (as long as it implements the IPageableItemContainer interface). What does this mean to you? It means you can embed the data pager in the layout template of the ListView control and avoid setting the PagedControlID property on the pager explicitly.

Sorting the List The data bound to the ListView control can be sorted using a button in the layout template with the command name of Sort:

You specify the sort expression and the initial sort direction using the CommandArgument property of the button. You use asc and desc to indicate the desired direction. Multiple sorting fields can be listed as well. The sorting automatically reverses from ascending to descending and vice versa as you click. The ListView’s SortExpression and SortDirection ­read-only properties tell you at any time about the current status of the sort.

Summary The ListView control adds the benefits of ASP.NET view controls (such as the GridView or DetailsView control) to classic repeater data-bound controls such as DataList. The resulting control weds the extreme layout flexibility of a DataList or Repeater control with the power of two-way data binding of data source controls. The ListView control can be used to implement virtually any reporting and publishing ­scenarios you can imagine. The distinct layout template gives you total control over the HTML being generated and the style it must have. Various item templates (regular, alternate, edit, selected, insert) let you decide about the markup to output for each possible state of the control. Finally, the ListView control is a pageable control. Unlike other view controls, though, the ListView control binds to an external pager control—the new DataPager control. The ­connection between the two controls is all in the IPageableItemContainer interface. As a result, each data-bound control with this interface can be paged without incorporating the logic to page.

Chapter 12

Custom Controls Ignorance, the root and the stem of every evil. —Plato Server controls are one of the pillars of the entire ASP.NET Web Forms framework. Server controls are compiled classes that encapsulate user-interface and other functionality into reusable packages. ASP.NET provides a full bag of stock controls to serve most developers’ needs. However, writing custom controls is possible and sometimes necessary. Custom controls are no different than standard ASP.NET server controls except that they are bound to a different tag prefix and must be registered and deployed explicitly. Aside from that, custom controls can have their own object model, fire events, and support all the designtime ­features of Microsoft Visual Studio, such as the Properties window, the visual designer, property builders, and the Toolbox. Because of their compiled nature, custom controls can be installed in a single copy in the global assembly cache (GAC), making them available to all applications, or they can simply be deployed to the \Bin directory for use by a single application. A custom control is a class and inherits from a base control class. The logic already available in the base class determines how much, and what, code you have to write. There are basically two ways of creating custom controls. If you find that an existing control meets your requirements only partially and lacks some key features, the simplest thing you can do is extend the control by deriving a new class from it. You can override specific properties, methods, and events as well as add new features. If none of the existing Web server controls meet your requirements, consider creating a custom control from scratch by deriving from one of the base control classes—Control and WebControl. These classes provide only the basic functionality of ASP.NET server controls, and they require that you take care of some of the control’s operational aspects yourself, such as rendering, styling, view state, and state management. Note  Custom controls are not to be confused with user controls (ASCX files). Web user ­controls are dynamic-compile components and cannot be added to the Toolbox. In addition, user ­controls should be deployed as source code unless the application that incorporates them is ­precompiled. In this case, you can extract the dynamic assembly that contains the user control and share it between applications. However, this technique is not supported by Microsoft and, well, requires a lot of familiarity with the ASP.NET internals.

513

514

Part II  ASP.NET Pages and Server Controls

Extending Existing Controls When you realize you need a custom control to accomplish a certain task, first pause and make sure the feature you devised can really be obtained with HTML, literals, and JavaScript code. If you know how to do that in pure HTML, you can start planning an ASP.NET control and then architect and engineer the feature for the best reusability and efficiency.

Choosing a Base Class A custom server control is a Microsoft .NET Framework class that inherits—either directly or indirectly—from Control. Control is the root class for all server controls in ASP.NET applications. It should be noted, though, that very few controls that you commonly use in ASP.NET applications really inherit directly from Control. For the most part, ASP.NET controls inherit from intermediate classes that encapsulate a given predefined behavior.

Inheriting from a Base Class Each ASP.NET server control that is not marked as sealed can be further inherited and ­specialized. Table 12-1 lists all the classes in ASP.NET that represent some sort of base ­functionality. Each class in the list represents the root of a family of controls. TABLE 12-1  Base

Control Classes Available in ASP.NET

Class

Description

BaseDataBoundControl

Incorporates the basic mechanism and object model for data binding. It inherits from WebControl.

BaseDataList

Adds grid capabilities such as advanced rendering, ­templates, and paging. It inherits from WebControl. This is considered deprecated in ASP.NET 4.

CompositeControl

Incorporates the mechanics of composite controls with ­regard to the building of the control’s tree. It inherits from WebControl.

CompositeDataBoundControl

Incorporates the mechanics of composite data-bound controls with regard to view-state management and the building of the control’s tree. It inherits from DataBoundControl.

DataBoundControl

Adds support for data source controls, and overrides some methods marked as abstract in the parent class. It inherits from BaseDataBoundControl.

HierarchicalDataBoundControl

Adds support for data hierarchical data source controls, and overrides some methods marked as abstract in the parent class. It inherits from BaseDataBoundControl.

ListControl

Adds support and an object model tailor-made for list c­ ontrols, such as CheckBoxList and DropDownList.

WebControl

Adds an array of user-interface (UI) properties, such as style settings, colors, font, and borders. It inherits from Control.

Chapter 12  Custom Controls

515

Among the commonly used controls that inherit directly from Control, you find Repeater, MultiView, Placeholder, and LiteralControl. All other controls in ASP.NET inherit from one of these classes.

Extending a Base Class The base Control class incorporates a number of features and makes them available to all child controls. A quick list includes view-state management, control identification, naming container capabilities, design-time support, themes, control state, and adaptive rendering. If you choose to inherit from any of the classes in Table 12-1, be prepared to write quite a bit of code because the control you get in the beginning is not particularly rich with concrete functionalities. You typically inherit from any of those classes if you’re going to write a control that provides unique capabilities that are hard to find in other ASP.NET controls. Inheriting from any of the classes in Table 12-1 is more like building a custom control from scratch, where the effective starting point is determined by the selected base class. If you opt for inheritance from a concrete control class—that is, a control that provides an observable behavior and user interface—you should strive to add new features or override existing capabilities without altering too much the structure and the personality of the ­control itself.

A Richer HyperLink Control Let’s start with a sample custom control that extends the standard behavior of the HyperLink built-in control. By default, the ASP.NET HyperLink control outputs an anchor tag that points to a URL. By design, any click on an anchor tag is served directly by the browser, which navigates to the specified page. No postback occurs to the page that originally displayed the anchor. Put another way, if you want to track that the user clicked on a given anchor, you need to extend the behavior of the hyperlink control.

Designing a Usage Scenario Let’s further develop the idea of a control that drives users to a different page but gives the page author a way to track the event. The canonical example used to illustrate the importance of this feature is the page hit counter. Monitoring the visitor activity is an important task that each administrator of a Web site should consider to improve the quality and content of the site. A click-through is the name commonly used to indicate the user’s clicking to see particular content, and it’s an important parameter for evaluating how the visitors of a site receive advertising. How would you implement a counter service that counts clickthroughs in a page?

516

Part II  ASP.NET Pages and Server Controls

You can associate each button control in a page (Button, HyperLink, ImageButton, LinkButton, and AdRotator) with an extra layer of code that first tracks the click and then proceeds with the expected behavior. Getting this behavior with controls that entail a postback is not difficult. Take the LinkButton class, for example. You can derive a new control and override the OnClick protected member as follows: protected virtual void OnClick(EventArgs e) { // Track the event ... // Proceed with the default behavior base.OnClick(e); }

What about the HyperLink control, though? The click on the hyperlink is handled directly by the browser and doesn’t pass through any ASP.NET code of yours.

A Redirector for the HyperLink Control The idea is that you force the HyperLink control to adopt a navigation URL that is different from the one set by the programmer. In other words, you divert the HyperLink to a custom page on your site where you first accomplish any custom tasks you need (such as tracking) and then redirect to the originally requested page. The code for such a modified version of the HyperLink control doesn’t look particularly scary: using System; using System.Web.UI.WebControls; namespace Samples { public class Hyperlink : System.Web.UI.WebControls.HyperLink { public string RedirectPage { get { var o = ViewState["RedirectPage"]; if (o == null) return "redir.aspx"; else return (String) o; } set { ViewState["RedirectPage"] = value; } } public new String NavigateUrl { get { return base.NavigateUrl; } set

Chapter 12  Custom Controls

517

{ var url = "{0}?page={1}"; url = String.Format(url, RedirectPage, value); base.NavigateUrl = url; } } } }

As you can see, the new control has a brand new property—RedirectPage—and overrides an existing property—NavigateUrl. RedirectPage indicates the URL of the intermediate page, where the user is temporarily redirected so that any custom tasks such as click-through ­tracking can be accomplished. Here’s an example of the code file of such a page: public partial class Redir : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // Capture the originally requested page var url = String.Empty; var o = Request["Page"]; if (o != null) { url = Server.UrlEncode((String) o); if (String.IsNullOrEmpty(url) return; } // Do something here, such as updating a counter ... // Redirect the user to the originally requested page Response.Redirect(url); } }

You are assuming that the custom HyperLink control calls the redirector page, passing a Page parameter on the query string set to the original URL. Of course, this trick is arbitrary and you can find any better workarounds if you want. The navigation URL for a hyperlink is set through the NavigateUrl property. You need to ­ensure that whenever a new value is assigned to the NavigateUrl property (say, http://www.asp.net), it gets overridden by something like the following: redir.aspx?page=http://www.asp.net

In this way, the user first reaches redir.aspx, where his action is tracked, and then he is ­directed to his final destination. To override the setter (or the getter) of a control property, you need the property to be marked as virtual at some point in the control’s inheritance chain. The HyperLink control has

518

Part II  ASP.NET Pages and Server Controls

a virtual property—Text—and a couple of public, but not virtual, properties such as Target and NavigateUrl. If the property is not marked as virtual (namely, it is overridable), you can’t override it; however, you can replace its implementation altogether. You do this through the new modifier in C# and the Shadows modifier in Microsoft Visual Basic .NET: public new string NavigateUrl { get { return base.NavigateUrl; } set { var url = "{0}?page={1}"; url = String.Format(url, RedirectPage, value); base.NavigateUrl = url; } }

The new modifier applied to a property instructs the compiler that the current implementation for the member replaces any other implementation available on base classes. If you redefine the NavigateUrl property without using the new keyword, you simply receive a warning from the compiler. The warning informs you that you are hiding an existing member, and it just recommends the use of the new modifier if hiding the member was intentional.

Building Controls from Scratch There are two main situations in which ASP.NET developers feel the need to create custom controls. At times, developers need a control that simply doesn’t exist in the ASP.NET builtin toolbox. And occasionally, developers need a control that is similar to one of the native controls but not close enough to justify using one. In this case, developers typically derive a new control from an existing one and add or override members as appropriate. Let’s discuss techniques and tricks to design and code completely new ASP.NET controls that address functionalities that ASP.NET doesn’t provide out of the box.

Base Class and Interfaces Several programming aspects support the development of a custom control in ASP.NET. First, there are base classes such as Control and WebControl. Each class provides a common set of base properties that address and fit into a particular use case. In addition to base classes, interfaces help you to better characterize the behavior and programming model of the control. A few interfaces are worth mentioning. They are INamingContainer, IPostBackDataHandler, and IPostBackEventHandler. In Table 12-1, you see listed all base classes for controls and data-bound controls. For now, let’s focus on Control and WebControl.

Chapter 12  Custom Controls

519

Control vs. WebControl The Control class defines the properties, methods, and events common to all ASP.NET server controls. These include the methods and events that determine and govern the life cycle of the control, plus a few properties such as ID, UniqueID, Parent, and ViewState and the ­collection of child controls named Controls. The WebControl class derives from Control and adds extra properties and methods, mostly regarding control styles that affect rendering. These properties include ForeColor, BackColor, Font, Height, and Width. WebControl, in particular, is the base class for the family of Web server controls in ASP.NET. When developing a new ASP.NET control, there’s just one guideline to follow. If your control renders a user interface, you should derive it from WebControl. If you’re authoring a component that doesn’t provide specific user-interface features, you’re better off using Control as your base class. Although these rules are effective in most cases, there might be exceptional situations in which you would reasonably do otherwise. For example, you can derive from Control if you want to provide a subset of the user-interface features. When building composite controls—that is, controls designed by aggregating multiple ­controls together—you might want to use CompositeControl as the base class. You should never use UserControl, on the other hand, as a base class for a custom control.

Related Interfaces Depending on the functionality of your control, you might have to implement additional ­interfaces. Typically, a server control implements some of the following interfaces: ■

INamingContainer  This interface, also referred to as a marker interface, doesn’t contain methods—it simply notifies the ASP.NET runtime that the control that exposes it should be treated as a naming container. Child controls contained within a naming container control have their UniqueID property prefixed with the ID of the container. The naming container, therefore, acts as a namespace and guarantees the uniqueness of the control’s client IDs within the specified naming scope. (Note that if the ClientIDMode property is set to Static, this warranty just ceases.) The use of the INamingContainer interface is essential if you’re writing composite controls or controls that include templates.

IPostBackDataHandler  The interface is needed whenever your control has to ­examine postback data. If the user can execute actions that affect the state of the control, you need to look into the postback data. For example, a TextBox control stores its ­configuration in the view state but also needs to read what the user typed in through the browser. This scenario is just where the IPostBackDataHandler interface fits in. The method LoadPostData lets controls examine posted values. The interface is also

520

Part II  ASP.NET Pages and Server Controls

­ elpful if you need to raise events on the server based on changes to the data (method h RaisePostDataChanged). Again, the TextBox is the perfect sample control; if the data changed between postbacks, the TextChanged event is also raised. ■

IPostBackEventHandler  The interface serves to capture a client-side postback event (for example, a click). Upon postback, after raising data change events, the ASP.NET runtime looks for a server control whose UniqueID property matches the name of a posted value (for example, the name of the clicked button). If a match is found and the control implements IPostBackEventHandler, ASP.NET invokes the RaisePostBackEvent method on the control. RaisePostBackEvent is the only method defined on the IPostBackEventHandler interface. What a particular control does within the RaisePostBackEvent method can vary quite a bit. The Button control—a simple control that implements this interface—fires its Click event when ASP.NET invokes the RaisePostBackEvent method.

Choosing a Rendering Style For an ASP.NET server control, the sole purpose in life is outputting markup text. The ­control’s object model and the system infrastructure it leverages serve to determine the ­contents to output, but the whole life cycle of controls (and host pages) inevitably ends with the rendering step. There are various ways for a server control to render out.

The Render Method Typically, an ASP.NET control renders out through the Render method. To take total control of the control’s rendering, you therefore override the Render method and write markup code to the specified HTML text writer object: protected override void Render(HtmlTextWriter writer)

The HTML text writer object is a sort of buffer where you can accumulate all the text to be output—nearly the same as a string builder. You can compose markup using the methods of the HtmlTextWriter object or by building plain strings. Writing to the text writer is indeed the fastest way for controls to generate their markup, but unfortunately it doesn’t result in easily readable code. If you take this route for a reasonably complex control, your final code will look like an intricate mess of nested if-then-else statements. Your code will be hard to read and maintain. There’s another aspect to consider about direct markup output. Consider the following code snippet: protected override void Render(HtmlTextWriter writer) { writer.Write(""); }

Chapter 12  Custom Controls

521

The final page contains an input field of type text with an ID of TextBox1. The server environment, though, doesn’t know anything about this element and might not be able to process server events for this element correctly. In other words, you should render the markup directly only for controls that output raw HTML that don’t match ASP.NET controls and don’t need to raise or handle server events such as postbacks or post-data-changed events. If you’re going to write a server control that renders an HTML marquee or a table of data, writing to the control’s text writer buffer is fine. If you’re building a control that results from the composition of other controls, you’re better off taking another approach—building the control tree programmatically.

Building the Control Tree When your control embeds constituent child controls, you have a composite control. In this case, it is recommended that you build the final tree of controls programmatically by overriding the CreateChildControls method defined on the Control class. You do this by adding all constituent controls to the Controls collection of the control being developed. Here’s an example: protected override void CreateChildControls() { // Clears child controls Controls.Clear(); // Build the control tree CreateControlHierarchy(); // Clear the view state of child controls ClearChildViewState(); }

ClearChildViewState is a method on the Control class that deletes the view-state information for all the server child controls. CreateControlHierarchy, on the other hand, is an arbitrary name and represents a user-defined method that builds the control’s tree. You should feel free to replace that function with your own function or plain code. As a matter of fact, though, most ASP.NET built-in composite controls define a protected, overridable method with just that name. Here’s a possible implementation for CreateControlHierarchy that creates a text box with a leading label. Note that not only is the name of CreateControlHierarchy arbitrary, but its prototype also is. protected void CreateControlHierarchy() { // Add the label var lbl = new Label(); lbl.Text = "Some text"; Controls.Add(lbl);

522

Part II  ASP.NET Pages and Server Controls // Add a blank literal control for spacing Controls.Add(new LiteralControl("")); // Add the text box var txt = new TextBox(); txt.Text = String.Empty; Controls.Add(txt); // Specifies that child controls have been created ChildControlsCreated = true; }

The ultimate goal of CreateControlHierarchy is populating the Controls collection of the current control with all child controls in the proper position in the final hierarchy. The ChildControlsCreated Boolean property is defined on the Control class and indicates whether all child controls have been created or not. For a composite control, you don’t need to override the Render method, but it is recommended that you implement the marker interface INamingContainer to facilitate ASP.NET’s ability to recognize postback events caused by any child control. Finally, a method that is worth mentioning regarding composite controls is EnsureChildControls. This method checks whether all child controls have been created and, if not, it re-creates them. How can the control know about that? It simply reads the value of the ChildControlsCreated Boolean property and calls CreateChildControls if all child controls haven’t been created. The following code snippet illustrates the behavior of EnsureChildControls: protected virtual void EnsureChildControls() { if (!ChildControlsCreated) { try { CreateChildControls(); } finally { ChildControlsCreated = true; } } }

The SimpleGaugeBar Control To get a grip on building new ASP.NET controls, let’s create a control with a limited state buta significant rendering engine. The control, named SimpleGaugeBar, is a simple,

Chapter 12  Custom Controls

523

­ on-data-bound gauge bar that you can use to implement a rating system that represents n the progress made for certain tasks. Generally, it can be used to give a friendly user interface to measurable quantities.

Defining the Object Model A gauge control needs to have at least two properties—one to indicate the value being ­rendered, and one that provides the scale. In addition, you can also give users a chance to control the ruler and the descriptive text for the gauge. Table 12-2 lists the properties of a gauge control. TABLE 12-2  Properties

of the SimpleGaugeBar Control

Property

Description

FormatString

Formats the string that the control will render alongside the bar. The string can contain up to two placeholders. The first placeholder is set with the value; the ­second placeholder is set with the scale. The default string has the following form: {0} / {1}.

GridLines

Indicates whether vertical delimiters should be displayed to mark notches.

Maximum

Indicates the maximum value the gauge can represent. It’s set to 100 by default.

Segments

Indicates the number of notches to draw on the gauge ruler. It’s set to 4 by default.

Value

Indicates the value to represent. It’s set to 0 by default, and it cannot be higher than the scale.

The setter method of the Value property adjusts any value provided that exceeds the current Maximum. The value stored in Maximum is the highest value you can assign to Value. The format string should be formed using two parameters in a fixed order: Value and Maximum. In the format string, you can use any HTML formatting and even reference the parameters in the reverse order. The following code snippet shows possible ways of setting the format string: GaugeBar1.FormatString = "{0} ({1})"; GaugeBar2.FormatString = "Maximum is {1}. Value is {0}";

The SimpleGaugeBar control has no methods and doesn’t fire any events.

Implementing the Object Model Internally, the control renders the gauge using an HTML table. The Value and Maximum pair are translated as percentages, and the ruler is drawn using table cells. Figure 12-1 shows the control within the Microsoft Visual Studio designer.

524

Part II  ASP.NET Pages and Server Controls

FIGURE 12-1  The SimpleGaugeBar control in action in the Visual Studio designer.

The notches on the ruler are obtained simply by adding as many cells to the underlying table as there are units in the Segments property. The following listing shows the implementation of the control properties: public class SimpleGaugeBar : CompositeControl { private int _dividerCell; public SimpleGaugeBar() { } // Gets and sets the value to represent in the gauge bar public float Value { get { var o = ViewState["Value"]; if (o == null) return 0; return (float) o; } set { ViewState["Value"] = value; if (value > Maximum) ViewState["Value"] = Maximum; } } // Gets and sets the maximum value representable in the gauge bar public float Maximum { get { var o = ViewState["Maximum"];

Chapter 12  Custom Controls if (o == null) return 100; return (float) o; } set { ViewState["Maximum"] = value; } } // Number of segments to divide the bar into public int Segments { get { var o = ViewState["Segments"]; if (o == null) return 4; return (int) o; } set { ViewState["Segments"] = value; if( value < 1) ViewState["Segments"] = 1; } } // Gets and sets the pattern to format the value in the gauge bar public string FormatString { get { var o = ViewState["FormatString"]; if (o == null) return "{0} / {1}"; return (string) o; } set { ViewState["FormatString"] = value; } } // Gets and sets whether the gauge bar has grid lines public bool GridLines { get { var o = ViewState["GridLines"]; if (o == null) return true; return (bool) o; } set { ViewState["GridLines"] = value; } } ... }

525

Download from Wow! eBook

526

Part II  ASP.NET Pages and Server Controls

The control maintains some state by using the view-state collection. All the properties, in fact, are persisted using ViewState. Because all the persisted properties are marked as public, you can disable the view state altogether and still keep the control fully functional by ­explicitly setting properties upon page loading.

Setting Up the Ruler The ruler divides the area of the control into segments, which are filled proportionally based on the current value of the gauge. Each segment of the ruler corresponds to a cell in the underlying table. All cells but one are entirely filled or entirely empty. Filled cells are rendered using the current foreground color; empty cells are rendered using the current background color. One cell, named the divider cell, contains a child table with exactly one row and two cells. The first cell is rendered with the foreground color; the second cell is colored as the control’s background. The two cells have a width, measured in percent, whose total amounts to 100. The latter cell denotes how much is still left to do to reach the maximum. The following HTML code snippet shows the final HTML markup to render a value of 52 out of 100 ­using a ruler with four notches or segments:

Figure 12-2 shows gauges with different ruler settings.

FIGURE 12-2  The effect of different settings on the gauge ruler.

Setting Up the Control’s Site As you might have guessed already from the preceding figures, other properties get into the game in addition to those discussed in Table 12-2. Admittedly, the grayscale rendering used

Chapter 12  Custom Controls

527

in this book doesn’t do justice to the actual capabilities of the SimpleGaugeBar control in terms of color support. However, the control exploits a few color-related properties defined on the base class. These properties are BackColor, ForeColor, Width, and Height. Width and Height are used to delimit the control’s site—that is, the area within the ­container the control is assigned for rendering. The control is assigned a default size that can be changed either programmatically or through the Visual Studio Properties window. The value of the ForeColor property is used to render the text of the label that accompanies the gauge. The value of the BackColor property determines the color to be used for the progress bar. Note that the implementation we just discussed assumes that only known ­colors can be used.

Rendering the SimpleGaugeBar Control The user interface of a Web control is pure HTML, sometimes topped off with a bit of client script. As mentioned, there are basically two ways in which this HTML can be generated. You can compose the HTML code in the supplied writer, or you can build an in-memory representation of the output using existing HTML and Web server controls and then have them recursively render their contents to the writer. Let’s discuss these two options in more detail.

Generating the HTML for a Custom Control From a pure performance standpoint, writing out the control’s markup to an HTML text writer object is the preferred approach. No server control is ever instantiated, and the final and correct markup is sent to the browser. There are a few downsides to this approach you should consider, however. One is that you end up making several calls to the writer. And, aside from some negligible repercussions in terms of the performance (repercussions that are negligible when compared to control instantiation), the size of the code grows considerably, making your source code on the whole much less readable and harder to maintain. Let’s ­consider a quick but significant example. To write the content of a string in a table cell, you need the following code if you decide to opt for the rich interface of the writer: output.WriteFullBeginTag("table"); output.WriteFullBeginTag("tr"); output.WriteFullBeginTag("td"); output.Write(text); output.WriteEndTag("td"); output.WriteEndTag("tr"); output.WriteEndTag("table");

528

Part II  ASP.NET Pages and Server Controls

However, as long as you don’t have a full bag of attributes to render, or a really complex structure to build, the following code is equally effective and even slightly faster: output.Write("

"); output.Write(text); output.Write("

");

In general, neither of these two approaches is always the best possible approach. A good compromise between the two is recommended to optimize performance while producing compact code. Taking the first approach to its natural limit, you end up with many more lines of code than are necessary. Taking the second approach further, you resort to building the control using strings, which is indeed not the best thing you can do, mainly from a maintenance point of view. In ASP.NET, every piece of HTML code can be managed on the server as an instance of a class. This pattern results in extreme flexibility and ease of development. However, it doesn’t come without problems either. The rub lies in the fact that you instantiate lots of controls, which always affects performance. Let’s take a look at this in more detail.

Using Child Controls for Rendering Sometimes the custom control needs to build up a complex infrastructure with nested tables and elements. In this case, it makes sense to build an in-memory representation of the overall tree and then render everything to HTML using the RenderContents method of the root control. Typically, for controls with a relatively complex hierarchy of child controls and rich styles, you override the Render method as follows: protected override void Render(HtmlTextWriter output) { // This is a custom method that you normally use // to ensure that all elements are styled properly. // We’ll show an implementation of this method later. PrepareControlForRendering(); // Render the contents of the control base.RenderContents(output); }

The SimpleGaugeBar control renders a nontrivial table structure that is much more manageable through a control tree: protected override void CreateChildControls() { Controls.Clear(); CreateControlHierarchy(); ClearChildViewState(); } protected virtual void CreateControlHierarchy()

Chapter 12  Custom Controls

529

{ // Build the outermost container table var outer = new Table(); var outerRow = new TableRow(); outer.Rows.Add(outerRow); // Ruler cell var rulerCell = new TableCell(); outerRow.Cells.Add(rulerCell); BuildGaugeBar(rulerCell); // Text cell var textCell = new TableCell(); if (!_textStyle.DisplayTextAtBottom) { outerRow.Cells.Add(textCell); BuildLabel(textCell); } // Save the control tree—add the table as a child of the gauge Controls.Add(outer); // Build the label if (!_textStyle.RenderInsideTable && _textStyle.DisplayTextAtBottom) BuildLabel(null); } void BuildGaugeBar(TableCell container) { // Create the table with one or two rows: ruler (and label) var t = new Table(); var ruler = new TableRow(); t.Rows.Add(ruler); // Build the ruler row BuildRuler(ruler); // Build the label if (_textStyle.RenderInsideTable) BuildLabelIntoTable(t); // Save the control tree container.Controls.Add(t); }

The output of the SimpleGaugeBar control consists of an outermost table that has one rowand two cells. The first cell contains the gauge bar; the second cell optionally contains the text, when the companion text has to be displayed on the side of the gauge. (See Figure12-2.) If the text goes below the gauge, it can either be part of the table (a second row) or just an additional Label control. You control rendering styles of the text through a custom style property—the TextStyle property—that I’ll say more about in a moment. Let’sfirst focus on the ruler.

530

Part II  ASP.NET Pages and Server Controls

The ruler is a sequence of table cells. Each cell corresponds to a notch you want to see on the final gauge. The number of notches is determined by the Segments property. The Value property is scaled as a percentage of the Maximum value, and the resulting value is used to determine the color of the various cells. If the value to represent is larger than the value represented by the current notch, a cell is added with the average width determined by dividing 100 by the number of notches. The same happens if the value is smaller and the divider cell has been rendered already. (In this case, finished is true.) void BuildRuler(TableRow ruler) { // Calculate the value to represent var val = GetValueToRepresent(); float valueToRepresent = 100f * val / Maximum; var numOfSegments = GetNumOfSegments(); int segmentWidth = 100 / numOfSegments; bool finished = false; for (int i = 1; i (i - 1) * segmentWidth) { TableCell left = new TableCell();

Chapter 12  Custom Controls

531

childRow.Cells.Add(left); left.Width = Unit.Percentage(fx); } var right = new TableCell(); childRow.Cells.Add(right); right.Width = Unit.Percentage(100 - fx); finished = true; } } else { // Done var done = new TableCell(); ruler.Cells.Add(done); done.Width = Unit.Percentage(segmentWidth); } } }

The divider cell is the cell that is split in two to represent the remaining value, as shown in Figure 12-3.

FIGURE 12-3  The divider cell in sample SimpleGaugeBar controls.

The divider cell is the first cell where the value of the corresponding notch is larger than the value to represent. The divider cell is rendered through an embedded table with one row and two cells. The index of the divider cell is cached for further use. The companion text of the gauge can be displayed to the right of the gauge or below it. When rendered below it, the text can either be incorporated in the table or added as an ­extra control. BuildLabel can either add the text as an additional control or place it in the rightmost cell. BuildLabelIntoTable writes the text in an additional table row below the gauge. In this case, the text inherits most of the gauge graphical settings. void BuildLabel(TableCell container) { // Calculate the value to represent float buf = GetValueToRepresent(); // Get the string to display on the label string msg = GetTextToRepresent(); var lbl = new Label(); if (container is TableCell) container.Controls.Add(lbl);

532

Part II  ASP.NET Pages and Server Controls else Controls.Add(lbl); lbl.Text = String.Format(msg, buf, Maximum); } // Build the control tree for the label void BuildLabelIntoTable(Table t) { // Calculate the value to represent float valueToRepresent = GetValueToRepresent(); int numOfSegments = GetNumOfSegments(); // Get the string to display on the label var companionText = GetTextToRepresent(); if (_textStyle.DisplayTextAtBottom) { // Add a bottom row var label = new TableRow(); t.Rows.Add(label); var lblCell = new TableCell(); label.Cells.Add(lblCell); lblCell.ColumnSpan = numOfSegments; lblCell.Text = String.Format(companionText, valueToRepresent, Maximum); } }

Note  In the code shown thus far for the SimpleGaugeBar control, there a pair of unexplained methods: GetValueToRepresent and GetTextToRepresent. In this simple control, the methods return, respectively, the value of the Value and FormatString properties. However, you can extend the control with data-binding capabilities. In doing so, most of the changes will consist of ­extending the GetValueToRepresent and GetTextToRepresent methods. There’s no functional difference between the two approaches—it’s purely a matter of ­appearance and preference. But how can you control the rendering and the styles of the companion text? You do that through a new style property.

The Gauge in Action After it’s compiled, the SimpleGaugeBar control can be installed in the Visual Studio toolbox and dragged and dropped onto any Web Forms page you’re developing. Here’s some sample code being added to a page:

Chapter 12  Custom Controls

533

The properties of the control that feature simple types can be set using the Properties ­window; for properties of a complex type, such as classes, you need to write a type converter and configure the property for the design-time environment of Visual Studio. The following code shows how to set properties on the gauge control programmatically: private void Button1_Click(Object sender, EventArgs e) { GaugeBar1.Maximum = 200; GaugeBar1.Value = 55; }

You should try to set the Maximum property first because, in this way, the control ­automatically validates the value. Maximum and Value are stored in the view state and areautomatically restored when the page posts back. If the host page disables the view state, you should modify the code that relates to the control so that the needed properties are set on each request.

Building a Data-Bound Control So far, we’ve created the SimpleGaugeBar control as a composite control to display a notched indicator of a given quantity. By setting the Value and Maximum properties on the control, you can graphically represent a value on the proper scale. The SimpleGaugeBar control is not data bound, meaning that no elements in its programming interface can be automatically and declaratively bound to external data. Derived from CompositeControl, the SimpleGaugeBar control doesn’t incorporate any of the features listed previously regarding data-bound controls. The goal of this section is to extend the SimpleGaugeBar control to make it support data binding through enumerable objects and data source controls.

Key Features A data-bound version of SimpleGaugeBar is a form of simple binding. A couple of existing properties—Value and FormatString—can be automatically filled with external data according to the classic data-binding pattern of ASP.NET. A data source object specified through either DataSource or DataSourceID and bindable properties are mapped to public fields on the data source object through mapper properties. In simple binding, the bound data source object is an individual object that contains just one logical piece of information—no items, no lists. The key features of a data-bound control can be summarized as follows: ■

Additional properties to represent mappings between control properties and data source fields

534

Part II  ASP.NET Pages and Server Controls ■

An additional property to represent and persist the data source object

Additional view-state management to persist the data source object

Modified rendering to take bound data into account

Let’s dig out more.

Adding Data-Bound Properties When you bind data to, say, a DropDownList control, you first set the data source and then specify which fields on the data source should be used to display the text and the value of the resulting list. The DropDownList control features a pair of DataTextField and DataValueField string properties. The former is set to the name of the public field on the data source that will render the text of displayed list items. The latter is set to the name of the field on the bound data source ­object that will render the unique value associated with each displayed list item. On a new data-bound control, you need to define similar properties to specify any required mapping between data source fields and bindable control properties. All these properties are usually string properties stored in the view state; the name is arbitrary, but it generally follows the pattern DataXxxField, where Xxx indicates the role of the bindable control property.

Adding a Data Item Property By design, the bound data source object must be an object that implements any of the ­following interfaces: IEnumerable (collections), IListSource (ADO.NET objects), or IDataSource (data source controls). Let’s suppose you bind a control to one row of a DataTable. Do you ­really need to persist the whole data row? If yes, what if the data row contains a couple of large binary large object (BLOB) fields? The recommended approach entails that you extract a subset of information from the ­originally bound data source object and copy that to a control-specific data item object. This object is an instance of a custom class that typically has as many public properties as there are bindable properties on the control. For example, the DropDownList control has two bindable properties: Text and Value. Subsequently, the data item object—named ListItem—has two properties: Text and Value. (Naming is arbitrary, though.) In a new data-bound control, you define a data item class that will be filled with any necessary information contained in the bound data source. This data item object must be persisted through the view state to guarantee that the control refreshes properly across postbacks. For performance reasons, the data item class must be able to serialize itself to the view state without resorting to the binary formatter. Put another way, it means that the data item class must implement IStateManager, just like style classes do.

Chapter 12  Custom Controls

535

Note  The data item class will be a collection of single data item classes if the data binding ­involves the association of a list of elements to a control.

Overriding the PerformDataBinding Method The final key feature for a custom data-bound control is overriding the PerformDataBinding method. The method receives the contents of the bound data source object in the form of an enumerable object. As a control developer, you must read any required data from the source and cache it in the data item object. Finally, you modify the rendering engine of the control to display bound data. Note  Unless you need a data-bound control that behaves in a particular way (for example, a list

control or a composite data-bound control), deriving your control from DataBoundControl is the most reasonable thing to do most of the time. If you need to start from a lower level, though, you can inherit from BaseDataBoundControl and override PerformSelect and ValidateDataSource. Needless to say, you might want to take this route only if you need to change the way a data source is validated, retrieved, or both.

The GaugeBar Control Let’s apply all the steps outlined so far to a new version of the SimpleGaugeBar control, aptly named GaugeBar. The new control will still be a composite control, but it will inherit from DataBoundControl to gain standard data-binding capabilities. public class GaugeBar : DataBoundControl { ... }

To be precise, ASP.NET also features a class that incorporates both composition and data binding. The name of the class is CompositeDataBoundControl.

Mapping Data Source Fields to Control Properties The new GaugeBar control uses the same code as SimpleGaugeBar and extends it to define a couple of bindable properties—say, Value and FormatString. This choice of bindable ­properties is arbitrary, however. You define a pair of DataXxxField properties—one for Value and one for FormatString. These string properties contain the name of the data source fields mapped to the Value

536

Part II  ASP.NET Pages and Server Controls

and FormatString. In particular, DataValueField indicates that the field mapped to Value and DataTextField specifies the field linked to FormatString. Once again, note that the names used here are arbitrary. public virtual string DataValueField { get { var o = ViewState["DataValueField"] as String; return o ?? String.Empty; } set { ViewState["DataValueField"] = value; } } public virtual string DataTextField { get { var o = ViewState["DataTextField"] as String; return o ?? String.Empty; } set { ViewState["DataTextField"] = value; } }

As you can see, both properties use the ViewState as the storage medium and are set to the empty string by default. Other popular data-bound properties available on the GaugeBar class are DataSource, DataSourceID, and DataMember, all of which are inherited from parent classes.

The GaugeBar’s Data Item Object After the GaugeBar control is bound to some external data, you need to track and cache any bound data. For this purpose, you need a data item object. As mentioned, a data item o ­ bject is a custom class with as many public properties as there are bindable properties in the control’s interface. The data item class for the GaugeBar control is named GaugeBarDataItem (again, an arbitrary name) and is defined as follows: public class GaugeBarDataItem : IStateManager { private string _text; private float _value; private bool _marked; public GaugeBarDataItem() { }

Chapter 12  Custom Controls

537

public GaugeBarDataItem(float value, string text) { _text = text; _value = value; } public string Text { get { return _text; } set { _text = value; } } public float Value { get { return _value; } set { _value = value; } } public bool IsTrackingViewState { get { return _marked; } } public void LoadViewState(object state) { if (state != null) { Pair p = (Pair)state; _value = (float)p.First; _text = (string)p.Second; } } public object SaveViewState() { return new Pair(_value, _text); } public void TrackViewState() { _marked = true; } }

The class has two public properties—Text and Value—persisted through local members. More interestingly, the class also implements the IStateManager interface, which provides a standard interface to save any valuable contents to the view state across postbacks. The SaveViewState method returns a Pair object (a sort of simplified array of two elements) filled with the current values of the Text and Value properties. The Pair object returned by SaveViewState becomes the input argument of LoadViewState, which unpacks the Pair object and initializes the Text and Value properties.

538

Part II  ASP.NET Pages and Server Controls

The GaugeBar control needs to expose a read-only property of type GaugeBarDataItem. You can use any name for this variable—I’m using DataItem here. More important than the name of the property is its implementation. Take a look at the following code: private GaugeBarDataItem _dataItem; ... private GaugeBarDataItem DataItem { get { if (_dataItem == null) { _dataItem = new GaugeBarDataItem(); if (base.IsTrackingViewState) _dataItem.TrackViewState(); } return _dataItem; } }

Unlike other control properties that are persisted directly in the ViewState collection o ­ bject, the DataItem property uses a private member (_dataItem) to persist its value. A private ­member, though, is not persistent and doesn’t survive postbacks. For this reason, in the get accessor of the property you need to check _dataItem for nullness and create a new instance if it is null. The code contained in the get accessor of a property runs whenever that property is invoked. As you’ll see in a moment, the preceding code ensures that no access to DataItem results in a null object exception and that the state of the object is restored correctly after each postback.

Data Item and View State Most of the control properties we’ve considered thus far use the ViewState container to persist the values. Why should we not store DataItem or style properties in the same way? Is there anything wrong with the following code? // NB: for this code to work, GaugeBarDataItem must be // a serializable type public virtual GaugeBarDataItem DataItem { get { var o = ViewState["DataItem"] as GaugeBarDataItem; return o ?? new GaugeBarDataItem(); } set { ViewState["DataItem"] = value; } }

Chapter 12  Custom Controls

539

Actually, nothing is “wrong” with the code per-se—but consider for a moment view-state size and performance. Saving a class type directly in the ViewState container results in the object being serialized using the binary formatter. The BinaryFormatter class—the standard way to serialize managed objects in .NET applications—is not particularly fast and is designed to save the entire state of the object, including both public and private members, both simple and complex. The use of the BinaryFormatter increases the response time for each request and generates a larger view-state output. By customizing the view-state serialization, you obtain much faster code and save exactly the information you need to save. As a rule of thumb, you should use the ViewState container to store property values if the type of the property is primitive—a string, numbers, Boolean values, colors, dates, bytes, and arrays of any of these types. Reference types (for example, custom classes) should be serialized by implementing IStateManager and exposing the property via a get accessor like the one shown previously. As far as control development is concerned, this is commonly required for styles and data item properties.

Ad Hoc View-State Management A control that has properties that take advantage of custom view-state serialization must override the SaveViewState and LoadViewState protected methods. These methods are ­defined on the Control class, and they indicate how to save and restore the state of the ­control to and from the view state. The default implementation of both methods takes care of the contents of only the ViewState container object. protected override object SaveViewState() { // Get the standard state object—ViewState container var baseState = base.SaveViewState(); // Get the state object for the DataItem property var itemState = DataItem.SaveViewState(); // Get the state object for the TextStyle object var styleState = TextStyle.SaveViewState(); // Pack everything into a unique object return new Triplet(baseState, itemState, styleState); }

The SaveViewState method of the GaugeBar control needs to save three objects: the ­standard view state, the DataItem property, and the TextStyle property. You get the standard view-state output by calling SaveViewState on the base class, and you get other state objects by calling SaveViewState on the IStateManager implementation of DataItem and TextStyle. The SaveViewState method on the control needs to return a single object, so you just group all data to return in a single object—typically, an array or a combination of Pair and Triplet objects.

540

Part II  ASP.NET Pages and Server Controls

The object returned by SaveViewState is received by LoadViewState, which extracts and ­assigns data back to the original objects. protected override void LoadViewState(object savedState) { if (savedState != null) { var t = (Triplet) savedState; base.LoadViewState(t.First); DataItem.LoadViewState(t.Second); TextStyle.LoadViewState(t.Third); } else { base.LoadViewState(null); } }

The IStateManager implementation of LoadViewState on the serialized objects determines how each object (for example, styles and data items) restores its own data. Note that when DataItem.LoadViewState is called, the get accessor of DataItem is invoked and initializes the internal _dataItem member on the first call.

Getting Bound Data In ASP.NET, a bound control obtains bound data through the PerformDataBinding method. Overriding this method is mandatory for any data-bound control because the standard implementation of the method does nothing. It is important to recall that the IEnumerable argument passed to PerformDataBinding represents the collection of bound data regardless of the format of the originally bound data source—whether it is an ADO.NET object, c­ ollection, or data source control. Here’s the implementation of PerformDataBinding for the GaugeBar control: protected override void PerformDataBinding(IEnumerable data) { // In this control, in spite of the IEnumerable type being used // the argument "data" is a single object, not a real list to enumerate. // You need to get an enumerator and call MoveNext once to get the effective // content to bind. if (data == null) return; var e = data.GetEnumerator(); e.MoveNext(); // Set default values for bindable properties float displayValue = 0; var displayText = String.Empty;

Chapter 12  Custom Controls

541

// Read the value for the Value property if (!String.IsNullOrEmpty(DataValueField)) displayValue = (float) DataBinder.GetPropertyValue( e.Current, DataValueField); // Read the value for the FormatString property if (!String.IsNullOrEmpty(DataTextField)) displayText = (String) DataBinder.GetPropertyValue( e.Current, DataTextField); // Fill the DataItem property DataItem.Value = displayValue; DataItem.Text = displayText; }

In this particular case, the IEnumerable object passed to PerformDataBinding contains just one element. The IEnumerable interface, though, doesn’t distinguish between a single ­element or a list of elements. In other words, to get the data object you need to get the ­enumerator and move to the first item: // data is of type IEnumerable IEnumerator e = data.GetEnumerator(); e.MoveNext(); // Use e.Current to get the physical data object

The e.Current expression returns the data object bound to the control—that is, the container from which you extract the fields mapped to bindable properties. If you know the control is bound to, say, a DataRow object, you can retrieve the value for the Value property through the following code: displayValue = ((DataRow) e.Current)[DataValueField];

Using the DataBinder class adds greater flexibility to your code and makes your code ­independent from the type of the bound data source. The GetPropertyValue method on the DataBinder class uses reflection to query the object to see whether it contains a public ­property with the specified name: displayText = (string) DataBinder.GetPropertyValue( e.Current, DataTextField);

GetPropertyValue returns an object and requires a cast to the proper type.

542

Part II  ASP.NET Pages and Server Controls

The remaining step is updating the rendering engine so that it accesses the DataItem object whenever it requires bound data. The BuildLabel method shown next displays the descriptive text around the gauge: void BuildLabel(TableCell container) { // Calculate the value to represent var valueToRepresent = GetValueToRepresent(); // Get the string to display on the label var msg = GetTextToRepresent(); var lbl = new Label(); if (container is TableCell) container.Controls.Add(lbl); else Controls.Add(lbl); lbl.Text = String.Format(msg, valueToRepresent, Maximum); }

The BuildLabel method adds a Label control to the control hierarchy under construction. The text displayed through the label is composed using the value and the format string of the gauge. Both Value and FormatString can be either data-bound or statically assigned. For this reason, you should use a get function that checks the current binding, if any, and returns the bound value or the assigned value. Note the bound value is returned in favor of an assigned value, if both are present. float GetValueToRepresent() { float f = 0; if (DataItem.Value >=0) f = DataItem.Value; else f = Value; return f; } string GetTextToRepresent() { var msg = ""; if (!String.IsNullOrEmpty(DataItem.Text)) msg = DataItem.Text; else msg = FormatString; return msg; }

No other changes are required to enhance the SimpleGaugeBar control and make it data-bound.

Chapter 12  Custom Controls

543

The following code shows the Load handler of a sample page that uses the GaugeBar control and binds it to a dynamically generated DataTable object: public class MyDataContainer { public float Numbers { get; set; } public String Label { get; set; } } protected void Page_Load(object sender, EventArgs e) { // Uses a random number as the value of the GaugeBar. // The value is stored in a custom object. Random rnd = new Random(); var container = new MyDataContainer(); container.Numbers = rnd.Next(0,100); container.Label = "{0} out of {1}"; // Binds the DataTable to the GaugeBar GaugeBar1.DataValueField = "Numbers"; GaugeBar1.DataTextField = "Label"; GaugeBar1.DataSource = container; GaugeBar1.DataBind(); }

The DataTable has two columns—Numbers and Label—of type float and string, respectively. The table contains one data row. If the table contained multiple rows, only the first would be taken into account according to the code in PerformDataBinding. Note that you can also use the DataItem property to bind data to the GaugeBar control: GaugeBar1.DataItem.Value = 12; GaugeBar1.DataItem.Text = "{0} %";

Note that no call to DataBind is required to trigger the process and update the control’s user interface.

Building a Composite Templated Control The CompositeDataBoundControl class is the starting point for building rich, complex, and data-bound composite controls. A composite data-bound control must do the following: ■

Act as a naming container.

Create its own user interface through the CreateChildControls method.

Implement the necessary logic to restore its hierarchy of child elements after postback.

544

Part II  ASP.NET Pages and Server Controls

The good news is that you can completely ignore the third point if you derive your control class from the CompositeDataBoundControl class. The class, in fact, implements internally any necessary logic.

Generalities of Composite Data-Bound Controls The main aspect you care about when building a composite data-bound control is designing the internal hierarchy of your control. The method to override for this purpose is an overloaded version of CreateChildControls. In addition, you typically add styles and templates. In a real-world composite control, the internal control tree is usually quite complex. The ­outermost container is often a multirow HTML table (or perhaps a collection of tags, each with specific semantics associated with it). However, what’s in the various cells and rows can vary quite a bit and result in a pretty sophisticated combination of child controls and literals.

Creating a Hierarchy of Child Controls You should know by now that composite controls build their own interface by composing controls in the override of the CreateChildControls method. Defined on the Control class, the method has the following prototype: protected override void CreateChildControls()

In the CompositeDataBoundControl class, the method is overridden and overloaded. In ­particular, the overridden version accomplishes a few interesting tasks. Here’s its pseudo-code: protected override void CreateChildControls() { Controls.Clear(); var o = ViewState["_!ItemCount"]; if ((o == null) && RequiresDataBinding) EnsureDataBound(); else { int numOfItems = (int) o; object[] items = new object[numOfItems]; CreateChildControls(items, false); base.ClearChildViewState(); } }

Chapter 12  Custom Controls

545

The method first empties the Controls collection so that no pending child controls are left around. Next, it retrieves a value from a particular (and internally managed) view-state entry named _!ItemCount. The view-state entry caches the number of items that form the ­composite control. The code that actually builds the control tree is responsible for storing this value in the view state. Knowing the number of items that form the control hierarchy is important to optimize the data-binding process. In ASP.NET, complex controls that show a possibly long list of data items are implemented as composite data-bound controls. In what way is this different from list and simple-bound controls? List controls and simple-bound controls, such as the GaugeBar we considered earlier, cache the data item or items in the view state. In addition, they can either receive data from the data-binding process or programmatically through the Items collection and the DataItem property, respectively. Composite data-bound controls (such as ListView and GridView) work on the assumption that they receive data exclusively from data binding and, for this reason, don’t persist bound data in any form. Consider now the following scenario. Imagine a page that contains a rich control such as the GridView and some button controls. One of the button controls, when clicked, executes no code that involves the GridView but still refreshes the page. Without some special tricks in the control’s code, you can be sure that the composite data-bound control would be empty upon postback. Why is this so? If the postback event handler doesn’t bind data back to the composite control, the control has no way to figure it out and refresh properly. In ASP.NET, by design, composite data-bound controls take their data only from data binding and don’t cache any bound data. So a special workaround is required to handle postback events. For composite data-bound controls, the CreateChildControls method works in either of two modes: binding or nonbinding. When CreateChildControls is working in binding mode, the control tree is created as usual. When it’s working in nonbinding mode, the control calls an overloaded version of CreateChildControls. The method is defined as abstract on the CompositeDataBoundControl and must be overridden in any derived class.

The Overloaded CreateChildControls The overloaded version of CreateChildControls that is defined on the CompositeDataBoundControl class is shown here: protected abstract int CreateChildControls( IEnumerable dataSource, bool dataBinding);

The first parameter is the collection of bound data. The second parameter indicates whether the control is being bound to fresh data (that is, it is working in binding mode)

546

Part II  ASP.NET Pages and Server Controls

or is being refreshed after a postback. The return value indicates the number of items added to the control tree. This value will then be stored in the view state during the call to PerformDataBinding. The following code snippet shows an excerpt from the source code of PerformDataBinding on the CompositeDataBoundControl class: protected internal override void PerformDataBinding(IEnumerable data) { base.PerformDataBinding(data); Controls.Clear(); base.ClearChildViewState(); TrackViewState(); int numOfItems = CreateChildControls(data, true); base.ChildControlsCreated = true; ViewState["_!ItemCount"] = numOfItems; }

Note that PerformDataBinding calls into the new overload of CreateChildControls and passes it true as the second argument, indicating that a binding operation is taking place. This makes sense because executing PerformDataBinding, by definition, means you are ­performing a binding operation. What kind of code should you place in the overloaded CreateChildControls? Basically, you call your own control builder method (typically, CreateControlHierarchy) and return its return value. I’ll return to this point later when discussing the sample BarChart control. ­ ithin The overloaded CreateChildControls method is invoked in binding mode from w PerformDataBinding, and it’s invoked in nonbinding mode from within the other CreateChildControls method: // o is the value read from ViewState int numOfItems = (int) o; object[] items = new object[numOfItems]; CreateChildControls(items, false);

In this case, the bound data passed to the method is an empty array of objects of a well-known size. The goal of this array is to force the control builder method (typically, CreateControlHierarchy) to loop the right number of times and build an outermost container with the right configuration—for example, a table with the right number of rows and columns. As you’ll see in detail for the sample BarChart control, a composite data-bound control neatly separates hierarchy from data. If the Boolean parameter of CreateChildControls is false, no data is added to the hierarchy. How can the control show up as it did the last time? The ASP.NET postback mechanism guarantees that child controls are restored with all their values. In other words, if a composite data-bound control displays bound data through, say, a Label control, after a postback the composite control doesn’t restore its bound data directly. However, it asks any child control, including the Label, to restore itself from the view state. In doing so, the Label restores the bound data from its Text property.

Chapter 12  Custom Controls

547

The bottom line is that the amount of extra data that flows in the view state for a composite control is limited to the number of constituent items, and the control refreshes correctly after a postback. (Of course, child controls put in the view state the usual amount of data.)

The Control Item It should be clear from the previous discussion that the ASP.NET team had excellent ­arguments to dictate that composite data-bound controls get their data exclusively from the data-binding process. This fact eliminates the need of having a kind of Items property on composite data-bound controls that works like the Items property of list controls. This said, feel free to add support for data item objects and collections to your composite controls if you need to. Most composite controls feature a collection of items, but not a collection of data items. Each item represents a control item—that is, a logical building block of the control’s user interface. For a GridView, it is a GridViewRow object that represents a table row. For a sample BarChart control that displays a bar chart, the control item will be a class derived from TableRow that contains all the information needed to handle a single bar. The number of items that composite controls store in the view state is exactly the number of “control” items. Let’s see how these concepts apply to a sample composite data-bound control such as BarChart.

The BarChart Control The BarChart control inherits from CompositeDataBoundControl and defines the properties listed in Table 12-3. TABLE 12-3  BarChart

Properties

Property

Description

DataTextField

Name of the data field to use as the label of each bar.

DataTextFormatString

Format string for the display text.

DataValueField

Name of the data field to use as the value of each bar.

DataValueFormatString

Format string for the value to display on top of each bar.

Items

Collection of BarChart items. Each element represents a bar in the chart. Elements in the Items collection are of type BarChartItem.

Maximum

Gets and sets the maximum value that can be represented in the chart.

SubTitle

Gets and sets the subtitle of the final chart.

Title

Gets and sets the title of the bar chart.

The final markup for the control is a horizontal bar chart such as the one illustrated in Figure12-4.

548

Part II  ASP.NET Pages and Server Controls

FIGURE 12-4  The BarChart control in action.

Each bar is fully represented by an element in the Items collection. In addition, the BarChart control features a few style properties, as Table 12-4 details. TABLE 12-4  BarChart

Style Properties

Property

Description

BarStyle

The style of the whole row that contains the bar

LabelStyle

The style of the label

SubTitleStyle

The style of the subtitle in the control’s header

TitleStyle

The style of the title in the control’s header

ValueStyle

The style of the element displaying the value rendered

The attributes of all style properties are applied in the Render method, as in other ­data-bound controls.

The BarChart Item Object The user interface of the BarChart control is created in the overloaded version of CreateChildControls. protected override int CreateChildControls( IEnumerable dataSource, bool dataBinding) { return CreateControlHierarchy(dataSource, dataBinding); }

Chapter 12  Custom Controls

549

Both input arguments are passed down to an internal CreateControlHierarchy method, which is ultimately responsible for the creation of the bar chart: int CreateControlHierarchy(IEnumerable dataSource, bool dataBinding) { // Get the data to display (either from data source or viewstate) if (dataSource == null) { RenderEmptyControl(); return 0; } // Start building the hierarchy of controls Table t = new Table(); Controls.Add(t); // Add the header row with the caption CreateTitle(t); // Add the subtitle row CreateSubTitle(t); // Add bars int totalItems = CreateAllItems(t, dataSource, dataBinding); return totalItems; }

The control hierarchy is a table with two rows for the title and subtitle and other rows for the bars of the chart. CreateAllItems adds bar chart items and counts their number. This number is then returned and ends up in the view state. int CreateAllItems(Table t, IEnumerable data, bool useDataSource) { // Count how many items we add int itemCount = 0; // Clears the Items collection (creates it, if null) Items.Clear(); // Scroll data items, and create table items foreach (object o in data) { // Create the match item object BarChartItemType itemType = BarChartItemType.Item; BarChartItem item = CreateBarChartItem(t, itemType, o, useDataSource); // Add the newly created object to the Items collection _items.Add(item); // Increase the counter itemCount++; } // Return how many items we have into the viewstate (for postbacks) return itemCount; }

550

Part II  ASP.NET Pages and Server Controls

For each bound item, the method creates a BarChartItem object and adds it to the Items ­collection. We’ll discuss the BarChartItem class in a moment. Note that you use Items.Clear to clear the collection and _items.Add to add a new bar chart item to the collection. The Items property is implemented as follows: private BarChartItemCollection _items; ... public virtual BarChartItemCollection Items { get { if (_items == null) _items = new BarChartItemCollection(); return _items; } }

The property Items uses the _items variable as its storage medium. The first call to Items.Clear ensures that the collection is properly initialized. The second call to the same collection can go through the local variable to save a call to the get accessor of the Items property. The BarChartItem class represents a bar in the chart and is defined as follows: public class BarChartItem : TableRow { private object _dataItem; private BarChartItemType _itemType; public BarChartItem(BarChartItemType itemType) { _itemType = itemType; } public object DataItem { get {return _dataItem;} set {_dataItem = value;} } public BarChartItemType ItemType { get {return _itemType;} } }

The class inherits from TableRow (actually, a bar in the chart is a table row) and defines a couple of properties: DataItem and ItemType. The DataItem property references the data item in the bound data source associated with the corresponding item. For example, if the BarChart is bound to a DataTable, DataItem is bound to the DataRow that corresponds to a given bar.

Chapter 12  Custom Controls

551

ItemType, on the other hand, indicates the type of table row—such as a title, subtitle, or item. The item types are defined through an enumerated type: public enum BarChartItemType { Title, SubTitle, Item }

The Items property groups a bunch of BarChartItem objects in a collection. The collection type is BarChartItemCollection: public class BarChartItemCollection : Collection { }

Because bar chart item objects don’t go to the view state, there’s no need to implement IStateManager and add extra view-state management methods as we did previously for the hyperlink control.

Adding Bound Data With a composite data-bound control, you don’t need to override the PerformDataBinding method. However, you should pay some attention to keeping neatly separated the code that builds the structure of the control and the code that adds data. The CreateBarChartItem method creates a new table row and enriches it with a DataItem property. What’s the content of the row? Looking at Figure 12-3, you can see that each table row has a cell for the label and a cell for the progress bar. BarChartItem CreateBarChartItem(Table t, BarChartItemType itemType, object dataItem, bool useDataSource) { // Create a new row for the outermost table var item = new BarChartItem(itemType); // Create cells for label and value var labelCell = CreateLabelCell(item); var valueCell = CreateValueCell(item); // Add the row to the table t.Rows.Add(item); // Handle the data object binding if (useDataSource) { // Get the data source object item.DataItem = dataItem;

552

Part II  ASP.NET Pages and Server Controls // Data bind the team labels BindLabelCell(labelCell, dataItem); BindValueCell(valueCell, dataItem); } // Return the fully configured row item return item; }

CreateLabelCell and CreateValueCell add cells to the table row. Here is their implementation: private TableCell CreateLabelCell(BarChartItem item) { // Create and add the cell var cell = new TableCell(); item.Cells.Add(cell); return cell; } private TableCell CreateValueCell(BarChartItem item) { // Create and add the cell var cell = new TableCell(); item.Cells.Add(cell); // Add the internal labels var lblGraph = new Label(); var lblText = new Label(); cell.Controls.Add(lblGraph); cell.Controls.Add(new LiteralControl("")); cell.Controls.Add(lblText); return cell; }

The colored bar is represented with a label whose width is a percentage of the maximum value possible on the chart. As you can see in the code of CreateBarChartItem, an if statement separates the creation of required child controls from the data binding. If the method is working in binding mode, the DataItem property is set on each bar chart item and the following two methods are called to add data to the child controls of the BarChart control: private void BindLabelCell(TableCell cell, object dataItem) { if (!String.IsNullOrEmpty(DataTextField)) { string txt = DataBinder.GetPropertyValue( dataItem, DataTextField, DataTextFormatString); cell.Text = txt; } }

Chapter 12  Custom Controls

553

private void BindValueCell(TableCell cell, object dataItem) { // Bind the label for the graph var lblGraph = (Label) cell.Controls[0]; object o = null; if (!String.IsNullOrEmpty(DataValueField)) o = DataBinder.GetPropertyValue(dataItem, DataValueField); else return; var val = Convert.ToSingle(o); float valueToRepresent = 100 * val / Maximum; lblGraph.Width = Unit.Percentage(valueToRepresent); // Bind the label for the text var lblText = (Label) cell.Controls[2]; lblText.Text = DataBinder.GetPropertyValue( dataItem, DataValueField, DataValueFormatString); }

The data-binding process works in a way that is no different from what you saw earlier for other types of data-bound controls. The trickiest part here is the calculation of the width of the label that, when properly styled, generates the horizontal bar. Note  As you can see, no style properties are assigned when the control hierarchy is being built. Just as for other data-bound controls, style attributes are applied later in the control life cycle in the Render method, immediately before generating the control’s markup.

Events of the BarChart Control The BarChart control also features a couple of events: BarChartCreated and BarChartDataBound. It is not coincidental that these two events mimic analogous events on the DataGrid control. Although far simpler, the BarChart is a control designed along the same guidelines that inspired the creation of the DataGrid control: public event EventHandler BarChartItemCreated; public event EventHandler BarChartItemDataBound; protected virtual void OnBarChartCreated(BarChartItemEventArgs e) { if (BarChartItemCreated != null) BarChartItemCreated(this, e); } protected virtual void OnBarChartItemDataBound(BarChartItemEventArgs e) { if (BarChartItemDataBound != null) BarChartItemDataBound(this, e); }

554

Part II  ASP.NET Pages and Server Controls

The BarChartItemCreated event is fired whenever a new table row is added to represent a bar. The BarChartItemDataBound event fires when a newly added table row is bound to its data. The former event fires regardless of the working mode of the control. The latter fires only when the control is created in binding mode. The data carried out with the event is grouped in the BarChartItemEventArgs class: public class BarChartItemEventArgs : EventArgs { private BarChartItem _item; public BarChartItemEventArgs(BarChartItem item) { _item = item; } // Properties public BarChartItem Item { get { return _item; } } }

Both events are fired from within the CreateBarChartItem method: BarChartItem CreateBarChartItem(Table t, BarChartItemType itemType, object dataItem, bool useDataSource) { // Create a new row for the outermost table var item = new BarChartItem(itemType); // Create cells for the label and value var labelCell = CreateLabelCell(item); var valueCell = CreateValueCell(item); var argsCreated = new BarChartItemEventArgs(item); OnBarChartItemCreated(argsCreated); ... if (useDataSource) { ... BarChartItemEventArgs argsData = new BarChartItemEventArgs(item); OnBarChartItemDataBound(argsData); } }

Chapter 12  Custom Controls

555

Using the BarChart Control Let’s see how to consume these events from within a host page. The following markup enables a BarChart control in an ASP.NET page: ...

Nothing in the preceding markup indicates the data source. In the Page_Load event, the control is bound to its data—a collection of custom objects with a couple of properties. One property indicates the amount of sales for an employee in the specified year; the other indicates the name of the employee: protected void Button1_Click(object sender, EventArgs e) { var data = GetDataByYear(1997); BarChart1.Maximum = 150000; BarChart1.Title = "Northwind Sales"; BarChart1.SubTitle = "(Year 1997)"; BarChart1.DataSource = data; BarChart1.DataTextField = "Employee"; BarChart1.DataValueField = "Sales"; BarChart1.DataBind(); }

The bar chart shown in Figure 12-3 is obtained by running the preceding code. The sample page handles the BarChartDataBound event through the following code: void BarChart1_BarChartDataBound(object sender, BarChartItemEventArgs e) { // Get the amount of sales for the current bar var sales = (Decimal) DataBinder.GetPropertyValue( e.Item.DataItem, "sales"); // Add a ToolTip var tip = sales.ToString(); e.Item.Attributes["title"] = tip; // Highlight bar where sales > 50000 if (sales > 50000) e.Item.Cells[1].BackColor = Color.LightGreen; }

The amount of sales for the current employee is retrieved and added to the row as a ToolTip. In addition, if the sales are larger than 50,000, the cell is highlighted by using a different background color. (See Figure 12-5.)

556

Part II  ASP.NET Pages and Server Controls

FIGURE 12-5  Output of a BarChart control modified by page-level event handlers.

Note  All data-bound controls feature a couple of common events: DataBinding and DataBound. The former event fires before the data-binding process begins. The DataBound event, on the other hand, signals that the data-binding phase has terminated.

Adding Template Support The BarChart control accepts two strings to display as the title and subtitle of the chart. Likewise, you can define a similar property for the footer. Title, subtitle, and footer are distinct items in the BarChart control hierarchy. What are you allowed to display in these items? As long as the properties are implemented as plain strings, there’s not much more than static text that can show up through the items. A bit more flexibility can be added with format strings. A format string is a string that ­contains a predefined number of placeholders that the control machinery fills with internal data. For example, the FormatString property of the GaugeBar defaults to {0} / {1}—namely, aformat string with two placeholders. The string is resolved as follows: // First placeholder gets the Value to represent // Second placeholder gets the Maximum value that can be represented String.Format(FormatString, Value, Maximum);

You can enrich the format string with HTML tags to obtain more appealing results but, in the long run, this approach results in unmanageable code. A much better route to deep ­customizations of the user interface of controls is to use templates.

Download from Wow! eBook

Chapter 12  Custom Controls

557

Templates and User Controls In ASP.NET, you can import templates in two ways: through properties of type ITemplate or by dynamically loading user controls. A Web user control is a custom component that can be used wherever a server control is valid. You can import such a user-defined control into the layout of the main control and make the interface more flexible and generic. You put a PlaceHolder control in the location in which you want custom contents to be injected, and then at run time you create an instance of the user control and add it to the Controls ­collection of the placeholder: placeHolder.Controls.Add(Page.LoadControl("usercontrol.ascx"));

The right time to call this code is early in the control life cycle—that is, in an Init event ­handler. Using the LoadControl method, the code of the template is insulated in a separate file. This can be a good thing or a bad thing, depending on the context. If the template you want to implement is complex, keeping it off the main page is positive. Otherwise, it would certainly add a layer of unnecessary complexity. Having the template directly available in the source code of the page makes authoring the page much more intuitive and fast because you don’t have to follow code into a separate file. There’s also a sort of compromise between the two approaches. You can define an ITemplate property in the control and leave the page author free to decide how to set it—with statically defined markup or using the contents of an .ascx file.

Defining a Template Property A template property represents a collection of text and controls that is hosted within a ­container. The container is also responsible for exposing properties that page authors can use to create data-bound expressions. The following code snippet shows how to define a template property named TitleTemplate: [PersistenceMode(PersistenceMode.InnerProperty)] [TemplateContainer(typeof(TitleTemplateContainer))] public ITemplate TitleTemplate { get { return _titleTemplate; } set { _titleTemplate = value; } }

The storage of the template is guaranteed by the private member _titleTemplate, defined as follows: private ITemplate _titleTemplate = null;

A template property is characterized by a couple of attributes: PersistenceMode and TemplateContainer.

558

Part II  ASP.NET Pages and Server Controls

The PersistenceMode attribute indicates how a control property is persisted declaratively in a host page. Table 12-5 lists possible modes of persistence. TABLE 12-5  Persistence

Modes for Control Properties

Property

Description

Attribute

The property persists as an encoded HTML attribute in the final markup.

EncodedInnerDefaultProperty

The property persists as the only inner text of the control. The property value is HTML encoded. Only a string can be given this designation.

InnerDefaultProperty

The property persists in the control as inner text and is the ­element’s default property. Only one property can be ­designated the default property.

InnerProperty

The property persists in the control as a nested tag. This is c­ ommonly used for complex objects with templates and styles.

The most common setting is InnerProperty, which instructs Microsoft Visual Studio to save the contents of the template as a nested tag named after the property: ...

If you choose InnerDefaultProperty, you can have only one nested tag; by opting for InnerProperty, you can have as many nested tags as needed. This is good for rich controls with multiple templates and styles. The TemplateContainer attribute declares the type of the naming container that will contain the template once it is created. As mentioned, a template is hosted by a container which, in turn, is appended to the control’s Controls collection. The TemplateContainer attribute ­references a type that you, as a control developer, are responsible for declaring.

Defining a Template Container A template container type is a simple Web control decorated with the INamingContainer interface. This control can be given any public members you like. However, it will typically expose the host control as a whole and a bunch of quick-access properties. Here’s a sample container type for the TitleTemplate property: public class TitleTemplateContainer : WebControl, INamingContainer { private BarChart _parent; public TitleTemplateContainer(BarChart parent) { _parent = parent;

Chapter 12  Custom Controls

559

} public string Title { get { return _parent.Title; } } public string SubTitle { get { return _parent.SubTitle; } } public BarChart BarChart { get { return _parent; } } }

Once again, be sure to note that there are no constraints or special guidelines to influence the set of members of the class. The class needs to have a reference to the parent control— the BarChart in this case. Normally, you create this class for a particular control (or set of controls) and don’t reuse it beyond that. It is up to you to expose the parent control through a direct property (BarChart in the preceding code) or filter the control’s programming interface with a subset of properties (for example, Title and SubTitle). You can also do both things. The programming interface of the template container class is important because it defines the information that page authors have access to when creating a template for the property. The template container is made accessible through the Container property.

Setting a Template Property You can use any combination of controls and literals to populate a template. To access ­external information, though, you need to use data-bound expressions. Here’s an example:

The code snippet demonstrates a BarChart title that displays an image in addition to the text set through the Title property. Here’s another example: ()

Figure 12-6 shows a templated title item where the originally set Title property is displayed side by side with the current time. The current time is rendered with a smaller font and withinparentheses.

560

Part II  ASP.NET Pages and Server Controls

FIGURE 12-6  A BarChart control with a templated title.

Note that any style attributes set through the TitleStyle property are maintained in the template. The Container keyword references an instance of the template container type. You use the Container keyword to access any control properties exposed through the template container class. Nonstatic information requires a data-bound expression, just like in the templates of ASP.NET built-in controls.

Rendering a Template So far you’ve seen how to define a template property in a server control. But what other changes to the code are required to host a template? In summary, to define a template ­property you need to do the following: ■

Define a property of type ITemplate, and use a private variable as its storage medium.

Decorate the property with the PersistenceMode attribute.

Define a template container class.

Decorate the property with the TemplateContainer attribute.

These steps define only the public interface of the template; more is needed to embed the template in the control’s hierarchy. In particular, you need to tweak the code that creates the portion of the control tree where you want the template to display. For example, the

Chapter 12  Custom Controls

561

TitleTemplate property refers to the title item; so the internal method to modify is CreateTitle. Here’s the updated version: private void CreateTitle(Table t) { // Create the table row var item = new BarChartItem(BarChartItemType.Title); t.Rows.Add(item); // Add the title cell var cell = new TableCell(); cell.ColumnSpan = BarChart.ColumnsCount; item.Cells.Add(cell); // Decide between plain string and template if (TitleTemplate != null) { _titleTemplateContainer = new TitleTemplateContainer(this); TitleTemplate.InstantiateIn(_titleTemplateContainer); cell.Controls.Add(_titleTemplateContainer); } else cell.Text = Title; // Must call DataBind to enable #-expression on templates item.DataBind(); }

You check whether a template for the title item is defined; if it is not, you just set the Text property of the title cell with the contents of the Title property. Otherwise, you get an instance of the template container type and use it as the input argument of the InstantiateIn method—the only method on the ITemplate interface. When done, you add the template container to the control hierarchy—in this case, to the Controls collection of the title cell. A fundamental further step is required to enable the template to successfully process databound expressions. You must place a call to DataBind on the title item. Data-bound expressions, in fact, are evaluated only after a call to DataBind is made that involves the parent control that hosts the expression. Without the DataBind call, templates will work correctly but won’t display any expression.

Summary ASP.NET provides a wealth of server controls from which you can likely choose exactly the control you are looking for. If this is not the case, and the control simply doesn’t exist, you can create your own control from the ground up or by extending an existing control, and

562

Part II  ASP.NET Pages and Server Controls

obtain incredibly powerful results. Writing a control is a matter of defining an appropriate object model and providing an effective rendering algorithm. Aside from these two points, other equally important aspects of control development are containment, naming, and ­integration with the engine that supplies state management. In this chapter, we’ve built a few ASP.NET controls with different capabilities, from simple components capable of rendering an HTML tree to controls with rich support for data ­binding and templates.

Programming Microsoft® ASP.NET 4

Part III

Design of the Application In this part: Chapter 13: Principles of Software Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565 Chapter 14: Layers of an Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 593 Chapter 15: The Model-View-Presenter Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . 615

563

Chapter 13

Principles of Software Design There is nothing like returning to a place that remains unchanged to find the ways in which you yourself have altered. —Nelson Mandela Maintaining a software application is probably harder, and definitely more bothersome, than writing it from the ground up. A large part of a developer’s career is spent performing maintenance tasks on existing code rather than planning and writing new software. Armed with this knowledge, I usually advise developers and architects I work with to always give top ­priority to one specific attribute of the numerous possible attributes of a software system— that attribute is maintainability. The biggest challenge that many software architects face today is how to design and ­implement an application that can meet all of the requirements for version 1 plus other requirements that show up afterward. Maintainability has been one of the fundamental ­attributes of software design since the first draft of the ISO/IEC 9126 paper, back in 1991. (The paper provides a formal description of software quality and breaks it down into a set of characteristics and subcharacteristics, one of which is maintainability. A PDF version of the paper can be obtained at http://www.iso.org.) The mother of all challenges for today’s software architects is focusing on current requested features while designing the system in a way that keeps it flexible enough to support future changes and additions. In this regard, maintainability is king and you should favor it over ­everything else. Maintainability represents the best compromise you can get; with a high level of maintainability in your code, you can achieve anything else—including scalability, performance, and security. That sounds very nice, but how do you write software that is easy to maintain? There are a few basic principles of software design that if properly, and extensively, ­applied will transform a piece of code into a manageable and flexible piece of code. Doing this ­probably won’t be enough to save your team from having to fix a few bugs once the application has been deployed to production, but at least it will keep regression at a reasonable level. More importantly, these principles make it less likely that you’ll have to fix a bug with a workaround rather than with a definitive update. Let’s start by reviewing some of the most alarming symptoms that generally signal that c­ ode-related suffering is on the horizon.

565

566

Part III  Design of the Application

The Big Ball of Mud The expression “big ball of mud” (or BBM) refers to a software system that shows no clear sign of thoughtful design and results in a jungle of spaghetti code, duplicated data and ­behavior, piecemeal growth, and frequent expedient repair. Coined by Brian Foote and Joseph Yooder, the term indicates a clear anti-pattern for developers and architects. You can read the original paper that formalized BBM at http://www.laputan.org/mud.

Reasons for the Mud A BBM system usually results from the combined effect of a few causes: the limited skills of the team, frequent changing of requirements, and a high rate of turnover among team members. Often when you face a BBM the best thing you could ideally do is just rewrite the application based on a new set of reviewed requirements. But, honestly, I’m not sure I’ve ever seen this happen even once. Most of the time, a complete rewrite is simply not a feasible option. If you have no way out other than facing the BBM, a reasonable but still painful approach consists of stopping any new development and starting to arrange a bunch of significant tests. What types of tests? Well, in a BBM scenario you can hardly expect to write plain ­isolated unit tests. You wouldn’t be immersed in a big ball of mud if you could write plain unit tests! More likely, you write some sort of integration tests that involve different layers (when not tiers) and that are not especially quick to run, but at least they provide you with an automated tool to measure any regression as you proceed with refactoring the existing code. To try to keep your head above mud, you can only patiently refactor the code and introduce a better architecture, being very much aware that you’re operating in a fragile environment and any approach must be as delicate as possible. Obviously, this process won’t be completed quickly. It might even take years if the project is very large. On the other hand, the alternative is to just kill the project. Let’s find out more about the factors that can lead to a big ball of mud.

Limited Skills Architecting a system requires some fundamental skills, maybe a bit of talent, and definitely hands-on experience. Knowledge of best and worst practices also helps a lot. In a word, ­education is key. However, the development team is not usually given enough power to cause huge damage on their own. Management and customers are usually responsible as well, maybe even more. When management is too demanding, and when customers don’t really know what they want, the information being conveyed to developers won’t be clear and unambiguous. This leads to arbitrary choices, compromises, and workarounds at all levels that just make it ­impossible to come up with a defined architecture.

Chapter 13  Principles of Software Design

567

Requirements Churn The term requirements churn refers to making numerous changes to the initially agreedupon requirements. Incorporating a new requirement into an existing system, which was ­architected without that particular requirement, can be problematic. The cost of such a change depends on the size of the change, the dependencies in the code, and whether or not the change affects the structure of the system. Adding a single change, even a significant one, is not enough to jeopardize the entire ­architecture. But when individual significant changes are frequent, over time you transform a system devised in a given way into something that probably requires a different architecture. If you keep adding new requirements individually without reconsidering the system as a whole, you create the ideal conditions for a big ball of mud.

Members Turnover When technical documentation is lacking or insufficient, the worst thing that can happen is that the rationale for making particular decisions is lost forever. As long as the application is deployed, works, and doesn’t require proactive or passive maintenance (although I still haven’t found such an application), you’re fine. But what if this is not the case? If the rationale for design and architectural decisions is not entirely evident, how can you expect new members of the team to take over the maintenance or additional development for the system? At some point, in their efforts to understand the system, these new members must be informed of the rationale for various decisions. If they can’t figure out the real ­rationale, inevitably they will make further changes to the system based on their assumptions. Over time, this leads to a progressive deterioration of the system that is what we’ve been referring to as the big ball of mud.

Alarming Symptoms The big ball of mud doesn’t get formed overnight. How can you detect that your system is deteriorating? There a few hard-to-miss symptoms you don’t want to ignore. They are very serious. Let’s find out what they are.

Make a Change Here, Break the Code There Can you bend a piece of wood? And what do you risk if you insist on trying to do that? A piece of wood is typically stiff and rigid and characterized by some resistance to ­deformation. When enough force is applied, the deformation becomes permanent. What about rigid software?

568

Part III  Design of the Application

Rigid software is characterized by some level of resistance to changes. Resistance is measured in terms of regression. You make a change in one module, but the effects of your change cascade down the list of dependent modules. As a result, it’s really hard to predict how large the impact of a change—any change, even the simplest—will actually be. If you pummel a glass or any other fragile material, you succeed only in breaking it down into several pieces. Likewise, when you enter a change in software and cause it to misbehave in some places, that software is definitely fragile. Just as fragility and rigidity go hand in hand in real life, they also do so in software. When a change in a software module breaks (many) other modules because of (hidden) dependencies, you have a clear symptom of a bad design, and you need to remedy that situation as soon as possible.

Easier to Use Than to Reuse Imagine you have a piece of software that works in one project; you would like to reuse it in another project. However, copying the class or linking the assembly in the new project just doesn’t work. Why is this so? If the same code doesn’t work when it’s moved to another project, it’s because of ­dependencies. However, the real problem isn’t just dependencies; it’s the number and depth of dependencies. The risk is that to reuse a piece of functionality in another project, you’ll have to import a much larger set of functions. In such cases, no reuse is ever attempted and code is rewritten from scratch. (Which, among other things, increases duplication.) This also is not a good sign either for your design. This negative aspect of a design is often referred to as immobility.

Easier to Work Around Than to Fix When applying a change to a software module, it is not unusual that you find two or more ways to do it. Most of the time, one way of doing things is nifty, elegant, coherent with the design, but terribly laborious to implement because of certain constraints. The other way is, instead, much smoother and quicker to code, but it is sort of a hack. What should you do? Actually, you can solve the problem either way, depending on the given deadlines and your manager’s directives about it.

Chapter 13  Principles of Software Design

569

In summary, it’s not an ideal situation because a workaround might be much easier to apply than the right solution. And that’s not a great statement about your overall design either. It simply means that too many unneeded dependencies exist between classes and that your classes do not form a particularly cohesive mass of code. This negative aspect of a design is often referred to as viscosity. So what should you do to avoid these symptoms showing up in your code and creating a big ball of mud?

Universal Software Principles In my opinion, maintainability is the fundamental attribute of modern software. The ­importance of maintainability spans the technology spectrum and applies to the Web as well as desktop applications. A few universally valid design principles help significantly to produce code that is easier to maintain and evolve. It is curious to note that they are all principles devised and formulated a few decades ago. Apparently, for quite some time we’ve had the tools to build and manage complex software but real applications were just lacking the complexity to bring them to the forefront as design best practices. This is also my interpretation of the advent of the Rapid Application Development (RAD) paradigm a decade ago, which complemented (and in some cases superseded) object-oriented programming (OOP). Today, the situation is different. With large companies now taking full advantage of Internet, cloud, and mobile computing, developers and architects are swamped with an incredible amount of complexity to deal with. That’s why RAD is no longer sufficient in many scenarios. On the other hand, not everybody is skilled enough to use OOP. It’s about time we all rediscover some fundamentals of software programming—regardless of the type of application we’re building. Summarizing, I would boil software principles down to two principles: the High Cohesion and Low Coupling principle and the Separation of Concerns principle.

Cohesion and Coupling Cohesion and coupling go hand in hand even though they refer to orthogonal aspects ofyour code. Cohesion leads you toward simple components made of logically related functions—kind of atomic components. Coupling indicates the surface area between two interfacing components: the wider the area is, the deeper the dependency is between the components. The magic is all in finding the right balance between cohesion and coupling while trying to maximize both.

570

Part III  Design of the Application

Cohesion at a Glance Cohesion indicates that a given software module—a class, if we assume the object-oriented paradigm—features a set of responsibilities that are strongly related. Put another way, cohesion measures the distance between the logic expressed by the various methods on a class. If you look for a moment at the definition of cohesion in another field—chemistry—you can get a clearer picture of software cohesion. In chemistry, cohesion is a physical property of a substance that indicates the attraction existing between like-molecules within a body. Cohesion measurement ranges from low to high, with the highest possible cohesion being preferable. Highly cohesive modules favor maintenance and reusability because they tend to have no dependencies. Low cohesion, on the other hand, makes it much harder to understand the purpose of a class, and it creates a natural habitat for rigidity and fragility in your software. Low-cohesive modules also propagate dependencies, thus contributing to the ­immobility and viscosity of the design. Decreasing cohesion leads to creating classes where methods have very little in common and refer to distinct and unrelated activities. Translated into a practical guideline, the principle of cohesion recommends creating extremely specialized classes with few methods that refer to logically related operations. If the “logical” distance between methods needs to grow, well, you just create a new class.

Coupling at a Glance Coupling measures the level of dependency existing between two software classes. An­excellent description of coupling comes from the Cunningham wiki at http://c2.com/cgi/wiki?CouplingAndCohesion. Two classes, A and B, are coupled when it turns out that you have to make changes to B every time you make any change to A. In other words, B is not directly and logically involved in the change being made to module A. However, because of the underlying dependency B is forced to change; otherwise, the code won’t compile any longer. Coupling measurement ranges from low to high, with the lowest possible coupling being preferable. Low coupling doesn’t mean that your modules have to be completely isolated from one another. They are definitely allowed to communicate, but they should do that through a set of well-defined and stable interfaces. Each class should be able to work without intimate knowledge of the internal implementation of another class. You don’t want to fight coupling between components; you just want to keep it under control. A fully disconnected system is sort of nonsense today. Conversely, high coupling hinders testing and reusing and makes understanding the system nontrivial. It is also one of the primary causes of a rigid and fragile design.

Chapter 13  Principles of Software Design

571

Low coupling and high cohesion are strongly correlated. A system designed to achieve low coupling and high cohesion generally meets the requirements of high readability, ­maintainability, easy testing, and good reuse.

Separation of Concerns Functional to achieving high cohesion and low coupling is the separation of concerns (SoC) principle, introduced by Edsger W. Dijkstra in his paper “On the role of scientific thought” which dates back to 1974. If you’re interested, you can download the full paper from http://www.cs.utexas.edu/users/EWD/ewd04xx/EWD447.PDF.

Identifying the Concerns SoC is all about breaking the system into distinct and possibly non-overlapping features. Each feature you want in the system represents a concern and an aspect of the system. Terms like feature, concern, and aspect are generally considered synonyms. Concerns are mapped to software modules and, to the extent that it is possible, there’s no duplication of functionalities. SoC suggests that you focus your attention on one particular concern at a time. It doesn’t mean, of course, that you ignore all other concerns of the system. More simply, after you’ve assigned a concern to a software module, you focus on building that module. From the ­perspective of that module, any other concerns are irrelevant. Note  If you go through the original text written by Dijkstra back in 1974, you note that he uses the expression “separation of concerns” to indicate the general principle, but he switches to the word “aspect” to indicate individual concerns that relate to a software system. For quite a few years, the word “aspect” didn’t mean anything special to software engineers. Things changed in the late 1990s when aspect-oriented programming (AOP) came into the industry. Ignored for many years, AOP is being rediscovered today mostly thanks to some ad hoc frameworks such as Spring .NET and other Inversion of Control (IoC) frameworks.

Modularity SoC is concretely achieved through modular code and making large use of information hiding. Modular programming encourages the use of separate modules for each significant feature. Modules are given their own public interface to communicate with other modules and can contain internal chunks of information for private use. Only members in the public interface are visible to other modules. Internal data is either not exposed or it is encapsulated and exposed in a filtered manner. The implementation of the

572

Part III  Design of the Application

interface contains the behavior of the module, whose details are not known or accessible to other modules.

Information Hiding Information hiding (IH) is a general design principle that refers to hiding behind a stable interface some implementation details of a software module that are subject to change. In this way, connected modules continue to see the same fixed interface and are unaffected by changes. A typical application of the information hiding principle is the implementation of properties in Microsoft C# or Visual Basic .NET classes. The property name represents the stable interface through which callers refer to an internal value. The class can obtain the value in various ways (for example, from a private field, from a control property, from a cache, and from the view state in ASP.NET) and can even change this implementation detail without breaking ­external code. // Software module where information hiding is applied public class Customer { // Implementation detail being hidden private string _name; // Public and stable interface public string CustomerName { // Implementation detail being hidden get {return _name;} } }

Information hiding is often referred to as encapsulation. I like to distinguish between the principle and its practical applications. In the realm of object-oriented programming, ­encapsulation is definitely an application of IH. In general, though, the principle of SoC manifests itself in different ways in different ­programming paradigms, and so it is also for modularity and information hiding. Note  Separation of concerns is the theoretical pillar of multitiered (or just ­multilayered)

s­ ystems. When you try to apply SoC to classes, you run across just onefundamental concept that you can then find formulated in a number of ­different ways. You essentially achieve separation of concerns by isolating dependencies and abstracting them to interfaces. This is called low ­coupling, interface-based programming or, perhaps in a more formal way, the Dependency Inversion principle that I’ll cover in just a moment. Different names—each appropriate in its own context—but just one key idea.

Chapter 13  Principles of Software Design

573

SOLID Principles Recently, a particular acronym is gaining a lot of popularity—SOLID. The acronym results from the initials of five design principles formulated by Robert Martin. The S stands for Single Responsibility; the O is for the Open/Closed principle; the L is for Liskov’s principle; the I is for Interface Segregation; and finally, the D is for Dependency Inversion. Taken individually, these principles are nothing new. Any experienced developer and ­architect should be at least vaguely familiar with the idea behind each principle, either ­because it is part of the developer’s personal education or because of the experience the ­developer has gained the field. SOLID principles are just a further specialization and refinement of universal and ­object-oriented design principles. Their definition is relatively simple; yet the adoption of these principles can be fairly complex. Note  As you’ll see in a moment, not all principles should be taken literally. Some of them are just driving vectors that attempt to show you the right direction, but without being dogmatic. You can download the original papers describing the SOLID principles and their canonical ­examples from http://www.objectmentor.com.

The Single Responsibility Principle The Single Responsibility Principle (SRP) is a formal way of rephrasing the idea behind ­cohesion. The principle states that there should never be more than one reason for a class to change. Applied to the design of the class, it means each class you add to your solution should focus on just one primary task. The responsibilities of a class that does just one thing are much smaller than the responsibilities of a class that does multiple things. A responsibility is defined as a “reason to change”; more specifically, it’s a reason for you—the developer—to put your hands on the class’s source code and edit it. The purposes of SRP are to simplify maintenance and improve readability. Keeping the code simple at the root—by taking out additional features—is an effective way to smooth maintenance chores. At the end of the day, SRP is a form of defensive programming.

574

Part III  Design of the Application

SRP Canonical Example Like any other SOLID principle, SRP has its own canonical example aimed at illustrating the point of the principle. Here’s a piece of code that contains the gist of SRP: public class Modem { public void Dial(String number); public void Hangup(); public void Send(Char c); public Char Receive(); }

How many sets of responsibilities do you see in the Modem class? Dial and Hangup ­represent the connection management functionality, whereas the Send and Receive pair of ­methods represent communication functionalities. Should these two sets of responsibilities be ­separated? As usual, it depends.

SRP Real-World Considerations An effective implementation of SRP passes through the identification of specific ­responsibilities in the programming interface of the class. One task is identifying ­responsibilities; it is quite a different task to actually split the class into two other classes, each taking care of a specific responsibility. In general, you should always consider splitting responsibilities in distinct classes when the two sets of functions have little in common. If this happens, the two resulting classes will likely change for different reasons and will likely be called from different parts of the application. In addition, different parts of the application will change for different reasons. Another scenario that suggests the need to split a class into two (or more) is when the two sets of functions you identified in the original interface are large enough to require sufficiently complex logic of their own. In this case, you simply lower the level of granularity of your design a bit. However, the size is not always, and not necessarily, a good parameter to use to make a decision about SRP. A function can be complex and large enough to justify a breakup; however, if it’s not likely to change over time, it might not require a distinct class. Scenarios like this, however, represent a tough call, with no uniform guidance to help you determine what to do. Finally, you should pay a lot of attention not to split the original class into small pieces. The risk of taking SRP to the limit is falling into the Overdesign anti-pattern that occurs when a system is excessively layered and each layer ends up being a thin wrapper around an if ­statement. As mentioned, SRP is a driving vector rather than a dogma. You should always keep it in your mind but never apply it blindly and blissfully.

Chapter 13  Principles of Software Design

575

The Open/Closed Principle We owe the Open/Closed Principle (OCP) to Bertrand Meyer. The principle addresses the need of creating software entities (whether classes, modules, or functions) that can happily survive changes. The purpose of the principle is to provide guidance on how to write components that can be extended without actually touching the source code. Sounds like quite an ambitious plan, doesn’t it? Let’s get at the formulation: A class should be open for extension but closed for modification. Honestly, I find the formulation a bit idealistic and bordering on magic. However, the ­principle is in itself quite concrete. It essentially says that the source code of the class must remain intact, but the compiled code must be able to work with types it doesn’t know ­directly. This can be achieved in just one way: abstracting aspects of the class that can lead to changes over time. To abstract these aspects, you can either use interfaces and code injection or generics.

OCP Canonical Example Low coupling between interfacing modules is beneficial because it instructs the caller to work with an abstraction of its counterpart rather than with a concrete implementation. In their masterpiece Design Patterns: Elements of Reusable Object-Oriented Software (AddisonWesley, 1994), the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) formulate a basic principle of object-oriented design as “Program to an interface, not to an implementation.” The gist of OCP is all here. Code that is based on an abstraction can work with a concrete object it gets passed as long as this object is compatible with known abstraction. Behind the term “abstraction,” you can find at least two concrete syntax elements: a base class or an ­interface. Here’s the canonical example of a class—the Renderer class—that fully complies with OCP: public abstract class Shape { public abstract void Render(); } public class Renderer { public void Draw(IList shapes) { foreach(Shape s in shapes) s.Render(); } }

576

Part III  Design of the Application

The abstraction is represented by the base class Shape. The Renderer class is closed for ­modification but still open for extension because it can deal with any class that exposes the Shape abstraction—for example, any class that derives from Shape. Analogously, you can have a Renderer class that receives its working type as a generic argument.

OCP Real-World Considerations OCP should not be taken literally. Like SRP, it works much better if used as a driving vector. Frankly, no significant class can be 100 percent closed for modification. By “significant class” here, I mean most real-world classes or, better, all classes except those you write just for demo purposes! If closure of a given class can’t realistically be complete, it should then be strategic. It is a ­precise architect’s responsibility to identify the most likely changes in the class body and close the class design against them. In other words, designing a class that can support all possible future extensions is a utopian pursuit. However, identifying just one specific ­abstraction or two and making the class work against them is, most of the time, an excellent compromise. Aiming for fully pluggable classes is over designing; even a partial application of OCP is beneficial and rewarding. If you ignore OCP, at some point you might catch yourself downcasting to a specific s­ ubclass to compile the code and avoid run-time exceptions. If this happens, it’s a clear sign that something is wrong in the design.

Liskov’s Substitution Principle Of the five SOLID principles, Liskov’s principle is probably the only one that should be taken literally, because it serves you a detailed list of dos and don’ts and it isn’t limit to being a generic guidance on design. The formulation of the principle couldn’t be simpler. To some extent, it also seems a bit obvious: Subclasses should always be substitutable for their base classes. When a new class is derived from an existing one, it should always be possible to use the derived class in any place where the parent class is accepted. Wait a moment! Isn’t this ­something you get out of the box with any object-oriented language? Well, not exactly! What you really get from OOP is just the mere promise that derived classes can be used wherever their base class is accepted. However, OOP still lets you write hierarchies of classes where this basic requirement isn’t met; hence, the principle.

Chapter 13  Principles of Software Design

577

Substitution Principle Canonical Example Suppose you have a Rectangle class and a method that works with that. The method just receives a parameter of type Rectangle and, of course, it takes advantage of the logical ­contract this class exposes. For example, the Rectangle class exposes a Width and Height pair of properties that can be independently set. Suppose that after doing this, you then need to introduce a Square object. How would you do that? Logically speaking, you see the Square entity as a special case of the Rectangle ­entity. Therefore, the natural step is deriving Square from Rectangle and overriding Width and Height so that their values are always in sync. If you do this, you potentially break the original code written against the contract of the Rectangle class. The violation of the principle here doesn’t necessarily result in a run-time exception or a compile error. Your code might still work just fine, despite the Liskov violation. However, your code is inherently fragile because there’s the possibility of introducing bugs during maintenance. The violation has to be considered in the mathematical sense—you can find a counterexample that shows you can’t use a Square where a Rectangle is expected. Here’s a code snippet that illustrates this point: public class Rectangle { public virtual Int32 Width { get; set; } public virtual Int32 Height { get; set; } } public class Square : Rectangle { public override Int32 Width { get {return base.Width; } set {base.Width = value; base.Height = value; } } public override Int32 Height { get {return base.Height; } set {base.Height = value; base.Width = value; } } }

Here’s some client code that consumes the Rectangle class: public void Process(Rectangle rect) { rect.Width = 100; rect.Height = 2* rect.Width; Debug.Assert(rect.Height == 2*rect.Width); }

578

Part III  Design of the Application

This code works fine if a real Rectangle is passed, but it violates the assertion if a Square is passed. The easiest way to fix it—the workaround that increases viscosity—is simply the following: public void Process(Rectangle rect) { if (rect is Rectangle) { rect.Width = 100; rect.Height = 2* rect.Width; Debug.Assert(rect.Height == 2*rect.Width); } else { ... } }

The real problem you have here, instead, is an incorrect definition of inheritance rules. Square can’t be derived from Rectangle because it is not expected to be able to do at least all the things that the base class does.

Substitution Principle Real-World Considerations Liskov’s principle can be difficult to grasp for many developers. An easier way to explain it is the following: each derived class should expect no more than the parent and provide no less than the parent. This means, for example, that you break the principle if a virtual member ends up using a private member of the class. Likewise, you break the principle if a derived class adds more preconditions to a virtual method. Liskov’s principle isn’t meant to portray inheritance—a pillar of OOP—in a bad light. Quite the reverse, it calls your attention to a safe use of virtual members. If you derive and just add new features, you’re absolutely safe. If you don’t have virtual members, you’re absolutely safe. If you have virtuals and actually override them in derived class, you should pay ­additional attention. Note  In .NET 4, you have the Code Contracts API to express preconditions, postconditions, and invariants around your classes. A precondition is simply an IF in a method that executes at the very beginning of the code. If you use this API to express preconditions for the methods of a class, and happen to add preconditions to an overridden method of a class, you get a warning (not an error) from the C# compiler.

Chapter 13  Principles of Software Design

579

The Interface Segregation Principle There’s an aspect of coding that I find particularly annoying—being forced to write code that I don’t really need. You might say that you should not write any code that you don’t need. There are situations, however, in which this is necessary. When? For sure, when the code ­ignores the Interface Segregation principle. The principle is so simple that it seems like merely common sense. It says that client ­components should not be forced to depend upon interfaces that they do not use. More precisely, components should not be forced to implement members of interfaces (or base classes) that they don’t plan to use. There’s nothing bad in a client that provides a void implementation of a particular i­nterface method or that just throws if invoked. Sometimes this happens simply because the c­ lient deliberately intends to provide a partial implementation; sometimes, however, it h ­ appens because the interface is poorly designed. Fat interfaces are a bad thing. The Single Responsibility principle should hold true for interfaces also.

Interface Segregation Canonical Example The classic scenario to examine to start thinking about interface segregation is the d ­ efinition of a door. If you’re asked to simply define a door, you would probably come up with an interface with just a couple of Lock and Unlock methods and perhaps a Boolean property IsDoorOpen. However, if you know that you also need to deal with timed doors that sound an alarm if left open for too long, you might arrange something like this: public interface IDoor { void Lock(); void Unlock(); Boolean IsDoorOpen { get; }

Int32 OpenTimeout { get; set; } event EventHandler DoorOpenForTooLong; }

The apparent plus of this design is that it gives you just one interface that can serve up both scenarios: timed and regular doors. This is an apparent benefit because it forces you to have two extra members on any class that implements the interface: the event DoorOpenForTooLong and the timeout property. Why on earth should you have these ­members where they aren’t needed?

580

Part III  Design of the Application

Interface Segregation Real-World Considerations Note also that code that is not strictly needed can’t be ignored after it is compiled. In other words, any code that gets compiled does count, regardless of whether or not it was necessary when you designed your classes. Because it’s there, it can influence the application, it must be tested, it must be debugged, and it must be maintained. Quite paradoxically, b ­ ecause it’s there it can even represent a constraint and limit further necessary improvements! The natural solution is to use slimmer interfaces. The IDoor interface should be split into two smaller and much more specific interfaces—say IDoor and ITimedDoor: public interface IDoor { void Lock(); void Unlock(); Boolean IsDoorOpen { get; } } public interface ITimedDoor { Int32 OpenTimeout { get; set; } event EventHandler DoorOpenForTooLong; }

Now if you need to create RegularDoor and TimedDoor classes, you proceed as shown here: public class RegularDoor : IDoor { ... } public class TimedDoor : IDoor, ITimedDoor { ... }

Unlike classes, interfaces can be easily summed up; so there’s really no reason to have fat ­interfaces any more.

The Dependency Inversion Principle I consider Dependency Inversion to be the most important of the five SOLID principles. You don’t need it in every class and method that you write, but if you miss it where you need it, well, you’re in serious trouble. Take a look at Figure 13-1.

Chapter 13  Principles of Software Design

581

Replace me Change me FIGURE 13-1  Hard to change blocks in software architecture.

I’m not sure the idea behind this figure is completely original, and I don’t even know if there’s anybody I should thank for that. For sure, I remember having seen it somewhere, likely at some software conference somewhere in the world. Then I simply revised the idea and made it mine. So you have built the architecture of your system by simply composing parts. What if, at some point during development, you need to replace or significantly modify one of the building blocks? As the graphics attempts to show, it might be hard to change some of the building blocks that form the skeleton of the system. Dependency inversion is simply aimed at making this difficult task simpler and less expensive. The principle says that every high-level module should always depend on abstractions of lower level modules. This is just a reformulation of the concept of interface-based programming.

Dependency Inversion Canonical Example The idea behind Dependency Inversion doesn’t need a complex scenario to be effectively illustrated. Just imagine a method that reads bytes from a stream and writes them out to some buffer: void Copy() { Byte byte; while(byte = ReadFromStream()) WriteToBuffer(byte); }

582

Part III  Design of the Application

The pseudocode just shown depends on two lower level modules: the reader and writer. According to the principle, we should then abstract the dependencies to interfaces—say, IReader and IWriter. The method can be rewritten as follows: void Copy() { Byte byte; IReader reader; IWriter writer; while(byte = reader.Read()) writer.Write(byte); }

Who really does provide instances of the reader and writer modules? That’s the principle, or the general law; to actually solve the issue, you need some further specification. In other words, you need a pattern. The first pattern used to apply the Dependency Inversion principle is Service Locator pattern, which can be summarized as follows: void Copy() { Byte byte; var reader = ServiceLocator.GetService(); var writer = ServiceLocator.GetService(); while(byte = reader.Read()) writer.Write(byte); }

You use a centralized component that locates and returns an instance to use whenever the specified abstraction is requested. The service locator operates while embedded in the code that it serves. You can say that it looks for services, but it is not a service itself. Most of the time, you use this pattern when you have some legacy code that you need to make easier to extend that is hard to redesign in a different way—for example, in a way that uses ­dependency injection. A better alternative is to use Dependency Injection (or inversion of control). The resulting code looks like this: void Copy(IReader reader, IWriter writer) { Byte byte; while(byte = reader.Read()) writer.Write(byte); }

Chapter 13  Principles of Software Design

583

The list of dependencies is now explicit from the signature of the method and doesn’t require you to go down the line to pinpoint calls to a service locator component. In addition, the burden of creating instances for each spot dependency is moved elsewhere.

Dependency Inversion Real-World Considerations Dependency inversion is about layers, and layers don’t reduce the total amount of code (quite the reverse, I’d say). Layers, however, contribute to readability and, subsequently, to maintainability and testability. In light of this, the motivation for special frameworks such as Inversion of Control (IoC) frameworks is right in front of your eyes. You don’t want to write the factory yourself for all instances that populate the graph of ­dependencies for pieces of your application. The task is repetitive and error prone. Although it might be a boring task for developers, it’s just plain business as usual for certain tools. IoC frameworks are just a way for you to be more productive when it comes to implementing the Dependency Inversion principle. These days, we tend to oversimplify things by using the name of the most popular pattern— Dependency Injection—to refer to the universal principle. Even more often, we just use the name of a family of tools (IoC) to refer to the principle. What really matters is that you give the principle its due consideration. The details of how you actually implement it are up to you and your team. You don’t need an IoC tool to implement good dependency injection; you can get it through overloaded constructors (also known as the poor man’s dependency injection) or even by writing your own homemade IoC framework. In the simplest case, it’s a thin layer of code around some .NET reflection primitives. You can ignore the Dependency Inversion principle, but you do so at your own peril. Note  Dependency injection is also fundamental from a testability standpoint because it makes it natural to inject dependencies in classes as you test them.

Tools for Dependency Injection The list of tools for dependency injection is quite long in the .NET space nowadays. Most of these tools provide the same set of core functionalities and are, to a large extent, equivalent. Choosing one is often a matter of preference, skill level, and perhaps your comfort with the exposed API. There are some who prefer simplicity and speed and opt for Autofac or Ninject. Others would opt for rich functionality and go for Spring.NET or Castle Windsor. Another

584

Part III  Design of the Application

group would pick up the entire Microsoft stack and then use Unity. Table 13-1 lists the most popular options today, with the URL from where you can get further information. Table 13-1  Some Popular IoC Frameworks Framework

URL

Autofac

http://code.google.com/p/autofac

Castle Windsor

http://www.castleproject.org/container/index.html

Ninject

http://www.ninject.org

Spring.NET

http://www.springframework.net

StructureMap

http://structuremap.sourceforge.net/Default.htm

Unity

http://codeplex.com/unity

All IoC frameworks are built around a container object that, bound to some configuration information, resolves dependencies. The caller code instantiates the container and passes the desired interface as an argument. In response, the IoC framework returns a concrete object that implements that interface. Let’s top off the chapter by taking a quick tour of two frameworks in the Microsoft stack that, although they have different characteristics and goals, can both be employed to implement the Dependency Inversion principle.

Managed Extensibility Framework at a Glance Introduced with the Microsoft .NET Framework 4, the Managed Extensibility Framework (MEF) attempts to give a consistent answer to the loud demand for tools for building ­plugin-based applications. A plugin-based application is an application that can rely on a number of optional ­components that are discovered and composed together at run time. Microsoft Visual Studio is an excellent example of this application; a simpler but still valid example is Windows Explorer, whose menus can be extended by registering shell extensions. A plugin-based ­application provides a number of extensibility points and builds its core user interface and logic using abstractions for those extensibility points. Some run-time code then attempts to resolve all pending dependencies in a quick and direct way.

MEF vs. IoC MEF does some work that any IoC does. Like an IoC framework, MEF is able to spot ­dependencies and resolve them, returning a usable graph of objects to the caller application. In raw terms of functionality, MEF is not as powerful as most IoC tools. MEF has limited support for managing the object’s lifetime and doesn’t currently support any form of aspect orientation. MEF also requires that classes it deals with be decorated with ad hoc attributes.

Chapter 13  Principles of Software Design

585

You can’t just take a plain old CLR class and use it with MEF. On the other hand, MEF swallows exceptions when some particular things go wrong during the composition process. In summary, MEF is an IoC framework optimized for the specific task of discovering and ­loading optional and compatible components on the fly.

Should You Choose MEF or an IoC? MEF is already in the .NET Framework 4; any IoC tools of choice is a separate set of ­assemblies and adds dependencies to the project. MEF is available only for .NET 4, whereas most IoC frameworks are available for most .NET platforms. This said, however, I’d like to ­remark that MEF doesn’t bring new significant capabilities to the table that you couldn’t code yourself or achieve through an IoC. MEF, however, makes writing plugin-based applications really fast and simpler than ever before. If MEF serves all your IoC needs, choose MEF and code your way within the .NET Framework 4. If you’re happy with the IoC you’re using today, perhaps there’s no need for you to change it. In this regard, MEF won’t give you an ounce more than your favorite IoC. The real issue is when you want to use MEF because of plugins but still need to mix it with an IoC because MEF doesn’t offer the advanced services of rich IoC—for example, call interception. In this case, either you drop MEF in favor of IoC or configure MEF to accept instances created by the IoC of choice.

MEF in Action An MEF application is based on components known as composable parts. Each part can ­contain some members decorated as imports. An import is a class member with the Import attribute, and it indicates a member that will be resolved and instantiated by the MEF ­runtime. In a MEF application, you also find classes decorated as exports. An export is a class decorated with the Export attribute. An instance of an export class can be used to perform an import as long as the import and export match. What does determine a valid match? An import/export match is based on a contract. A contract here has little to do with service or interface contracts. An MEF contract is a collection of meta information that both imports and exports contain. In most cases, it is a simple string. In other cases, it contains type ­information or both unique strings and type information. The list of exports is determined by catalogs. A catalog is a provider that returns the list of available exports to be matched to the imports of the object being resolved. Finally, the composition process is the process in which all imports (that is, dependencies) are resolved.

586

Part III  Design of the Application

Here’s a brief code example to illustrate: public class PasswordCreator { private CompositionContainer _container; public ProgramBody() { InitializeMef(); } private void InitializeMef() { var catalog = new DirectoryCatalog("Plugins"); _container = new CompositionContainer(catalog); // Fill the imports of this object try { _container.ComposeParts(this); } catch (CompositionException compositionException); } [Import] public IPasswordFactory PasswordFactory { get; set; } public String CreatePassword() { if (PasswordFactory == null) { return "Dependency not resolved."; } return PasswordFactory.Create(12); } }

The class PasswordCreator generates a random password using the services of an object that implements the IPasswordFactory interface. No such a component, though, is instantiated by the class itself. The task, in fact, is delegated to MEF. MEF will use a directory catalog to explore all assemblies in the specified relative folder, l­ooking for exports that match the contract of the IPasswordFactory import. So where’s the contract name? When you use the plain attribute, the contract name defaults to the name of the member. In this case, it is typeof(IPasswordFactory). What about exports? Consider the following example: [Export(typeof(IPasswordFactory))] public class DefaultPasswordFactory : IPasswordFactory { public String Create(Int32 passwordLength) { // Create the password }

Chapter 13  Principles of Software Design

587

protected virtual String GeneratePasswordCore(Int32 passwordLength) { // ... } }

Deployed to an assembly located in the specified plugin folder, the class DefaultPasswordFactory exports the typeof(IPasswordFactory) factory. If the class features the simple Export attribute, the contract then corresponds to the class name, thus missing the previous import. Note that if an export, in turn, misses one key import, the export is ignored to ensure the s­ tability of the solution. If multiple exports qualify to resolve the same import, you get a composition exception.

Unity at a Glance Unity is an open-source project from the Patterns & Practices group at Microsoft, which is attempting to provide an IoC framework for developers to build object instances in a smart and highly configurable way. Unity works as a standalone framework, but it’s also packaged along with Enterprise Library. To add Unity to a project, you add a reference to the Microsoft.Practices.Unity assembly. You optionally add a reference to Microsoft.Practices. Unity.Configuration if you configure the library using the application’s configuration file. Let’s see how to accomplish some key IoC operations with Unity, such as registering types both programmatically and declaratively.

Registering Types and Instances Just like any other IoC library, Unity is centered around a container object. In Unity, the ­container type is UnityContainer, and you use it to register types and instances, as shown here: var container = new UnityContainer(); container .RegisterType() .RegisterType(); var serviceLayer = container.Resolve();

You use the RegisterType method to establish a mapping between an abstract type and a concrete type. If the same abstract type should be mapped to different types in different contexts of the same application, you can use the following overload: container .RegisterType() .RegisterType("Tracing");

Download from Wow! eBook

588

Part III  Design of the Application

The additional string parameter disambiguates the request and gives Unity enough ­information about which concrete type to pick up. You use RegisterInstance instead of RegisterType to supply the container a prebuilt instance of a type. In this case, Unity will use the provided instance instead of creating one on its own. Does it really make sense for an a ­ pplication to pass to a factory the instance it will get back later? The purpose is to preserve the benefits of an IoC also in situations in which you can’t annotate a class to be ­automatically resolved by Unity. To see an example of this, let’s first introduce the syntax required to annotate constructors and properties for injection. When requested to create an instance of a given type, Unity gets information about the constructors of the type. If multiple constructors are found, Unity picks up the one with the longest signature. If multiple options are available, an exception is thrown. It might be the case, however, that you want a particular constructor to be used. This requires that an attribute be attached to the selected constructor: [InjectionConstructor] public MyClass() { ... }

If you have no access to the source code, you might want to consider RegisterInstance. Similarly, if injection happens through the setter of a property, you need to decorate the property accordingly, as shown here: private ILogger _logger; [Dependency] public ILogger Logger { get { return _logger; } set { _logger = value; } }

RegisterType and RegisterInstance are the methods you work with if you opt for ­configuring the Unity framework programmatically. However, offline configuration is also supported via an ad hoc section in the application’s configuration file. In any case, programmatic and ­declarative configuration is totally equivalent.

Resolving Dependencies In Unity, you invoke the method Resolve on the container class to trigger the process that returns an instance of the type at the root of the dependency chain: container.Resolve(registeredType);

The resolver can be passed any additional information it might need to figure out the correct type to return: var logger = container.Resolve("Tracing");

Chapter 13  Principles of Software Design

589

If you have multiple registrations for the same type, only the last one remains in the ­container’s list and will be taken into account. The resolver can walk down the chain of ­dependencies and resolve everything that needs to be resolved. However, you get an ­exception if the chain is broken at some point and the resolver can’t locate the proper ­mapping. When this happens in MEF, instead, the dependency is simply not resolved and is skipped over. On the other hand, multiple candidates to resolve a dependency are managed by Unity (the last wins) but cause a composition exception in MEF.

Declarative Configuration The Unity framework comes with a custom configuration section that can be merged with the web.config file of a Web application. Here’s the script you need to register types:

Under the section, you list the abstract types mapped to some concrete ­implementation. The following code shows how to map ILogger to DefaultLogger:

If the type is a generic, you use the following notation: ...

After you have the aliases all set, you can use alias names in the section where you register types.

Summary Just as an architect designing a house wouldn’t ignore building codes that apply to the ­context, a software architect working in an object-oriented context shouldn’t ignore principles of software design such as the SOLID principles discussed in this chapter when designing a piece of software. Proper application of these principles leads straight to writing software that is far easier to maintain and fix. It keeps the code readable and understandable and makes it easier to test, both during development and for refactoring and extensibility purposes.

592

Part III  Design of the Application

Most developers (and even more managers) commonly think that using software principles is first and foremost a waste of time and that no distinction is actually possible between “well-designed code that works” and “software that just works.” Guess what? I totally agree with this statement. If you don’t need design, any effort is overdesign. And overdesign is an anti-pattern. So you save a lot of time by skipping over principles. However, if your “software that just works” has to be fixed or extended one day, be aware that you will find yourself in a serious mess. The costs at that point will be much higher. It all depends on the expected lifespan of the application. Ideally, you learn principles and make them a native part of your skill set so that you use them all the time in a natural way. Otherwise, the costs of applying principles will always be too high to seem effective. Ad hoc tools can help a lot in making the ­development of good code more sustainable. IoC frameworks are just one of these tools. In the next chapter, I’ll continue with the theme of application design by tackling layers (and communication related to them) in ASP.NET applications.

Chapter 14

Layers of an Application The advantage of a bad memory is that one enjoys several times the same good things for the first time. —Friedrich Nietzsche Any software of any reasonable complexity is best designed if organized in layers. Each layer represents a logical section of the system. A layer is hosted on a physical tier (for example, a server machine). Multiple layers can be hosted on the same tier, and each layer can optionally be moved to a separate tier at any time and possibly with limited work. Most of the time, you arrange a three-tiered architecture with some flavors of service ­ rientation just to make each layer ready for a possible move to a different physical tier. o There are various reasons to move a layer onto its own tier: a quest for increased scalability, the need for stricter security measure, and also increased reliability because the layers ­become decoupled in case of machine failure. In a three-tiered scenario, you typically have a presentation layer where you first take care of processing any user input and then arranging responses, a business logic layer (BLL) that includes all the functional algorithms and calculations that make the system work and interact with other layers, and the data access layer (DAL) where you find all the logic required to read and write from a storage. When it comes to layers, the principle of separation of concerns (SoC) that I introduced in Chapter 13, “Principles of Software Design,” is more important than ever. A golden rule of any layered system states that no communication should be allowed between ­non-­i­nterfacing layers. In other words, you should never directly access the DAL from within the presentation layer. In terms of Web Forms development, this point is blissfully ignored when you use a SqlDataSource component right from the button click event handler of a Web page! In this chapter, I’ll describe the intended role and content of business and data access ­layers and touch on a few technologies that help you write them. I’ll do that from a Web Forms andASP.NET perspective, but be aware that a large part of the content has a general validity that goes beyond the Web world. I’ll cover presentation layers and related patterns in the nextchapter.

593

594

Part III  Design of the Application

A Multitiered Architecture Everybody agrees that a multitiered system has a number of benefits in terms of maintainability, ease of implementation, extensibility, and testability. Implementation of a multitiered system, however, is not free of issues and, perhaps more importantly, it’s not cheap. Can you afford the costs? Do you really need it? A three-tiered architecture is not mandatory for every Web application or for software ­applications in general. Effective design and, subsequently, layers are a strict requirement for systems with a considerable lifespan—typically, line-of-business systems you build for a customer and that are vital to the activity of that given customer. When it comes to Web sites, however, a lot of them are expected to stay live for only a short time or are fairly simple online windows for some shops or business. Think, for example, of sites arranged to promote a community meeting or a sports event. These sites are plain content management systems where the most important aspect is providing timely information via a back-office module. Honestly, it’s not really key here to design them carefully with service orientation, layers, and cross-database persistence. Themes like scalability, robustness, and security don’t apply to just any site or application. However, the longer the lifespan is, the more likely it is that you will also need to address carefully these concerns.

The Overall Design Figure 14-1 provides a glimpse of a three-tiered system with all the modules that we are ­going to consider in this chapter and the next one. As I see things, it’s essential that you, as an architect or developer, be very aware of this model. However, awareness means that you know it, and because you know it, you also know when it’s worthwhile for you to opt for such a complex and sophisticated design. Some of the blocks shown in Figure 14-1 can be merged if there’s really no reason for them to have their own life. The general view might not faithfully represent the particular view of your application. In architecture, it always depends on the context. And adapting the general view to the particular context is the essence of the architect’s job. Note  In my training classes, I always use a specific example to illustrate the previous point. As a parent, you must tell your kids that they use the crosswalk whenever they need to go across the street. When you do it yourself, in some situations, you adapt the general rule to a specific shortcut, and if no cars are coming you just cross wherever you are. That’s because you’re an adult and because, having evaluated pros and cons, you actually made a good decision. If the nearest crosswalk is half a mile away and no car is in sight, why walk that extra distance? In my classes, I always take the example a bit farther and tell nice stories about how Italians apply the pattern to parking. But if you’re interested in hearing about that, well, it’s best if you attend the next class!

Chapter 14  Layers of an Application

595

Presentation Layer User Interface ASP.NET

Win Forms

WPF

Mobile

Presentation Logic

Business Layer Application Logic (Service Layer) User Interface Services Workflows

DTO Adapters

Object/Domain Model

Data Access Layer Persistence Layer (O/RM, custom data context) FIGURE 14-1  A general view of a three-tiered system.

Methodologies Where do you start designing the layers of a real-world system? Ouch! That’s a really tough point. It depends. Trying to go to the root of it, I’d say it depends on the methodology you use to process requirements. Which methodology you apply also depends on something. It usually depends on your skills, your attitude, and your preference, as well as what seems to be best in the specific business scenario and context. Any system has its own set of requirements that originate use-cases. The ultimate goal of the application is implementing all use-cases effectively. A classic approach entails that you figure out what data and behaviors are required by all use-cases (or just one use-case) and build a good representation of the data involved and the related actions. So you start from the business layer, and in particular from modeling the entities in play and their relationships. I start my building from the business layer, and my main reason for that is to have as soon as possible a well-defined set of entities and relationships to persist and build a user interface around. To get my entities and relationships, however, I need to take a deep look at UI expectations and storage constraints, if any.

596

Part III  Design of the Application

The Business Layer The business logic layer is the heart of the system and the place where the behavior of the system is implemented. The behavior is just one aspect of the design of a system; another key aspect is data. Data is collected and displayed in the presentation layer, and it’s persisted and retrieved in the data access layer. Living in the middle, the business layer is where the data is processed according to some hard-coded behavior. (Note that in some very dynamic systems, the ­behavior can also be dynamically defined.) Generally speaking, the BLL is made of a few parts: the application’s logic, the domain logic, a representation for domain data, plus optional components such as local services and workflows. Invoked from the presentation layer, the application logic orchestrates services and DAL to produce a response for any client requests. The domain logic is any logic that can be associated with entities (if any) that populate the problem’s domain. The domain logic represents the back end of the application and can be shared by multiple applications that target the same back end. For example, an online banking application, a trading application, and a back-office application will likely share a common piece of logic to deal with accounts and money transfers. On top of that, each ­application might invoke the common logic through different algorithms. Application and domain logic work side by side and exchange data represented in some way. Finally, domain and application logic might need to invoke the services of special components that provide business-specific workflows or calculations. Business logic is a collection of assemblies to host. In a typical Web scenario, the BLL goes ­in-process with ASP.NET on the Web server tier. It goes in a separate server process mostly for scalability reasons. In a smart-client scenario, the location of the BLL might vary a bit. For example, the BLL can live entirely on the client, can be split across the client and server, or live entirely on the server. When the BLL is deployed remotely, you need services (for ­example, WCF services) to communicate with it. The list of components that form the BLL can be implemented using a number of design patterns.

Design Patterns for the BLL Design patterns for the BLL belong to two major groups: procedural and object-oriented patterns. For many years, we’ve been using procedural patterns such as Transaction Script (TS) and Table Module (TM). More recently, a significant increase in complexity and flexibility demand had us shifting toward object-oriented patterns such as Active Record and Domain Model.

Chapter 14  Layers of an Application

597

In the .NET space, the Table Module pattern has been popularized by special Microsoft Visual Studio technologies such as typed DataSets and table adapters. LINQ-to-SQL and Entity Framework move toward a Domain Model pattern. A number of open-source frameworks, on the other hand, provide an effective implementation of the Active Record pattern. Among the others, we have Subsonic and Castle Active Record. Let’s review the basics of the various design patterns and how concrete technologies are related to each.

The Transaction Script Pattern The Transaction Script (TS) pattern envisions the business logic as a series of logical ­transactions triggered by the presentation. Subsequently, modeling the business logic means mapping transactions onto the methods of one or more business components. Each business component then talks to the data access layer either directly or through relatively dumb data objects.

Presentation Layer Presentation Logic

.aspx

Business Layer

DTO

Form

T-Script 1 T-Script 2 . .

T-Script (class)

ADO.NET Data Access layer

DB FIGURE 14-2  The Transaction Script pattern.

Providers

When you partition transaction scripts into business components, you often group methods by entity. You create one method per each logical transaction, and the selection of methods is heavily inspired by use-cases. For example, you create an OrderAPI business component to house all transaction scripts related to the “order” entity. Likewise, you create a CustomerAPI component to expose all methods related to action the system needs to perform on customers. In relatively simple scenarios, you come up with one business component per significant database table. Each UI action ends up mapped to a method on a TS object. The TS pattern encompasses all steps, including validation, processing, and data access. Figure 14-2 shows a graphical representation of the BLL according to the Transaction Script pattern.

Part III  Design of the Application

Note that in the context of TS, a transaction indicates a monolithic logical operation; it has no relationships to database management systems (DBMS) transactions. The TS pattern is good for simple scenarios. The logic is implemented in large chunks of code, which can be difficult to understand, maintain, and reuse. In addition, TS favors code duplication and requires a lot of attention and refactoring to keep this side effect under control.

The Table Module Pattern According to the Table Module pattern, each object represents a database table and its ­entire content. The table module class has nearly no properties and exposes a method for each operation on the table, whether it’s a query or an update. Methods are a mix of ­application logic, domain logic, and data access code. This is the pattern behind typed DataSets and table adapters that you find in Visual Studio 2005 and later. The overall design of the BLL is clearly database-centric with a table-level granularity. Compared to TS, the Table Module pattern gives you a bit more guidance on how to do things. The success of this pattern is largely attributable to the support offered by Visual Studio and the availability in .NET of handy recordset data structures such as DataSets. Figure 14-3 shows the design of a system architected with the Table Module pattern.

Presentation Layer Presentation Logic

.aspx

Business Layer

DataSets

Form

CustomerTableAdapter Object

Providers

598

ADO.NET Data Access Layer

DB FIGURE 14-3  The Table Module pattern.

In procedural patterns, BLL and DAL are too often merged together. Most of the time, the DAL is where you package your ADO.NET code for physical data access.

Chapter 14  Layers of an Application

599

The Active Record Pattern The Table Module pattern is based on objects, but it’s not an object-based pattern for ­modeling the business logic. Why? Because it doesn’t care much about the business; it ­focuses, instead, on the tables. Table Module does have objects, but they are objects ­representing tables, not objects representing the domain of the problem. The real shift toward an object-oriented design starts when you envision the application as a set of interrelated objects—which is a different thing than using objects to perform data ­access and calculations. An object-based model has two main levels of complexity—simple and not-so-simple. A good measure of this complexity is the gap between the domain’s ­object model and the relational data model you intend to create to store your data. A simple model is when your entities map closely to tables in the data model. A not-so-­ simple model is when some mapping is required to load and save domain objects to a relational database. The Active Record pattern is your choice when you want an object-oriented design and when your domain logic is simple overall. In Active Record, each class essentially represents a record in a database table: the classes usually have instance methods that act on the represented record and perform common ­operations such as save and delete. In addition, a class might have some static methods to load an object from a database record and it might perform some rich queries involving all records. Classes in an Active Record model have methods, but these methods are mostly ­doing Create, Read, Update, Delete (CRUD) operations. There’s nearly no domain logic in the classes of an Active Record model, even though nothing prevents you from adding that. An aspect that makes Active Record so attractive to developers is its extreme simplicity and elegance and, just as significantly, the fact that in spite of its simplicity it works surprisingly well for a many Web applications—even fairly large Web applications. I wouldn’t be exaggerating to say that the Active Record model is especially popular among Web developers and less so among Windows developers. Beyond the simplicity and elegance of the model, available tools contribute significantly to make Active Record such a popular choice. Which tool should you use to implement an Active Record model? LINQ-to-SQL is definitely an option. Fully integrated in Visual Studio 2008 and later, LINQ-toSQL allows you to connect to a database and infer a model from there. As a developer, your classes become available in a matter of seconds at the end of a simple wizard. In addition, your classes can be recreated at any time as you make changes, if any, to the database. In terms of persistence, LINQ-to-SQL is not really a canonical Active Record model because it moves persistence to its internal DAL—the data context. LINQ-to-SQL incorporates a persistence engine that makes it look like a simple but effective Object/Relational Mapper (O/RM) tool with full support for advanced persistence patterns such as Identity Map and, especially, Unit of Work.

600

Part III  Design of the Application

Castle Active Record is another framework that has been around for a few years and that offers a canonical implementation of the Active Record pattern. Finally, an emerging ­framework for Active Record modeling is SubSonic. (See http://www.subsonicproject.com.) Unlike Castle Active Record, SubSonic can generate classes for you but does so in a way that is more flexible than in LINQ-to-SQL: it uses T4 templates. A T4 template is a .tt text file that Visual Studio 2008 and later can process and expand to a class. If you add a T4 template to a Visual Studio project, it soon turns it into a working class. This mechanism offers you an unprecedented level of flexibility because you can modify the structure of the class from the inside and not just extend it with partial classes as in LINQ-to-SQL, and it also removes the burden of writing that yourself as you must do with Castle Active Record.

The Domain Model Pattern In the Domain Model pattern, objects are aimed at providing a conceptual view of the ­problem’s domain. Objects have no relationships with the database and focus on the data owned and behavior to offer. Objects have both properties and methods and are not ­responsible for their own persistence. Objects are uniquely responsible for actions related to their role and domain logic. Note  Two similar terms are often used interchangeably: object model and domain model. An

object model is a plain graph of objects, and no constraints exist on how the model is ­designed. A domain model is a special object model in which classes are expected not to have any ­knowledge of the persistence layer and no dependencies on other classes outside the model.

A domain model is characterized by entities, value objects, factories, and aggregates. Entities are plain .NET objects that incorporate data and expose behavior. Entities don’t care about persistence and are technology agnostic. In a domain model, everything should be represented as an object, including scalar values. Value objects are simple and immutable containers of values. You typically use value objects to replace primitives such as integers. An integer might indicate an amount of money, a ­temperature, or perhaps a quantity. In terms of modeling, by using integers instead of more specific types you might lose some information. In a domain model, using a factory is the preferred way of creating new instances. Compared to the new operator, a factory offers more abstraction and increases the readability of code. With a factory, it’s easier to understand why you are creating a given instance. ­ ssociation Finally, an aggregate is an entity that controls one or more child entities. The a between an aggregate root and its child objects is stronger than a standard relationship. Callers, in fact, talk to the aggregate root and will never use child objects directly. Subsequently, controlled entities are processed and persisted only through the root

Chapter 14  Layers of an Application

601

­ ggregate. Aggregates are generally treated as a single unit in terms of data exchange. The a major benefit of aggregates is grouping together strongly related objects so that they can be handled as a single unit while being expressed as individual classes. Figure 14-4 is a representation of a system that uses the Domain Model pattern. Domain Model

View Model

Presentation Layer Presentation Logic

.aspx

Data Access Layer

Business Layer

DTO / DM

Form

Service Layer

DM

Providers

Repository

Domain Model

Persistence

DB FIGURE 14-4  The Domain Model pattern.

There are two ways of going with a Domain Model pattern. The simplest way is to design your entities and relationships with Entity Framework. After you have designed the layout of your entities and scalar objects, you generate the code using, preferably, the POCO code generator. What you get in the first place is an anemic domain model, where anemic indicates that classes are plain data containers and offer no behavior. However, Entity Framework lets you add methods to entities through the mechanism of partial classes. This also allows you to create factories quite easily. The second way is to create your own set of classes and then use an O/RM tool (for example, Entity Framework or NHibernate), or a handmade ADO.NET layer, to persist it. This approach offers greater expressivity because it allows you to introduce aggregates. Note that value ­objects, factories, and aggregates are concepts related to Domain Model that are introduced by a specific design methodology—Domain-Driven Design, or DDD. Although DDD is a proven methodology to deal with real-world complexity, it doesn’t mean that you can’t have an effective model without following literally all DDD recommendations. Entity Framework doesn’t help you much when it comes to DDD, but it doesn’t prevent you from using it as well. In Entity Framework, you have no native API to create aggregates.

602

Part III  Design of the Application

However, your data access layer can be designed to expose aggregate roots and let you work with them in a way that is consistent with DDD practices. Note  When you organize the business layer around a web of interconnected objects—a ­domain model—you neatly separate entities that the application logic (and sometimes the ­presentation logic) works with from any layer of code that is responsible for persistence. In this context, the DAL gains its own valuable role with full separation of concerns and ­responsibilities—the DAL just gets an object model and persists it to a store.

The Application Logic The application logic is the part of the BLL that contains endpoints, as required by use-cases. The application logic is the layer that you invoke directly from the presentation layer. The layer coordinates calls to the domain model, workflows, services, and the DAL to orchestrate just the behavior required by the various use-cases. You can’t just believe that all this logic belongs to the presentation layer. (As you’ll see better in the next chapter, in ASP.NET Web Forms the presentation layer is mostly the code-behind class!)

The Service Layer Pattern To better understand the role and importance of the application logic, consider the ­following example. You are working on a use-case that describes the submission of a new order. Therefore, you need an endpoint in the application logic that orchestrates the various steps of this operation. These might be any of the following: validating customer and order ­information, checking the availability of ordered goods, checking the credit status of the ­customer, finding a shipper that agrees to deliver the goods within the specified time, s­ ynching up with the shipper system, registering the order, and finally triggering any ­automatic refill procedures if the order reduces goods in stock below a safe threshold. The Service Layer pattern defines an additional layer that sits in between two interfacing ­layers—typically, the presentation layer and BLL. In practical terms, implementing a service layer requires you to create a collection of classes that include all the methods you need to call from the presentation layer. In other words, the classes that form the “service layer” shield the presentation layer from the details of the BLL and DAL. These classes are also the sole part of the application to modify if use-cases happen to change. The word “service” here isn’t necessarily meant to suggest some specific technology to build services (for example, WCF). The service layer is just a layer of classes that provides services to the presentation. However, service-orientation and specific service technologies make the whole solution even worthier of your consideration and more successful.

Chapter 14  Layers of an Application

603

When the Application Logic Is Deployed Remotely In a typical Web Forms scenario, the application logic lives side by side with the ASP.NET pages on the Web server machine. This means that any calls from the code-behind to classes in the service layer are in-process calls. Likewise, classes in the service layer are plain CLR classes and don’t require service contracts and configuration. In a desktop scenario, or if you implement a multitiered Web architecture, the service layer is likely living in a different process space. In this case, the service layer is implemented as a real layer of Windows Communication Foundation (WCF) or REST services. I recommend you start coding plain classes and upgrade to services just when you need to. In WCF, at least, a service is a class with something around it, and that “something” is ­essentially the service contract and configuration. If you design your service layer classes to be highly decoupled, based on an interface, and to expose data contracts, it will take you just a few moments to add attributes and binding information and switch to WCF services for, say, queued or transactional calls. A service layer is almost always beneficial to nearly all applications of some complexity that use a layered architecture. A possible exception is when you find out that your service layer is just a pass-through layer and is limited to forward calls to a specific component in the BLL or DAL. If you need some orchestration before you accomplish an operation, you do need a service layer. Take a look at Figure 14-5.

Code Behind

Service Layer Macro Services

Service 1

Service 2

Service N

Code Behind

FIGURE 14-5  Breaking apart dependency between layers.

If the orchestration logic (represented by the gears) lives on the presentation tier, you end up placing several cross-tier calls in the context of a single user request. With a remotable

604

Part III  Design of the Application

service layer, though, you go through just one roundtrip per request. This is just what SOA papers refer to as the Chatty anti-pattern. In Figure 14-5, you also see different blocks referring to services. The service layer is made of a collection of methods with a coarse-grained interface that I refer to in the figure as macro services. These services implement use-cases and do not contain any domain logic. Micro services, conversely, are domain-logic services you control or just autonomous services that your BLL needs to consume.

Exposing Entities to the Presentation Layer In a service layer, you should have only methods with a direct match to actions in a u ­ se-case. For example, you should have a FindAllOrders method only if you have a use-case that ­requires you to display all orders through the user interface. However, you should not have such a method if the use-case requires the user to click a button to escalate all unprocessed orders to another department. In this case, there’s no need to display to the user interface (and subsequently to roundtrip from the service layer) the entire list of orders. Here’s a ­sample class in a service layer: public interface IOrderService { void Create(Order o); IList FindAll(); Order FindByID(Int32 orderID); } public class OrderService : IOrderService { ... }

A fundamental point in a service layer is the types used in the signatures. What about the Order type in the previous code snippet? Is it the same Order entity you might have in the domain model? Is it something else? In general, if you can afford to expose domain model objects in the service contract, by all means do that. Your design is probably not as pure as it should be, but you save yourself a lot of time and effort. You must be aware that by using the same entity types in the presentation layer and BLL, you get additional coupling between the presentation and business layers. This is more than acceptable if the presentation and business layers are within the same layer. Otherwise, sharing the domain model forces you to have the same (or compatible) runtime platform on both sides of the network.

Chapter 14  Layers of an Application

605

Data Transfer Objects If you’re looking for the greatest flexibility and loose coupling, you should consider using ad hoc data transfer objects (DTO). A data transfer object is a plain container shaped by the needs of the view. A data transfer object contains just data and no behavior. When you use data transfer objects, you likely need an extra layer of adapters. An adapter is a class that builds a data transfer object from a graph of domain entities. An adapter is ­bidirectional in the sense that it also needs a method to take a data transfer object coming from the presentation and break up its content into pieces to be mapped on entities. The additional workload required by using data transfer objects is significant in moderately complex projects also. In fact, you need two adapters (or translators) for each data transfer object and likely two data transfer objects for each service layer method (one for input and one for output.) It’s not a matter of being lazy developers; it’s just that a full data transfer object ­implementation requires a lot of work. Note  Effective tools are a great way to make a full DTO implementation affordable and, to some extent, sustainable. A common tool that really saves you a ton of work is AutoMapper. (See http://automapper.codeplex.com.) AutoMapper is an object-to-object mapper that employs a convention-based algorithm. All it does is copy values from one object (for example, a ­domain entity) to another (for example, a DTO) using a configurable algorithm to resolve mapping ­between members. At this point, AutoMapper can be easily considered a best practice in modern development.

The Data Access Layer No matter how many abstraction layers you build in your system, at some point you need to open a connection to some database. That’s where and when the data access layer (DAL) fits in. The DAL is a library of code that provides access to data stored in a persistent container, such as a database. In a layered system, you delegate this layer any task that relates to ­reading from, and writing to, the persistent storage of choice.

Implementation of a DAL Until recently, the DAL was just fused to the BLL and limited to a superset of the ADO.NET library created for the purpose of making writing data access code easier. In other words, for many years of the .NET era, the DAL has been simply a layer of helper methods to write data access quickly.

606

Part III  Design of the Application

The shift toward a more conceptual view of the problem’s domain, and subsequently the advent of the Domain Model pattern, brought new interest in the role of the DAL. Now that you have a domain model exposed as the real database in the application’s eyes, you really need a distinct and well-defined layer that bridges the gap between the domain model and storage. Although the role of the DAL is still the same as it was 20 years ago, the technologies are ­different, as is the approach to it taken by architects and developers. It’s interesting to briefly review the inner nature of the DAL in light of the pattern used for the BLL.

DAL and the Table Module Pattern Having a BLL organized in table module classes leads you to having a database-centric ­vision of the system. Every operation you orchestrate in the application logic is immediately ­resolved in terms of database operations. It’s natural, at this point, to create an in-memory representation of data that closely reflects the structure of the underlying tables. With the Table Module pattern, you have table classes and methods to indicate query or ­update operations. Any data being exchanged is expressed through ADO.NET container types such as DataSet and DataTable. Any data access logic is implemented through ADO. NET commands or batch updates. The DAL still has the role of persisting data to databases, but data is stored in database-like structures (for example, DataSet), and a system framework (for example, ADO.NET) offers great support for working with DataSet types. As a result, BLL and DAL are merged together and are rather indistinguishable. The DAL, when physically distinct from BLL and living in its own assembly, is nothing more than a library of helper methods.

DAL and the Active Record Pattern An Active Record BLL makes domain data available in the form of objects with a close ­resemblance to records of database tables. You no longer deal with super-array types such as DataSet, but instead have an object model to map to table records. The DAL, therefore, has a clearer role here. It exists to bridge the gap between entities in the object model and database tables. The mapping work required from the DAL is relatively simple because there’s not much abstraction to cope with. Mapping between object ­properties and table columns is neat and well defined; the storage is a relational database. The benefit of using Active Record instead of Table Module is mostly in the fact that an Active Record object model can be created to be a real strongly typed counterpart of a table. In this regard, it fits better than a generic container such as DataSets and can be extended at will. The drawback is in the extra work required to create the model, but fortunately many tools exist to infer the model directly from tables.

Chapter 14  Layers of an Application

607

DAL and the Domain Model Pattern According to the Domain Model pattern, you create from the ground up an entity model with any appropriate relationships. More importantly, you do so while being completely ­ignorant about persistence. First, you create the object model that best represents the business domain; second, you think of how to persist it. As odd as it might sound, in a Domain Model scenario the database is purely part of the infrastructure. (See Figure 14-6.)

Service Layer Business Layer

Object Model

Object model to persist

Services Workflows

Object model created from persisted data Data Access Layer

DB FIGURE 14-6  The DAL is just part of the infrastructure.

A DAL has four main responsibilities toward its consumers. In the first place, a DAL persists data to the physical storage and supplies CRUD services to the outside world. Second, the DAL is responsible for servicing any queries for data it receives. Finally, a DAL must be able to provide transactional semantics and handle concurrency properly. Conceptually, the DAL is a sort of black box exposing four contracted services, as shown in Figure 14-7. Who does really write the DAL? Is it you? And why should you write a DAL yourself? Is it perhaps that you have a strict nonfunctional requirement that explicitly prohibits the use of an ad hoc tool such as an O/RM? Or is it rather that you think you would craft your DAL better than any commercial O/RM tools?

608

Part III  Design of the Application Business Layer

Data Access Layer CRUD Services

Query

Transactions Management

Concurrency

DB FIGURE 14-7  A conceptual view of the DAL’s interior.

In a domain-based world, a well-built DAL is nearly the same as a well-built O/RM tool. So unless you have strict nonfunctional requirements that prohibit it, you should use an O/RM. Entity Framework is the official O/RM tool you find in the Microsoft stack of technologies. NHibernate is an even more popular tool that has been around for quite a few years now and that is close to its maturity.

Interfacing the DAL In some simple scenarios, it might be acceptable for the DAL to be invoked from the ­presentation layer. This happens when you actually have only two tiers: the presentation layer and the storage. Beyond this, the DAL is a constituent part of the back end and is invoked from the application logic. The next issues to resolve are the following: Should you allow any service layer classes to know the nitty-gritty details of the DAL implementation? Should you wrap the DAL ­implementation in an interfacing module that provides a fixed interface regardless of the ­underlying details? As usual, it depends.

Support for Multiple Databases In the past, every architect would have answered the previous questions with a sounding yes. Today, it is likely the same answer, but for different reasons. For years, the primary reason for wrapping the DAL in the outermost container has been to achieve database independence.

Chapter 14  Layers of an Application

609

Wrapping the DAL in a pluggable component greatly simplifies the task of installing the same application in different servers or using it for customers with a different database system. Today, the advent of O/RM tools has dwarfed this specific aspect of the DAL. It’s the O/ RM ­itself that provides database independence today. At the same time, other compelling ­reasons show up that make interfacing the DAL still a great idea.

The Repository Pattern A common way to hide the implementation details and dependencies of the DAL is using the Repository pattern. There are a couple of general ways you can implement the pattern. One consists of defining a CRUD-like generic interface with a bunch of Add, Delete, Update, and Get methods. Here’s an example: public interface IRepository : ICollection, IQueryable { public void Add(T item) { ... } public bool Contains(T item) { ... } public bool Remove(T item) { ... } public void Update(T item) { ... } public IQueryable Include(Expression subSelector) { ... } }

The type T indicates the type of entity, such as Customer, Order, or Product. Next, you create an entity-specific repository object where you add ad hoc query methods that are suited for the entity. Here’s an example: public interface IProductRepository : IRepository { IQueryable GetProductsOnSale(); }

Classes in the service layer deal with repositories and ignore everything that’s hidden in their implementation. Figure 14-8 shows the summarizing graphic. Another approach to building a repository consists of simply creating entity-specific ­repository classes and giving each one the interface you like best. Today, testability is an excellent reason to interface the DAL. As shown in Figure 14-8, it ­allows you to plug in a fake DAL just for the purpose of testing service layer classes. Another scenario, however, is gaining ground on popularity: upgrading the existing DAL based on an on-premises database for the cloud.

610

Part III  Design of the Application DAL

Service Layer CustomerServices

Repository interfaces

OrderServices ProductServices > Fake DAL

O/RM

FIGURE 14-8  The Repository pattern applied to the DAL.

Using an Object/Relational Mapper So it seems that a common practice for implementing a DAL is using an O/RM. Using an O/RM is not trivial, but tools and designers in the Microsoft and Visual Studio world make it considerably simpler. More to the point, with Entity Framework or LINQ-to-SQL you can hardly do it wrong. (Even though you can sometimes do it in a suboptimal way.) Which O/RM should you use? In this brief gallery, I present two of the most popular choices (Entity Framework and NHibernate) and one for which there’s considerable debate about whether it belongs to the group (LINQ-to-SQL). I’m skipping over a review of all commercial O/RMs.

LINQ-to-SQL I like LINQ-to-SQL, period. When Microsoft released Entity Framework 1 in the fall of 2008, it promptly signaled the end of LINQ-to-SQL development—at least, active development, which involved adding new features. In the .NET Framework 4, LINQ-to-SQL is fully supported and it has even been slightly improved by the fixing of a few bugs and less-than-optimal features. Still, LINQ-to-SQL is a sort of dead-end; however, if it works for you today, it’ll likely work for you in the future. I like to call LINQ-to-SQL “the poor man’s O/RM.” It’s a lightweight, extensible, data access option with some known limitations; however, it’s a well-defined and well-balanced set of features. LINQ-to-SQL pushes the “It’s easy, it’s fast, and it works” standard that is just what many developers are looking for. LINQ-to-SQL works by inferring the entity model from a given database, and it supports only Microsoft SQL Server. LINQ-to-SQL doesn’t let you add much abstraction (for example, scalar types), but it does offer a POCO model that you can extend with partial classes and customize with partial methods.

Download from Wow! eBook

Chapter 14  Layers of an Application

611

Technically, LINQ-to-SQL implements many of the design patterns that characterize a true O/RM, such as Unit of Work (transactionality), Query object (query), and Identity Map. If yourefer to the responsibilities of the DAL shown in Figure 14-7, you find nothing that LINQ-to-SQL can’t do. That’s why I’m listing it here. It’s the simplest of the O/RMs, but it’s not simplistic.

Entity Framework If you know LINQ-to-SQL, then Entity Framework might look like its big brother. Entity Framework is more expressive, comes with a richer designer for model and mappings, and supports multiple databases. If you get to Entity Framework from another O/RM tool, you might find it a bit different. The only purpose of an O/RM tool is to persist an object model to some database. Entity Framework certainly does this, but it also helps you in the creation of the model. Most O/RM tools out there can persist any object model you can map to a database. Entity Framework builds on the Entity Relationship Model to let you create an abstract model of entities and relationships that it can then transform into plain C# partial classes. When Entity Framework generates the source files for a model, it creates distinct files for each entity plus a class for the data context. From a design perspective, it’s key that these files go in distinct assemblies. Logically speaking, in fact, entities form the domain model whereas the data context object belongs to the DAL. It’s perhaps a little difference, but it’s immensely important from a design perspective. Entity Framework can generate source files in three different ways. The default approach ­entails you getting entity classes with a dependency on the framework. All entity classes ­inherit from a built-in class defined in Entity Framework and incorporate some default ­behavior related to persistence. Another approach gets you plain old CLR classes with no ­dependencies on anything. This approach is POCO. Finally, Entity Framework can also ­generate entity classes that have the additional capability of tracking their changes. Finally, Entity Framework supports Code-Only mode, which basically consists of the behavior that most of the other O/RM tools offer—you create your domain model as a plain library and then instruct Entity Framework on how to persist it. Code-Only is just the fluent API you use to define mappings to a database. Note  As long as you intend to remain within the Microsoft stack, which O/RM should you

use? LINQ-to-SQL or Entity Framework? The simple answer is Entity Framework because Entity Framework is the flagship product that will receive care and attention in the foreseeable future. What if you feel comfortable with LINQ-to-SQL and find it kind of costly to upgrade to Entity Framework? In general, if your application has enough life ahead of it (no less than two years), after which a full redesign is acceptable, you can go with LINQ-to-SQL today and plan to upgrade later. However, keep in mind LINQ-to-SQL is not the light edition of Entity Framework; it has a slightly different programming API, and no migration path exists yet.

612

Part III  Design of the Application

NHibernate NHibernate is perhaps the most popular O/RM available today. It’s open-source software with a strong and active community to back it up. NHibernate requires you to provide a ­library of classes and a bunch of mapping XML files. Based on that, it offers a rich API to write your logic for persistence. With the release of Entity Framework 4, the technical gap is shrinking more and more and mostly has been reduced to fine-tuning the framework’s behavior. The two main differences are the LINQ provider for expressing queries on entities, which is definitely superior in Entity Framework, and the absence in Entity Framework of second-level caching. In addition, NHibernate looks like a more mature framework, closer to perfection in a way. Put another way, with the exception of adding a LINQ provider to it, I don’t really see how NHibernate can be significantly improved. As it is, NHibernate offers a number of extensibility points (lacking in Entity Framework) and more expressivity when it comes to dealing with paged collections and batch reads and writes. Companion tools (for example, profilers, caches, and sharding) are numerous for NHibernate and (currently) hard to write for Entity Framework because of the aforementioned lack of extensibility points. Note  Sharding is a database design technique that consists of horizontal partitioning. In

­ ssence, the row set of a logical table is physically stored in multiple tables. Each partition is e known as a shard and may be located on a distinct database server. The goal of sharding is ­gaining scalability by reducing table and index size and making search faster.

Note  So here’s a point-blank question: Entity Framework or NHibernate? Skipping the usual (and reasonable) point that it depends on the context, skills, and requirements, I’d say that with Entity Framework you don’t get the same programming power of NHibernate. However, if you don’t need that power, over all, you can work nicely and safer with Entity Framework, in the sense that you hardly ever screw things up.

O/RM Tools and SQL Code An O/RM tools persists entities by generating and executing SQL commands. Is the SQL code generated by O/RMs reliable? In general, trusting an O/RM is not a bad idea, but constantly verifying the quality of the job they do is an even better idea. With any O/RM, a savvy ­developer will usually define the fetch plan and use the SQL profiler tool of choice to see what is coming out. Obviously, if the SQL code is patently bad, you intervene and in some way (changing the fetch plan or inserting stored procedures) you fix it. In general, using stored procedures should be considered a last resort, but there might be cases in which they come to the rescue. An example is when quite complex queries can’t be

Chapter 14  Layers of an Application

613

expressed efficiently through classic cursor-based syntax and requires, instead, a SET-based approach to boost the performance. In this case, a stored procedure can be the best option.

Beyond Classic Databases A plausible scenario that could lead you to unplugging your DAL is that you replace the ­current storage with something else, from yet another relational DBMS system. If you simply switch from SQL Server to, say, Oracle, most of the O/RM tools can absorb the change quite nicely. At worst, you pay some money to a third-party company to get a driver. A more ­delicate situation, though, is when you replace the storage layer of the application with something different, such as a cloud database or, say, a model managed by Microsoft Dynamics CRM, or perhaps a NoSQL solution such as MongoDB, RavenDB, or CouchDB.

Going to the Cloud As far as cloud databases are concerned, you can use a variety of solutions. For example, you can move to SQL Azure, which offers a transparent API and can be easily plugged into your system via Entity Framework. Alternatively, you can choose a cloud solution such as Amazon SimpleDB, Amazon RDS, or perhaps S3. In all these cases, your access to data happens through Web services. And Web services require you to rewrite your DAL to invoke the proper Web service instead of opening an O/RM session. More in general, perhaps with the sole (current) exception of SQL Azure and Entity Framework, going to the cloud requires you to unplug the current DAL and roll a new one. It’s definitely a compelling reason to keep the DAL loosely coupled to the rest of the system.

Microsoft Dynamics CRM 2011 A layered system doesn’t necessarily have to rely on a classic relational storage whose ­physical model is the topic of endless discussion and whose optimization is left to external gurus. In some business scenarios, Microsoft Dynamics CRM represents an even better ­option for building line-of-business applications that fall under the umbrella of a Customer Relationship Management (CRM) system. Within Dynamics CRM 2011, you express the data model using a mix of built-in and custom entities. You can think of a CRM entity as a database record where attributes of an entity map roughly to columns on a database table. Dynamics CRM 2011 exposes data to developers using a bunch of WCF and REST endpoints. This makes it possible for developers of Web applications to capture data, process that as necessary, and arrange a custom user interface. In other words, the Dynamics CRM model might become the BLL and DAL that the service layer talks to. It’s yet another scenario that makes loosely coupling of back-end layers exactly the way to go when building layered solutions.

614

Part III  Design of the Application

Schema-less Storage A storage option that is gaining momentum is schema-less storage that is often summarized as a NoSQL solution. A classic relational database is a collection of relations where a relation is defined as a set of tuples sharing the same attributes—the schema. NoSQL stores just ­refuse relations. NoSQL stores still refer to a form of structured storage in which each stored document may have its own schema, which is not necessarily shared with other documents in the same store. A document is exposed as a collection of name/value pairs; it is stored in some way (for ­example, as individual files) and accessed through a REST interface. A NoSQL database is characterized by the lack of a schema, the lack of a structured query language, and an often distributed and redundant architecture. NoSQL databases belong to three main families: document stores, key/value stores, and object databases. A document store saves documents as JSON objects and defines views/indexes. Objects can be arbitrarily complex and have a deep structure. To this category belong popular tools such as CouchDB, Raven, and MongoDB. A key/value store saves tuples of data in a main table. Each row has a set of named columns, and values can be arbitrarily complex. Google’s BigTable, Cassandra, and Memcached are ­examples of key/value NoSQL stores. Finally, an object database stores serialized objects instead of primitive data and offers query capabilities. A popular choice is Db4O.

Summary Most applications today are articulated in layers. Every time you add a layer to an ­application, you add a bit more code and, subsequently, extra CPU cycles. And you worsen the overall performance of the application. Is this all true? Technically speaking, it couldn’t be truer. However, a few extra CPU cycles are not necessarily what really matters. Software architecture, more than programming, is a matter of tradeoffs. Layers add benefits to any software of some complexity. Layers add separation of concerns and favor code injection and the right level of coupling. In this chapter, I went through two of the three layers you find in a classic multitiered system: the business layer and the data access layer. The next chapter is reserved for the presentation layer and for the most appropriate pattern for Web Forms applications—the Model-ViewPresenter pattern.

Chapter 15

The Model-View-Presenter Pattern I have never let my schooling interfere with my education. —Mark Twain Generally speaking, creating the user interface of an ASP.NET Web Forms application is kind of easy. Even though I’m not sure the effort it takes to create a compelling and graphically appealing user interface should be referred to as “easy,” arranging the desired functionality is usually not a big deal thanks to the full bag of server controls you can get. If there’s a reason behind the success of ASP.NET, it’s likely the ease of development—a clear offspring of the Rapid Application Development (RAD) paradigm. For many years, it was so easy (and quite effective) to drop a control on the Microsoft Visual Studio Web page designer, double-click, and fill in the method stub created for you in the page’s code-behind class. Therefore, a plain, old T-SQL query attached to the handler of, say, a Click event did the job of retrieving data and populating a grid after a user’s click. Like it or not, it was quick and effective—the beauty of RAD. As the complexity of the application grows, RADness loses most of its appeal and stops being an ideal companion for the presentation layer. This is the biggest change in the software industry that we’ve gone through in the past few years. The need for a more structured way of writing software doesn’t mean, of course, we don’t need Visual Studio anymore. However, we probably need to consider Visual Studio designers for what they actually are—facilities for the UI rather than an aid for software design. (Visual Studio 2010 Ultimate does have a number of interesting facilities for design and modeling, but these tools cross-cut the platform you’re using for building the presentation.) In Chapter 14, “Layers of an Application,” we reviewed layered applications and considered a number of patterns for the business and data access layers. In this chapter, we consider patterns for layering the presentation layer of applications with a particular eye to ASP.NET applications.

Patterns for the Presentation Layer Your design of the presentation layer of any system should be driven by one fundamental principle: keep the presentation layer separated from anything else, including business and data access layers. Over the years, a few design patterns have been identified to help you with the presentation layer. We recognize three main families of patterns: Model-ViewController (MVC), Model-View-Presenter (MVP), and Presentation Model (PM). The last one is more popularly known in the .NET space as Model-View-ViewModel (MVVM).

615

616

Part III  Design of the Application

Not all of them can be applied to ASP.NET Web Forms with the same effectiveness. Actually, the design pattern that best applies to ASP.NET Web Forms applications is MVP, which will be the main topic of this chapter. Before I go any further on that topic, you should note that these patterns span 30 years of computer design and programming. And many, many things have changed in the past 30 years. So, for example, what we call MVC today doesn’t exactly match the definition of MVC you find in the original paper that dates back about 30 years ago. To some extent, the same can be said for MVP. Two flavors of MVP exist—Passive View and Supervising Controller— and, in many applications, you actually use a personal mix of both. Likewise, even though PM and MVVM share the same architectural idea, MVVM is associated today with specific technologies such as Microsoft Silverlight, Windows Presentation Foundation (WPF), and Windows Phone. This is to say that patterns like MVP and MVC give you quite a good idea of the approach behind a pattern, but details might change once the pattern is applied to a framework and a bunch of technologies. If you have a framework that gives you testability and separation of concerns (and whatever else you ask for), by all means use it and don’t get bothered by ­possible patterns it does or does not implement. Note  The impact of frameworks and technologies on the presentation layer is a huge point to consider. Originally, the MVP pattern had been proposed as a way to improve MVC. So does this mean that ASP.NET MVC is a thing of the past? Of course, not. There are probably ways to further improve the design of ASP.NET MVC applications, but the ASP.NET MVC framework is highly ­usable and guides you toward writing well-designed code without the cost of arranging ­everything yourself.

The MVC Pattern In the earliest software, the whole application was made of monolithic blocks that ­encompassed everything—the user interface, logic, and data. The user interacted with the view and generated some input. The view captured the input, processed it internally, and ­updated itself or moved to another view. The MVC pattern was introduced in the late 1970s as a way to break such monoliths (called autonomous views) into distinct pieces.

Generalities of the MVC Pattern According to the MVC pattern, the application is split into three distinct pieces: the model, the view, and the controller. The model refers to the data being worked on in the view. The model is represented with an active object that is updated by the controller and notifies the view of its state changes. The view refers to the generation of any graphical elements displayed to the user, and it captures and handles any user gestures. The controller responds to

Chapter 15  The Model-View-Presenter Pattern

617

solicitations and executes actions against the application’s back end. Such actions produce fresh data that alter the model. The controller is also responsible for selecting the next view. Figure 15-1 provides a graphical representation of the MVC schema.

Controller

View

...

Controller

Model

View

Model

Presentation

Entity Model

Data transfer objects or entity objects or scalar values to be copied to/from the view-specific model

Service Layer Business Data Access Layer

Storage FIGURE 15-1  The Model-View-Controller pattern.

The major benefit of MVC is the application of the separation of concerns (SoC) principle. Itmostly consists of taking as much code as possible out of the front end to build structured layers. As mentioned earlier, in the beginning, MVC was just a way to break up m ­ onolithic programs in which the code basically consists of a loop around some input and each ­command is resolved through a logical transaction. (See the Transaction Script pattern in Chapter 14.) Let’s see what it takes to use the MVC pattern to build Web Forms applications.

Role of the Model According to the definition, the model in MVC is the representation of the data as the view is expected to consume it. The model is where the view copies any data posted by requests and where it gets any data to be incorporated in the response. You should have a different model (say, a class) for each view. The model should have properties for each significant piece of data the view handles. The problem here is that in Web Forms the model is often bypassed because views are made of server controls and server controls expose input data and receive response data.

618

Part III  Design of the Application

Role of the View In Web Forms, the view coincides with a page. The page is made of server controls, and ­server controls provide input elements for the user to interact with. The view is responsible for producing the collection of visual elements displayed to users—the ASPX markup—as well as for processing user gestures—the code-behind class. In an MVC scenario, there should be some sort of event-based dependency between the view and model. When the model is updated, the view should be notified, grab up-to-date information, and refresh. The code in the view should be as simple as possible, ideally limited to just dispatching calls to another layer—the controller.

Role of the Controller The controller is the place where the action that the user requested is actually performed. The action might require the controller to select a new view which, in Web Forms, would be a redirect. More likely, the action will lead the controller to interact with the middle tier to ­obtain a response. The response obtained will then be massaged in some way and placed in the model for the view to consume. The controller has no dependencies on the view; the controller only knows the model and how to reach the middle tier.

Web Forms and the MVC Pattern Heavily based on view state, code-behind, and server controls, the Web Forms programming model follows its own pattern—the Page Controller pattern. The core idea is that any ­request is mapped to a page and an internal component controls the request, including input processing, action, and output generation. If you stick to Web Forms, you can’t eliminate the Page Controller pattern. If you want more SoC, you can build layers for models and controllers on a per-view ­basis. This means that each Web Forms page should have its own model class and its own ­controller class. The model will be updated with any posted data and any significant data that is read out of the view state. In the postback, the code-behind event handler will simply invoke a controller method. Finally, the controller will update the model, and these changes should walk their way to the view. The weak point of MVC is the communication between view and model. The original MVC paper suggests you set up event-based communication between the two. Years of experience suggest a different model should be used. Enter the MVP pattern.

Chapter 15  The Model-View-Presenter Pattern

619

The MVP Pattern MVP is a derivative of MVC aimed at providing a cleaner separation between the view, the model, and the controller. The most relevant change from MVC is that view and model are physically separated and have no intimate knowledge of each other. The controller (renamed as presenter) is a mediator between the user and the application. Solicited by the view, it performs any work associated with the request and passes data back to the view. In MVP, the controller class is essentially responsible for presenting data to the view, which explains the new name of “presenter.”

Generalities of the MVP Pattern As mentioned, in MVP the view and the model are neatly separated, and the view exposes a contract through which the presenter can read input values and provide results of the action. Summarizing the situation further, we can say that MVP is a refinement of MVC based on three facts: ■

The view doesn’t know the model.

The presenter ignores any UI technology behind the view.

Abstracted to an interface, the view is easy to mock up, which makes testing the ­controller far easier.

Figure 15-2 provides an overall view of the MVP pattern. View View Forwards user actions

Model

Model

Presenter

Presenter Redirect to a new MVP triad

Yes

Return values No

New view?

Invoke method

Middle Tier

FIGURE 15-2  The MVP pattern.

The presenter is at the center of the universe and incorporates the presentation logic behind the view. The presenter in MVP is logically bound to the view, which is another reason for emphasizing the presentation role of the component. Figure 15-3 attempts to compare MVC and MVP graphically.

620

Part III  Design of the Application Model

View

Presenter

Middle Tier

Controller

Middle Tier

Forwards user gestures

MVP MVC View

Forwards user gestures

Read/write

Model

Read/write

FIGURE 15-3  Comparing MVC and MVP.

Note  In addition to the change of name (controller vs. presenter), there’s a more subtle but

r­ elevant point. In MVC, the controller is a centralized class that handles multiple calls from ­multiple views. In MVP, the presenter is bound to a single view or to a hierarchy of views with thesame characteristics. In MVP, the presenter is a controller for a specific segment of the ­presentation logic. Hence, the name “presenter.”

Role of the Model The best possible definition of the model doesn’t change in MVP. The model is the ­representation of the data being worked on in the view. As shown in Figure 15-2, the view exposes a contracted interface, which represents the core functionality of the view to the presenter’s eyes. In other words, the presenter should be able to work with any object that implements that contracted interface. In theory, it could be an ASP.NET page as well as a Windows Forms window. The model in MVP, therefore, is the interface that the view object implements. Being an interface, it can include properties, but it can also include methods and events. In a Web Forms scenario, events are not required, and most of the time it will contain just properties.

Role of the View The view is the Web Forms page that you build. This view is typically an instance of a class that inherits from Page or UserControl and implements the model. The view also holds a ­reference to an instance of the presenter. Between views and presenters, you typically have a one-to-one cardinality, even though you can still reduce the number of presenter classes by creating some sort of hierarchy and reusing a bit of code.

Chapter 15  The Model-View-Presenter Pattern

621

Role of the Presenter The presenter is just an additional layer you build on top of code-behind classes. It is a class that can be easily designed to have no hidden dependencies. The presenter requires a ­reference on the view, but thanks to the contracted interface of the view the reference can be injected. The presenter will use the view object to grab input values and prepare a call to the middle tier. After the response has been received, the presenter will pass data back to the view, always through the members of the interface. As mentioned, the interface that ­abstracts the view is logically equivalent to the model in MVC.

Web Forms and the MVP Pattern As you’ll see in the rest of the chapter, MVP lends itself very well to being implemented in Web Forms. The pattern can be easily outlined as a step-by-step procedure and doesn’t require you to twist the Web Forms programming model. As a developer, you only need to add a bit of abstraction to your Web Forms pages to gain the benefits of the MVP pattern—­ testability and maintainability. Having said that, I also feel obliged to mention that MVP is not a pattern for everyone and for just any application. MVP provides guidance on how to manage heaps of views and, quite obviously, comes at a cost—the cost of increased complexity in the application code. As you can imagine, these costs are easier to absorb in large applications than in simple ones. MVP, therefore, is not just for any application. In MVP, the view is defined through an interface, and this interface is the only point of ­contact between the system and the view. As an architect, after you’ve abstracted a view with an interface, you can give developers the green light to start developing presentation logic without waiting for designers to produce the graphics. After developers have interfaces, they can start coding and interfaces can be extracted from user stories, if not from full specifications. MVP is an important presentation pattern that can be a bit expensive to implement in r­elatively simple applications. On the other hand, MVP shines in enterprise-class applications, where you really need to reuse as much presentation logic as possible, across multiple ­platforms and in Software-as-a-Service (SaaS) scenarios. And many of these applications have an ASP.NET Web Forms front end.

The MVVM Pattern A recently introduced pattern, Model-View-ViewModel is built around the same concepts presented years ago for the Presentation Model (PM) pattern. The PM pattern is described here: http://martinfowler.com/eaaDev/PresentationModel.html. How does PM differ from MVP?

622

Part III  Design of the Application

PM is a variation of MVP that is particularly suited to supporting a rich and complex user interface. On the Windows platforms, PM works well with user interfaces built with Windows Presentation Foundation (WPF) and Silverlight. Microsoft developed a WPF-specific version of PM and named it Model-View-ViewModel (MVVM).

Generalities of the MVVM Pattern MVVM, like MVP, is based on three actors—the view, the model, and the presenter—with the presenter now renamed as view-model. The difference with MVP is that the view doesn’t expose any interface, but a data model for the view is incorporated in the presenter. The view elements are directly bound to properties on the model. In summary, in MVVM the view is passive and doesn’t implement any interface. The interface is transformed into a model class and incorporated in the presenter. The resulting object gets the name “view-model.” See Figure 15-4. View View Forwards user actions

Model

Data binding

Presenter

Presenter Redirect to a new PM/MVVM triad

Yes

Model No

New view?

Return values

Invoke method

Middle Tier

FIGURE 15-4  The MVVM Pattern.

The innovative point of MVVM is that the presenter doesn’t operate on the view. The ­presenter, instead, exposes an object model tailor-made for the view and takes care of ­populating it with fresh data. The view, in turn, gains access to the presenter’s object model in some way. In the .NET space, data binding is a common way in which this is achieved.

Web Forms and MVVM MVVM is not a pattern I recommend for Web Forms. More precisely, the inherent plusses of MVVM don’t show up in Web Forms (or ASP.NET MVC) with the same effectiveness as they do in WPF or Silverlight. Still, MVVM can give you some benefits, such as layering, SoC, and testability. However, it won’t be anything more than what you would get with MVP. Why is MVVM particularly effective in WPF or Silverlight?

Chapter 15  The Model-View-Presenter Pattern

623

The answer is shown in Figure 15-4. Used in a platform that provides superb support for (two-way) data binding, the MVVM shines and really gives you the aforementioned benefits with a fraction of the effort it would take to do the same in MVP. Used in Web Forms, where you have only one-time binding, it loses most of its appeal. Figure 15-5 shows the graph of a XAML-based view designed in accordance with MVVM.

Sample.xaml

UI elements

Data binding

ViewModel instance

SampleViewModel.cs

Method binding

BLL FIGURE 15-5  Schema of the MVVM pattern in a XAML-based view.

The XAML file can be a WPF, Silverlight, or even Windows Phone 7 view. It is made of markup elements bound to properties of the view-model object. The view-model object can be attached to the view in a number of equally effective ways, including programmatic access to the DataContext property of the view and a custom tag in the markup that references an external object. The view-model class exposes methods to retrieve and update data through the middle tier. View events (for example, a user’s clicking) are bound to commands, and commands are ultimately mapped to methods on the view-model object. This can be done in a number of ways. You can, for example, use the code-behind class of the XAML view and just invoke methods on the view-model object, or perhaps you can use the XAML commanding interface to forward user events to command objects that, in turn, invoke the view-model and then the middle tier. Architecturally speaking, I don’t see relevant differences. It mostly ­depends on attitude and tooling. For example, if you use Microsoft Blend to create the XAML view, you’ll likely end up with codeless code-behind classes. If you stick to Visual Studio as your IDE, you will probably write classic event handlers in the code-behind class.

Implementing Model View Presenter As it turns out, MVP is probably the most beneficial way of adding layering and t­ estability to Web Forms applications. Let’s see how to implement the MVP pattern in a sample application.

624

Part III  Design of the Application

Abstracting the View In an MVP scenario, the first step to complete is defining the contract for each required view. Each page in the ASP.NET application will have its own interface to talk to the rest of the ­presentation layer. The interface identifies the data model that the page requires and ­supports. Two logically equivalent views will subsequently have the same interface. A view that extends an existing view will probably have its interface inherited from an existing one. Note  What I’ll be saying for a global view such as a page applies verbatim to subviews such as user controls or, perhaps, frames.

From Use-Cases to the View You always start development efforts from a use-case or perhaps a user story. In any case, you have a more or less defined idea of what the client expects your module to do and look like. You typically use wireframes to sketch out the final user interface of a given part of the application—commonly an individual view. After an agreement has been reached as to the information to show and templates to use (whether they are lists, tabs, collapsible panels, or data formats), you have just abstracted the view to a model. Figure 15-6 shows a possible mockup created with one of the most popular tools in this area—Balsamiq Mockups (see http://www.balsamiq.com).

FIGURE 15-6  A mockup showing a view for some default.aspx page.

Chapter 15  The Model-View-Presenter Pattern

625

With this idea in mind, getting an interface (or a base class) is usually not so hard. Here’s an example: public interface IDefaultView { IList Quotes { get; set; } String Message { get; set; } String Symbols { get; set; } }

You need to be able to read and write the content of the text box that contains the list of current symbols. Additionally, you need to read and write the content of a message, such as an error message or the time of the last update. You also need a collection of objects ­representing the stock information you want to display. With the view contract defined in detail (for example, agreement is required on IDs), ­designers and developers can start working in parallel. The nicest thing is that it really works like this in practice—it’s not a theory or an empty platitude trying to sell a certain point of view.

Implementing the Interface The view you are creating must implement the interface. In Web Forms, a view is a Web page. So here’s the skeleton of default.aspx: public partial class Default : Page, IDefaultView { ... }

The main responsibility of the view is hiding the details of the user interface elements. The interface exposes just a string property representing the stock symbols to retrieve, but the use-case suggests the user is expected to type names in an input field. Subsequently, the implementation of the Symbols property wraps the text box, as shown here: public partial class Default : Page, IDefaultView { ... #region IDefaultView Members public IList Quotes { get { return GridView1.DataSource as IList; } set { GridView1.DataSource = value; GridView1.DataBind(); } }

626

Part III  Design of the Application public string Message { get { return lblMessage.Text; } set { lblMessage.Text = value; } } public string Symbols { get { return txtSymbols.Text; } set { txtSymbols.Text = value; } } #endregion }

Where does StockInfo come from? That could either be one of the entities that populate the business layer or a specific data transfer object that is returned by the service layer. (See Chapter 14.) In this case, for simplicity, you can assume that you share the domain model with the presentation layer; so StockInfo comes from an Entities assembly referenced by both the service layer and the presentation. public class StockInfo { public String Company { get; set; } public String CurrentQuote { get; set; } public String Change { get; set; } public String Date { get; set; } public String Time { get; set; } }

The next step is adding a presenter component to the view. The presenter is a plain .NET class that must be instructed to work against any objects that implement the view interface.

Creating the Presenter Just like the controller in ASP.NET MVC, the presenter is a simple class in which all ­dependencies are (or at least, should be) injected explicitly. The class holds no significant state. The presenter lifetime is usually bound to the view.

Getting a Presenter’s Instance A common way of implementing MVP entails that the page (for example, the view) gets a new instance of the presenter for each request. You typically create the presenter instance in Page_Load, as shown here: public partial class Default : Page, IDefaultView { private DefaultPresenter _presenter = null; protected void Page_Load(Object sender, EventArgs e)

Chapter 15  The Model-View-Presenter Pattern

627

{ _presenter = new DefaultPresenter(this); } // Implementation of the IDefaultView interface ... }

An issue you might face is arranging a convenient naming convention to give each presenter class a name that is both unique and meaningful. The name of the page with a trailing string such as Presenter is a good approach; anyway, feel free to adapt it to your needs.

Using the Presenter In summary, a Web Forms page designed around the MVP pattern is a class that implements a given view interface and holds an instance of a presenter class. What’s the purpose of the presenter instance? The presenter is expected to expose a method for each possible action invoked by the user. Put another way, the presenter will have a unique, unambiguous, parameterless method for each event handler you need to have in the code-behind class. You still bind handlers to events fired by server controls; however, these handlers will simply forward the call to a method on the presenter instance. Here’s an example: public partial class Default : Page, IDefaultView { private DefaultPresenter _presenter = null; protected void Page_Load(Object sender, EventArgs e) { _presenter = new DefaultPresenter(this); } protected void btnRefresh_Click(Object sender, EventArgs e) { _presenter.Refresh(); } protected void btnRedirect_Click(Object sender, EventArgs e) { _presenter.Redirect(); } // Implementation of the IDefaultView interface ... }

From an architectural standpoint, something in the preceding code clashes with common sense: the code-behind class is merely a pass-through layer, so either the code-behind class or the presenter might be perceived as unnecessary layers. The fact is, you can’t easily ­remove code-behind in ASP.NET Web Forms—not without paying some costs in terms of the

628

Part III  Design of the Application

decreased productivity of teams. Code-behind classes have been around and have worked for a decade; you can’t just modify the framework to get rid of them. On the other hand, the presenter is just an extra layer you add deliberately with the precise intention of increasing maintainability and testability. Although it’s not objectively perfect, the code shown earlier is probably the best possible compromise to bring the benefits of MVP to ASP.NET Web Forms.

How Does the Presenter Retrieve Data? Let’s stop for a while and think about the type of code one would write in the Refresh method of the presenter. Given the use-case (and given the view mockup in Figure 15-6), the method is expected to connect to the application’s service layer and, from there, orchestrate a call to some service that actually provides quote information. The Refresh method needs to know the list of stocks for which you intend to run the query. Where does it get that information? The list of symbols is typed by the user in a text box; the presenter needs to access the text box but, ideally, you want this to happen without exposing the view inner details to the presenter. (Doing so would bind the presenter to a particular view.) Here’s the code of the­­ presenter, including its Refresh method: public class DefaultPresenter { private readonly IDefaultView _view; private readonly IQuoteServices _quoteServices; public DefaultPresenter(IDefaultView view) : this(view, new QuoteServices()) { } public DefaultPresenter(IDefaultView view, IQuoteServices quoteService) { _view = view; _quoteServices = quoteService; } public void Refresh() { // Get input from the view var symbols = _view.Symbols; // Execute the action var stocks = _quoteServices.GetQuotes(symbols); // Update the view _view.Quotes = stocks; _view.Message = String.Format("Data downloaded at: {0}", DateTime.Now); } }

Chapter 15  The Model-View-Presenter Pattern

629

At a minimum, the presenter is injected with the view object (for example, a reference to the ASP.NET page) through the constructor. The presenter, however, is instructed to work only against the members of the view interface. This means that the same presenter could be reused on different client platforms. As long as you have a Web and Windows application that operates according to the same actions, the chances for you to reuse the same presenter are definitely high. (This fact might not be true if you take advantage of some platform-specific features and operate the view through a different set of actions.) The presenter retrieves any input data it needs from the injected view. For example, it grabs any content in the txtSymbols text box via the Symbols property on the view interface. Likewise, it displays any response, such as the last update time, via the Message property. How the Message and Symbols properties actually operate on the view is transparent to the presenter. This transparency is the guarantee that the presenter is absolutely independent from the view.

Connecting the Presenter to the Service Layer The presenter is clearly part of the presentation layer. In a layered solution, the presentation layer is where you bridge the middle tier. The nearest endpoint in the middle tier land is the service layer, as discussed in Chapter 14. The service layer is a collection of classes (sometimes just WCF services) that orchestrate all the actions required by the application logic to serve a given request. The service layer should accept and retrieve data in a format that suits the presentation; if necessary, the service layer will translate from middle tier entities to presentation-only data transfer objects. If you’re lucky, you can even use the same entities on both the presentation and business layers. Note  In this regard, your luck mostly depends on the complexity of the use-case and the

c­ omplexity of the problem’s domain. In many cases, you can’t just find a match between middle tier models and view models. When this happens, using two distinct object models is the only way to go.

The presenter needs a reference to one or multiple objects in the service layer assembly. This means that, in the first place, the presentation layer needs to reference the service layer assembly. More importantly, you must inject in the presenter a reference to the specific service it will be using. In the previous listing, I used the poor man’s dependency injection approach. It consists of an overloaded constructor that defaults to a concrete class in production. However, by using a different constructor, you can support a fake layer for the purpose of unit testing. You can use any Inversion of Control (IoC) tool of choice here if you like that best. Hence, the presenter places a single call to the service layer to grab all it needs in the context of that use-case. The service layer returns data that the presenter will then incorporate

630

Part III  Design of the Application

in the view. The communication between the presenter and the service layer can be both ­synchronous and asynchronous, depending on your needs. Note  The service layer typically (but not necessarily) lives in its own assembly on the same server that hosts the Web application. With this configuration, there’s no need for you to implement the service layer as real WCF services. It becomes a necessity, instead, as soon as you need to use queued or transactional calls or just to deploy the middle tier on a different machine for scalability reasons.

Presenter in Action Wrapping up, the user is displayed a page with server controls for input as usual. The user ­interacts with the page and causes a postback. On the server, the request is processed as usual and results in a page call. During the page loading, the presenter is instantiated and receives a reference to the current page object. The postback event originates a call to a method in the presenter. The method typically uses the view object to retrieve input data and places a call to the service layer. An operation develops on the server and a response is served back to the service layer and,from there, is served to the presenter. Finally, the presenter updates the view. (See Figure 15-7.)

FIGURE 15-7  MVP is used under the hood, but you can’t see it from the UI.

Chapter 15  The Model-View-Presenter Pattern

631

Sharing the Presenter with a Windows Application Admittedly, the feature I’m going to discuss is pretty cool and makes for a compelling demo. There’s no magic behind it, and I consider it to be a lucky scenario—real, but special. Under certain conditions, the presenter can be shared with the same application written for other platforms, such as Windows Forms and WPF. (See Figure 15-8.) All that you need is a Windows form that implements the same view interface. At that point, the presenter has all it needs—a view object. What about the service layer? If you can’t reuse the same service layer you had for the Web application, the dependency injection design you adopted for the presenter class makes it easy to change to a more specific one: public partial class DefaultForm : Form, IDefaultView { private DefaultPresenter _presenter; public DefaultView() { InitializeComponent(); // Initialize the presenter (for Windows) _presenter = new DefaultPresenter(this, new WinFormsQuoteServices()); } ... }

FIGURE 15-8  Distinct applications share the same presenter.

Figure 15-7 and Figure 15-8 show two different versions of the same application—one for the Web and one for the desktop. As long as the presentation logic remains the same, and the dependency on the service layer can be managed, you can (and are encouraged to) reuse the same presenter class. Be aware, however, that this might not always be the case.

632

Part III  Design of the Application

Note  If one of the potential clients is based on Silverlight, you should also consider that some of the features your code relies on might not be supported in Silverlight. In addition, Silverlight 4 has binary compatibility with .NET code, but the same isn’t true for earlier versions. Also, you are still unable to reference a .NET assembly from a Silverlight project; the opposite, though, works as long as there are no code incompatibilities.

Navigation The presenter is also responsible for implementing navigation within the application. In ­particular, the presenter is responsible for enabling (or disabling) any subviews contained in the primary view and for selecting and reaching the next view.

The Application Controller Pattern To handle navigation within views, MVP goes hand in hand with another pattern—Application Controller. The pattern defines a central console that holds all the logic to ­determine the next view and handle the screen navigation and the flow of an application. (See Figure 15-9.) View

View

View

Presenter

Presenter

Presenter

Application Controller front-end

Navigation Workflow FIGURE 15-9  The application controller.

When it comes to implementation, you can proceed by creating a static class (say, you call it Navigator) that acts as the application’s front end for navigation. Here, the Navigator class is a plain container for the real navigation logic. You inject the application-specific navigation workflow through an additional component. The extra layer represented by the navigation workflow shields the presenter from knowing the details of the platform specific navigation. For example, navigation within a Web Forms application is based on Response.Redirect, whereas navigation relies on form-based display in Windows Forms.

Chapter 15  The Model-View-Presenter Pattern

633

Defining the Navigation Workflow Here’s a possible implementation of the interface that represents the navigation workflow. The interface includes a method to navigate directly to a given view and another to navigate from the current view to the next: public interface INavigationWorkflow { void Goto(String view); void NextViewFrom(String currentView); }

The Navigator class wraps an object that implements this interface and exposes a façade to the presenter: public static class Navigator { private static INavigationWorkflow _navigationWorkflow; private static Object _navigationArgument; public static void Attach(INavigationWorkflow workflow) { if (workflow != null) _navigationWorkflow = workflow; } public static Object Argument { get { return _navigationArgument; } } public static void Goto(String view) { if (_navigationWorkflow != null) _navigationWorkflow.Goto(view); } public static void Goto(String view, Object argument) { if (_navigationWorkflow != null) { _navigationArgument = argument; Navigator.Goto(view); } } public static void NextViewFrom(String currentView) { if (_navigationWorkflow != null) _navigationWorkflow.NextViewFrom(currentView); }

634

Part III  Design of the Application public static void NextViewFrom(String currentView, Object argument) { if (_navigationWorkflow != null) { _navigationArgument = argument; Navigator.NextViewFrom(currentView, argument); } } }

The Navigator class is a little more than just a wrapper for the interface. The class features an Argument property through which the presenter can specify data to be passed to the view. How navigation is implemented and how data is passed depends on the actual implementation of the navigation workflow.

Navigating Within a Web Forms Site In Web Forms, navigation between pages can be achieved through a redirect. The workflow interface allows you to assign a name to a view (possibly, but not necessarily, the name of the page), which is then resolved with a redirect to a given URL. Here’s an example: public class SiteNavigationWorkflow : INavigationWorkflow { public void Goto(String view) { switch (view) { case "home": HttpContext.Current.Response.Redirect("/default.aspx"); break; case "test": HttpContext.Current.Response.Redirect( HttpUtility.UrlEncode(String.Format("/test.aspx?x='{0}'", Navigator.Argument))); break; } } public void NextViewFrom(String currentView) { switch (currentView) { case "home": // Calculate next view using logic break; } } }

Chapter 15  The Model-View-Presenter Pattern

As an example, let’s have a look at a possible implementation of the same interface for a Windows Forms application: public class AppNavigationWorkflow : INavigationWorkflow { private Form _fooForm; private readonly Form _defaultView; public AppNavigationWorkflow(Form main) { _defaultView = main; } public void Goto(string view) { switch (view) { case "home": if (_fooForm != null && !_fooForm.IsDisposed) { _fooForm.Close(); _fooForm = null; } break; case "foo": if (_fooForm == null || _fooForm.IsDisposed) { _fooForm = new FooForm(); _fooForm.Owner = _defaultView; } _fooForm.ShowDialog(); break; } } public void NextViewFrom(string currentView) { switch (currentView) { case "home": // Calculate next view using logic break; } } }

As you can see, in Windows you might have a radically different approach to navigation, which basically consists of displaying and hiding dialog boxes and windows. Still, from the presenter’s standpoint, all you need to do is invoke the same Goto method: // From presenter's code public void Redirect() { Navigator.Goto("test", "test value"); }

635

636

Part III  Design of the Application

In ASP.NET, it produces the view shown in Figure 15-10.

FIGURE 15-10  Navigating to a specific page.

Finally, let’s see how you can attach a platform-specific workflow to the presentation layer. The binding takes place at the application startup—for example, in global.asax: void Application_Start(Object sender, EventArgs e) { var simpleWorkflow = new SiteNavigationWorkflow(); Navigator.Attach(simpleWorkflow); }

The use of the term “workflow” here is not coincidental. The method Goto in INavigationWorkflow allows you to reach a specific URL; the method NextViewFrom, which can be implemented just by using a workflow based on the current view, determines what comes next.

Testability in Web Forms with MVP For many years, developers in the .NET space didn’t pay much attention to emerging ­patterns and practices. The deep application of the RAD paradigm led to a focus on tools and techniques to do it faster, rather than doing it right the first time. Debugging always prevailed over unit testing as the major technique to help check whether your development efforts were on track. Web Forms has a number of merits, but it certainly doesn’t stand out for the aid it provides with regard to testability. However, using the MVP pattern makes the most relevant part of your Web Forms code—for example, the presenter—far easier to test, and especially unit-test.

Chapter 15  The Model-View-Presenter Pattern

637

Writing Testable Code If you look at the functionality, there’s nearly no difference at all between testable-code-thatworks and untestable-code-that-works. So where’s the benefit of testing? Essentially, it lies in what might happen after you deploy your code to production. The customer might come back and ask you to make changes or implement new features. Or, worse yet, an unexpected bug might show up. In all these cases, you need to put your hands on the code to update it. Your goal is updating what has to be updated without breaking anything else. How do you prove that you didn’t break any existing features? A well-written set of unit tests can give you the measure of how good your software is now compared to the stage before. If your software still passes all the tests after the updates, well, there’s a great chance that untouched features are still effective.

Aspects of Testable Code The beauty of tests is in the speed at which you can check whether your changes caused ­regression. At its core, a test is a program that invokes a software module that passes edge values to prove whether or not a particular behavior is working correctly. Note that not all code is inherently testable. You have to keep three fundamental aspects in mind when writing a test: visibility, control, and simplicity. Visibility indicates the degree at which the code under test allows you to observe changes in the state of the code. If changes are not observable, how can you determine whether the code works or fails? Control indicates the degree at which the behavior of code under test can be influenced by external input. To be effective, a test must pass in selected input values. If input is hard-­ coded, running an effective test is much harder. Finally, simplicity is an aspect of code that is never out of place. The simpler the code is, the simpler it is to test and the more reliable any results will be.

Unit Testing Unit testing verifies that individual units of code are working properly according to their software contract. A unit is the smallest part of an application that is testable—typically, a method. Unit testing consists of writing and running a small program (referred to as a test harness) that instantiates classes and invokes methods in an automatic way. In the end, running a ­battery of tests is much like compiling. You click a button, you run the test harness and, at the end of it, you know what went wrong, if anything.

638

Part III  Design of the Application

In its simplest form, a test harness is a manually written program that reads test-case input values and the corresponding expected results from some external files. Then the test harness calls methods using input values and compares results with expected values. Obviously, writing such a test harness entirely from scratch is, at a minimum, time consuming and error prone. But, more importantly, it is restrictive in terms of the testing capabilities you can take advantage of. A very effective way to conduct unit testing is to use an automated test framework. An ­automated test framework is a developer tool that normally includes a runtime engine and a framework of classes for simplifying the creation of test programs. One of these frameworks—MSUnit—is integrated in Visual Studio. All you have to do is create a new project of type Test. (Note that other tools, both open-source and commercial, are available for unit testing, some of which are also integrated with Visual Studio.)

Test-Driven Development You can write tests at any time—before or after the method you intend to test. This is mostly a matter of preference and methodology. It can become a religious matter sometimes, but frankly nobody can claim that one approach or the other is absolutely and objectively better. Test-driven development (TDD) is a methodology that naturally gets you to think about the expected interface and behavior of methods well before you actually start writing the code. TDD is an approach that might appear radical at first and that certainly takes time to fully digest. However, its goal is quite simple in the end: help to quickly write clean code that works. In the traditional approach to coding, you develop a method according to the idea you have of the features the method must support. You start with a relatively simple body, and then you increase its capabilities until you reach the original goal. Along the way, you use the debugger to see how things are going and whether data is being moved around correctly. When you’ve determined that all is OK, if you’re a scrupulous developer you consider writing a bunch of unit tests to verify the behavior of the method from a few other angles to make it easier to catch regression failures later. If you proceed this way, you eventually decide that tests are way too boring and hard to write and don’t really give you any concrete benefits. It’s your code, after all, and it works. A test won’t make the code richer and more appealing to users. So you just stop writing tests! When the complexity of the code rises above a certain threshold, the debugger alone is no longer sufficient for testing and needs to be backed by some good unit tests. You can write unit tests before you code (as TDD suggests) or after you’re done. It doesn’t really matter when you do it, as long as you come up with an exhaustive set of tests. TDD is considered an effective methodology to achieve just this result. Some small changes in the Visual Studio 2010 refactoring tools and the Test project template also make it worth a try for Web Forms developers.

Chapter 15  The Model-View-Presenter Pattern

639

Testing a Presenter Class To test-run the increased testability of Web Forms with MVP let’s go through a test project aimed at ensuring an application correctly gets price quotes for a list of stock symbols.

Creating a Unit Test Suppose you have the test project ready and you’ve added a unit test to it. A unit test is a class that looks like the one shown here: [TestClass] public class DefaultPresenterTests { [TestMethod] public void TestIfQuotesAreBeingReturnedForEverySymbol() { } }

Note  Choosing the right name for a test method is as important as writing good code.

Thetest name must be representative of the scenario you’re testing and include a quick explanation of the scenario. A detailed guide from an expert in the field can be found here: http://www.osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.htm.

The test is expected to test the Refresh method of the presenter class we considered earlier. Figure 15-11 illustrates the first roadblock we encounter.

FIGURE 15-11  Attempting to create a unit test.

The idea is to get an instance of the presenter and invoke each method, passing some ad hoc input value (the aspect control) and observing results (the aspect visibility). As IntelliSense shows up in the figure, however, you need to provide some objects in order to instantiate the presenter.

640

Part III  Design of the Application

The presenter has two dependencies—on the view and on the quote service. Your goal is to test the logic in the presenter class; you don’t want to test the view here and the service. These will possibly be the subject of other tests. On the other hand, you still need to provide a view and a service. Thankfully, you used a bit of dependency injection pattern in the design of the presenter class; therefore, you can now obtain a test double object and pass that in. A test double is an object that looks like another one without providing the same behavior. A test double is typically coded in either of two ways—as a fake or a mock. A fake is an object with a hard-coded behavior and no state; a mock is an object with a dynamically defined behavior. You typically code the fake yourself as a custom class in the project and use ad hoc frameworks for mocks. Moq is one of these frameworks. Important  Overall, there are four main types of test doubles: dummy objects, fakes, stubs, and mocks. A general consensus about what each test double object does exists for dummy objects and mocks only. A dummy object has no behavior and exists just to fill in a parameter list. A mock object is what I described above. What about fakes and stubs, then? You might read subtly different definitions for each term and even find out that various authors use the term fake to indicate what another author classifies as a stub, and vice versa. My pragmatic approach is that it would be much simpler if we limit ourselves to two types of doubles: mocks and another, simpler, type of double you name the way you like. In this context, I prefer the term fake. As far as this chapter is concerned, feel free to ­replace fake with stub if that makes your reading easier in any way.

Here are some sample test doubles for the view and quote service: public class FakeDefaultView : IDefaultView { private readonly String _symbols; public FakeDefaultView(String fakeSymbols) { _symbols = fakeSymbols; } public IList Quotes { get; set; } public String Message { get; set; } public String Symbols { get { return _symbols; } set {} } } public class FakeQuoteService : IQuoteServices { public IList GetQuotes(String symbols) { var stocks = symbols.Split(','); return stocks.Select(s => new StockInfo() { Change = "0%", Company = s,

Download from Wow! eBook

Chapter 15  The Model-View-Presenter Pattern

641

CurrentQuote = "1.1", Date = DateTime.Today.ToString(), Time = DateTime.Now.ToString() }).ToList(); } }

Finally, here’s the unit test: [TestMethod] public void TestIfQuotesAreBeingReturnedForEverySymbol() { // Arrange const String testData = "XXX,YYY,ZZZ"; var inputSymbols = testData.Split(',').ToList(); var view = new FakeDefaultView(testData); var presenter = new DefaultPresenter(view, new FakeQuoteService()); // Act presenter.Refresh(); // Assert Assert.AreEqual(view.Quotes.Count, inputSymbols.Count); foreach(var quote in view.Quotes) { Assert.IsTrue(inputSymbols.Contains(quote.Company)); } }

Ideally, a unit test is articulated in three main blocks: prepare the ground for executing the method under test, execute the method, and then check results against assertions.

Testing Presenters in Isolation A relevant benefit that MVP provides is isolating the presenter code from the rest of the world. To be precise, MVP gives you guidance on how to isolate the presenter from the view, but it says nothing specific about the rest of the system. This means that keeping the ­presenter isolated from the middle tier is your responsibility. When you test a method, you want to focus only on the code within that method. All that you want to know is whether that code provides the expected results in the tested scenarios. To get this, you need to get rid of all dependencies the method might have. If the method, say, invokes another class, you assume that the invoked class will always return correct results. In this way, you eliminate at the root the risk that the method fails under test because a failure occurred down the call stack. If you test method A and it fails, the reason has to be found exclusively in the source code of method A and not in any of its dependencies. Achieving isolation is far easier if you apply dependency injection to the design of classes. For presenters, this means being injected with the view object and also any service layer component the presenter needs to work with. When this happens, testing methods on the presenter is really a piece of cake. (See Figure 15-12.)

642

Part III  Design of the Application

FIGURE 15-12  Running unit tests.

Summary For an ASP.NET application, you have two main options when it comes to choosing an application model. You can go with the traditional ASP.NET Web application model, which is based on the Page Controller pattern, or you can move toward ASP.NET MVC. The traditional ASP.NET application model can be improved with a deeper separation of concerns by using a manual implementation of the MVP pattern. The MVP pattern isolates the view from the presenter and abstracts the view to an interface. In this way, the presenter can be coded against the view interface and becomes a reusable and testable piece of code. To finish with a flourish, you might also want to take out of the presenter any code that represents a dependency on the service layer. If you do, writing unit tests for the presenter becomes really easy and effective. Even with these changes in place, however, ASP.NET Web Forms remains a hard-to-test framework. What if you need to deal with Cache or Session in your presenter? None of these objects will be available in the test project unless you spin the entire ASP.NET runtime. In other words, testing in isolation is very difficult. Options? Well, the best you can do is wrap access to Session, Cache, and other intrinsic ASP.NET objects in custom classes exposing a fixed interface. At the cost of an additional fairly thin layer, you gain the benefit of isolating presenters from ASP.NET runtime objects. And ASP.NET intrinsic objects are the subject of the next few chapters.

Programming Microsoft® ASP.NET 4

Part IV

Infrastructure of the Application In this part: Chapter 16: The HTTP Request Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Chapter 17: ASP.NET State Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Chapter 18: ASP.NET Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Chapter 19: ASP.NET Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

645 675 721 779

643

Chapter 16

The HTTP Request Context All great things are simple, and many can be expressed in single words. —Winston Churchill Each ASP.NET request goes hand in hand with a companion object for its entire lifetime—an instance of the HttpContext class. The HttpContext object wraps up all the HTTP-specific ­information available about the request. It is then used by the various HTTP modules and used to group references to intrinsic worker objects such as Request, Response, and Server. In this chapter, we’ll first review the startup process of the ASP.NET application and then move on to examine the various objects that form the context of the HTTP request.

Initialization of the Application Each ASP.NET request is carried out by an ASP.NET application object. An ASP.NET ­application consists of an instance of the HttpApplication class that you briefly met in Chapter2, “ASP.NET and IIS.” HttpApplication is a global.asax-derived object that handles all HTTP requests directed to a particular virtual folder. An ASP.NET running application is wholly represented by its virtual folder and, optionally, by the global.asax file. The virtual folder name is a sort of key that the HTTP runtime uses to ­selectively identify which of the running applications should take care of the incoming request. The global.asax file, if present, contains settings and code for responding to application-level events raised by ASP.NET or by registered HTTP modules that affect the application. The particular HttpApplication selected is responsible for managing the entire lifetime of the request it is assigned to. That instance of HttpApplication can be reused only after the ­request has been completed. If no HttpApplication object is available, either because the ­application has not been started yet or all valid objects are busy, a new HttpApplication is created and pooled.

Properties of the HttpApplication Class Although the HttpApplication provides a public constructor, user applications never need to create instances of the HttpApplication class directly. The ASP.NET runtime infrastructure always does the job for you. As mentioned, instances of the class are pooled and, as such, can process many requests in their lifetime, but always one at a time. Should concurrent

645

646

Part IV Infrastructure of the Application

r­ equests arrive for the same application, additional instances are created. Table 16-1 lists the ­properties defined for the class. TABLE 16-1  HttpApplication

Properties

Property

Description

Application

Instance of the HttpApplicationState class. It represents the global and shared state of the application. It is functionally equivalent to the ASP intrinsic Application object.

Context

Instance of the HttpContext class. It encapsulates in a single object all HTTPspecific information about the current request. Intrinsic objects (for example, Application and Request) are also exposed as properties.

Modules

Gets the collection of modules that affect the current application.

Request

Instance of the HttpRequest class. It represents the current HTTP request. It is functionally equivalent to the ASP intrinsic Request object.

Response

Instance of the HttpResponse class. It sends HTTP response data to the client. It is functionally equivalent to the ASP intrinsic Response object.

Server

Instance of the HttpServerUtility class. It provides helper methods for processing Web requests. It is functionally equivalent to the ASP intrinsic Server object.

Session

Instance of the HttpSessionState class. It manages user-specific data. It is f­ unctionally equivalent to the ASP intrinsic Session object.

User

An IPrincipal object that represents the user making the request.

The HttpApplication is managed by the ASP.NET infrastructure, so how can you take ­advantage of the fairly rich, public programming interface of the class? The answer is that properties and, even more, overridable methods and class events can be accessed and ­programmatically manipulated in the global.asax file. (I’ll return to global.asax in a moment.)

Application Modules The property Modules returns a collection of application-wide components providing ad hoc services. An HTTP module component is a class that implements the IHttpModule interface. Modules can be considered the managed counterpart of ISAPI filters; they are kind of request interceptors with the built-in capability of modifying the overall context of the request being processed. The Microsoft .NET Framework defines a number of standard modules, as listed in Table 16-2. Custom modules can be defined too. I cover this particular aspect of HTTP programming in Chapter 18, “ASP.NET Caching.” TABLE 16-2  ASP.NET

Modules

Module

Description

AnonymousIdentification

Assigns anonymous users a fake identity.

FileAuthorization

Verifies that the remote user has Microsoft Windows NT ­ ermissions to access the requested resource. p

FormsAuthentication

Enables applications to use forms authentication.

Chapter 16  The HTTP Request Context

Module

Description

OutputCache

Provides page output caching services.

PassportAuthentication

Provides a wrapper around Passport authentication services.

Profile

Provides user profile services.

RoleManager

Provides session-state services for the application.

ScriptModule

Used to implement page methods in AJAX pages.

SessionState

Provides session-state services for the application.

UrlAuthorization

Provides URL-based authorization services to access specified r­ esources.

UrlRouting

Provides support for URL routing

WindowsAuthentication

Enables ASP.NET applications to use Windows and Internet Information Services (IIS)-based authentication.

647

The list of default modules is defined in the machine.config file. By creating a proper web.config file, you can also create an application-specific list of modules. (Configuration is covered in Chapter 3, “ASP.NET Configuration.”)

Methods of the HttpApplication Class The methods of the HttpApplication class can be divided into two groups: operational ­methods and event handler managers. The HttpApplication operational methods are ­described in Table 16-3. TABLE 16-3  HttpApplication

Operational Methods

Method

Description

CompleteRequest

Sets an internal flag that causes ASP.NET to skip all successive steps in the pipeline and directly execute EndRequest. It’s mostly useful to HTTP modules.

Dispose

Overridable method, cleans up the instance variables of all ­registered modules after the request has been served. At this time, Request, Response, Session, and Application are no longer available.

GetOutputCacheProviderName

Overridable method, returns the currently configured provider for handling output page caching. (I’ll say more about output page caching in Chapter 18.)

GetVaryByCustomString

Overridable method, provides a way to set output caching based on a custom string for all pages in the application. (I’ll say more about output page caching in Chapter 18.)

Init

Overridable method that executes custom initialization code ­after all modules have been linked to the application to serve the request. You can use it to create and configure any object that you want to use throughout the request processing. At this time, Request, Response, Session, and Application are not yet available.

648

Part IV Infrastructure of the Application

Note that the Init and Dispose methods are quite different from well-known event handlers such as Application_Start and Application_End. Init executes for every request directed to the Web application, whereas Application_Start fires only once in the Web application’s lifetime. Init indicates that a new instance of the HttpApplication class has been initialized to serve an incoming request; Application_Start ­denotes that the first instance of the HttpApplication class has been created to start up the Web application and serve its very first request. Likewise, Dispose signals the next ­termination of the request processing but not necessarily the end of the application. Application_End is raised only once, when the application is being shut down. Note  The lifetime of any resources created in the Init method is limited to the execution of the

current request. Any resource you allocate in Init should be disposed of in Dispose, at the latest. If you need persistent data, resort to other objects that form the application or session state.

In addition to the operational methods in Table 16-3, a few other HttpApplication methods are available to register asynchronous handlers for application-level events. These methods are of little interest to user applications and are used only by HTTP modules to hook up the events generated during the request’s chain of execution.

Events of the HttpApplication Class Table 16-4 describes the event model of the HttpApplication class—that is, the set of events that HTTP modules, as well as user applications, can listen to and handle. TABLE 16-4  HttpApplication

Events

Event

Description

AcquireRequestState, PostAcquireRequestState

Occurs when the handler that will actually serve the request ­ cquires the state information associated with the request. a

AuthenticateRequest, PostAuthenticateRequest

Occurs when a security module has established the identity of the user.

AuthorizeRequest, PostAuthorizeRequest

Occurs when a security module has verified user authorization.

BeginRequest

Occurs as soon as the HTTP pipeline begins to process the r­ equest.

Disposed

Occurs when the HttpApplication object is disposed of as a result of a call to Dispose.

EndRequest

Occurs as the last event in the HTTP pipeline chain of execution.

Error

Occurs when an unhandled exception is thrown.

LogRequest, PostLogRequest

Occurs when the system logs the results of the request.

PostMapRequestHandler

Occurs when the HTTP handler to serve the request has been found.

Chapter 16  The HTTP Request Context

649

Event

Description

PostRequestHandlerExecute

Occurs when the HTTP handler of choice finishes execution. The response text has been generated at this point.

PreRequestHandlerExecute

Occurs just before the HTTP handler of choice begins to work.

PreSendRequestContent

Occurs just before the ASP.NET runtime sends the response text to the client.

PreSendRequestHeaders

Occurs just before the ASP.NET runtime sends HTTP headers to the client.

ReleaseRequestState, PostReleaseRequestState

Occurs when the handler releases the state information associated with the current request.

ResolveRequestCache, PostResolveRequestCache

Occurs when the ASP.NET runtime resolves the request through the output cache.

UpdateRequestCache, PostUpdateRequestCache

Occurs when the ASP.NET runtime stores the response of the current request in the output cache to be used to serve subsequent requests.

To handle any of these events asynchronously, an application will use the corresponding method whose name follows a common pattern: AddOnXXXAsync, where XXX stands for the event name. To hook up some of these events in a synchronous manner, an application will define in the global.asax event handler procedures with the following signature: public void Application_XXX(Object sender, EventArgs e) { // Do something here }

Of course, the XXX placeholder must be replaced with the name of the event from Table 16-4. All the events in the preceding table provide no event-specific data. You can alsouse the following simpler syntax without losing additional information and ­programming power: public void Application_XXX() { // Do something here }

In addition to the events listed in Table 16-4, in global.asax an application can also handle Application_Start and Application_End. When ASP.NET is about to fire BeginRequest for the very first time in the application lifetime, it makes Application_Start precede it. EndRequest will happen at the end of every request to an application. Application_End occurs outside the context of a request, when the application is ending. As you saw in Chapter 2, application events are fired in the following sequence:

1. BeginRequest  The ASP.NET HTTP pipeline begins to work on the request. This event reaches the application after Application_Start.

650

Part IV Infrastructure of the Application

2. AuthenticateRequest  The request is being authenticated. All the internal ASP.NET authentication modules subscribe to this event and attempt to produce an identity. If no authentication module produced an authenticated user, an internal default ­authentication module is invoked to produce an identity for the unauthenticated user. This is done for the sake of consistency so that code doesn’t need to worry about null identities.

3. PostAuthenticateRequest  The request has been authenticated. All the information available is stored in the HttpContext’s User property.

4. AuthorizeRequest  The request authorization is about to occur. This event is commonly handled by application code to do custom authorization based on business logic or other application requirements.

5. PostAuthorizeRequest  The request has been authorized.

6. ResolveRequestCache  The ASP.NET runtime verifies whether returning a previously cached page can resolve the request. If a valid cached representation is found, the ­request is served from the cache and the request is short-circuited, calling only any registered EndRequest handlers.

7. PostResolveRequestCache  The request can’t be served from the cache, and the ­procedure continues. An HTTP handler corresponding to the requested URL is created at this point. If the requested resource is an .aspx page, an instance of a page class is created.

8. MapRequestHandler  The event is fired to determine the request handler.

9. PostMapRequestHandler  The event fires when the HTTP handler corresponding to the requested URL has been successfully created.

10. AcquireRequestState  The module that hooks up this event is willing to retrieve any state information for the request. A number of factors are relevant here: the handler must support session state in some form, and there must be a valid session ID.

11. PostAcquireRequestState  The state information (such as Application, Session) has been acquired.

12. PreRequestHandlerExecute  This event is fired immediately prior to executing the handler for a given request. The handler does its job and generates the output for the client.

13. ExecuteRequestHandler  The handler does its job and processes the request.

14. PostRequestHandlerExecute  This event is raised when the handler has generated the response text.

15. ReleaseRequestState  This event is raised when the handler releases its state ­information and prepares to shut down. This event is used by the session state module to update the dirty session state if necessary.

Chapter 16  The HTTP Request Context

651

16. PostReleaseRequestState  The state, as modified by the page execution, has been persisted. Any relevant response filtering is done at this point. (I’ll say more about this topic later.)

17. UpdateRequestCache  The ASP.NET runtime determines whether the generated ­output, now also properly filtered by registered modules, should be cached to be reused with upcoming identical requests.

18. PostUpdateRequestCache  The page has been saved to the output cache if it was ­configured to do so.

19. LogRequest  The event indicates that the runtime is ready to log the results of the ­request. Logging is guaranteed to execute even if errors occur.

20. PostLogRequest  The request has been logged.

21. EndRequest  This event fires as the final step of the HTTP pipeline. Control passes back to the HttpRuntime object, which is responsible for the actual forwarding of the ­response to the client. At this point, the text has not been sent yet. If an unhandled error occurs at any point during the processing, it is treated using the code (if any) associated with the Error event. As mentioned, events can be handled in HTTP ­modules as well as in global.asax. Note  The Error event provides a centralized console for capturing any unhandled exception in order to recover gracefully or just to capture the state of the application and log it. By writing an HTTP module that just intercepts the Error event, you have a simple but terribly effective and reusable mechanism for error handling and logging. At the end of the day, this is the core of the engine of popular tools for ASP.NET error handling, logging, and reporting—ELMAH.

The global.asax File The global.asax file is used by Web applications to handle some application-level events raised by the ASP.NET runtime or by registered HTTP modules. The global.asax file is ­optional. If it is missing, the ASP.NET runtime environment simply assumes you have no application or module event handlers defined. To be functional, the global.asax file must be located in the root directory of the application. Only one global.asax file per application is accepted. Any global.asax files placed in subdirectories are simply ignored. Note that Microsoft Visual Studio doesn’t list global.asax in the items you can add to the project if there already is one.

652

Part IV Infrastructure of the Application

Compiling global.asax When the application is started, global.asax, if present, is parsed into a source class and ­compiled. The resultant assembly is created in the temporary directory just as any other ­dynamically generated assembly would be. The following listing shows the skeleton of the C#code that ASP.NET generates for any global.asax file: namespace ASP { public class global_asax : System.Web.HttpApplication { // // The source code of the "global.asax" file is flushed // here verbatim. For this reason, the following code // in global.asax would generate a compile error. // int i; // i = 2; // can’t have statements outside methods // } }

The class is named ASP.global_asax and is derived from the HttpApplication base class. In most cases, you deploy global.asax as a separate text file; however, you can also write it as a class and compile it either in a separate assembly or within your project’s assembly. The class source code must follow the outline shown earlier and, above all, must derive from HttpApplication. The assembly with the compiled version of global.asax must be deployed in the application’s Bin subdirectory. Note, though, that even if you isolate the logic of the global.asax file in a precompiled ­assembly, you still need to have a (codeless) global.asax file that refers to the assembly, as shown in the following code:

You’ll learn more about the syntax of global.asax in the next section, “Syntax of global.asax.” With a precompiled global application file, you certainly don’t risk exposing your source code over the Web to malicious attacks. However, even if you leave it as source code, you’re ­somewhat safe. The global.asax file, in fact, is configured so that any direct URL request for it is automatically rejected by Internet Information Services (IIS). In this way, external users cannot download or view the code it contains. The trick that enables this behavior is the following line of code, excerpted from machine.config:

Chapter 16  The HTTP Request Context

653

ASP.NET registers with IIS to handle .asax resources, but then it processes those direct ­requests through the HttpForbiddenHandler HTTP handler. As a result, when a browser requests an .asax resource, an error message is displayed on the page, as shown in Figure 16-1.

FIGURE 16-1  Direct access to forbidden resources, such as *.asax files, results in a server error.

When the global.asax file of a running application is modified, the ASP.NET runtime detects the change and prepares to shut down and restart the application. It waits until all pending requests are completed and then fires the Application_End event. When the next request from a browser arrives, ASP.NET reparses and recompiles the global.asax file, and again raises the Application_Start event.

Syntax of global.asax A few elements determine the syntax of the global.asax file. They are application directives, code declaration blocks, server-side tags, and static properties. These elements can be used in any order and number to compose a global.asax file.

Application Directives The global.asax file supports three directives: @Application, @Import, and @Assembly. The @Import and @Assembly directives work as shown in Chapter 3. The @Import directive ­imports a namespace into an application; the @Assembly directive links an assembly to the application at compile time.

654

Part IV Infrastructure of the Application

The @Application directive supports a few attributes: Description, Language, and Inherits. Description can contain any text you want to use to describe the behavior of the application. This text has only a documentation purpose and is blissfully ignored by the ASP.NET parser. Language indicates the language being used in the file. The Inherits attribute indicates a code-behind class for the application to inherit. It can be the name of any class derived from the HttpApplication class. The assembly that contains the class must be located in the Bin subdirectory of the application.

Code Declaration Blocks A global.asax file can contain code wrapped by a tag. Just as for pages, the tag must have the runat attribute set to server. The language attribute indicates the language used throughout: ...

If the language attribute is not specified, ASP.NET defaults to the language set in the ­configuration, which is Microsoft Visual Basic .NET. The source code can also be loaded from an external file, whose virtual path is set in the Src attribute. The location of the file is ­resolved using Server.MapPath—that is, starting under the physical root directory of the Web application.

In this case, any other code in the declaration block is ignored. Notice that ASP.NET enforces syntax rules on the tag. The runat attribute is mandatory, and if the block has no content, the Src must be specified.

Server-Side Tags The server-side tag lets you create new objects using a declarative syntax. The ­ tag can take three forms, as shown in the following lines of code, depending on the specified reference type:

In the first case, the object is identified by the name of the class and assembly that c­ ontains it. In the last two cases, the object to create is a COM object identified by the program identifier (progid) and the 128-bit CLSID, respectively. As one can easily guess, the classid, progid, and class attributes are mutually exclusive. If you use more than one within a single

Chapter 16  The HTTP Request Context

655

s­ erver-side tag, a compile error is generated. Objects declared in this way are loaded when the application is started. The scope attribute indicates the scope at which the object is declared. The allowable v­ alues are defined in Table 16-5. Unless otherwise specified, the server-side object is valid only ­within the boundaries of the HTTP pipeline that processes the current request. Other settings that increase the object’s lifetime are application and session. TABLE 16-5  Feasible

Scopes for Server-Side Tags

Scope

Description

pipeline

Default setting, indicates the object is available only within the context of the ­current HTTP request

application

Indicates the object is added to the StaticObjects collection of the Application object and is shared among all pages in the application

session

Indicates the object is added to the StaticObjects collection of the Session object and is shared among all pages in the current session

Static Properties If you define static properties in the global.asax file, they will be accessible for reading and writing by all pages in the application: public static int Counter = 0;

The Counter property defined in the preceding code works like an item stored in Application—namely, it is globally visible across pages and sessions. Consider that concurrent access to Counter is not serialized; on the other hand, you have a strong-typed, direct global item whose access speed is much faster than retrieving the same piece of information from a generic collection such as Application. To access the property from a page, you must use the ASP.global_asax qualifier, shown here: Response.Write(ASP.global_asax.Counter.ToString());

If you don’t particularly like the ASP.global_asax prefix, you can alias it as long as you use C#. Add the following code to a C#-based page (or code-behind class) for which you need to ­access the globals: using Globals = ASP.global_asax;

The preceding statement creates an alias for the ASP.global_asax class (or whatever name your global.asax class has). The alias—Globals in this sample code—can be used throughout your code wherever ASP.global_asax is accepted. In ASP.NET 4, however, you can also rely on the dynamic type.

656

Part IV Infrastructure of the Application

The HttpContext Class During the various steps of the request’s chain of execution, an object gets passed along from class to class—this object is the HttpContext object. HttpContext encapsulates all the information available about an individual HTTP request that ASP.NET is going to handle. The HttpContext class is instantiated by the HttpRuntime object while the request processing mechanism is being set up. Next, the object is flowed throughout the various stages of the request’s lifetime. Important  Before I get into the details of HttpContext and other ASP.NET intrinsic ­objects,

I should note that in ASP.NET 4 all these objects inherit from a base class. For example, HttpContext derives from HttpContextBase and HttpResponse extends the capabilities of HttpResponseBase. The reason is to make it easier to write unit tests to check the behavior of code-behind classes. By using base classes, you can more easily create mocks of intrinsic objects and inject them into the classes. In Chapter 15, “The Model-View-Presenter Pattern,” you saw an approach to testability that will benefit from base classes for intrinsic objects. Note that the ASP.NET Cache is not included in the list of objects with a base class.

Properties of the HttpContext Class Table 16-6 enumerates all the properties exposed by the HttpContext class. The class ­represents a single entry point for a number of intrinsic objects such as classic ASP intrinsics and ASP.NET-specific Cache and User objects. TABLE 16-6  HttpContext

Properties

Property

Description

AllErrors

Gets an array of Exception objects, each of which represents an error that occurred while processing the request.

Application

Gets an instance of the HttpApplicationState class, which contains the global and shared states of the application.

ApplicationInstance

Gets or sets the HttpApplication object for the current request. The actual type is the global.asax code-behind class. It makes a cast to access public properties and methods you might have defined in global.asax.

Cache

Gets the ASP.NET Cache object for the current request.

Current

Gets the HttpContext object for the current request.

CurrentHandler

Gets the handler for the request that is currently being executed by the application. It is a read-only property that returns the value stored in Handler.

CurrentNotification

Indicates which event in the request pipeline is currently processing the request. It works only if the application is running in integrated pipeline mode.

Chapter 16  The HTTP Request Context

657

Property

Description

Error

Gets the first exception (if any) that has been raised while processing the current request.

Handler

Gets or sets the HTTP handler for the current request.

IsCustomErrorEnabled

Indicates whether custom error handling is enabled for the current r­ equest.

IsDebuggingEnabled

Indicates whether the current request is in debug mode.

IsPostNotification

Indicates whether the current request has been processed and whether we’re in the middle of a PostXxx stage. It works only if the application is running in integrated pipeline mode.

Items

Gets a name/value collection (hash table) that can be used to share c­ ustom data and objects between HTTP modules and HTTP handlers ­during the request lifetime.

PreviousHandler

Gets the last handler before the current request was executed.

Profile

Gets the object that represents the profile of the current user.

Request

Gets an instance of the HttpRequest class, which represents the current HTTP request.

Response

Gets an instance of the HttpResponse class, which sends HTTP response data to the client.

Server

Gets an instance of the HttpServerUtility class, which provides helper methods for processing Web requests.

Session

Gets an instance of the HttpSessionState class, which manages sessionspecific data.

SkipAuthorization

Gets or sets a Boolean value that specifies whether the URL-based ­authorization module will skip the authorization check for the current ­request. This is false by default. It is mostly used by authentication ­modules that need to redirect to a page that allows anonymous access.

Timestamp

Gets a DateTime object that represents the initial timestamp of the c­ urrent request.

Trace

Gets the TraceContext object for the current response.

User

Gets or sets the IPrincipal object that represents the identity of the user making the request.

The Current property is a frequently used static member that returns the HttpContext object for the request being processed. The Items property is a dictionary object—a hash table, to be exact—that can be used to share information between the modules and handlers involved with the particular request. By using this property, each custom HTTP module or handler can add its own information to the HttpContext object serving the request. The information stored in Items is ultimately made available to the page. The lifetime of this information is limited to the request.

658

Part IV Infrastructure of the Application

Methods of the HttpContext Class Table 16-7 lists the methods specific to the HttpContext class. TABLE 16-7  HttpContext

Methods

Method

Description

AddError

Adds an exception object to the AllErrors collection.

ClearError

Clears all errors for the current request.

GetAppConfig

Returns requested configuration information for the current ­application. The information is collected from machine.config and theapplication’s main web.config files. It is marked as obsolete in ASP.NET 4.0.

GetConfig

Returns requested configuration information for the current request. The information is collected at the level of the requested URL, taking into account any child web.config files defined in subdirectories. It is marked as obsolete in ASP.NET 4.0.

GetGlobalResourceObject

Loads a global resource.

GetLocalResourceObject

Loads a local, page-specific resource.

GetSection

Returns requested configuration information for the current request.

RemapHandler

Allows you to programmatically set the handler to serve the request. It must be invoked before the runtime reaches the MapRequestHandler stage. If the Handler property of HttpContext is not null at that stage, the runtime defaults to it.

RewritePath

Mostly for internal use; overwrites URL and the query string of the ­current Request object.

SetSessionStateBehavior

Allows you to programmatically set the expected behavior for the session state—either read-only, read-write, or no session. It must be called before the AcquireRequestState event fires.

Over time, the GetSection method has replaced GetConfig, which has been marked as ­obsolete and should not be used. If you have old code using GetConfig, just change the name of the method. The prototype is the same. Also, GetAppConfig is marked as obsolete in ASP.NET 4. It has been replaced by GetWebApplicationSection, a static member of the new WebConfigurationManager class. Also, in this case, no changes are required to be made to the prototype. Let’s spend a few more words to dig out some interesting characteristics of other methods of the HttpContext class.

URL Rewriting The RewritePath method lets you change the URL of the current request on the fly, thus ­performing a sort of internal redirect. As a result, the displayed page is the one you set through RewritePath; the page shown in the address bar remains the originally requested one. The change of the final URL takes place on the server and, more importantly, within the context of the same call. RewritePath should be used carefully and mainly from within the

Chapter 16  The HTTP Request Context

659

global.asax file. If you use RewritePath in the context of a postback event, you can experience some view-state problems. protected void Application_BeginRequest(Object sender, EventArgs e) { var context = HttpContext.Current; var o = context.Request["id"]; if (o != null) { var id = (Int32) o; var url = GetPageUrlFromId(id); context.RewritePath(url); } } protected String GetPageUrlFromId(Int32 id) { // Return a full URL based on the input ID value. ... }

The preceding code rewrites a URL such as page.aspx?id=1234 to a specific page whose real URL is read out of a database or a configuration file. Note  In general, IIS-level URL rewriting (which was discussed in Chapter 2) is a better

­ lternative. The newer and more general ASP.NET Routing is perhaps better suited for a more a complex use case, but it can achieve the same result pretty easily.

Loading Resources Programmatically In Chapter 7, “Working with the Page,” we discussed expressions allowed in ASP.NET pages to bind control properties to embedded global or local resources. The $Resources and meta:resourcekey expressions for global and local resources, respectively, work only at design time. What if you need to generate text programmatically that embeds resource expressions, instead? Both the Page and HttpContext classes support a pair of programmatic methods to retrieve the content of resources embedded in the application. GetGlobalResourceObject retrieves a global resource—that is, a resource defined in an .resx file located in the App_GlobalResources special folder. GetLocalResourceObject does the same for an .resx file located in the App_LocalResources special folder of a given page. msg1.Text = (String) HttpContext.GetGlobalResourceObject( "Test", "MyString"); msg2.Text = (String) HttpContext.GetLocalResourceObject( "/MyApp/Samples/ResPage.aspx", "PageResource1.Title");

The first parameter you pass to GetGlobalResourceObject indicates the name of the .resx resource file without an extension; the second parameter is the name of the resource to

660

Part IV Infrastructure of the Application

retrieve. As for GetLocalResourceObject, the first argument indicates the virtual path of the page; the second is the name of the resource.

The Server Object In the all-encompassing container represented by the HttpContext object, a few popular objects also find their place. Among them are Server, Request, and Response. They are old acquaintances for ASP developers and, indeed, they are feature-rich elements of the ASP.NET programming toolkit. The set of properties and methods still makes these objects a fundamental resource for developers. Let’s learn more about them, starting with the Server object. The functionality of the ASP intrinsic Server object in ASP.NET is implemented by the HttpServerUtility class. An instance of the type is created when ASP.NET begins to process the request and is then stored as part of the request context. The bunch of helper methods that HttpServerUtility provides are publicly exposed to modules and handlers—including global. asax, pages, and Web services—through the Server property of the HttpContext object. In addition, to maintain ASP.NET coding as close as possible to the ASP programming style, several other commonly used ASP.NET objects also expose their own Server property. In this way, developers can use in the code, say, Server.MapPath without incurring compile errors.

Properties of the HttpServerUtility Class This class provides two properties, named MachineName and ScriptTimeout. The MachineName property returns the machine name, whereas ScriptTimeout gets and sets the time in seconds that a request is allowed to be processed. This property accepts integers and defaults to 90 seconds; however, it is set to a virtually infinite value if the page runs with the attribute debug=true, as shown here: this.Server.ScriptTimeout = 30000000;

The ScriptTimeout property is explicitly and automatically set in the constructor of the ­dynamically created class that represents the page.

Methods of the HttpServerUtility Class Table 16-8 lists all methods exposed by the HttpServerUtility class. As you can see, they ­constitute a group of helper methods that come in handy at various stages of page ­execution. The class provides a couple of methods to create instances of COM components and a few others to deal with errors. Another group of methods relates to the decoding and encoding of content and URLs.

Chapter 16  The HTTP Request Context TABLE 16-8  Methods

661

of the Server Object

Method

Description

ClearError

Clears the last exception that was thrown for the request.

CreateObject

Creates an instance of the specified COM object.

CreateObjectFromClsid

Creates an instance of the COM object identified by the specified CLSID. The class identifier is expressed as a string.

Execute

Passes control to the specified page for execution. The child page ­ xecutes like a subroutine. The output can be retained in a writer object e or automatically flushed in the parent response buffer.

GetLastError

Returns the last exception that was thrown.

HtmlDecode

Decodes a string that has been encoded to eliminate invalid HTML ­characters. For example, it translates < into Click

The ApplyAppPathModifier method takes a string representing a relative URL and returns an absolute URL, which embeds session information. This trick is especially useful when you need to redirect from an HTTP page to an HTTPS page, where the full, absolute address is mandatory. Note that ApplyAppPathModifier returns the original URL if session cookies are enabled and if the path is an absolute path. Caution  You can’t use code blocks in server-side expressions—that is, expressions

flagged with the runat=server attribute. It works in the preceding code because the tag is emitted verbatim, being devoid of the runat attribute. Code blocks mentioned here have nothing to do with data binding expressions , which are perfect legal and even desirable in server-side code. The reason why you can’t use code blocks in server-side expressions is that the presence of the runat attribute forces the creation of a server object that is not designed for handling code blocks.

Cookieless Sessions and Security Another issue regarding the use of cookieless sessions is related to security. Session h ­ ijacking is one of the most popular types of attacks and consists of accessing an external system through the session ID generated for another, legitimate user. Try this: set your application to work without cookies and visit a page. Grab the URL with the session ID as it appears in the browser’s address bar, and send it immediately in an e-mail to a friend. Have your friend paste the URL in his or her own machine and click Go. Your friend will gain access to your session state as long as the session is active. The session ID is certainly not well-protected information (and probably couldn’t work ­otherwise). For the safety of a system, an unpredictable generator of IDs is key because it makes it difficult to guess a valid session ID. With cookieless sessions, the session ID is ­exposed in the address bar and visible to all. For this reason, if you are storing private or ­sensitive information in the session state, it is recommended that you use Secure Sockets Layer (SSL) or Transport Layer Security (TLS) to encrypt any communication between the browser and server that includes the session ID.

Chapter 17  ASP.NET State Management

691

In addition, you should always provide users the ability to log out and call the Abandon method when they think security has been breached in this way. This contrivance reduces the amount of time available for anybody attempting to use your session ID to exploit data stored in the session state. And, speaking of security, it is important that you configure the system to avoid the reuse of expired session IDs when cookieless sessions are used. This behavior is configurable in ASP.NET through the section, as you can read in the following section.

Cookieless Sessions and SEO Cookieless sessions are also problematic from a Search-Engine Optimization (SEO) ­perspective. Pages based on cookieless sessions are poorly ranked by Web spiders such as Googlebot. The reason is that every time the spider attempts to crawl the page, ASP.NET generates a different session ID, which results in a different URL for the same content. So a crawler typically concludes that you have several pages with the same content and gives you a low ranking. An effective workaround for this issue is using UseDeviceProfile (described in Table 17-7) instead of the default value. In addition, you create in web.config a browser profile for each of the major crawlers, such as Googlebot. In the profile, you just declare that any agent that contains the word “Googlebot” in the user agent string should be treated like a browser that supports cookies. In this way, ASP.NET will not append the session ID to the URL. It’s not ­really a clean solution, but it does work. You can add a new profile for each crawler that is not indexing your pages well enough.

Configuring the Session State The section groups the settings you can apply to configure the behavior of ASP.NET session state. Here’s what it looks like: ...

692

Part IV Infrastructure of the Application

Table 17-8 details the goals and characteristics of the various attributes. TABLE 17-8 

Attributes

Mode

Description

allowCustomSqlDatabase

If true, enables specifying a custom database table to store session data instead of using the standard ASPState.

compressionEnabled

Indicates whether the session state content is compressed during ­serialization and deserialization to and from an out-of-process provider. Compression is disabled by default and, if enabled, uses the built-in Gzip stream. This feature is available only in ASP.NET 4.

cookieless

Specifies how to communicate the session ID to clients.

cookieName

Name of the cookie, if cookies are used for session IDs.

customProvider

The name of the custom session state store provider to use for ­storing and retrieving session state data.

mode

Specifies where to store session state.

partitionResolverType

Indicates the type and assembly of the partition resolver component to be loaded to provide connection information when session state is working in SQLServer or StateServer mode. If a partition resolver can be correctly loaded, sqlConnectionString and stateConnectionString attributes are ignored.

regenerateExpiredSessionId

When a request is made with a session ID that has expired, if this ­ ttribute is true, a new session ID is generated; otherwise, the expired a one is revived. The default is false.

sessionIDManagerType

Null by default. If set, it indicates the component to use as the ­generator of session IDs.

sqlCommandTimeout

Specifies the number of seconds a SQL command can be idle before it is canceled. The default is 30.

sqlConnectionString

Specifies the connection string to SQL Server.

stateConnectionString

Specifies the server name or address and port where session state is stored remotely.

stateNetworkTimeout

Specifies the number of seconds the TCP/IP network connection between the Web server and the state server can be idle before the request is canceled. The default is 10.

timeout

Specifies the number of minutes a session can be idle before it is abandoned. The default is 20.

useHostingIdentity

True by default. It indicates that the ASP.NET process identity is ­impersonated when accessing a custom state provider or the SQLServer provider configured for integrated security.

In addition, the child section lists custom session-state store providers. ASP.NET session state is designed to enable you to easily store user session data in different sources, such as a Web server’s memory or SQL Server. A store provider is a component that manages the storage of session state information and stores the information in an alternative media (for example, Oracle) and layout. We’ll return to this topic later in the chapter.

Chapter 17  ASP.NET State Management

693

Lifetime of a Session The life of a session state begins only when the first item is added to the in-memory ­dictionary. The following code demonstrates how to modify an item in the session dictionary. “MyData” is the key that uniquely identifies the value. If a key named “MyData” already exists in the dictionary, the existing value is overwritten. Session["MyData"] = "I love ASP.NET";

The Session dictionary generically contains object types; to read data back, you need to cast the returned values to a more specific type: var tmp = (String) Session["MyData"];

When a page saves data to Session, the value is loaded into an in-memory dictionary—an instance of an internal class named SessionDictionary. (See Figure 17-1 to review session state loading and saving.) Other concurrently running pages cannot access the session until the ongoing request completes.

The Session_Start Event The session startup event is unrelated to the session state. The Session_Start event fires when the session-state module is servicing the first request for a given user that requires a new session ID. The ASP.NET runtime can serve multiple requests within the context of a single session, but only for the first of them does Session_Start fire. A new session ID is created and a new Session_Start event fires whenever a page is requested that doesn’t write data to the dictionary. The architecture of the session state is quite ­sophisticated because it has to support a variety of state providers. The overall schema has the content of the session dictionary being serialized to the state provider when the request completes. However, to optimize performance, this procedure really executes only if the ­content of the dictionary is not empty. As mentioned earlier, though, if the application ­defines a Session_Start event handler, the serialization takes place anyway.

The Session_End Event The Session_End event signals the end of the session and is used to perform any clean-up code needed to terminate the session. Note, though, that the event is supported only in InProc mode—that is, only when the session data is stored in the ASP.NET worker process. For Session_End to fire, the session state has to exist first. That means you have to store some data in the session state and you must have completed at least one request. When the first value is added to the session dictionary, an item is inserted into the ASP.NET cache—the aforementioned Cache object that we’ll cover in detail in the next chapter. The behavior is

694

Part IV Infrastructure of the Application

specific to the in-process state provider; neither the out-of-process state server nor the SQL Server state server work with the Cache object. However, much more interesting is that the item added to the cache—only one item per ­active session—is given a special expiration policy. You’ll also learn more about the ASP.NET cache and related expiration policies in the next chapter. For now, it suffices to say that the session-state item added to the cache is given a sliding expiration, with the time interval set to the session timeout. As long as there are requests processed within the session, the sliding period is automatically renewed. The session-state module resets the timeout while processing the EndRequest event. It obtains the desired effect by simply performing a read on the cache! Given the internal structure of the ASP.NET Cache object, this evaluates to renewing the sliding period. As a result, when the cache item expires, the session has timed out. An expired item is automatically removed from the cache. As part of the expiration policy for this item, the state-session module also indicates a remove callback function. The cache ­automatically invokes the remove function which, in turn, fires the Session_End event. Note  The items in Cache that represent the state of a session are not accessible from outside

the system.web assembly and can’t even be enumerated, because they are placed in a systemreserved area of the cache. In other words, you can’t programmatically access the data resident in another session or even remove it.

Why Does My Session State Sometimes Get Lost? Values parked in a Session object are removed from memory either programmatically by the code or by the system when the session times out or it is abandoned. In some cases, though, even when nothing of the kind seemingly happens, the session state gets lost. Is there a ­reason for this apparently weird behavior? When the working mode is InProc, the session state is mapped in the memory space of the AppDomain in which the page request is being served. In light of this, the session state is subject to process recycling and AppDomain restarts. The ASP.NET worker process is ­periodically restarted to maintain an average good performance; when this happens, the session state is lost. Process recycling depends on the percentage of memory consumption and maybe the number of requests served. Although it’s cyclic, no general estimate can be made regarding the interval of the cycle. Be aware of this when designing your sessionbased, ­in-process application. As a general rule, bear in mind that the session state might not be there when you try to access it. Use exception handling or recovery techniques as ­appropriate for your application. Consider that some antivirus software might be marking the web.config or global.asax file as modified, thus causing a new application to be started and subsequently causing the loss of

Chapter 17  ASP.NET State Management

695

the session state. This holds true also if you or your code modify the timestamp of those files or alter the contents of one of the special folders, such as Bin or App_Code. Note  What happens to the session state when a running page hits an error? Will the current

dictionary be saved, or is it just lost? The state of the session is not saved if, at the end of the request, the page results in an error—that is, the GetLastError method of the Server object returns an exception. However, if in your exception handler you reset the error state by calling Server. ClearError, the values of the session are saved regularly as if no error ever occurred.

Persist Session Data to Remote Servers The session state loss problem that I mentioned earlier for InProc mode can be neatly solved by employing either of the two predefined out-of-process state providers: StateServer or SQLServer. In this case, though, the session state is held outside the ASP.NET worker process and an extra layer of code is needed to serialize and deserialize it to and from the actual storage medium. This operation takes place whenever a request is processed. The need to copy session data from an external repository into the local session dictionary might tax the state management process to the point of causing a 15 percent to 25 percent decrease in performance. Note, though, that this is only a rough estimate, and it’s closer to the minimum impact rather than to the maximum impact. The estimate, in fact, does not fully consider the complexity of the types actually saved into the session state. Note  When you get to choose an out-of-process state provider (for example, StateServer and

SQLServer), be aware that you need to set up the runtime environment before putting the application in production. This means either starting a Windows service for StateServer or configuring a database for SQLServer. No preliminary work is needed if you stay with the default, in-process option.

State Serialization and Deserialization When you use the InProc mode, objects are stored in the session state as live instances of classes. No real serialization and deserialization ever takes place, meaning that you can ­actually store in Session whatever objects (including COM objects) you have created and ­access them with no significant overhead. The situation is less favorable if you opt for an ­out-of-process state provider. In an out-of-process architecture, session values are copied from the native storage ­medium into the memory of the AppDomain that processes the request. A serialization/deserialization layer is needed to accomplish the task and represents one of the major costs for ­out-of-process state providers. How does this affect your code? First, you should make sure

696

Part IV Infrastructure of the Application

that only serializable objects are ever stored in the session dictionary; otherwise, as you can easily imagine, the session state can’t be saved and you’ll sustain an exception, moreover. To perform the serialization and deserialization of types, ASP.NET uses two methods, each providing different results in terms of performance. For basic types, ASP.NET resorts to an optimized internal serializer; for other types, including objects and user-defined classes, ASP.NET makes use of the .NET binary formatter, which is slower. Basic types are string, DateTime, Guid, IntPtr, TimeSpan, Boolean, byte, char, and all numeric types. The optimized serializer—an internal class named AltSerialization—employs an instance of the BinaryWriter object to write out one byte to denote the type and then the value. While reading, the AltSerialization class first extracts one byte, detects the type of the data to read, and then resorts to a type-specific method of the BinaryReader class to take data. The type is associated with an index according to an internal table, as shown in Figure 17-3. Session Timeout

Int32

Cookieless

Bool

Dictionary empty?

Bool

StaticObjects empty?

Bool

Dictionary

Array of bytes

StaticObjects

Array of bytes

FIGURE 17-3  The serialization schema for basic types that the internal AltSerialization class uses.

Note  While Booleans and numeric types have a well-known size, the length of a string

canvary quite a bit. How can the reader determine the correct size of a string? The BinaryReader.ReadString method exploits the fact that on the underlying stream the string is ­always prefixed with the length, encoded as an integer seven bits at a time. Values of the DateTime type, on the other hand, are saved by writing only the total number of ticks that form the date and are read as an Int64 type.

As mentioned, more complex objects are serialized using the relatively slower BinaryFormatter class as long as the involved types are marked as serializable. Both simple and complex types use the same stream, but all nonbasic types are identified with the same type ID. The performance-hit range of 15 percent to 25 percent is a rough estimate based on the assumption that basic types are used. The more you use complex types, the more the overhead grows, and reliable numbers can be calculated only by testing a particular ­application scenario.

Chapter 17  ASP.NET State Management

697

In light of this, if you plan to use out-of-process sessions, make sure you store data ­effectively. For example, if you need to persist an instance of a class with three string ­properties, performancewise you are probably better off using three different slots filled with a basic type rather than one session slot for which the binary formatter is needed. Better yet, you can use a type converter class to transform the object to and from a string format. However, understand that this is merely a guideline to be applied case by case and this ­advice should be taken with a grain of salt.

Storing Session Data When working in StateServer mode, the entire content of the HttpSessionState object is ­serialized to an external application—a Windows service named aspnet_state.exe. The service is called to serialize the session state when the request completes. The service internally stores each session state as an array of bytes. When a new request begins processing, the array corresponding to the given session ID is copied into a memory stream and then deserialized into an internal session state item object. This object really represents the contents of the whole session. The HttpSessionState object that pages actually work with is only its ­application interface. As mentioned, nonbasic types are serialized and deserialized using the system’s binary ­formatter class, which can handle only classes explicitly marked to be serializable. This means that COM objects, either programmatically created or declared as static objects with a ­session scope in global.asax, can’t be used with an out-of-process state provider. The same limitation applies to any nonserializable object.

Configuring the StateServer Provider Using out-of-process storage scenarios, you give the session state a longer life and your ­application greater robustness. Out-of-process session-state storage basically protects the session against Internet Information Services (IIS) and ASP.NET process failures. By separating the session state from the page itself, you can also much more easily scale an existing application to Web farm and Web garden architectures. In addition, the session state living in an external process eliminates at the root the risk of periodically losing data because of process recycling. As mentioned, the ASP.NET session-state provider is a Windows service named aspnet_state.exe. It normally resides in the installation folder of ASP.NET: %WINDOWS%\Microsoft.NET\Framework\[version]

As usual, note that the final directory depends on the .NET Framework version you’re ­actually running. Before using the state server, you should make sure that the service is up and r­ unning on the local or remote machine used as the session store. The state service is a

698

Part IV Infrastructure of the Application

c­ onstituent part of ASP.NET and gets installed along with it, so you have no additional setup application to run. By default, the state service is stopped and requires a manual start. You can change its ­configuration through the properties dialog box of the service, as shown in Figure 17-4.

FIGURE 17-4  The properties dialog box of the ASP.NET state server.

An ASP.NET application needs to specify the TCP/IP address of the machine hosting the ­session-state service. The following listing shows the changes that need to be made to the web.config file to enable the remote session state:

Note that the value assigned to the mode attribute is case sensitive. The format of the ­stateConnectionString attribute is shown in the following line of code. The default machine address is 127.0.0.1, while the port is 42424. stateConnectionString="tcpip=server:port"

The server name can be either an IP address or a machine name. In this case, though, ­non-ASCII characters in the name are not supported. Finally, the port number is mandatory and cannot be omitted.

Chapter 17  ASP.NET State Management

699

Important  The state server doesn’t offer any authentication barrier to requestors, meaning that anyone who can get access to the network is potentially free to access session data. To protect session state and make sure that it is accessed only by the Web server machine, you can use a firewall, IPSec policies, or a secure net 10.X.X.X so that external attackers can’t gain direct access. Another security-related countermeasure consists of changing the default port number. To change the port, you edit the Port entry under the registry key: HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters. Writing the port in the web.config file isn’t enough. The ASP.NET application attempts to connect to the session-state server immediately after loading. The aspnet_state service must be up and running; otherwise, an HTTP exception is thrown. By default, the service is not configured to start automatically. The state service uses .NET Remoting to move data back and forth. Note  The ASP.NET state provider runs under the ASP.NET account. The account, though, can be

configured and changed at will using the Service Control Manager interface. The state service is slim and simple and does not implement any special features. It is limited to holding data and listens to the specified port for requests to serve. In particular, the service isn’t cluster-aware (that is, it doesn’t provide a failover monitor to be error tolerant) and can’t be used in a clustered world when another server takes on the one that fails.

Finally, note that by default the state server listens only to local connections. If the state server and Web server live on different machines, you need to enable remote connections. You do this through another entry in the same registry key as mentioned earlier. The entry is AllowRemoteConnection, and it must be set to a nonzero value.

Persist Session Data to SQL Server Maintaining the session state in an external process certainly makes the whole ASP.NET ­application more stable. Whatever happens to the worker process, the session state is still there, ready for further use. If the service is paused, the data is preserved and automatically retrieved when the service resumes. Unfortunately, if the state provider service is stopped or if a failure occurs, the data is lost. If robustness is key for your application, drop the StateServer mode in favor of SQLServer.

Performance and Robustness When ASP.NET works in SQLServer mode, the session data is stored in a made-to-measure database table. As a result, the session data survives even SQL Server crashes, but you have to add higher overhead to the bill. SQLServer mode allows you to store data on any

700

Part IV Infrastructure of the Application

c­ onnected machine, as long as the machine runs SQL Server 7.0 or newer. Aside from the different medium, the storage mechanism is nearly identical to that described for remote servers. In particular, the serialization and deserialization algorithm is the same, only it’s a bit slower because of the characteristics of storage. When storing data of basic types, the time required to set up the page’s Session object is normally at least 25 percent higher than in an InProc scenario. Also in regard to this issue, the more complex types you use, the more time it will take to manage the session data. Note  When you get to make a decision between state server or SQL server storage, consider

the fact that SQL Server is cluster-aware, which makes a solution based on it more robust (and also more robust across machine restarts) and more reliable than one based on a state server.

Configuring Session State for SQL Server Support To use SQL Server as the state provider, enter the following changes in the section of the web.config file:

In particular, you need to set the mode attribute (which is case sensitive) to SQLServer and specify the connection string through the sqlConnectionString attribute. Note that the ­sqlConnectionString attribute string must provide credentials (user ID and password or integrated security) and a server name. However, it cannot contain tokens, such as Database and Initial Catalog, unless a custom database is enabled using allowCustomSqlDatabase, as mentioned in Table 17-8. You can specify a SQL Server Initial Catalog database name or use the SQL Server Express attachDBFileName to point to an MDB file in the connection string only if the allowCustomSqlDatabase configuration setting is enabled. If that is disabled, any attempts to specify these settings in the connection string will result in an exception. Note  The connection string for an out-of-process session state implementation (both

SQLServer and StateServer) can also be specified to refer to a connection string defined in the section. The session state module first attempts to look up a connection string from the section with the name specified in the appropriate ­ attribute; if it is not found, the session state attempts to use the specified string directly.

Chapter 17  ASP.NET State Management

701

As for credentials to access the database, you can either use User ID and passwords or resort to integrated security. Note  Whatever account you use to access session state in SQL Server, make sure that it is

­granted the db_datareader and db_datawriter permissions at least. Note also that to configure the SQL Server environment for storing session state, administrative privileges are required, as a new database and stored procedures need to be created.

Session state in SQL Server mode supports the specification of a custom command timeout value (in seconds) to accommodate slow-responding-server scenarios. You control it through the sqlCommandTimeout attribute, as mentioned in Table 17-8.

Creating the SQL Server Data Store You use the aspnet_regsql.exe tool to configure the database environment by creating any needed tables, stored procedures, triggers, and jobs. In general, the tool works through the command line but also offers a visual interface. It is located in the following system folder: %Windows%\Microsoft.NET\Framework\v4.0.30319

To create the ASPState database, you must use the command line, as shown here: aspnet_regsql.exe –S [SqlServer Instance] –E –ssadd –sstype p

The tables that get created are named ASPStateTempApplications and ASPStateTempSessions. Figure 17-5 shows a view of the session database in SQL Server.

FIGURE 17-5  The ASPState database in SQL Server Enterprise Manager.

The ASPStateTempApplications table defines a record for each currently running ASP.NET ­application. The table columns are listed in Table 17-9.

702

Part IV Infrastructure of the Application TABLE 17-9  The

ASPStateTempApplications Table

Column

Type

Description

AppId

Int

Indexed field. It represents a sort of autogenerated ID that identifies a running application using the SQLServer session mode.

AppName

char(280)

Indicates the application ID of the AppDomain running the ­application. It matches the contents of the AppDomainAppId property on the HttpRuntime object.

The ASPStateTempSessions table stores the actual session data. The table contains one row for each active session. The structure of the table is outlined in Table 17-10. TABLE 17-10  The

ASPStateTempSessions Table

Column

Type

Description

SessionId

Char(88)

Indexed field. It represents the session ID.

Created

DateTime

Indicates the time at which the session was created. It defaults to the current date.

Expires

DateTime

Indicates the time at which the session will expire. This value is normally the time at which the session state was created plus the number of minutes specified in Timeout. Note that Created refers to the time at which the ­session started, whereas Expires adds minutes to the time in which the first item is added to the session state.

Flags

Int

Indicates action flags—initialize items or none—from the SessionStateActions enum.

LockCookie

Int

Indicates the number of times the session was locked— that is, the number of accesses.

LockDate

DateTime

Indicates the time at which the session was locked to add the last item. The value is expressed as the current Universal Time Coordinate (UTC).

LockDateLocal

DateTime

Like the previous item, except that this one expresses the system’s local time.

Locked

bit

Indicates whether the session is currently locked.

SessionItemLong

Image

Nullable field, represents the serialized version of a s­ ession longer than 7000 bytes.

SessionItemShort

VarBinary(7000)

Nullable field. It represents the values in the specified session. The layout of the bytes is identical to the layout discussed for StateServer providers. If more than 7000 bytes are needed to serialize the dictionary, the SessionItemLong field is used instead.

Timeout

int

Indicates the timeout of the session in minutes.

The column SessionItemLong, contains a long binary block of data. Although the user always works with image data as if it is a single, long sequence of bytes, the data is not stored in that format. The data is stored in a collection of 8-KB pages that aren’t necessarily located next to each other.

Chapter 17  ASP.NET State Management

703

When installing the SQL Server support for sessions, a job is also created to delete expired sessions from the session-state database. The job is named ASPState_Job_ DeleteExpiredSessions, and the default configuration makes it run every minute. You should note that the SQLServerAgent service needs to be running for this to work.

Reverting to the Hosting Identity The useHostingIdentity attribute (shown in Table 17-8) lets you decide about the identity to use to grant access to the SQL Server table with session state. When the SQLServer state ­provider is used with integrated security, the identity is the one impersonated by the ASP.NET process. This simplifies the administrative experience for intranet sites, requiring that only the ASP.NET account be granted access to protected and critical resources. The ­useHostingIdentity attribute defaults to true, which enables you to revert to the ASP.NET identity before making calls to the SQLServer session state provider. This will also happen if acustom provider is used. Note  If you’re using Windows integrated authentication to access SQL Server, reverting to

the host identity is the most recommended option, for security reasons. Otherwise, it is advisable that you create a specific account and grant it only rights to execute session state stored ­procedures and access related resources.

Session State in a Web Farm Scenario ASP.NET applications designed to run in a Web farm or Web garden hardware ­configuration cannot implement an in-process session state. The InProc mode won’t work on a Web farm because a distinct worker process will be running on each connected machine, with each process maintaining its own session state. It doesn’t even work on a Web garden because multiple worker processes will be running on the same machine. Keeping all states separate from worker processes allows you to partition an a ­ pplication across multiple worker processes even when they’re running on multiple computers. In both Web farm and Web garden scenarios, there can be only one StateServer or SQLServer process to provide session-state management. If you’re running a Web farm, make sure you have the same in all your Web servers. (More details can be found in Knowledge Base article Q313091.) In ­addition, for the session state to be maintained across different servers in the Web farm, all applications should have the same application path stored in the IIS metabase. This value is set as the AppDomain application ID and identifies a running application in the ASP.NET state database. (See Knowledge Base article Q325056 for more details.)

704

Part IV Infrastructure of the Application

Partition resolvers exist to let a session state provider partition its data onto multiple back-end nodes. This allows you to scale session state on large Web farms, according to a custom, user-defined load-balancing scheme. A partition provider is a component that supplies the connection string (the actual string, not the pointer to a string in the web.config file) to the session state that is used to access data, overriding any other ­settings in the section.

Customizing Session State Management Since its beginning, the ASP.NET session state was devised to be an easy-to-customize and extensible feature. All things considered, you have the following three options to customize session state management: ■

You can stay with the default session state module but write a custom state provider to change the storage medium (for example, to a non–SQL Server database or a different table layout). In doing so, you also have the chance to override some of the helper classes (mostly collections) that are used to bring data from the store to the Session object and back.

You can stay with the default session state module but replace the session ID g ­ enerator. But hold on! The algorithm that generates session IDs is a critical element of the ­application, because making session IDs too easy for attackers to guess can lead straight to session-hijacking attacks. Nonetheless, this remains a customizable aspect of session state that, properly used, can make your application even more secure.

You can unplug the default session state module and roll your own. This option, however, should be used as a last resort. Obviously, it provides the maximum flexibility, but it is also extremely complicated and is recommended only if strictly necessary and if you know exactly what you’re doing. We won’t cover this topic in the book.

The first option—the easiest and least complicated of all—addresses most of the scenarios for which some custom session management is desirable. So let’s tackle it first.

Building a Custom Session State Provider A session state provider is the component in charge of serving any data related to the ­current session. Invoked when the request needs to acquire state information, it retrieves data from a given storage medium and returns that to the module. Invoked by the module when the request ends, it writes the supplied data to the storage layer. As mentioned, ASP.NET supports three state providers, as listed in Table 17-11.

Chapter 17  ASP.NET State Management TABLE 17-11  Default

Name

705

State Providers

Class

Storage Medium

InProc

InProcSessionStateStore

Stores data as live objects in the ASP.NET Cache.

StateServer

OutOfProcSessionStateStore

Stores data as serialized objects to the memory of a Windows service named aspnet_state.exe.

SQLServer

SqlSessionStateStore

Stores data as serialized objects into a SQL Server database.

You can write your own state provider class that uses the storage medium of your choice. Note that the default state providers also make use of various helper classes to move data around. In your custom provider, you can replace these classes too, or just stick to the ­standard ones.

Defining the Session State Store A state provider (also often referred to as a session state store) is a class that inherits from SessionStateStoreProviderBase. The main methods of the interface are listed in Table 17-12. TABLE 17-12  Methods

of the SessionStateStoreProviderBase Class

Method

Description

CreateNewStoreData

Creates an object to contain the data of a new session. It should r­ eturn an object of type SessionStateStoreData.

CreateUninitializedItem

Creates a new and uninitialized session in the data source. The method is called when an expired session is requested in a cookieless session state. In this case, the module has to generate a new session ID. The session item created by the method prevents the next request with the newly generated session ID from being ­mistaken for a request directed at an expired session.

Dispose

Releases all resources (other than memory) used by the state ­ rovider. p

EndRequest

Called by the default session state module when it begins to handle the EndRequest event.

GetItem

Returns the session item matching the specified ID from the data store. The session item selected is locked for read. The method serves requests from applications that use a read-only session state.

GetItemExclusive

Returns the session item matching the specified ID from the data store and locks it for writing. It’s used for requests originated by ­applications that use a read-write session state.

Initialize

Inherited from the base provider class, performs one-off i­nitialization.

InitializeRequest

Called by the default session state module when it begins to handle the AcquireRequestState event.

ReleaseItemExclusive

Unlocks a session item that was previously locked by a call to the GetItemExclusive method.

706

Part IV Infrastructure of the Application

Method

Description

RemoveItem

Removes a session item from the data store. It’s called when a s­ ession ends or is abandoned.

ResetItemTimeout

Resets the expiration time of a session item. It’s invoked when the application has session support disabled.

SetAndReleaseItemExclusive

Writes a session item to the data store.

SetItemExpireCallback

The default module calls this method to notify the data store class that the caller has registered a Session_End handler.

Classes that inherit the SessionStateStoreProviderBase class work with the default ASP.NET session state module and replace only the part of it that handles session-state data storage and retrieval. Nothing else in the session functionality changes.

Locking and Expiration Can two requests for the same session occur concurrently? You bet. Requests can certainly arrive in parallel—for example, from two frames or when a user works with two instances of the same browser, the second of which is opened as a new window. To avoid problems, a state provider must implement a locking mechanism to serialize access to a session. The session state module determines whether the request requires read-only or read-write access to the session state and calls GetItem or GetItemExclusive accordingly. In the implementation of these methods, the provider’s author should create a reader/writer lock mechanism to allow multiple concurrent reads but prevent writing on locked sessions. Another issue relates to letting the session state module know when a given session has expired. The session state module calls the method SetItemExpireCallback when there’s a Session_End handler defined in global.asax. Through the method, the state provider receives a callback function with the following prototype: public delegate void SessionStateItemExpireCallback( string sessionID, SessionStateStoreData item);

It has to store that delegate internally and invoke it whenever the given session times out. Supporting expiration callbacks is optional and, in fact, only the InProc provider actually supports it. If your custom provider is not willing to support expiration callbacks, you should instruct the SetItemExpireCallback method to return false. Note  A provider that intends to support cookieless sessions must also implement the

CreateUninitialized method to write a blank session item to the data store. More precisely, a blank session item is an item that is complete in every way except that it contains no session data. In other words, the session item should contain the session ID, creation date, and perhaps lock IDs, but no data. ASP.NET generates a new ID (in cookieless mode only) whenever a request is made for an expired session. The session state module generates the new session ID and redirects the browser. Without an uninitialized session item marked with a newly generated ID, the new request will again be recognized as a request for an expired session.

Download from Wow! eBook

Chapter 17  ASP.NET State Management

707

Replacing the Session Data Dictionary SessionStateStoreData is the class that represents the session item—that is, a data s­ tructure that contains all the data that is relevant to the session. GetItem and GetItemExclusive, in fact, are defined to return an instance of this class. The class has three properties: Items, StaticObjects, and Timeout. Items indicates the collection of name/values that will ultimately be passed to the page through the Session property. StaticObjects lists the static objects belonging to the session, such as objects declared in the global.asax file and scoped to the session. As the name ­suggests, Timeout indicates how long, in minutes, the session state item is valid. The default value is 20 minutes. After the session state module has acquired the session state for the request, it flushes the contents of the Items collection to a new instance of the HttpSessionStateContainer class. This object is then passed to the constructor of the HttpSessionState class and becomes the data container behind the familiar Session property. The SessionStateStoreData class is used in the definition of the base state provider class, meaning that you can’t entirely replace it. If you don’t like it, you can inherit a new class from it, however. To both the session module and state provider, the container of the session items is merely a class that implements the ISessionStateItemCollection interface. The real class ­being used by default is SessionStateItemCollection. You can replace this class with your own as long as you implement the aforementioned interface. Note  To write a state provider, you might find helpful the methods of the SessionStateUtility

class. The class contains methods to serialize and deserialize session items to and from the storage medium. Likewise, the class has methods to extract the dictionary of data for a session and add it to the HTTP context and the Session property.

Registering a Custom Session State Provider To make a custom session state provider available to an application, you need to register it in the web.config file. Suppose you have called the provider class SampleSessionStateProvider and compiled it to MyLib. Here’s what you need to enter:

708

Part IV Infrastructure of the Application

The name of the provider is arbitrary but necessary. To force the session state module to find it, set the mode attribute to Custom.

Generating a Custom Session ID To generate the session ID, ASP.NET uses a special component named SessionIDManager. Technically speaking, the class is neither an HTTP module nor a provider. More simply, it is a class that inherits from System.Object and implements the ISessionIDManager interface. You can replace this component with a custom component as long as the component implements the same ISessionIDManager interface. To help you decide whether you really need a custom session ID generator, let’s review some facts about the default module.

The Default Behavior The default session ID module generates a session ID as an array of bytes with a cryptographically strong random sequence of 15 values. The array is then encoded to a string of 24 URL-accepted characters, which is what the system will recognize as the session ID. The session ID can be round-tripped to the client in either an HTTP cookie or a mangled URL, based on the value of the cookieless attribute in the configuration section. Note that when cookieless sessions are used, the session ID module is responsible for adding the ID to the URL and redirecting the browser. The default generator redirects the browser to a fake URL like the following one: http://www.contoso.com/test/(S(session_id))/page.aspx

How can a request for this fake URL be served correctly? In the case of a cookieless ­session, the Session ID module depends on a small and simple ISAPI filter (aspnet_filter.dll) to ­dynamically rewrite the real URL to access. The request is served correctly, but the path on the address bar doesn’t change. The detected session ID is placed in a request header named AspFilterSessionId.

A Homemade Session ID Manager Now that we’ve ascertained that a session ID manager is a class that implements ISessionIDManager, you have two options: build a new class and implement the interface from the ground up, or inherit a new class from SessionIDManager and override a couple of virtual methods to apply some personalization. The first option offers maximum flexibility; the second is simpler and quicker to implement, and it addresses the most compelling ­reason you might have to build a custom session ID generator—to supply your own session ID values.

Chapter 17  ASP.NET State Management

709

Let’s start by reviewing the methods of the ISessionIDManager interface, which are shown in Table 17-13. TABLE 17-13  Methods

of the ISessionIDManager Interface

Method

Description

CreateSessionID

Virtual method. It creates a unique session identifier for the session.

Decode

Decodes the session ID using HttpUtility.UrlDecode.

Encode

Encodes the session ID using HttpUtility.UrlEncode.

Initialize

Invoked by the session state immediately after instantiation; performs ­ ne-time initialization of the component. o

InitializeRequest

Invoked by the session state when the session state is being acquired for the request.

GetSessionID

Gets the session ID from the current HTTP request.

RemoveSessionID

Deletes the session ID from the cookie or from the URL.

SaveSessionID

Saves a newly created session ID to the HTTP response.

Validate

Confirms that the session ID is valid.

If you plan to roll your own completely custom session ID generator, bear in mind the ­following points: ■

The algorithm you choose for ID generation is a critical point. If you don’t implement strong cryptographic randomness, a malicious user can guess a valid session ID when the same session is still active, thus accessing some user’s data. (This is known as session hijacking.) A good example of a custom session ID algorithm is one that returns a ­globally unique identifier (GUID).

You can choose to support cookieless sessions or not. If you do, you have to endow the component with the ability to extract the session ID from the HTTP request and redirect the browser. You probably need an ISAPI filter or HTTP module to preprocess the request and enter appropriate changes. The algorithm you use to store session IDs without cookies is up to you.

If you are absolutely determined to have the system use your session IDs, you derive a new class from SessionIDManager and override two methods: CreateSessionID and Validate. The former returns a string that contains the session ID. The latter validates a given session ID to ensure it conforms to the specification you set. After you have created a custom session ID module, you register it in the configuration file. Here’s how to do it:

710

Part IV Infrastructure of the Application

Session State Performance Best Practices State management is a necessary evil. By enabling it, you charge your application with an extra burden. To reduce the performance impact of session state on Web applications, the first guideline is to disable session state whenever possible. However, to prevent the session from expiring, the HTTP module still marks the session as active in the data store. For out-of-process state servers, this means that a roundtrip is made. Using a custom session ID manager returning a null session ID for requests that are known not to require session state is the best way to work around this issue and avoid the overhead entirely. (Write a class that inherits from SessionIDManager and overrides GetSessionID.) The second guideline entails minimizing contention on session data by avoiding frames and downloadable resources served by session-enabled handlers. The third guideline relates to data serialization and deserialization. You should always use simple types and break complex classes into arrays of simple properties, at least as far as session management is concerned. In other words, I’m not suggesting that you should factor out your DAL classes—just change the way you serialize them into the session store. An alternate approach entails building a custom serialization algorithm that is optimized for session state storage. Breaking a class into various properties, with each stored in a session slot, is advantageous because of the simple types being used, but also because the extreme granularity of the solution minimizes the data to save in case of changes. If one property changes, only one slot with a simple type is updated instead of a single slot with a complex type.

The View State of a Page ASP.NET pages supply the ViewState property to let applications build a call context and ­retain values across two successive requests for the same page. The view state represents the state of the page when it was last processed on the server. The state is persisted—usually, but not necessarily, on the client side—and is restored before the page request is processed. By default, the view state is maintained as a hidden field added to the page. As such, it ­travels back and forth with the page itself. Although it is sent to the client, the view state does not represent, nor does it contain, any information specifically aimed at the client. The information stored in the view state is pertinent only to the page and some of its child ­controls and is not consumed in any way by the browser. The view state comes at a cost. At the same time, however, the view state is one of the most important features of ASP.NET, not so much because of its technical relevance but because it allows you to benefit from most of the magic of the Web Forms model. Used without strict criteria, though, the view state can easily become a burden for pages.

Chapter 17  ASP.NET State Management

711

The StateBag Class The StateBag class is the class behind the view state that manages the information that ASP.NET pages and controls want to persist across successive posts of the same page instance. The class works like a dictionary and, in addition, implements the IStateManager interface. The Page and Control base classes expose the view state through the ViewState property. So you can add or remove items from the StateBag class as you would with any ­dictionary object, as the following code demonstrates: ViewState["FontSize"] = value;

You should start writing to the view state only after the Init event fires for the page request. You can read from the view state during any stage of the page life cycle, but not after the page enters rendering mode—that is, after the PreRender event fires.

View State Properties Table 17-14 lists all the properties defined in the StateBag class. TABLE 17-14  Properties

of the StateBag Class

Property

Description

Count

Gets the number of elements stored in the object.

Item

Indexer property. It gets or sets the value of an item stored in the class.

Keys

Gets a collection object containing the keys defined in the object.

Values

Gets a collection object containing all the values stored in the object.

Each item in the StateBag class is represented by a StateItem object. An instance of the StateItem object is implicitly created when you set the Item indexer property with a value or when you call the Add method. Items added to the StateBag object are tracked until the view state is serialized prior to the page rendering. Items serialized are those with the IsDirty property set to true.

View State Methods Table 17-15 lists all the methods you can call in the StateBag class. TABLE 17-15  Methods

of the StateBag Class

Method

Description

Add

Adds a new StateItem object to the collection. If the item already exists, it gets updated.

Clear

Removes all items from the current view state.

GetEnumerator

Returns an object that scrolls over all the elements in the StateBag.

IsItemDirty

Indicates whether the element with the specified key has been modified ­ uring the request processing. d

Remove

Removes the specified object from the StateBag object.

712

Part IV Infrastructure of the Application

The IsItemDirty method represents an indirect way to call into the IsDirty property of the specified StateItem object. Note  The view state for the page is a cumulative property that results from the contents of the ViewState property of the page plus the view state of all the controls hosted in the page.

Common Issues with View State Architecturally speaking, the importance of the view state cannot be denied because it is key to setting up the automatic state management feature of ASP.NET. A couple of hot issues are related to the usage of the view state, however. The most frequently asked questions about the view state are related to security and performance. Can we say that the view state is inherently secure and cannot be tampered with? How will the extra information contained in the view state affect the download time of the page? Let’s find out.

Encrypting and Securing Many developers are doubtful about using the view state specifically because it is stored in a hidden field and left on the client at the mercy of potential intruders. Although the data is stored in a hashed format, there’s no absolute guarantee that it cannot be tampered with. The first comment I’d like to make in response to this is that the view state as implemented in ASP.NET is inherently more secure than any other hidden fields you might use (and that you were likely using, say, in old classic ASP applications). My second remark is that only data confidentiality is at risk. While this is a problem, it is minor compared to code injection. Freely accessible in a hidden field named __VIEWSTATE, the view state information is, by ­default, hashed and Base64 encoded. To decode it on the client, a potential attacker must ­accomplish a number of steps, but the action is definitely possible. Once decoded, though, the view state reveals only its contents—that is, confidentiality is at risk. However, there’s no way an attacker can modify the view state to post malicious data. A tampered view state, in fact, is normally detected on the server and an exception is thrown. For performance reasons, the view state is not encrypted. If it’s needed, though, you can turn the option on by acting on the web.config file, as follows:

When the validation attribute is set to 3DES, the view-state validation technique uses 3DES encryption and doesn’t hash the contents. If you use web.config, the settings apply to all ­pages in the application. You can also control encryption settings separately for each page.

Chapter 17  ASP.NET State Management

713

Furthermore, individual controls on the page can request to encrypt the view state. In case of a conflict, page settings win. You use the ViewStateEncryptionMode property, which accepts values from the ViewStateEncryptionMode enumeration. Feasible values are Auto, Always, and Never. The default value is Auto. When the value is Auto, ASP.NET encrypts the entire view state only if all controls want it encrypted. With values like Always and Never, the view state is always or never encrypted, regardless of the control settings.

Machine Authentication Check The @Page directive contains an attribute named EnableViewStateMac, whose only purpose is making the view state a bit more secure by detecting any possible attempt at corrupting the original data. When serialized, and if EnableViewStateMac is set to true, the view state is appended with a validator hash string based on the algorithm and the key defined in the section of the configuration file. The resulting array of bytes, which is the output of the StateBag’s binary serialization plus the hash value, is Base64 encoded. By default, the encryption algorithm to calculate the hash is SHA1, and the encryption and decryption keys are autogenerated and stored in the Web server machine’s Local Security Authority (LSA) subsystem. The LSA is a protected component of Windows. It provides security services and maintains information about all aspects of local security on a system. If EnableViewStateMac is true, when the page posts back, the hash value is extracted and used to verify that the returned view state has not been tampered with on the client. If it has been, an exception is thrown. The net effect is that you might be able to read the contents of the view state, but to replace it you need the encryption key, which is in the Web server’s LSA. The MAC in the name of the EnableViewStateMac property stands for Machine Authentication Check, which is enabled by default. If you disable the attribute, an attacker could alter the view-state information on the client and send a modified version to the server and have ASP.NET blissfully use that tampered-with information. To reinforce the security of the view state, you can use the ViewStateUserKey property. The property evaluates to a user-specific string (typically, the session ID) that is known on the server and hard to guess on the client. ASP.NET uses the content of the property as an input argument to the hash algorithm that generates the MAC code.

Size Thresholds and Page Throughput My opinion is that you should be concerned about the view state, but not for the potential security holes it might open in your code—it can let hackers exploit only existing holes. You should be more concerned about the overall performance and responsiveness of the page. Especially for feature-rich pages that use plenty of controls, the view state can reach a considerable size, measured in KB of data. Such an extra burden taxes all requests, in downloads and uploads, and ends up creating serious overhead for the application as a whole.

714

Part IV Infrastructure of the Application

What is a reasonable size for an ASP.NET page? And for the view state of a page? Let’s take a look at a sample page that contains a grid control bound to about 100 records (the Customers table in the Northwind database of SQL Server): Measure Up Your ViewState function ShowViewStateSize() { var buf = document.forms[0]["__VIEWSTATE"].value; alert("View state is " + buf.length + " bytes"); }

Here’s the related code-behind class: public partial class Message : System.Web.UI.UserControl { public String ForeColor; public String Text; }

To insert a user control into an ASP.NET page, you drag it from the project onto the Web form, when in design mode. Visual Studio .NET registers the user control with the page and prepares the environment for you to start adding code.

In the page code-behind class, you work the Message1 variable as you would do with any other server control: protected void Page_Load(Object sender, EventArgs e) { Message1.ForeColor = "blue"; Message1.Text = "Hello world"; }

770

Part IV Infrastructure of the Application

Caching the Output of User Controls User controls are not only good at modularizing your user interface, they’re also great at caching portions of Web pages. User controls, therefore, fully support the @OutputCache directive, although they do so with some differences with ASP.NET pages, as outlined in Table 18-8. A page that contains some dynamic sections cannot be cached entirely. What if the page also contains sections that are both heavy to compute and seldom updated? In this case, you move static contents to one or more user controls and use the user control’s @OutputCache directive to set up output caching. To make a user control cacheable, you declare the @OutputCache attribute using almost the same set of attributes we discussed earlier for pages. For example, the following code snippet caches the output of the control that embeds it for one minute:

The Location attribute is not supported because all controls in the page share the same location. So if you need to specify the cache location, do that at the page level and it will work for all embedded controls. The same holds true for the VaryByHeader attribute. The output of a user control can vary by custom strings and form parameters. More often, though, you’ll want to vary the output of a control by property values. In this case, use the new VaryByControl attribute. Note  A user control is made cacheable in either of two ways: by using the @OutputCache

­ irective, or by defining the PartialCaching attribute on the user control’s class declaration in the d code-behind file, as follows: [PartialCaching(60)] public partial class CustomersGrid : UserControl { ... }

The PartialCaching attribute allows you to specify the duration and values for the VaryByParam, VaryByControl, and VaryByCustom parameters.

Vary by Controls The VaryByControl attribute allows you to vary the cache for each specified control property. For user controls, the property is mandatory unless the VaryByParam attribute has been specified. You can indicate both VaryByParam and VaryByControl, but at least one of them is required.

Chapter 18  ASP.NET Caching

771

The following user control displays a grid with all the customers in a given country/region. The ­country/region is specified by the user control’s Country property.

Here is the code file of the user control: public partial class CustomersGridByCtl : System.Web.UI.UserControl { public String Country; protected void Page_Load(Object sender, EventArgs e) { if (!String.IsNullOrEmpty(Country)) { ObjectDataSource1.SelectParameters.Add("country", Country); GridView1.DataSourceID = "ObjectDataSource1"; } } }

The @OutputCache directive caches a different copy of the user control output based on the different values of the Country property. Figure 18-9 shows it in action.

772

Part IV Infrastructure of the Application

FIGURE 18-9  Two pages created at different moments use the same user control output, as you can see from the creation time of the grid.

The strings you assign to VaryByControl can be properties of the user controls as well as ID property values for contained controls. In this case, you’ll get a distinct cached copy for each distinct combination of property values on the specified control.

The Shared Attribute In Figure 18-9, you see two instances of the same page sharing the cached output of a user control. Try the following simple experiment. Make a plain copy of the page (say, page1.aspx), and give it another name (say, page2.aspx). You should have two distinct pages that generate identical output. In particular, both pages contain an instance of the same cacheable user control. Let’s say that the cache duration of the user control is 30 seconds. As the next step of the experiment, you open both pages at different times while the cacheis still valid. Let’s say you open the second page ten seconds later than the first. Interestingly enough, the two pages no longer share the same copy of user control output, as Figure 18-10 documents.

Chapter 18  ASP.NET Caching

773

FIGURE 18-10  Distinct pages don’t automatically share the output of the same user control.

By default, distinct pages don’t share the output of the same cacheable user control. Each page will maintain its own copy of the user control response, instead. Implemented to guarantee total separation and avoid any sort of conflicts, this feature is far more dangerous than one might think at first. It might lead to flooding the Web server memory with copies and copies of the user control responses—one for each varying parameter or control property and one set for each page that uses the control. To allow distinct pages to share the same output of a common user control, you need to set the Shared attribute to true in the user control’s @OutputCache directive:

Fragment Caching in Cacheable Pages If you plan to cache user controls—that is, if you’re trying for partial caching—it’s probably because you just don’t want to, or cannot, cache the entire page. However, a good question to ask is this: What happens if user controls are cached within a cacheable page?

774

Part IV Infrastructure of the Application

Both the page and the controls are cached individually, meaning that both the page’s raw response and the control’s raw responses are cached. However, if the cache duration is ­different, the page duration wins and user controls are refreshed only when the page is refreshed. A cacheable user control can be embedded both in a cacheable page and in a wrappercacheable user control. Important  Cacheable user controls should be handled with extreme care in the page’s code. Unlike regular controls, a user control marked with the @OutputCache directive is not guaranteed to be there when your code tries to access it. If the user control is retrieved from the cache, the property that references it in the code-behind page class is just null. if (CustomerGrid1 != null) CustomerGrid1.Country = "USA";

To avoid bad surprises, you should always check the control reference against the null value ­before executing any code.

Advanced Caching Features The output caching subsystem has also a few other cool features to offer. They are caching profiles and post-cache substitution. In brief, caching profiles let you save a block of output caching-related settings to the configuration file. Post-cache substitution completes the ASP.NET offering as far as output caching is concerned. In addition to saving the entire page or only fragments of the page, you can now also cache the entire page except for a few regions.

Caching Profiles The @OutputCache directive for pages supports the CacheProfile string attribute, which ­references an entry under the section in the web.config file: operator selects elements that are direct child elements (and not just descendants) of the elements matched by the first selector: // All anchors direct child elements of a DIV $("div > a")

The preceding selector is functionally equivalent to the following jQuery expression: $("div").children("a")

Plain concatenation of selectors results in a logical AND of conditions. For example, consider the following query: $("div.header.highlight")

It selects all DIV elements styled using both the class header and class highlight. The + operator—the adjacent operator—selects sibling elements in the second selector ­immediately preceded by elements selected by the first selector. Here’s an example: // All P immediately preceded by A $("a + p")

Download from Wow! eBook

Chapter 21  jQuery Programming

911

The ~ operator—the next operator—is similar to + except that it selects sibling elements just preceded by others. Here’s an example: // All P preceded by A $("a ~ p")

By using the comma, instead, you return the union of elements queried by multiple selectors. In terms of operations, the comma represents a logical OR of selectors. The next example, in fact, picks up elements that are either A or P: // All A and all P $("a, p")

Beyond simple operators, you have filters. A filter is a jQuery-specific expression that contains some custom logic to further restrict the selected elements.

Predefined Filters Selectors can be further refined by applying filters on position, content, attributes, and ­visibility. A filter is a sort of built-in function applied to the wrapped set returned by a basic selector. Table 21-1 lists positional filters in jQuery. TABLE 21-1  Positional

Filters

Filter

Description

:first

Returns the first DOM element that matches

:last

Returns the last DOM element that matches

:not(selector)

Returns all DOM elements that do not match the specified selector

:even

Returns all DOM elements that occupy an even position in a 0-based indexing

:odd

Returns all DOM elements that occupy an odd position in a 0-based indexing

:eq(index)

Returns the DOM element in the wrapped set that occupies the specified 0-based position

:gt(index)

Returns all DOM elements that occupy a position in a 0-based indexing greater than the specified index

:lt(index)

Returns all DOM elements that occupy a position in a 0-based indexing less than the specified index

:header()

Returns all DOM elements that are headers, such as H1, H2, and the like

:animated()

Returns all DOM elements that are currently being animated via some functions in the jQuery library

Table 21-2 lists all filters through which you can select elements that are child elements of a parent element.

912

Part V

The Client Side

TABLE 21-2  Child

Filters

Filter

Description

:nth-child(expression)

Returns all child elements of any parent that match the given expression. The expression can be an index or a math sequence (for example, 3n+1), including standard sequences such as odd and even.

:first:child

Returns all elements that are the first child of their parent.

:last-child

Returns all elements that are the last child of their parent.

:only-child

Returns all elements that are the only child of their parent.

A particularly powerful filter is nth-child. It supports a number of input expressions, as shown here: :nth-child(index) :nth-child(even) :nth-child(odd) :nth-child(expression)

The first format selects the n.th child of all HTML elements in the source selector. All child ­elements placed at any odd or even position in a 0-based indexing are returned if you ­specify the odd or even filter instead. Finally, you can pass the nth-child filter a mathematical sequence expression, such as 3n to indicate all elements in a position that are a multiple of 3. The following selector picks up all rows in a table (labeled Table1) that are at the positions determined by the sequence 3n+1— that is, 1, 4, 7, and so forth: #Table1 tr:nth-child(3n+1)

Table 21-3 lists expressions used to filter elements by content. TABLE 21-3  Content

Filters

Filter

Description

:contains(text)

Returns all elements that contain the specified text

:empty

Returns all elements with no children

:has(selector)

Returns all elements that contain at least one element that matches the given selector

:parent

Returns all elements that have at least one child

As far as content filters are concerned, you should note that any text in an HTML element is considered a child node. So elements selected by the empty filter have no child nodes and no text as well. An example is the tag. A popular and powerful category of filters are attribute filters. Attribute filters allow you to select HTML elements where a given attribute is in a given relationship with a value. Table21-4 lists all attribute filters supported in jQuery.

Chapter 21  jQuery Programming TABLE 21-4  Attribute

913

Filters

Filter

Description

[attribute]

Returns all elements that have the specified attribute. This filter selects the element regardless of the attribute’s value.

[attribute = value]

Returns all elements where the specified attribute (if present) is set to the specified value.

[attribute != value]

Returns all elements whose specified attribute (if present) has a value ­ ifferent from the given one. d

[attribute ^= value]

Returns all elements whose specified attribute (if present) has content that starts with the given value.

[attribute $= value]

Returns all elements whose specified attribute (if present) has content that ends with the given value.

[attribute *= value]

Returns all elements whose specified attribute (if present) has content that contains the given value.

Attribute filters can also be concatenated by simply placing two or more of them side by side, as in the following example: var elems = $("td[align=right][valign=top]");

The returned set includes all

elements where the horizontal alignment is right and the vertical alignment is top. The next expression, which is much more sophisticated, demonstrates the power and ­ exibility of jQuery selectors, as it combines quite a few of them: fl #Table1 tr:nth-child(3n+1):has(td[align=right]) td:odd

It reads as follows: Within the body of element Table1, select all

elements at positions 1, 4, 7, and so forth. Next, you keep only table rows where a element exists with the attribute align equal to the value of right. Furthermore, of the remaining rows, you take only the cells on columns with an odd index. The result is a wrapped set made of elements. Finally, a couple more filters exist that are related to the visibility of elements. The :visible filter returns all elements that are currently visible. The :hidden filter returns all elements that are currently hidden from view. The wrapped set also includes all input elements of type hidden.

Form Filters A special family of filters exists for HTML input elements. Table 21-5 lists all of them.

914

Part V

The Client Side

TABLE 21-5  Input

Field Filters

Filter

Description

:input

Returns all elements that have a role in collecting input data

:text

Returns all input elements whose type attribute is text

:password

Returns all input elements whose type attribute is password

:checkbox

Returns all input elements whose type attribute is checkbox

:radio

Returns all input elements whose type attribute is radio

:submit

Returns all input elements whose type attribute is submit

:reset

Returns all input elements whose type attribute is reset

:image

Returns all input elements whose type attribute is image

:button

Returns all input elements whose type attribute is button

:file

Returns all input elements whose type attribute is file

:hidden

Returns all input elements whose type attribute is hidden

:enabled

Returns all input elements that are currently enabled

:disabled

Returns all input elements that are currently disabled

:checked

Returns all input elements that are currently checked

:selected

Returns all input elements that are currently selected

The :input filter, in particular, refers to all logical input elements you might find within a page form and is not limited solely to the elements. In fact, it also picks up and elements used to display multiline text boxes and lists. The filters in Table 21-5 provide handy shortcuts for selecting hom*ogeneous elements and are functionally equivalent to the other legal jQuery selectors. For example, :checkbox is equivalent to the following: form input[type=checkbox]

As you can see in Table 21-5, other nice helpers are available to grab all input elements in a page form that are currently enabled or disabled and all check boxes and radio buttons ­currently selected.

Filter vs. Find To further restrict a query, you can use either the find or filter function on a wrapped set. They are not the same, of course. The function filter explores the current wrapped set for matching elements and doesn’t ever look into DOM for descendants. The function find, ­instead, looks inside of each of the elements in the wrapped set for elements that match the expression. In doing so, however, the function explores the DOM of each element in the wrapped set.

Chapter 21  jQuery Programming

915

Operating on a Wrapped Set The power of jQuery descends primarily from the powerful query language that allows you to select nearly any possible combination of DOM elements you can think of. However, the query language would not be much without a rich collection of predefined operations to ­apply to selected elements. The jQuery library offers a wide range of functions you can ­apply to the content of a wrapped set. We have already taken a look at how to enumerate the ­content of a wrapped set; let’s now proceed with more specific operations. As mentioned, function calls can be chained because any wrapped set returned by a query is in turn another jQuery object that can be further queried. The following expression is just fine: $(selector).hide().addClass("hiddenElement");

It first hides from view all matching elements and then adds a specific CSS class to each of them. In jQuery, however, not all functions return a jQuery object. You must be aware of this to avoid nasty script errors. Chaining functions that act as value getters (not returning a jQuery object) is fine as long as these functions go at the end of the expression. Important  Before going any further, it is worth recalling that this is an ASP.NET Web Forms

book. This means that in spite of the changes introduced in version 4 to the algorithm for generating control IDs, there is still a chance you’ll end up using complex hierarchies of controls in which you don’t exactly know the actual ID being generated for a given segment of the markup. As you saw in Chapter 6, “ASP.NET Core Server Controls,” this problem is addressed by the ClientIDMode property added in ASP.NET 4 to the Control class. An easy way to retrieve the client ID of an ASP.NET control—at least when the ASP.NET control outputs a single piece of HTML—is the following:

var selector = "#"; ...

The code block captures the value of the ClientID property of the specified ASP.NET control and will emit it into the script block.

Controlling Visibility The functions hide and show allow you to remove from view or display all elements that match a given selector. These functions help a lot in building dynamic views where you need

916

Part V

The Client Side

to adjust the next user interface based on a current user’s choice. Here’s how to hide an element: $(document).ready(function () { $("#panelAdvancedOptions").hide(); });

To display it, you just replace the call to hide with a call to show. The most interesting aspect of show and hide methods is the built-in support for completion callbacks and effects. Here are the full signatures supported by the functions: $(selector).hide() $(selector).show() $(selector).hide(duration, $(selector).show(duration, $(selector).hide(duration, $(selector).show(duration,

callback) callback) easing, callback) easing, callback)

When duration is specified, functions perform an animation while hiding or showing the element. The duration argument indicates the time (in milliseconds) the animation will take to run. You can also specify a couple of descriptive values such as fast and slow, which ­correspond to fixed values—specifically, 200 and 600 milliseconds. The easing parameter indicates the internal function to use to perform the animation. Default values are linear and swing, which animate height, width, and opacity. Different effects can be achieved only through plug-ins. The callback function runs at the end of the animation. The function doesn’t get any ­parameter. However, the expression this in the context of the callback refers to the element being animated. $("#panelAdvancedOptions").show(1000, function () { // Perform some action necessary when the panel is displayed. // The panel takes 1 second of animation to display. ... });

Invoking show and hide methods without parameters is nearly equivalent to setting the ­display CSS attribute. The only difference is that the assigned value is cached for the purpose of toggling it through the toggle function: $("#panelAdvancedOptions").toggle();

Chapter 21  jQuery Programming

917

The preceding call toggles the visibility state of all elements in the wrapped set, making ­visible hidden elements and hiding visible elements. In addition to plain show and hide methods, you also have methods to apply visibility ­changes through specific animations, such as sliding and fading. Methods are listed in Table21-6. TABLE 21-6  Visibility

Effects

Function

Description

slideDown

Displays any matching elements by increasing their height progressively

slideUp

Hides any matching elements by decreasing their height progressively

slideToggle

Shows or hides all matching elements inverting the current sliding setting

fadeIn

Fades in any matching elements by reducing their opacity progressively

fadeOut

Fades out any matching elements by increasing their opacity progressively

fadeTo

Fades the opacity of all matching elements to a specified opacity

Styling Applying CSS classes to selected elements is easy too. If you’re interested in tweaking just individual CSS properties, you can use the css function, as shown here: $("form input").css( {‘color’ : ‘blue’, ‘background-color’ : ‘yellow’, ‘border-style’ : ‘dashed’} );

To work with entire CSS classes, you have ad hoc functions such as those in Table 21-7. TABLE 21-7  Working

with CSS Classes

Function

Description

addClass

Adds the specified CSS class to any matching elements

removeClass

Removes the specified CSS class from any matching elements

toggleClass

Toggles the specified CSS class from any matching elements, meaning that the elements will be added to the class if they’re not already assigned and removed from the class if they are currently assigned

Binding and Unbinding Events For years, it has been common to write HTML pages with client buttons explicitly attached to JavaScript event handlers. Here’s a typical example:

918

Part V

The Client Side

From a purely functional perspective, there’s nothing wrong with this code—it just works as expected and runs the fnClick JavaScript function whenever the user clicks the button. This approach, however, is largely acceptable when JavaScript is just used to spice up Web pages; it becomes unwieldy when the amount of JavaScript code represents a significant portion of the page. The expression “unobtrusive JavaScript” is popular these days, and it just means that it would be desirable not to have explicit links between HTML elements and JavaScript code. In a way, unobtrusive JavaScript is the script counterpart of CSS classes. With CSS, you write plain HTML without inline style information and designer style elements using classes. Likewise, you avoid using event handler attributes (onclick, onchange, onblur, and the like) and use a single JavaScript function to attach handlers, upon page loading, wherever required. The jQuery library provides a bunch of functions to bind and unbind handlers to events fired by DOM elements. The pair of bind and unbind functions are used to attach a callback ­function to the specified event: // All elements that match the selector will be attached // the same handler for the click event. $(selector).bind("click", function() { ... });

You use the unbind function to detach any currently defined handler for the specified event: $(selector).unbind("click");

The unbind function doesn’t remove handlers that have been inserted directly in the markup through any of the onXXX attributes. The jQuery library also defines a number of direct functions to bind specific events. Facilities exist for events such as click, change, blur, focus, dblclick, keyup, and so forth. The following code shows how to bind a handler for the click event: $(selector).click(function() { ... });

Invoked without a callback, the same event functions produce the effect of invoking the ­current handler, if any are registered. The following code, for example, simulates the user’s clicking on a specific button: $("#Button1").click();

You can achieve the same effect in a more generic way using the trigger function: $("#Button1").trigger("click");

Chapter 21  jQuery Programming

919

Event handlers receive a jQuery internal object—the Event object. This object provides a ­unified programming interface for events that goes hand in hand with the World Wide Web Consortium (W3C) recommendation, and it resolves discrepancies in the slightly different implementations provided by some browsers: $("#Button1").click(function(evt) { // Access information about the event : // Return false if you intend to stop propagation return false; });

The Event object features properties such as mouse coordinates, the JavaScript time of the event, which mouse button was used, and the target element of the event. Note  In JavaScript, the time is expressed as the number of milliseconds elapsed from a fixed date—January 1, 1970.

Live Event Binding Live binding is a nice feature of jQuery that allows you to keep track of event bindings for a given subset of DOM elements for the entire page lifetime. In other words, if you opt for live binding instead of plain binding, you are guaranteed that any new dynamically added ­elements that match the selector will automatically have the same handlers attached. You operate live binding through live and die functions. Here’s an example: $(".specialButton").live("click", function() { ... })

All buttons decorated with the specialButton CSS style are attached the given function as the handler for the click event. The difference between using live and bind (or specific event ­functions such as click) is that when live is used, any new DOM elements added to the page and decorated with the specialButton style automatically have the handler added. This won’t happen if bind is used. To stop live binding for some elements, you need to use the die function: $(".specialButton").die("click");

920

Part V

The Client Side

Manipulating the DOM The standard DOM provides a rich set of methods to create HTML trees dynamically. It turns out, however, that in nearly all browsers the performance of native DOM objects is poor compared to using the innerHTML property, which is not officially part of the DOM standard. While functions and objects to neatly compose a piece of HTML are great things to have, the ability to select a plain chunk of HTML and get the resulting DOM tree is even more ­compelling. In jQuery, you find an API that supports both approaches.

Creating a DOM Tree The simplest way to create a new DOM tree in jQuery consists of passing an HTML string to the jQuery (or $) function, as shown here: // Represents a DOM tree with a UL list and two child LI elements $("

  • One
  • Two
  • ");

    You can also indicate style information, event handlers, and set attributes. The following ­example returns a DIV element with some inner text, a CSS class, and a click handler: $("", { class: "panel", text: "Click me!", click: function() { $(this).toggleClass("extra"); } } );

    The DOM you created in this way is not part of the page yet. To add it to the existing page DOM, an additional step is required.

    Adding Elements to the DOM The jQuery library defines a bunch of functions to insert the DOM tree resulting from a piece of HTML somewhere in the existing DOM. The following code shows how to insert a dynamically created image after each element in the wrapped set. The wrapped set includes all LI child elements of a UL element identified by name: $("#ShoppingList li").after("");

    The function after inserts the DOM tree (specified via plain HTML text) after any matching element in the set. Other available functions are before, prepend, and append. The function prepend puts the DOM before the inner text of matching elements, whereas the function ­append puts the DOM right after the inner text of matching elements.

    Chapter 21  jQuery Programming

    921

    You can also add elements to an existing DOM the other way around—that is, by first ­creating the new DOM and then inserting it in some way around elements in a wrapped set. You use insertAfter and insertBefore to insert a DOM after or before an existing element: $(html).insertAfter(selector); $(html).insertBefore(selector);

    You use the prependTo and appendTo functions to insert something before and after, ­respectively, the inner text of a matching element: $(html).prependTo(selector); $(html).appendTo(selector);

    To detach an existing DOM subtree, you use the method detach. A detached DOM tree is treated like a dynamically created DOM tree and can be moved around the DOM. Imagine the following page content: Title

    Content

    Consider now the following script code: var naturalOrder = true; function swapText() { var title = $("h1", "#section").detach(); var content = $("p", "#section").detach(); if (naturalOrder) { title.insertAfter("#Separator"); content.insertBefore("#Separator"); } else { content.insertAfter("#Separator"); title.insertBefore("#Separator"); } naturalOrder = !naturalOrder; }

    The swapText function is defined as the click handler of a button in the page. When clicked, it first grabs a reference to the DOM subtrees for the title and content. Note that the #section parameter identifies the context for the selector—it gets all h1 elements within the specified section of the page. Next, the position of the title and content is toggled around the hr as you click the button. (See Figure 21-3.)

    922

    Part V

    The Client Side

    FIGURE 21-3  Toggling DOM subtrees in a page.

    Important  In general, it is preferable to create a DOM tree using plain HTML when the HTML

    block you need is fairly complex. You might want to use insertion methods only for single elements (including an entire DOM subtree), In other words, it is not recommended that, say, to create a UL list you place multiple calls to insert the UL tag and then each of the required LI tags. You compose the HTML string and get it as a DOM in a single step.

    Removing DOM Elements To remove elements from the DOM, you have various options. You can remove all elements that match a given selector using the following code: $(selector).remove();

    The empty function, on the other hand, just empties the body of each element that is ­selected through the query expression: $(selector).empty();

    Finally, the aforementioned detach function detaches a DOM subtree from the main DOM but keeps it referenced in memory so that you can re-add it everywhere at any time: $(selector).detach();

    Modifying DOM Elements HTML DOM elements are characterized by attributes, inner text, and HTML. For each of these, you have ad hoc functions. For example, you use the attr function to read and write

    Chapter 21  jQuery Programming

    923

    the content of a given attribute. The following code reads the value of the maxlength ­attribute of a given text box: var maxLength = $("#TextBox1").attr("maxlength");

    To set it, instead, you just add a second parameter to attr, as shown here: $("#TextBox1").attr("maxlength", 10);

    You can use the function attr to read and write any attributes. For the value attribute, ­however, you can resort to the more specific val function. The val function has the same ­usage as the attr function. To get and set the inner text of an element, you use the text function. The html function is used to get and set the inner HTML of an element. Sometimes you just want to make a copy of an element DOM element or subtree and ­duplicate it in various places around the page. The jQuery library offers the clone function: $(selector).clone();

    Used in this way, the function performs a deep copy of matching elements, including ­attributes and descendants. The function, however, also supports an optional Boolean argument: $(selector).clone(true);

    If set to true (false is the default), the function performs a deep copy of matching elements, including attributes and descendants plus event handlers.

    The jQuery Cache In client Web applications, data storage is an area of growing importance, and the work ­behind done on it around the HTML 5 specification confirms this fact. Some browsers currently support forms of local storage even though the API is not unified yet. Local storage is persistent and is meant to replace certain use of cookies in the long run—for example, to store user-specific data. An in-memory cache is a different kind of thing, but it still has its own space.

    Cached Data and DOM Elements The jQuery library offers a simple but quite effective API to store data in the browser’s ­memory for the duration of the session. Any data you park in the jQuery cache is lost once you close the browser window. The jQuery cache is centered on the data function. This ­method allows you to associate some arbitrary data with all elements that match the selector.

    924

    Part V

    The Client Side

    Note that most of the time, though, you’ll use selectors that match just a single DOM ­element. If multiple elements are selected, no data duplication will ever occur—only the ­reference is duplicated, not the data. The jQuery cache is implemented as a plain dictionary where each element is characterized by a name and a value. What about naming conventions to ensure uniqueness of entries? Binding data to DOM elements, in full respect of the jQuery philosophy, is also helpful because it makes it significantly simpler to name elements. Cached entries can have the same name as long as they are bound to different DOM elements.

    Working with Data in the In-Memory Cache To add data to the cache, you select the DOM elements and then invoke the data function, passing the key and value. $("#Grid1").data("DataSource", value)

    The cache is fairly useful for storing data you download once and reuse frequently within thepage. When you have a master/detail view and you get data for the detail view via Ajax, a call to the data function can save you roundtrips within the same session. Have a look at Figure 21-4.

    FIGURE 21-4  An Ajax page that retrieves customer details from the server.

    Chapter 21  jQuery Programming

    925

    Every time the user selects a letter, the page downloads the list of all customers whose name begins with the letter. If the user clicks twice on, say, “A,” the list of customers is downloaded only once. Here’s the script code that manages the clicking: // Attempt to grab data from the cache first var data = loadFromCache(selection); if (typeof (data) !== ‘undefined’) { fillViewInternal(data, true); return; } // Grab data from the server asynchronously loadFromSource(selection);

    Inside of the loadFromCache function, you simply build the key and place a call to the data function: function loadFromCache(query) { var key = "Customers_" + query; var cachedInfo = $("#RootView").data(key); return cachedInfo; }

    Inside of the loadFromSource function, instead, you store downloaded data right into the cache object: var key = "Customers_" + query; $("#RootView").data(key, downloadedInfo);

    Once it’s placed in the cache, the data never expires and must be removed manually to free up memory. To remove a piece of data from the cache, you use the removeData method: $("#RootView").removeData(key);

    Ajax Capabilities Ajax support in jQuery is centered on an abstraction of the browser’s XMLHttpRequest object and counts on a bunch of helper functions that address specific scenarios, such as getting a JSON response, getting a script file, or performing a cross-domain call.

    926

    Part V

    The Client Side

    Plain Ajax Caller In jQuery, to compose and control all aspects of your Web request, you use the ajax function, as shown next: $.ajax( { type: "POST", url: "getOrder.aspx", data: "id=1234&year=2010", success: function(response) { alert( response ); } } );

    The ajax function gets a list of parameters, such as type, url, data, dataType, cache, async, and success. The dataType parameter indicates the type of the expected response (for example, HTML, XML, JSON, JSONP, script). A few other parameters exist to let you further configure the HTTP request. You can refer to the jQuery online documentation for further details. The URL is http://api.jquery.com/jQuery.ajax. The async parameter indicates whether the call has to go asynchronously or not. The cache Boolean parameter indicates whether you want the library to cache the response for future access to the same URL. Ajax calls are always cached by default except for when the data type is JSONP or script. ­ efore The $.ajax function supports several callbacks. The beforeSend callback is invoked just b sending the request out. The callback receives the settings of the call and represents your last chance to modify the call. The complete callback is invoked as soon as the response is received and regardless of the outcome. The callback receives a description of the HTTP ­status of the request and indicates whether the request completed successfully, resulted in an error, timed out, or pointed to a resource that was not modified. The callback won’t receive the actual response, if there is any. Past the complete callback, the library fires either the ­success or error callback, depending on the context. The success callback receives the response sent over the wire by the server. The error callback gets a code for the type of error (timeout, parse, or error) and an exception object that provides, if possible, more details about the failure. On top of the ajax function, a number of shortcut functions have been created that make it simpler for developers to place certain specific types of calls, such as calls for getting a script file or a JSON string. The get and post functions also exist to perform plain HTTP GET and POST requests.

    Chapter 21  jQuery Programming

    927

    Global Ajax Event Handlers The jQuery library provides a bunch of global handlers for Ajax events so that you can ­register your handlers that are invoked for each Ajax operation regardless of the code that triggers it. You can add handlers for the events in Table 21-8. TABLE 21-8  Global

    Ajax Events

    Event

    Description

    ajaxComplete

    Fires upon completion of any Ajax request, regardless of the outcome

    ajaxError

    Fires when an Ajax call fails

    ajaxSend

    Fires when an Ajax request is sent

    ajaxStart

    Fires when an Ajax request begins being processed

    ajaxStop

    Fires when no pending Ajax requests are left

    ajaxSuccess

    Fires when an Ajax request completes with success

    You can have multiple handlers for each of these events. If multiple handlers are registered, all of them are invoked in order.

    Getting Scripts The getScript function requires you to provide the URL for the script file and an optional ­callback to execute upon downloading the script. Here’s the signature of the function: $.getScript(url, succeededCallback)

    The interesting thing about the function is that the downloaded script is processed by jQuery right after download. This means that in the callback, you can already start using objects and features available in the script: $.getScript("mylib.js", function() { // Start using the features of the downloaded script here ... });

    The request being placed for the script is an HTTP GET. Keep in mind that if you need to tweak the request beyond the hardcoded settings of the getScript function, you better resort to the ajax function.

    Getting JSON The getJSON function is ideal for invoking an HTTP endpoint that is expected to return a JSON-encoded string. Here’s the signature of the function: $.getJSON(url, inputData, succeededCallback)

    928

    Part V

    The Client Side

    When you make a JSON request, you might need to send some data over to the remote server to guide the generation of the response. The second argument to getJSON represents the input you intend to pass. Here’s an example: var playerId = 1; $.getJSON("/yourServer/Player/Details", playerId, function(jsonData) { // Start using the information stored in the downloaded object here displayDetailsForPlayer(jsonData); });

    The getJSON function appends any input data to the URL as a query string. If the data is not of a primitive type, the function will convert it to a string before appending it to the URL. The request is placed as an HTTP GET request. Any response is assumed to be JSON and is parsed as such using the global $.parseJSON function. The callback receives the parsed data ready to use.

    Getting HTML A frequent action you might want to perform from a client page is downloading some HTML via a simple GET request. The load function is an instance (as opposed to global) function that you can call over a wrapped set. Here’s the signature of the function: $(selector).load(url, inputData, succeededCallback)

    Note that input data and callback function are optional. In particular, the method is ­automatically bound to a default callback function that appends the downloaded markup to all elements in the wrapped set. Here’s an example: var templateType = 1; $("#panelAdvancedOptions").load("/template.aspx", templateType);

    If any callback is provided in the call, it is executed after the default callback. No call is ever attempted if the wrapped set is empty. If the input data is a JavaScript object, the request goes out as a POST instead of a GET. You are not forced to download the entire content of the provided URL. If the URL contains a white space, anything that follows is interpreted as a jQuery selector. Look at the following example: $("#panelAdvancedOptions").load("/template.aspx #area_1");

    The entire URL content is downloaded, but jQuery then discards everything but the DOM tree selected by the #area_1 expression. When you use load, you should be aware that some tags might be discarded during the parsing process. This typically occurs with tags such as and , which are usually already part of the page.

    Chapter 21  jQuery Programming

    929

    Cross-Domain Calls The biggest difference between making a browser-led request and an Ajax-led request is in what happens after the response has been downloaded. The browser safely processes the response to display it. The script, on the other hand, can potentially make any use of the downloaded response—from building hopefully innocuous mashups to preparing cross-site scripting attacks. For this reason, all browsers implement the Same-Origin Policy (SOP), which means that script-led calls are allowed to hit only the same server that served the current page. Nobody complained about SOP until Ajax became as popular as it is today. SOP represents a serious limitation for developers of Ajax applications because it prevents you from easily creating mashups and, more in general, to requesting data from a site that lives on a different host or that uses a different protocol. Workarounds have been in the works for years, but we’re still looking for an official standard solution to the issue. W3C has a working draft for something called Cross-Origin Resource Sharing (CORS), which defines a common ground for browsers and Web servers to interoperate and enable applications to perform secure ­cross-site data transfers. Some browsers currently support CORS to some extent and through different APIs. That will probably be the mainstream approach in the near future. While waiting for that, you might want to consider other approaches, such as using a ­server-side proxy, Silverlight or Flash applets and their workarounds to bypass SOP, and ­leveraging cross-domain enabled HTML tags such as and . Note  When it comes to cross-domain calls, these are the options that work without requiring each user to tweak security settings on her browser. SOP is ultimately a browser policy, and each user can disable it by changing the browser’s security settings.

    Cross-Domain HTML Tags Both the and tags can be configured to download resources from any site, regardless of any origin policy that might be set. An element can successfully download content from just about anywhere, but browsers apply restrictive policies as far as scripting that content is concerned. Cross-frame scripting is not allowed if content comes from different domains. So you’re back at square one: how can you actually consume the downloaded content? In the end, the trick proves helpful only when you need to upload data in a fire-and-forget manner to a cross-domain site. With the tag, instead, the downloaded content is restricted to JavaScript, but it can be freely consumed from within the caller page. With a bit of help from the remote server, you can download usable data from a different domain in the form of a JavaScript string and process it on the client. This requires using the JSON with Padding (JSONP) protocol.

    930

    Part V

    The Client Side

    A JSONP solution is effective and cross-browser capable, but it can be used only with ­agreeable sites and in conformance with the rules they set.

    Basics of JSONP A JSONP-enabled Web site is a Web site exposing a public endpoint that agrees to return a JSON string padded with a call to a caller-defined JavaScript function. For example, suppose that dino.com exposes an endpoint like this one: http://www.dino.com/public/getcustomer/123

    When invoked, the endpoint returns a JSON string that represents an object. If you try to call the URL just shown via Ajax, you likely will get an “access denied” error because of the SOP. If you use the same URL within a tag, however, you successfully download the ­response of the method, except that you can’t do much to further process it:

    A JSONP-enabled endpoint would rather wrap the JSON output string in a call to a JavaScript function that is defined locally within the context of the caller server. The JSONP output would look like this: myHandler("{‘Id’=’...’, ‘CompanyName’=’...’, ...}");

    Because all browsers evaluate any content downloaded via a immediately, JSONP does the trick of invoking some cross-domain code and processing the output locally. The myHandler function mentioned here is supposed to be a JavaScript function defined by the same developer who actually places the cross-domain Ajax call. With JSONP, you find a way to instruct the remote server to return a string that wraps the JSON data into a call to your JavaScript function. A JSONP-enabled site is a site that offers you a programmatic and documented way to indicate which JavaScript function the response has to be wrapped in. Most JSONP sites today allow this through an ad hoc parameter in the query string. For example, Flickr recognizes the jsoncallback parameter.

    JSONP in jQuery Cross-domain calls can be successful only when you call a server that is JSONP enabled. If this condition is true, you can use many of the jQuery Ajax functions to set up a successful crossdomain call. For example, you can use the $.getScript function, append the target JavaScript function name in the query string, and skip over the jQuery callback: $.getScript("http://someserver/GetCustomer?js=myHandler", function () { })

    Your JavaScript function will take care of processing the results of the query in JSON format.

    Download from Wow! eBook

    Chapter 21  jQuery Programming

    931

    Although this approach is fully functional, it deviates a bit from the standard jQuery ­programming model in which the callback function defines the behavior to take at the end of the operation. For this reason, the $.getJSON function offers to generate a predefined but randomly named function to bypass browser policies. The predefined behavior of the ­autogenerated function will just invoke the callback, passing the JSON data. You trigger the generation of the random name using the following notation: $.getJSON("http://someserver/GetCustomer?js=?", function () { // Place your code here that processes the response ... })

    The query string parameter ( js in the example) has to match the query string parameter that the server recognizes and supports. The ? placeholder instructs jQuery to generate a random and unique name. The following is a sample heading for a JSONP request that goes through jQuery: GET /GetCustomer?js=jsonp1294078062559 HTTP/1.1

    As a developer, you have no control over the algorithm that generates the JavaScript ­function name. Using a fixed name, however, brings you some benefits in terms of c­ aching ability. If you use the $.ajax function to arrange a JSONP call, you can use the jsonp and ­jsonpcallback parameters to replace the query string parameter name and the JavaScript function name, respectively. Important.  As mentioned, Microsoft provides full support for jQuery when it’s used within ASP.NET applications. Microsoft also created a few components that were accepted as official jQuery plug-ins in late 2010. At least the biggest of them—the Templates plug-in—is incorporated in the main library with version 1.5. The Templates plug-in fills a significant gap in client-side programming because it provides a way to declare HTML-based, data-bound templates. Ajax calls make it easy to download fresh JSON data, but displaying raw data is never easy because you just want to display data in the context of a rich graphical layout. With templates, you have the power of HTML for the layout and an ad hoc syntax to control how external data is inserted. Another interesting plug-in from Microsoft is the Data Link plug-in, which allows you to ­implement an MVVM-like design on the client. The plug-in keeps your user interface and data synchronized. It also makes it possible to keep the input fields of an HTML form in sync with the properties of a JavaScript object. Finally, the third Microsoft plug-in is the Globalization plug-in, which emits on the client ­information about more than 350 different cultures, thus enabling you to use formats or parse numbers, dates and times, calendars, and currencies according to the current settings.

    932

    Part V

    The Client Side

    Summary As emphatic as it might sound, knowing how to use JavaScript is a necessary skill today, whether you use a rich library or not. jQuery delivers a number of benefits. In the first place, it makes JavaScript code easier and quicker to write. The library provides helper functions that dramatically increase your productivity while decreasing frustration. The key to the wide adoption of jQuery is probably that it makes it simpler to do what developers need to do more often—query for DOM ­elements and manipulate them. No ASP.NET application today can ignore client programming and jQuery. Microsoft now fully supports jQuery and has abandoned further development of the Microsoft AJAX Client library. Isn’t this a big enough reason to develop JavaScript skills?

    Index Symbols $.ajax function, 926 $.getScript function, 930 $.parseJSON function, 928 @xxx syntax, 25–26

    A absolute expiration, 731 abstraction, 575–576 ASP.NET MVC and, 24 importance of, 19 of views, 624–626 Accept-Charset attribute, 82 access rules for, 818–819 securing with roles, 358 access control lists (ACLs), 790–791 AcquireRequestState event, 32, 650 .acsx files, @Control directive for, 180 actions in ASP.NET MVC applications, 22 Active Record pattern, 599–600 DAL and, 606 Active Server Pages (ASP), 3 Adapter property, 231 adapters, 605 control, 230–231 CSS-friendly, 232 writing, 232 adaptive rendering, 230–232 AdCreated event, 263 Add Managed Modules dialog box, 37 Add method, 726 tag, 287 AddOnPreRenderCompleteAsync method, 202–203, 207–208 AddValidationCallback method, 764 ADO.NET classes, binding data to, 413–414 images, reading, 134 AdRotator controls, 262–263, 268 advertisem*nt banners, 262–263 AggregateCacheDependency class, 738–739 aggregates, 600–601

    AJAX, 14–20, 313, 337, 839–840 advent of, 8 ASP.NET support for, 3 benefits of, 840 Browser-Side Templating pattern, 840 as built-in part of Web, 19 cross-domain calls, 850–851 Data-for-Data model, 17 events, jQuery handlers for, 927 HTML Message pattern, 839–840 HTTP façade, 881. See alsoHTTP façade infrastructure, 840–851 interaction model, 17 JavaScript and, 845–851 jQuery support, 925–928 JSON for, 892–893 Module Pattern, 849 out-of-band HTTP requests, 841–842 page methods, 895–897 partial rendering, 851–879 remote validation via, 385 REST and, 879–897 scriptable services, 880–889 ScriptManager control, 852–860 SEO and, 351 SOP and, 929 server controls and, 267–268 UpdatePanel control, 860–865 WCF services, hosting, 881 XMLHttpRequest object and, 840, 845 AJAX calls, replacing with postbacks, 10 AJAX-enabled services, 883 configuration settings, 107–108 ajax function, 926 AJAX HTML helpers, 20 AJAX postbacks, 868 AlachiSoft NCache, 755 allowAnonymous attribute, 294 allowDefinition attribute, 67 AllowDirectoryBrowsing property, 43 allowLocation attribute, 67–68, 71 allowOverride attribute, 70–71 AllowPartiallyTrustedCallers attribute, 789 allowPolicy attribute, 109

    AlternatingItemTemplate property, 483 AltSerialization class, 696 Amazon RDS, 613 Amazon SimpleDB, 613 anchor controls, 243–244 animations, 916–917 anonymous access, 781–782 anonymous accounts, impersonating through, 785 anonymous functions, 846 anonymous ID, 294, 671 anonymous identification feature, 73–74 anonymous users. See alsouser profiles user profiles for, 294–295 section, 73–74, 76 AOP, 571 Apache Web servers, 27 AppDomain, ASP.page_aspx class, obtaining, 35 AppendDataBoundItems property, 419–420 appendTo function, 921 AppFabric, 747–748 AppFabric Caching Services (ACS), 748–753 architecture of, 748–751 client-side configuration, 751–752 programming, 752–753 storing output caching in, 777 unnamed caches, 751 AppFabric Hosting Services, 748 App_GlobalResources folder, 304 Application Controller pattern, 632 application data, caching, 721–744 application deployment, 39–62 application warm-up and preloading, 59–62 files and settings, packaging, 43–51 IIS configuration, 55–59 mode settings, 81–82 site precompilation, 52–55 with XCopy, 40–43 @Application directive, 653–654 application directives for global. asax, 653–654

    933

    934

    application events, nondeterministic application events, nondeterministic, 33 application factory, 176–177 application logic, 596, 602–605 remote deployment, 603–604 Application object, 721 writing to, 679 application pages. See pages application pools defined, 29 identity of, custom, 38–39 identity of, modifying, 39 initialization of, 59 process recycling, 55–56 warmup of, 59–62 working mode for, 30 section, 95 Application Request Routing, 37 application restarts, 38, 56–58, 170 causes of, 179 application root folder, 786 application state, 675–679. See alsoHttpApplicationState class global state, storing, 679 synchronization of operations, 678–679 application warm-up, 59–62 application pools, configuring, 60–61 autostart provider, 61 behavior of, 59 specifying actions, 61–62 Application_End event handler, 648 Application_Error event handler, 283–284 Application_Error stub, 275 applicationHost.config file editing, 60, 93 mappings in, 37 application-hosting environment configuration settings, 84 application-level configuration settings, 111 accessing, 111–112 changing, 65 processing of, 65 updating, 112–113 application-level tracing, 100–101 ApplicationManager class, 34 application scope, 119 application services, centralization of, 30

    applications. See alsoASP. NET applications; Web applications binding pages to master, 326 claims-based identity, configuring for, 825 composable parts, 585 cookies, sharing, 801–802 data storage, 923 debugging, 284–285 domain logic, 596 error handling, 275–277 global.asax file, 651–655 inactive, unloading, 84 initialization code, 905–906 initialization of, 645–651 isolation between, 29 maintainability, 565 object-oriented design, 599 permissions for, 788–789 plugin-based, 584 resources embedded in, 659–660 security features of, 780. See alsosecurity symptoms of deterioration, 567–569 theme settings, 340 trust level of, 786–789 virtual folder for, 645 Application_Start event, 32 route definitions in, 160 Application_Start event handler, 648 Application_Xxx notation, 36 ApplyAppPathModifier method, 690 AppSettings collection, accessing, 111–112 section, 67, 105–106 App_Themes folder, 339 ArtOfTest, 363 .ascx extension, 768 ASHX extension and resources for handler mappings, 124 for HTTP handlers, 141–142 .asmx ASP.NET Web services, 881 ASP pages. See alsopages processing of, 170 tag, 325, 329–330 aspect-oriented programming (AOP), 571 ASP.global_asax class, 652 ASP.NET adoption of, 3 authentication API, 108

    authentication methods, 789– 791. See alsoauthentication browser information storage, 230 configuration hierarchy, 63–110 configuration in, 63. See alsoconfiguration files HTTP modules built-in, 154. See alsoHTTP modules and IIS, history of, 28–31 improvements to, 3 introduction of, 3 membership API, 88 perfect framework, characteristics of, 18–19 programming model, 19 runtime environment, 27. See alsoruntime environment site navigation API, 352–358 stateful behavior, 6–7 vulnerability patch, 64 worker process, standalone, 28–29 writing files on disk, 137 ASP.NET 4, 20–21 ASP.NET applications. See alsoWeb applications custom configuration data, 105–106 deploying, 39–62 error-handling settings, 80–81 health monitoring, 83 HTTP modules, registering, 37 identity of, 87 IIS, configuring for, 55–59 installing, 40 partial-trust applications, 103 preloading, 59–62 restarts of, 56–58 session-state information, 98–100 site-level settings, 108–110 termination tracking, 57 trust levels, 101–104 warmup of, 59–62 ASP.NET cache. SeeCache object; caching ASP.NET compiler tool, 53–54 parameters of, 54 target directory support, 53 ASP.NET Development Server section and, 109 ASP.NET HTTP runtime, 174 page processing, 169, 174. See alsopage life cycle;pages

    authentication modules ASP.NET MVC, 4, 21–25 abstraction, building with, 18–19 control over markup, 24 features of, 21–22 language, changing on the fly, 311 localizing applications in, 306 new paradigm of, 14 opting out of built-in features, 25 request processing, 24–25, 170 requests, 22 runtime environment, 22–24, 27 runtime stack, 23 Selective Update model, 20 separation of concerns, 23 simplicity, 24–25 state, maintaining, 23 testing code-behind, 363 URL patterns, 23 URL routing in, 157 visual components, 24 ASP.NET pages. See pages ASP.Net permission set, 103 ASP.NET requests. See alsoHTTP requests processing, 34–35 responses, building, 35–36 ASP.NET security context, 781–791 ASP.NET site administration tool, 302 ASP.NET temporary directory, 172 compiled code in, 178 ASP.NET Web Forms, 3–4. See alsoWeb Forms culture, setting, 309 HTML rendering, 9 localized text, 306–307 processing and rendering, separating, 9–10 request processing, 9 SEO and, 350–351 testing, 363 URL routing in, 157, 160–166 ASP.NET Web Pages, 25–26 audience of, 25 @xxx syntax, 25–26 ASP.NET Web services, 885–887 aspnet_client directory, 104 AspNetCompatibilityRequirements attribute, 889 aspnet_compiler –v command, 53 aspnet.config file, 95 aspnetdb.mdf file, 293–294 structure of, 302 AspNetInternalProvider provider, 79

    aspnet_isapi.dll, 28–30, 92, 170 mapping to resources, 30, 171–172 aspnet_regiis.exe, connection strings, encrypting with, 114 aspnet_regsql.exe, 80 AspNetSqlProfileProvider, 301 aspnet_state.exe, 697–699 AspNetXmlSiteMapProvider class, 100 ASP.page_aspx class, 35 ASPState database, 701–702 ASPStateTempApplications table, 702 ASPStateTempSessions table, 702 control, 775 .aspx files in ASP.NET MVC projects, 22 compiling, 284–285 handler for, 190 @Page directive for, 180, 181 serving markup with, 148 .aspx pages. See alsopages URLs, mapping to, 36 .aspx source files, changes in, 170, 173 ASPX templates, 217. See alsomarkup ASPXANONYMOUS cookie, 74 ASPXROLES, 97 assemblies business logic, 596 debug mode, 284 default linked assemblies, 185–186 early and late binding and, 187 generating, 170 loading, 187 modifying list of, 186 number of pages in, 169 referencing, 188 referencing from pages, 185 unloading vs. recompiling, 56 @Assembly directive, 185–187, 653–654 attributes of, 187 AssociatedControlID property, 260–261 Async attribute, 201–202 AsyncCallback objects, 146 asynchronous handlers, 121, 146–147, 201. See alsoHTTP handlers adding to page, 202 implementing, 147–148 Asynchronous JavaScript and XML. SeeAJAX asynchronous pages, 121, 201–209

    AddOnPreRenderCompleteAsync method, 202–203 Async attribute, 201–202 building, 203–206 operations for, 208 PreRenderComplete stage, 202–203 RegisterAsyncTask method, 206–207 asynchronous postbacks, 868, 869 concurrent, 877–878 events of, 872–874 triggers for, 872 asynchronous requests, 95 asynchronous tasks registering, 201–203, 206–207 within pages, 207 AsyncPostBackError event, 857 AsyncPostBackTrigger class, 869 attr function, 922–923 attribute filters, 912–913 AttributeCollection class, 238 attributes, directive, 181 Attributes collection, 237 AuthenticateRequest event, 32, 650, 820 AuthenticateUser function, 794 AuthenticateUser method, 794 authentication, 789–791 Basic authentication, 782 claims-based identity, 821–825 configuration settings, 74–76 Digest authentication, 782 Forms authentication, 783, 791–806 of HTTP requests, 32 integrated Windows authentication, 782 login pages, 792 LoginStatus control, 829–830 LoginView control, 830–832 None authentication, 789 password changes and, 833–834 principal objects, custom, 804–806 over secured sockets, 803–804 sign-outs, 795–796 state of, 829 user authentication, 784, 794–795 of view state, 713 Windows authentication, 790–791 Windows CardSpace, 791 authentication API, 108 authentication modules, 650

    935

    936

    section section, 74–76, 790, 792 authentication tickets, 792–793 encoding of, 800 getting and setting, 798 securing, 803–804 storage in cookies, 799–800 authorization, 76–77 file authorization, 790–791 of HTTP requests, 32 reauthorization, forcing, 663 URL authorization, 791 section, 76–77 AuthorizationStoreRoleProvider, 821 AuthorizeRequest event, 32, 650 AutoDetect, 801 AutoMapper, 605 automated test frameworks, 638 autonomous views, 616 AutoPostBack property, 258 autostart providers, 61 Preload method, 62 autostarting Web applications, 38–39 .axd extension, 127, 129 for handler mappings, 124

    B Balsamiq Mockups, 624 BarChart control, 547–556 BarChartItem class, 550 control hierarchy, 549 events of, 553–554 item object, 548–551 Items property, 550 properties of, 547 style properties, 548 using, 555–556 base classes, 513 choosing, 514–515 extending, 515 inheriting from, 514–515 unit testing and, 656 BaseCompareValidator class, 381 BaseDataBoundControl class, 514 BaseDataList class, 514 BaseValidator class, 379–380, 380–381 Basic authentication, 782 basicHttpBinding model, 885 batch mode compilation, 169 Begin/End asynchronous handlers, 201–203 BeginProcessRequest method, 147–148

    signature of, 146 beginRequest event, 873, 874 BeginRequest event, 32, 151, 649 BeginRequest event handler, 151–152 BeginXxx methods, 94 behaviorConfiguration attribute, 884 big ball of mud (BBM), 566 BigTable (Google), 614 binary large objects (BLOBs), database support for, 133 BinaryFormatter class, 539, 696–697 BinaryWrite method, 135, 669 bind function, 918–919 bind method, 907 binding containers, 226–227 BindingContainer property, 226 BLL, 593, 596–605 application logic, 602–605 design patterns for, 596–602 bound data adding to controls, 551–553 getting, 540–544 tracking and caching, 536–538 bound fields, 445 BoundField class, 445 browser cache, 316, 755 behavior of, 756 browser capabilities, 671–672 section, 77–78, 347 browser definition files, 345–346 .browser extension, 77, 230–232, 328 editing files, 346 browser IDs, detecting, 344 browser information reading, 345 repository for, 344 storage of, 230–231, 328 Browser property, 77, 344 browser providers, 78 browser-led processing model, 840–841 browsers browser-capabilities providers, 346–348 bypassing, 20 characteristics and capabilities of, enumerating, 77, 671–672 cross-browser rendering, 344–348 data storage and, 923 definition files, 345–346

    device-specific master pages and, 327–329 DOM and DHTML support, 842 geo-location capabilities, 312 Google Chrome, 902 IDs of, 328 JavaScript background compiler, 901 JavaScript engines, 902 programming in, 900–903 Same-Origin Policy, 929 script downloads, 313 scripting engines, 901–902 up-level browsers, 393 uploading files from, 249–251 XMLHttpRequest object support, 843 browser-sensitive rendering, 234–235 Browser-Side Templating (BST), 840 BulletedList control, 426–427 business logic, 596 modeling, 597 business logic layer, 593, 596–605 Button class UseSubmitBehavior property, 213 button clicks, processing of, 6 button controls, 257–258 command buttons, 247, 259 command name, 498 for JavaScript event handlers, 917–918 rendering as images, 447 button fields, 445–447 Button1_Click function, 5

    C cache, jQuery, 923–925 Cache class, 722–725 Cache object, working with, 725–732 methods of, 723–724 properties of, 722–723 cache items attributes of, 725–726 dependencies of, 725, 728. See alsodependencies expiration policy, 731–732 priority of, 730–731 cache manager, 175 Cache object, 676, 721, 722. See alsocache items; caching cache synchronization, 736 callback function, 732 clearing, 735

    ClientScript object Cache object (continued) data expiration, 731–732 dependencies, 728 dependencies, broken, 740 for globally shared information, 679 inserting new items, 725–727 limitations of, 744 memory pressure statistics, 732 priority of items, 730–731 removal callbacks, 729–730 removing items from, 727 scavenging, 731 session state expiration policy, 694 cache settings, 78–80 Cache-Control header, 757, 761 CacheControl property, 665 cached pages, returning, 32 CacheDependency class, 728, 737 constructors, 728 deriving from, 737 members of, 737 CacheDependency object, 725, 728 aggregate dependencies, 738–739 change notifications, 738 custom, 737–739 for SQL Server, 743–745 testing, 742 for XML data, 739–742 caching, 463–464, 721–778 of application data, 721–744 cacheability of pages, 758–762 Cache class, 722–725 custom dependency, designing, 737–739 DAL, relation to, 734–735 database dependency, creating, 743–745 dependencies, 722, 738–739. See alsodependencies distributed cache, 744–755 vs. fetching, 733 hashtable, 724–725 internal structure of, 724 isolating caching layer, 734–735 of multiple versions of pages, 765–768 of page output, 721, 755–777 pages, 665–666 and performance, 733 per request, 737 of portions of pages, 768–774 removal callbacks, 726

    sliding expiration, 723, 726, 731–732 update callbacks, 726 Web cache, 755 Windows Server AppFabric, 747–753 XML data, cache dependency for, 739–742 caching profiles, 774–775 section, 73, 78–80 caching services AppFabric, 747–753 architecture of, 748–751 client-side configuration, 751–752 programming, 752–753 CacheItemPriority enumeration, 730 CacheItemRemovedReason enumeration, 727 CacheMultiple class, 724 CacheProfile attribute, 774 CacheSingle class, 724 Calendar control, 263–264, 267 Cancel buttons, 876 CAS, 101 CAS policies, 103 cascading style sheets. SeeCSS Cassandra, 614 Cassini, 48 section and, 109 Castle Active Record, 600 catalogs, 585–586 catch blocks, 270–271 CausesValidation property, 248, 394 CDNs, 313–314 CGI, 120 ChangePassword control, 833–834 ChangePassword method, 812, 834 Chatty anti-pattern, 604 check box fields, 448 check boxes, 259–260 CheckBoxList control, 422–424 child controls hierarchy of, 544–545 initialization, 210 managing, methods for, 195–197 postbacks, detecting from, 866–868 for rendering, 528–532 state of, persisting, 6–7 storage of, 229 unique names for, 190

    child filters, 912 child requests, free threads for, 86 ChildrenAsTriggers, 867–869 Chirpy, 314 claims, 822, 823–824 claims-based identity, 821–825 using, 824–825 workflow, 822–823 claims-based Windows Identity Foundation (WIF), 76 class names, resolving, 188 classes adapters, 605 closed for modification, 575–576 code-behind classes, 12. See also code-behind classes coupling, 570–571 dynamically generated, 170 hierarchy of, 13 inports and exports, 585 page classes, 12–13 partial classes, 173 preconditions, 578 responsibilities of, 573–574 splitting, 574 system classes, 12 classic ASP, 4 classic ASP.NET, 4. See alsoWeb Forms moving away from, 15–19 cleanup code in exception handling, 272 click-throughs, 515 client behavior, controlling, 213 client cache, 748–753 client certificates, 782 client data, processing, 211 client IDs of controls, 220 client script files, storage of, 104–105 client script manager, reference to, 195 client side, 839. See alsoAJAX events for user feedback, 872–874 JSON representations on, 890 powering, 899–905 client Web programming, 3 clientIDMode attribute, 91 ClientIDMode property, 223 Predictable option, 224–225 Static option, 224 ClientID property, 211, 220 ClientIDRowSuffix property, 226 ClientScript object, 856

    937

    938

    ClientScript property, methods explosed by ClientScript property, methods explosed by, 198 ClientScriptManager class GetWebResourceUrl method, 195 clientScriptsLocation attribute, 104 client-side behavior, testing, 361–363 client-side message boxes, 501 client-side validation, 393–394 closures in JavaScript, 847–848 cloud databases, 613 CLR exceptions, 270. See alsoerror handling CLR security zones, 786 CMS, 157 code sandboxing, 789 testability, 636–642 for Web pages, 3 code access secruity (CAS), 101, 103 code blocks in server-side sections, 243 Code Contracts API, 578 code declaration blocks, 654 code-behind classes, 217 defined, 12 hierarchy of, 13 removing, 627–628 server control references, 173 testing, 361 WebMethod attribute, 895–896 cohesion, 569–571 collections binding data to, 412–413 in profiles, 289 COM, 843 command buttons, 247, 259, 498–499 custom, 499 command names, 498 Common Gateway Interface (CGI), 120 common language runtime (CLR), 270, 786 CompareValidator control, 380, 382–383, 386 compiled pages, 170 master pages, 329 Component Object Model (COM), 843 composable parts, 585 composite controls, 521. See also controls; server controls

    child controls, hierarchy of, 544–545 collections of items, 547 defined, 519 composite data-bound controls. See also controls; server controls adding bound data, 551–553 building, 543–561 data item object and collection support, 547 hierarchy and data separation, 546–547 receiving data, 545 template support, 556–561 CompositeControl class, 514 as base class, 519 CompositeDataBoundControl class, 514 deriving from, 544 compressed responses, 79 concerns, separation of, 571–572 concurrent calls, 877–878 conditional refreshes, 866–870 CONFIG directory, 64 section, 107 element, 67 configuration, declarative, 589–590 element, 50, 66 main children of, 66 configuration errors, 269 configuration files, 64–68. See also individual section names accessing, 63 add, remove, and clear elements, 68 section, 73–74 application-level settings, 111 section, 67, 105–106 section, 74–76 section, 76–77 section, 77–78, 347 section, 78–80 changes to, 63 section, 107 element, 67 element, 66 section, 106 creation of, 63

    section, 80–81, 278–279 custom sections, creating, 116–117 custom sections, registering, 117 section, 81–82 section, 114 encrypting, 107, 113–116 section, 82, 309 handler factories, registering, 145 section, 125 section, 83 section, 84 section, 84–85 section, 82, 123 HTTP modules, registering with, 153 section, 82, 153 section, 85–87 section, 87 section, 68–71 section, 87–88 machine-level settings, 111 machinewide settings, 70 managing, 110–117 section, 88–89 opening, 111 section, 89–92 section, 92–95 section, 96–97, 286–287 section, 286 protection of, 64 section, 301 section, 97 element, 67 element, 67–68 sections, declaring, 67 section, 97–98 section, 98–100 section, 100 section, 67 section, 107–108 section, 71–73 section, 108–110 section, 100–101 tree of, 64–65 section, 101–104 unmodifiable settings, 70–71 section, 104

    Cross-Origin Resource Sharing (CORS) configuration files (continued) user names and passwords in, 87 section, 104–105 section, 105 configuration management API, 110–113 configuration section handlers, 116 ConfigurationManager class, 111 OpenMachineConfiguration method, 112 ConfigurationProperty attribute, 117 connectionString attribute, 106 connection strings configuration settings, 106 encrypting, 114 for out-of-process session state, 700 from profile providers to database engine, 301 connectionStringName attribute, 80 ConnectionStrings collection, accessing, 111–112 section, 106 constructors, overloaded, 583 container controls, 239–240 Container keyword, 560 content. See also data default content, 323–324 downloading cross-domain, 929–930 inline content, 316–317 Content controls, 324–326 content delivery networks (CDNs), 313–314 content filters, 912 Content Management Systems (CMS), fiendly URLs and, 157 content pages binding definitions, 327 binding to master pages, 326 Content controls, 324–326 content placeholders, 320–321, 323 defined, 320 @MasterType directive, 335–336 processing, 329–334 serving to user, 329–330 source code of, 325 title of, 326 writing, 323–328

    ContentPlaceHolder controls, 320–323 ContentTemplate property, 863 contract attribute, 884 contracts, MEF, 585 control adapters, 230–231 writing, 232 Control class, 190, 218, 514. See alsocontrols; server controls ClientIDMode property, 223 deriving controls from, 513 events of, 229–230 extending, 515 IComponent interface, 218 IDisposable interface, 218 interfaces of, 218 methods of, 228–229 properties of, 218–228 RenderingCompatibility property, 233 vs. WebControl class, 519 @Control directive, 180 control IDs matching to posted names, 211 retrieving, 915 control properties persistence modes, 558 varying output caching by, 770–772 control skins, 235 control state, 214, 718 programming, 718–719 ControlAdapter class, 230 controllers in ASP.NET MVC, 21–22 defined, 616–617 role of, 618 ControlParameter class, 462 controlRenderingCompatibilityVersion attribute, 232 controls data-bound controls, 421–434. See alsodata-bound controls dynamically created, handling, 211–212 and input tags, correspondence between, 212 naming container for, 190 Page class methods related to, 195–197 prerendering stage, 214 state changes, detecting, 212–213 unloading, 215 validating groups of, 394–395 validation, support of, 382

    view-state tracking, 210 Controls collection, 229 adding controls to, 521 dynamically added controls, 266 section, 91 ControlStyle property, 254, 255 ControlToValidate property, 381 cookieless attribute, 74, 800–801 cookieless sessions, 688–691 CreateUninitialized method, 706 issues with, 689–690 Search-Engine Optimization and, 691 security and, 690–691 cookies, 675, 687–688 configuration settings, 84–85 cookieless sessions, 688–691 customizing, 804–806 Forms authentication through, 799–800 for HTTP façade, 887–888 for role information, 97 sharing between applications, 801–802 usage of, 74 Copy Web Site function (Visual Studio), 40–42 CopyFrom method, 255 CouchDB, 614 coupling, 569–571 between modules, 575 between presentation and business layers, 604 C++ server programming, 3 CPU bound operations, 208 CreateChildControls method binding and nonbinding modes, 545 overloaded version, 545–547 overriding, 521, 544 pseudocode, 544 CreateUninitialized method, 706 CreateUser method, 811 CreateUserWizard control, 834–835 credentials collecting, 794 getting, 822 cross-browser rendering, 344–348 cross-domain calls in AJAX, 850–851 jQuery and, 929–932 Cross-Origin Resource Sharing (CORS), 929

    939

    940

    cross-page communication cross-page communication, 189–190 cross-page posting, 365, 374–379 detecting, 377–378 @PreviousPageType directive, 376–377 redirecting to another page, 378 validation and, 395–396 view state information, 374–375 cross-site scripting (XSS), 780 GET and, 886–887 CSS, 319 applying to elements, 917 ASP.NET support for, 3 embedded vs. inline, 316 for ListView styling, 474, 480, 482, 494–497 minimizing impact of, 315–317 style sheets, 339 vs. themes, 220, 343, 357 use of, 255 CSS Control Adapter Toolkit (CSSCAT), 232 css function, 917 CSS-based selectors, 909–910 CssClass property, 493–494 CSS-friendly markup code, 232–234 CssStyleCollection class, 254 culture changing, 310–312 names of, 82, 309 resource assemblies, creating, 308 setting, in ASP.NET Web Forms, 309 setting, in .NET, 308–309 Culture attribute, 860 Culture property, 860 Cunningham wiki, 570 Current property, 657, 897 CurrentCulture property, 308–309 CurrentUICulture property, 308–317 custom controls, 513–561. See alsoserver controls building from scratch, 518–533 control state, 718–719 control tree, building, 521–522 Control vs. WebControl, 519 correct rendering of, 195 data-bound composite controls, building, 543–561 data-bound controls, building, 533–543

    embedded resources, 195 extending existing controls, 514–518 interfaces for, 519 markup, writing to HTML text writer object, 527–528 object model definition, 523 object model implementation, 523–526 rendering style, 520–522 template support, 556–561 usage scenario, defining, 515–516 custom object caching, 463 custom resources, HTTP handlers for, 126–127 custom types in profiles, 289–290 Customer Relationship Management (CRM) systems, 613 section, 80–81, 278–279, 857 customization themes, 338 CustomValidator control, 380, 383–385, 395–396, 504

    D DAL, 593, 596, 598, 605–614 Active Record pattern and, 606 alternatives to, 613–614 caching, relation to, 734–735, 747 conceptual view of, 608 database independence, 608–609 domain model organization, 602 Domain Model pattern and, 607–608 implementation of, 605–608 interfacing, 608–610 O/RM implementation, 610–613 Repository pattern for, 609–610 responsibilities of, 607 Table Module pattern and, 606 data deleting, 465–468 editing, 454–455 paging, 451–453 retrieving, 460–461 sorting, 453–454 storing on server, 65 updating, 465–468 data access layer. SeeDAL

    data binding application-level, 326 class diagram, 415 customizing controls for, 533–543 DataBinder class, 436–438 data source controls, 456–468 data sources, 412–415 defined, 411 Eval method, 436–438 to GridView control, 443–451 HtmlSelect control support of, 245 with ListView control, 477–479 ObjectDataSource class, 459–469 page-level, 326 parent controls, identifying, 226 process of, 411 separating from controlbuilding code, 551–553 for server control RAD designers, 218 simple, 434–436, 533 syntax, 434–435 Web pages vs. desktop applications, 414 data contracts for AJAX-enabled WCF services, 885 preservation of, 892 data controls, binding to data source controls, 474 data eviction, 747 data expiration in cache, 731–732 data function, 923 data item classes, defining, 536–538 data item containers, 227 data keys containers, 227 data models for WAP projects, defining, 290–292 for Web site projects, defining, 286–287 data paging with ListView control, 507–511 data representation, JSON for, 890–893 data source controls, 456–468 data controls, binding to, 474 defined, 456 hierarchical controls, 457–458 ID of, 417 named views, 456–458 parameters, using, 462–463 tabular controls, 456

    design patterns data source objects interface implementation, 534 mapping to control properties, 535–536 data sources, 412–415, 721 adding items to, 501–505 ADO.NET classes, 413–414 collection classes, 412–413 key fields, setting, 420 queryable objects, 414–415 specifying, 416 updates to, 454–455 viewing records, 432–433 data tables, managing, 438–455 data transfer objects (DTOs), 605 data validation. See also validation configuration settings, 87 for cookies, 799 database dependencies, 80 for caching, 743–745 database management systems (DBMS), BLOB support, 133 databases cloud databases, 613 images, loading from, 133–136 session state, storing in, 699–704 sharding, 612 DataBind method, 411 calling, 414, 561 DataBinder class, 436–438, 541 DataBinding event, 556 data-binding expressions, 434–438 evaluation of, 435 generating and parsing, 437 implementing, 436 data-binding properties, 411, 415–421 AppendDataBoundItems property, 419–420 DataKeyField property, 420 DataMember property, 417–418 DataSourceID property, 417 DataSource property, 416–417 DataTextField property, 418 DataValueField property, 419 data-bound controls, 421–434 bound data, getting, 540–544 bound fields, 445 building, 533–543 composite, 543–544 data-bound properties, adding, 534 data item classes, 534–535 DataPager control for, 507–508

    data source fields to control properties, mapping, 535–536 defined, 411 events of, 556 iterative controls, 427–432 key features of, 533–534 list controls, 421–427 receiving data, 545 types of, 411 view controls, 432–434 DataBound event, 556 DataBoundControl class, 514 DataBoundLiteralControl class, 434 DataContract attribute, 891–892 DataContractJsonSerializer class, 891 Data-for-Data model, 17 DataGrid control, 431–432 autoreverse sorting, 718 DataItemContainer property, 227 DataKeyField property, 420 DataKeyNames property, 421, 467–468, 474, 500 DataKeysContainer property, 227 DataList control, 430–431 DataMember property, 417–418 DataPager control, 507–511 embedding, 508–509 properties of, 508 types of, 509 DataPagerField class, 509 DataSet class, 414, 598 DataSource property, 416–417, 474 DataSourceID property, 417, 442, 474, 477 DataSourceView class, 457 methods of, 458 DataTextField property, 418 DataTextFormatString property, 418 DataValueField property, 419 Db4O, 614 debug mode, 284–285 debug script files, 859–860 debugging pages, 284–285 declarative authorization, 77 decryption attribute, 87 decryption keys, specifying, 87–88 decryptionKey attribute, 87 default ASP.NET account changing, 784–786 privileges of, 785–786 default.aspx skeleton, 625

    defaultProvider attribute, 79, 88, 97, 100 defaultRedirect attribute, 81 defaultUrl attribute, 799 delegate classes, 146 delete operations, in ListView control, 500–501 DeleteUser method, 811 denial of service (DoS), 780 dependencies, 568 aggregate, 738 broken, 740 of cached items, 722, 725, 728 database dependencies, 743–745 decreasing number of, 570 isolating, 572 polling, 739, 742 resolving, 588–589 Dependency Injection, 582–591 Dependency Inversion principle, 572, 580–583 dependency-changed event, 730 deploy packages building, 45–47 contents of, 45 running, 44 deployment. Seeapplication deployment deployment precompilation, 53–55 update support, 54–55 section, 81–82 derived classes generating, 177 naming convention for, 172 substitution principle and, 576–578 URLs, linking to, 172 description attribute, 349 description meta tag, 349 deserialization of session state, 695–697, 710 design patterns Active Record pattern, 599–600 for BLL, 596–602 Browser-Side Templating, 840 Domain Model pattern, 600–602 HTML Message, 839–840 Module Pattern, 849 MVC pattern, 616–618 MVP pattern, 619–621, 623–636 MVVM pattern, 621–623 for presentation layer, 615–623 Repository pattern, 609–610 Service Layer pattern, 602

    941

    942

    Design Patterns: Elements of Reusable Object-Oriented Software design patterns (continued) Table Module pattern, 598 Transaction Script pattern, 597–598 Design Patterns: Elements of Reusable Object-Oriented Software (Gamma, Helm, Johnson, and Vlissides), 575 design principles, 569–572 detach function, 922 detach method, 921 DetailsView control, 432 DataKeyNames property, 467 vs. ListView control, 476 development environment, web. config file for, 51 development frameworks, characteristics of, 18–19 device-specific content, ignoring, 92 die function, 919 Digest authentication, 782 Dijkstra, Edsger W., 571 direct scripting, 19–20 directives location of, 179 processing directives, 179–190 DirectoryInfo class, 130 disabled attribute, 875 disableExpiration attribute, 79 DisplayIndex property, 493 Dispose method, 149, 648 for custom HTTP modules, 151 overriding, 215 Disposed event, handling, 215 distributed cache, 744–755 of ACS, 748–751 AlachiSoft NCache, 755 data eviction, 747 design of, 745 features of, 745–747 freshness of data, 747 high availability, 746 Memcached, 753–754 NorthScale Memcached Server, 755 read-through and writethrough capabilities, 747 ScaleOut StateServer, 755 SharedCache, 754 storing output caching in, 777 topology of, 746 distributed caching system, 745 DOM (Document Object Model) adding elements to, 920–922 assumptions about, 8 elements, accessing, 91

    evolution of, 842 functional approach combined with, 903 manipulating in jQuery, 920–923 modifying elements in, 922–923 queries, running over, 904, 908 readiness for scripting, detecting, 906–907 readyState property, 906 removing elements from, 922 updating content with, 842–843 DOM trees adding to DOM, 920–922 creating, 920, 922 toggling, 921–922 domain attribute, 84, 802 domain logic, 596 domain model, defined, 600 Domain Model pattern, 600–602 DAL and, 607–608 Domain-Driven Design (DDD), 601 DOMContentLoaded event, 906 DoPostBackWithOptions function, 374 download experience, optimizing, 312–317 DPAPI protection provider, 107, 115 DropDownList control, 421–422 binding data to, 534 HTML server control for, 245 DTOs, 605 dummy objects, 640 Duration attribute, 759, 761 dynamic compilation, 52, 169 dynamic controls, handling, 211–212 Dynamic HTML (DHTML), 839, 842 dynamic keyword, 334 Dynamic Language Runtime (DLR) component, 335 dynamic resources. Seepages dynamic type, 375–376 dynamic user interfaces, 18–19

    E each function, 908–909 eavesdropping, 780 ECMAScript, 900 edit template defining, 497–498 predefined buttons, 498–499 editing data, 454–455

    Eich, Brendan, 900 ELMAH, 284 empty fields, 387 empty function, 922 EnableClientScript property, 393 enableKernelCacheForVaryByStar attribute, 79 EnablePageMethods property, 896–897 EnablePaging property, 464 EnablePartialRendering property, 863 EnableScriptGlobalization property, 860 EnableScriptLocalization property, 315 EnableTheming property, 235, 342 EnableViewState attribute, 715 EnableViewStateMac attribute, 713 EnableViewState property, 227 enableWebScript attribute, 883– 884, 893 encapsulation, 572 encoders, 137 encoding, 82, 661 section, 114 encryption of configuration files, 113–116 for cookies, 799 key containers, 115 of view state, 712–713 XML encryption, 107 encryption keys, specifying, 87–88 encryption providers, 107 choosing, 115–116 Enctype property, 249 endpoints, JSONP-enabled, 930 EndProcessRequest method, 147 EndRequest event, 33, 651, 874, 875 EndRequest event handler, 32, 151–152 EndRequestEventArgs control, 875 EnsureChildControls method, 522 Entity Framework, 458, 611–612 error codes, 64, 81 Error event handler, defining, 274 Error events, 33, 274, 651 error handling, 269–285 configuration settings, 80–81 Error event handler, 651 error pages, 273–274 error reporting, 283–285 errors, mapping to pages, 278–282

    element error handling (continued) exception handling, 270–272 exception logging, 277 for fatal errors, 283–285 global, 275–277 handler precedence, 277 HTTP errors, 280–281 page error handling, 272–278 page-level, 274–275 partial rendering, 857 reporting in e-mail messages, 276 robustness of, 278 Error Logging Modules And Handlers, 284 error messages custom, 278–279 displaying, 275, 388–389 summary and display of, 391–392 error pages, 273–274 code-behind, 281 context-sensitive, 282 custom, 279–280 custom, specifying, 81 for local and remote users, 279 sensitive information on, 275, 278 error reporting, 283–285 tag, 280 ErrorMessage property, 388, 392–393 errors HTTP 500 errors, 277 mapping to pages, 278–282 session state and, 695 types of, 269 Esposito, Dino, 26 Eval method, 436–438 syntax, 437 Event object, 919 events of BarChart control, 553–554 canceling, 407–408 of Control class, 229–230 of data-bound controls, 556 of GridView, 442–443 handling, 151–152. See alsoHTTP handlers for health monitoring, 83 of HttpApplication class, 150, 648–651 in IIS messaging pipeline, 32–33 of ListView control, 474–476 order of firing, 649–650 of Page class, 198–199

    personalization events, 298–299 of ScriptManager control, 856 Exception class, 272 exception handling, 270–272 ASP.NET-specific facilities for, 270 cleanup code, 272 finally blocks, 271–272 guidelines for, 271–272 in .NET, 270–272 try/catch blocks, 270 exceptions built-in types, 271 function of, 270–271 getting information about, 282 retrieving, 276 self-logging, 284 unhandled, 272 Execute method, 661–663 overloads of, 662 ExecuteRequestHandler event, 33, 650 handler for, registering, 34 ExecuteScalar method, 134 expiration callbacks, 706 expiration policies for cached items, 726, 731–732, 747 for session-state items, 694 expired cache items, automatic scavenging, 79 Expires HTTP header, 756 Expires property, 665 ExpiresAbsolute property, 665 exports, 585–587 extensions, aspnet_isapi.dll handling of, 171–172 external style sheet files, linking to, 242

    F Factory attribute, 884 fakes, 640 fatal exceptions, 283–285 feedback client-side events for, 872–874 for partial page updates, 875 progress screen, 871–872 for users, 870–876 fetching vs. caching, 733 ffSite.master file, 327 Fiddler, 363 fields, defined, 413 file authorization, 790–791 file system monitor, 175

    file types, searching for, 130 FileAuthorizationModule HTTP module, 790 files copying to target site, 42 packaging, 43–51 FileSystemWatcher object, 738, 742 FileUpload control, 261–262 filter function, 914 filters, 911–914 finally blocks, 271 FindControl method, 375 find function, 914 Firebug Web development tool, 317 Firesheep, 803 fixed identities, impersonating, 785 fixednames parameter, 55 flow layouts, 485–487 item layout, 486–487 Foote, Brian, 566 forbidden resources, blocking access to, 652–653 ForeColor property, 381 form filters, 913–914 form submissions, 6 client behavior, controlling, 213 tags, 365 multiple, 368–373 runat, visibility of, 371–373 format strings, defined, 556 Forms authentication, 783, 791–806 advanced features, 801–806 attributes of, 798–799 configuration of, 75, 798–801 control flow, 792–796 cookie-based, 799–800 cookieless, 800–801 cookies, sharing among applications, 801–802 custom types of, 76 encryption and decryption keys, 87 external applications for, 803 FormsAuthentication class, 796–798 section, 798–799 with HTTPS, 889 Login control, 826–828 resources protected by, 792 security concerns, 793, 804 setting up, 792 user credentials, collecting, 794 element, 74–75

    943

    944

    section section, 798–799 FormsAuthentication class, 796–798 methods of, 797–798 properties of, 796–797 SignOut method, 795–796 FormView control, 432, 433 vs. ListView control, 476 FTP, copying files to target with, 42 FullTrust permission set, 103 functional programming, 845–846 JQuery and, 905 functions closures, 847–848 defined, 846

    G GAC assemblies in, 786 HTTP handlers in, 124 Gamma, Erich, 575 gauge control, 523–533 GaugeBar control, 535–544 data item object, 536–538 mapping fields to properties, 535–536 PerformDataBinding method, 540–544 GDI+ subsystem, 140 geo-location capabilities, 312 get accessor, 538 get function, 909 GET verb, 365 enabling, 886–887 kernel caching and, 762 posting forms with, 367 GetAuthCookie method, 798 GetConfig method, 658 GetDataItem function, 438 GetEnumerator method, 678 GetFiles method, 130 GetGlobalResourceObject method, 307, 659–660 GetHandler method, 145 getJSON function, 927–928 GetLastError, 276 GetLocalResourceObject method, 660 GetPropertyValue method, 541 GetPropertyValue property, 290–291 GetRequiredString method, 163 getScript function, 927 GetUser method, 811

    GetVaryByCustomString method, 767 GetWebApplicationSection method, 658 GetWebResourceUrl method, 195, 315 global assembly cache (GAC) assemblies in, 786 HTTP handlers in, 124 global error handling, 275–277 global resources, 304–305, 307 retrieving, 659–660 global themes, 339 global.asax file, 651–655 aliasing file name, 655 application directives, 653–654 Application_Error stub, 275 blocking access to, 652–653 C# code skeleton, 652 changes to, 653 code declaration blocks, 654 compiling, 652–653 contents of, 645 editing, 170 extending, 36–37 in precompiled assembly, 652 routes in, 160 server-side tags, 654–655 static properties in, 655 syntax of, 653–655 globalization, 860 configuration settings, 82 section, 82, 860 culture settings, 309 Google Chrome browser, 902 Google Gears, 312 GridView control, 433 accessibility properties, 440 appearance properties, 440 behavior properties, 439 binding data to, 443–451 bound fields, 445 button fields, 445–447 check box fields, 448 columns, configuraing, 444–445 DataKeyNames property, 467 default user interface, 452 editing data, 454–455 events of, 442–443 hyperlink fields, 447–448 image fields, 448–449 interaction with host page, 451 vs. ListView control, 477, 482–483 object model, 439–443

    paging data, 451–453 predefined types, 440 Predictable algorithm implementation, 225–226 sorting data, 453–454 state properties, 441 style properties, 440 templated fields, 450–451 templating properties, 441–442 GroupItemCount property, 488, 489 GroupPlaceholderID property, 473 GroupSeparatorTemplate property, 489 GroupTemplate property, 487 Guthrie, Scott, 57 Gzip compression, 314

    H handler factories, 142, 144–145 IsReusable property and, 144 handler factory objects, 177 handlers, 5–6 for page requests, 177–178 section, 125 hashAlgorithmType attribute, 88 hashing algorithms, specifying, 87 section, code blocks in, 243 HeadContent placeholder, 326 health monitoring system, 83 section, 83 heartbeat event, interval for, 83 heartbeatInterval attribute, 83 Helm, Richard, 575 hidden fields creating, control for, 261–262 tracking values in, 7 view state information, saving in, 200 HiddenField control, 261–262 hidden-field tampering, 780 hide function, 915–917 HierarchicalDataBoundControlclass, 514 HierarchicalDataSourceView class, 458 high availability of distributed cache, 746 section, 84 HostSecurityManager object, 103 hostSecurityPolicyResolverType attribute, 104

    HTTP modules HTML ASP.NET MVC control over, 24 control over, 8 downloading, 928 literal strings, 7 syntax, 3 HTML attributes, setting, 237–238 HTML controls, correct rendering of, 195 HTML elements ID of, 16 predictable IDs, 91 HTML encoding, 661 HTML forms, 200 HTML input controls, 246–252 command buttons, 247 HtmlImage controls, 252 state changes, detecting, 248–249 for uploading files, 249–251 validation of input fields, 248 HTML markup. See also markup adaptive rendering of, 230–232 client ID, 220–223 control over, 234 HTML Message (HM) pattern, 19, 839–840 HTML output, layout of, 8 HTML pages, JavaScript code in, 16 HTML responses, arranging, 5 HTML server controls, 217, 235– 252. See alsoserver controls base class, 237 container controls, 239–240 external style sheet files, linking, 242 generic controls, 237 header information, 241–242 hierarchy of, 239 HTML attributes, mappings to, 237–238 HTML syntax support, 236 meta information, managing, 243 namespace definition, 237 page postbacks with, 244 predefined controls, 236 properties of, 237 runat=server attribute, 235 state changes, detecting, 248–249 HTML tables, 484–485 HTML text writer object, 520 writing markup to, 527–528 HtmlAnchor class, 243–244

    HtmlButton class CausesValidation property, 248 HtmlButton controls, 240 HtmlContainerControl class, 239, 366 HtmlControl class, 236–237 HtmlForm class, 200, 365–367 methods of, 367 properties of, 366–367 Visible property, 371 HtmlForm controls, 240 HtmlGenericControl class, 237 HtmlHead controls, 241 HtmlImage controls, 252 HtmlInputButton class, 247 CausesValidation property, 248 HtmlInputCheckBox controls, 260 HtmlInputControl class, 246 HtmlInputFile controls, 249–251 HtmlInputImage controls, 247 HtmlInputRadioButton control s, 260 HtmlInputReset controls, 247 HtmlInputSubmit controls, 247 HtmlLink controls, 242–243 HtmlMeta controls, 243 HtmlSelect controls, 244–245 data binding support, 245 HtmlTextArea control, 245 HTTP access error 403 (forbidden), 64 HTTP endpoints, 123 binding handlers to, 128–129 HTTP errors handling, 280–281 HTTP 302, 280, 349 HTTP 403, 64 HTTP 404, 280–281 HTTP 500, 277 HTTP façade, 880–881 ASP.NET Web services, 885–887 JSON content, 890 protecting services in, 887–888 proxy for, 893–895 trusting, 888–889 WCF services, 881–885 HTTP GET, enabling, 886–887 HTTP handlers, 11 for AJAX presentation layer, 881 for AJAX requests, 148 allowPolicy attribute, 109 alternate, specifying, 36 ASHX resources, defining as, 141–142 asynchronous handlers, 146–148 binding to endpoints, 136

    calling, 123 declaring, 123 determining, 32, 35 forbidden resources, preventing access to, 143 functionality of, 33, 119 handler factories, 142, 144–145 IHttpAsyncHandler interface, 121 IHttpHandler interface, 121, 121–127 images, controlling with, 140–141 images, database-stored, serving, 134–135 images, dynamically generated, serving, 137 images, serving, 128–133 loading, 125 mapping, 124 for new types of resources, 126–127 picture viewer handler, 128–133 precondition attribute, 109, 126 preconditions on, 126 ProcessRequest method, 138 query string parameters, accepting, 131 registering, 82, 121, 124–125, 132, 141 reusing, 143–144 ScriptResource.axd, 859 session state, access to, 141 synchronous or asynchronous mode, 121, 201 section and, 108–109 vs. URL routing, 165 uses of, 141 writing, 36, 121–148 HTTP headers fresh and stale resources, determining, 756 programmatically setting, 763–764 sending, 33 for static resources, 758 varying output caching by, 767 HTTP modules, 646–647 built-in, 154 for current application, 154 custom modules, 151–154 Dispose method, 151 events handled by, 149–154 events raised by, 119 functionality of, 119 Init method, 151

    945

    946

    HTTP pipeline HTTP modules (continued) loading and initialization of, 153 order of application, 153 pre- and post-processing of requests, 149 registering, 37, 82, 153 role management, 97 system modules, inheritance of, 149 section and, 108–109 URL routing engine, 119–120, 155, 157–159 writing, 149–156 HTTP pipeline, 176. See alsopage life cycle activation of, 174 HTTP requests, parsing to, 174 HttpRuntime class, 174–176 HTTP protocol stack, 29 HTTP requests anonymous ID, 671 asynchronous requests, 95 authentication of, 650 authorization of, 650 client data, 671 delays caused by, 59 filtering, 155, 164 free threads and, 86 handling of, 645 HTTP module processing of, 149 IIS processing of, 30–37 information about, encapsulation of, 656 information about connection, 672 information about request, 670–672 input validation, 674 logging, 33, 651 out-of-band, 841–842 output for, 122, 130–131 parsing to HTTP pipeline, 174 processing, 5, 9–10, 11, 27 processing with IIS worker process, 29 processing with standalone worker process, 28–29 queuing, 95 reducing number of, 316–317 routing, 24 saving to disk, 673 script-led requests, 890 sending, 844 serving from cache, 650 URL rewriting, 658–659

    HTTP responses cache policy, 665–666 compressed responses, 79 encapsulation of, 663 HttpResponse object, 663–670 large file transmission, 669 response filters, 666–667 HTTP runtime page request processing, 174 reason for processing pages, 209 HTTP verbs, 879 HttpApplication class events of, 648–651 IIS pipeline events and, 34 methods, 647–648 properties of, 645–646 HttpApplication object, 177, 645 Error event handler, 275–276 events raised by, 119 handling events from, 151–153 pooling of, 645 HttpApplicationFactory, 175–177 HttpApplicationState class, 676 methods of, 677–678 properties of, 676–677 synchronization of operations, 678–679 HttpBrowserCapabilities class, 77, 345 HttpBrowserCapabilitiesBase class, 345 HttpCachePolicy class, 666 methods of, 763–764 properties of, 763 HttpCapabilitiesDefaultProvider class, 347 HttpCapabilitiesProvider class, 78 HttpContext class, 656–660, 676 methods of, 658–660 properties of, 656–657 HttpContext object, role of, 645 HttpCookie object, 688 HttpCookieMode enumerated type, 688–689 section, 84–85 HttpForbiddenHandler class, 143 section, 82 handler information in, 177–178 handlers list, 123 section, 82 registering modules in, 153 HttpOnly cookie attribute, 84–85 httpOnlyCookies attribute, 84 HttpPostedFile class, 250 HttpRequest class methods of, 673–674

    properties of, 670–673 HttpRequest object, 670–674 HttpResponse class methods of, 667–670 properties of, 664–667 HttpResponse object, 663–670 BinaryWrite method, 135 large file transmission, 669 output caching, 669 response filters, 666–667 WriteSubstitution method, 776 HttpRuntime class, 174–176 public static methods, 174–175 UnloadAppDomain, 179 HttpRuntime object, 30 section, 85–87 maxRequestLength attribute, 251 HTTPS, Forms authentication with, 889 HttpServerUtility class methods of, 660–663 properties of, 660 HttpSessionState class, 676, 680 methods of, 686 properties of, 685–686 synchronization mechanism, 685–686 HttpSessionState object, creating, 682–683 http.sys, 29 HttpValidationStatus enumeration, 764 HttpWatch, 363 HyperLink control, 258 customizing, 515–518 hyperlink fields, 447–448

    I IAsyncResult interface, 146 IButtonControl interface, 257, 374 ICollection interface, 412, 685 IComponent interface, 218 IControlBuilderAccessor interface, 218 IControlDesignerAccessor interface, 218 ICustomTypeDescriptor interface, 413 ID autogeneration mechanism, 220–222 ID selectors, 909 IDataBindingsAccessor interface, 218 identity providers, 823

    Download from Wow! eBook

    input forms section, 87 worker process identity information, 784 Identity Selector, 791 IDictionary interface, 413 IDisposable interface, 218 idleTimeout attribute, 84 IEditableTextControl interface, 260 IEnumerable interface, 412, 417, 724 ieSite.master file, 327 IExpressionsAccessor interface, 218 tags, 929–930 section, 92 IHttpAsyncHandler interface, 121, 146, 201 implementing, 201 IHttpHandler interface, 11, 36, 121–127, 174 members of, 121–122 ProcessRequest method, 174 IHttpHandlerFactory interface, 144, 177 IHttpModule interface, 37, 149–150 module implementation of, 646 IIS administrator-level extensions, 37 ASP.NET applications, configuring for, 55–59 and ASP.NET, history of, 28–31 authentication tasks, 790 Classic mode, 31 handler mappings list, 177–178 HTTP request processing, 31–37 Integrated Pipeline mode, 30–31 ISAPI interface, 120 messaging pipeline, 32–35 new features, 37–39 process model and, 95 resources served by, 128, 140 runtime environment, 28 settings, propagating to destination, 48–49 unified architecture of, 30 warm-up feature, 59–62 worker process, 29 IIS 7 integrated mode HTTP handlers, registering, 132 HTTP handlers and modules and, 108–110 module and handler resolution, 119

    IIS Application Warm-up module, 59–62 IIS Express, 25 IIS kernel caching, 761–762, 766 IIS Manager Add Managed Modules dialog box, 37 applicationHost.config file, editing in, 93 Application Pool Identity dialog box, 39 Handler Mappings panel, 121 IIS Application Warm-up feature, 60 mapping HTTP modules, 37 IIS metabase, 171 IIS pipeline, 174 IIS script maps, 127 IIS SEO Toolkit, 351–352 IIS Web projects, defined, 48 IList interface, 412 Image controls, 259 image fields, 448–449 image inlining, 316 image URLs, 259 ImageButton controls, 259 ImageFormat structure, 137 ImageMap controls, 259 images advertisem*nts, 262–263 buttons, rendering as, 447 controlling with HTTP handler, 140–141 copyright notes, writing on, 137–140 databases, loading from, 133–136 display of, 259 display of, configuring, 252 dynamically generated, serving, 137 grouping into sprites, 315–316 hyperlinks displayed as, 258 minimizing impact of, 315–317 referencing on Web pages, 133 saving, 137 serving, 128–133 skin-compliant, 340 writing to disk, 137 tag, 133 immobility, 568 impersonate attribute, 87 impersonation through anonymous account, 785 of fixed identities, 785 per-request, 785

    thread identity and, 783–785 @Implements directive, 189 @Import directive, 188, 653–654 imports, 585–587 INamingContainer interface, 190, 221, 519, 558 information hiding, 571–572 inheritance from base class, 515 prototype feature and, 849 Inherits attribute, 96 Init events, 210 Init method, 149, 648 for custom HTTP modules, 151 InitComplete events, 210 initialization of applications, 38, 645–651 of child controls, 210 completion of, 210 modules, 646–647 of page, 210 initialization code, placement of, 905–906 Initialize method, 808 InitializeCulture method, 310 initializeRequest event, 873 inline content, 316–317 in-memory generation of images, 137 InnerException property, 276 innerHTML property, 842–843, 920 in-place precompilation, 53 InProc session state, 682, 694– 695, 705 in-process calls, 603 input controls accessing, 375 multiple validators for, 390–391 and validation controls, linking, 381–382 Wizard control, 374 element, 246 input field filters, 914 input fields, validation of, 248 input forms cross-page posting, 374–379 HtmlForm class, 366–367 logical forms, 368 login capabilities, 368–369 multiple forms, 368–374 MultiView control, 373–374 mutually exclusive, 371–373 nesting, 370 parent objects, 367 single-form model, 365–366, 368

    947

    948

    input tags and controls input forms (continued) unique name, 367 validation controls, 379–396 wizards, 397–409 input tags and controls, correspondence between, 212 input validation, 674 validation controls, 379–396 in wizards, 404 Insert method, 726 insert templates, 501–505 position of, 502–503 validation for, 504–505 insertAfter function, 921 insertBefore function, 921 InsertItemPosition property, 502 InsertItemTemplate property, 501 installer files, deployment with, 42–43 Integrated Pipeline mode (IIS), 30–31 integrated Windows authentication, 782 integration testing, 566 defined, 49 Interface Segregation principle, 579–580 interfaces, 319 for custom controls, 519–520 defined, 189 implementing, 189 in MVP pattern, 625 intermediate page classes, 13 Internet Explorer IE9 Developer toolbar, 317 JavaScript engine, 902 XMLHttpRequest object instantiation, 843 Internet Information Services. SeeIIS intrinsic objects, 191 invalid input, 386 inversion of control (IoC), 582 frameworks, 584 vs. MEF, 584–585 Unity, 587–592 I/O bound operations, 208 I/O threads, 94–95 IPageableItemContainer interface definition of, 510 IParserAccessor interface, 218 IPostBackDataHandler interface, 211, 245, 519–520 implementing, 249 IPostBackEventHandler interface, 213, 520

    IRepeatInfoUser interface, 430 IRequiresSessionState interface, 141 IRouteHandler interface, definition of, 159 ISAPI extensions, 170 ISAPI filters for cookieless authentication, 800 ISAPI interface, 120 IsAuthenticated property, 829 IsCrossPagePostBack property, 377–378 ISessionIDManager interface, 708 methods of, 709 IsInPartialRendering property, 862 ISO/IEC 9126 paper, 565 isolation, testing in, 641–642 IsReusable property, 121–122, 174 handler pooling, 143 IStateManager interface, 534, 711 IsValid property, 379, 387–388 IsViewStateEnabled property, 228 ItemCanceling event, 503 ItemCommand event, 476 ItemDataBound event, 491–492 ItemEditing event, 498 ItemInserted event, 504 ItemPlaceholderID property, 473 Item property, 723, 726 item templates selected item template, 505–507 setting up, 501–503 ITemplate type, 557 Items property, 423, 657 ItemTemplate property, 472, 483 iterative controls, 411, 427–432 DataGrid control, 431–432 DataList control, 430–431 vs. list controls, 427–428 Repeater control, 428–430 ITextControl interface, 260 IUrlResolutionService interface, 218

    J Java Server Pages (JSP), 3 JavaScript AJAX and, 845–851 in browsers, 899–900 in client pages, 198 client-side code, 903 closures in, 847–848 drawbacks of, 902–903

    functional programming in, 845–846 goals of, 900 in HTML pages, 16 initialization code, 905–906 introduction of, 900 JSON and, 890 libraries, 899 message boxes, 501 for navigation systems, 352 object model for, 903 Object type, 846 OOP in, 846–847 prototypes in, 848–849 proxy class, generating, 893–894 ready event, 906 scripting engine, 901–902 skills with, 3 unobtrusive, 918 writing and importing, 899 JavaScript background compiler, 901 JavaScript Object Notation. SeeJSON JavaScriptSerializer class, 890–891 JetBrains ReSharper, 188, 270 Johnson, Ralph, 575 jQuery function, 905 jQuery library, 18–19, 313, 422, 846, 877, 899, 903–905, 905–931 AJAX support, 925–928 benefits of, 904 binding and unbinding functions, 918–919 cache, 923–925 chainability, 909, 915 cross-domain calls and, 929–932 Data Link plug-in, 931 DOM manipulation, 920–923 DOM readiness, detecting, 906–907 DOM tree, adding to DOM, 920–922 DOM tree, creating, 920 filters, 911–914 functional programming and, 905 Globalization plug-in, 931 HTML, downloading, 928 IntelliSense support, 904 JSON, getting, 927–928 linking, 903 live binding, 919 load function, 907

    localization jQuery library (continued) Microsoft support, 931 naming convention, 904 query capabilities, 908 ready function, 906–907 root object, 904–905 selectors, 909–911 Templates plug-in, 931 UI, 18, 877 wrapped sets, 905, 908–919 .js file, 903 /js suffix, 893 JScript, 900 JSON, 890–893 DataContract attribute, 891–892 format, 890 getting, 927–928 vs. XML, 892–893 JSON format, 20 JSON object, 890 JSON serialization, 108 JSON with Padding (JSONP), 929–931 json2.js file, 890

    K kernel caching, 58–59, 761–762, 766 enabling, 79 key, cached item, 725 key containers, 115 keywords attribute, 349 keywords meta tag, 349 Kuhn, Thomas, 15

    L Label control, accessibility feature, 260 LABjs, 313 language, changing on the fly, 310–312 language attribute, 654 large file transmission, 669 Law of Demeter, 377 layers, 593 business logic layer, 596–605 data access layer, 605–614 page and database coupling and, 762 service layer, 602–603 SoC and, 593 in three-tiered architecture, 593

    layouts, 320. See alsomaster pages flow layouts, 485–487 multicolumn layouts, 485 tabular layouts, 480–485 tiled layouts, 487–493 LayoutTemplate property, 472 legacyCasModel attribute, 103 length property, 909 lengthy tasks, asynchronous operation for, 201–209 lifetime managers, 590–592 LinkButton control, 213 LINQ, 414–415 LINQ-to-SQL, 458, 610–611 linking to design patterns with, 599 Liskov’s substitution principle, 576–578 list controls, 411, 421–427 BulletedList control, 426–427 CheckBoxList control, 422–424 DataTextField property, 418 DataValueField property, 419 DropDownList control, 421–422 Items property, 423 vs. iterative controls, 427–428 ListBox control, 425 RadioButtonList control, 424– 425, 427 receiving data, 545 SelectedIndexChanged event, 425 ListBox control, 425 HTML server control for, 245 ListControlclass, 514 listeners, 101 ListItemCollection class, 423 ListView control, 433–434, 471–512 alternate item rendering, 483–484 buttons in layout, 496–511 CSS styling, 480, 482, 494–497 data-based operations, 496 data binding, 471, 477–479 data-binding properties, 474 data-related properties, 492–493 editing, in-place, 496–499 events of, 474–476, 498–499 flow layout, 485–487 vs. GridView control, 482–483 ItemTemplate property, 472 layout template, 479 LayoutTemplate property, 472 list layout, 479–480

    new data items, adding, 501–505 object model, 472–479 vs. other view controls, 476–477 paging capabilities, 507–512 populating dynamically, 491–493 Predictable algorithm implementation, 225 properties of, 472–474 rendering capabilities, 471 selecting items, 505–507 sorting items, 511 style properties, 494 styling the list, 493–496 tabular layout, 480–485 template properties, 473, 480 tiled layout, 487–493 updates, conducting, 499–501 user-interface properties, 474 literal controls, 252 literals, 260 live binding, 919 live function, 919 Load events, 211 load function, 907, 928 LoadComplete event, 213 LoadControl method, 197 LoadControlState method, 719 loadFromCache function, 925 loadFromSource function, 925 LoadPageStateFromPersistenceMedium method, 215 LoadPostData method, 211 LoadTemplate method, 197 LoadViewState method, 540 overriding, 539 Local Security Authority (LSA), 88 local storage, 923 local themes, 339 local users, error pages for, 273–274 localization, 303–312 culture, changing, 310–312 culture, setting in .NET, 308–309 culture, setting in Web Forms, 309 global resources, 307 localized text, 306–307 local resources, 304–305, 308 of navigation system, 360 resources, localizable, 304–308 of script files, 314–315 of site map information, 359–361

    949

    950

    section section, 68–71 allowLocation attribute, 71 allowOverride attribute, 70–71 Path attribute, 69–70 Locator attribute, 50 locking mechanisms on Cache object, 736 logging of HTTP requests, 651 of request results, 33 logging exceptions, 277 logic of pages, testing, 361 logical forms, 368. See alsoinput forms login capabilities of input forms, 368–369 Login control, 826–828 appearance of, 827 customizing, 827 events of, 828 membership API and, 826 login pages, 792–793 for AJAX-enabled pages, 887–888 credentials, collecting through, 794 in external applications, 803 layout of, 826 login templates, 831 LoginName control, 828–829 LoginStatus control, 829–830 properties of, 830 LoginView control, 830–832 properties of, 830 LogRequest event, 33, 651 low coupling, interface-based programming, 572

    M machine scope, 119 machine.config file for configuration, 63 configuring, 65 default modules list, 647 element, 68, 69 location of, 64 section, 92 machine.config.comments file, 64 machine.config.default file, 64 section, 87–88 machine-level configuration settings, 70, 111 MailDefinition element, 832–833 maintainability, 565 importance of, 569 maintenance, 565

    Managed Extensibility Framework (MEF), 584–587 managed HTTP modules, defined, 37 MapPageRoute method, 161 mappedUrl attribute, 104 mappings between fake URLs and real endpoints, 104 between HTTP handlers and Web server resources, 123 between properties and section attributes, 117 between security levels and policy files, 97 MapRequestHandler event,32, 650 markup ASPX templates, 217 CSS-friendly, 232–234 generating, 194–195, 215 graphical aspects, configuring, 422 server-side programming, 839 style-ignorant, 255 writing, 520–521 writing to HTML text writer object, 527–528 markup files (ASPX), compilation of, 52 markup mix, 3 Martin, Robert, 573 Master attribute, 327 @Master directive, 180, 320–323 attributes of, 322 master page properties, 333 exposing, 333 invoking, 334–335 master pages, 180, 319–324 ASPX markup for, 222, 225 binding content pages to, 326 changing, 209 compiling, 329 ContentPlaceHolder controls, 320–322 default content, defining, 323–324 device-specific, 327–329 dynamic changes to, 336–337 @Master directive, 320–323 nested forms and, 370–371 nesting, 330–333 processing, 329–334 programming, 333–336 sample page, 321 UpdatePanel controls in, 864–865

    Master property, 333, 336 MasterPage class, 321, 329 MasterPageFile attribute, 326, 327, 336–337 @MasterType directive, 335–336 max-age HTTP header, 756 maxBatchGeneratedFileSize attribute, 169 maxBatchSize attribute, 169 maxPageStateFieldLength attribute, 90 maxRequestLength attribute, 251 MEF, 584–587 membership API, 88, 806–821 data stores, integrating with, 812 login controls and, 826 Membership class, 807–812 membership provider, 812–817 Membership class, 806–812 advanced functionality of, 808 methods of, 808–809 properties of, 807–808 membership providers, 300, 812–817 choosing, 809 configuration settings, 88–89 configuring, 816–817 custom, 815–816 extending, 815 key issues of, 816 list of, 808 MembershipProvider base class, 813–814 ProviderBase class, 813 section, 88–89 MembershipProvider class, 813–814 MembershipUser object, 811–812 Memcached, 614, 753–754 memory usage, polling for, 79 MergeWith method, 255 meta tags, 349 MethodName property, 775 methods of Control class, 228–229 page methods, transforming into, 895–896 testing, 641 of Web controls, 255–256 Meyer, Bertrand, 575 Microsoft Dynamics CRM, 613–614 Microsoft Internet Explorer. See Internet Explorer Microsoft Internet Information Services. SeeIIS

    OpenMachineConfiguration method Microsoft .NET Framework. See .NET Framework Microsoft Silverlight. See Silverlight Microsoft SQL Express, 284 Microsoft SQL Server. See SQL Server Microsoft Visual Basic, 3 Microsoft Visual Studio. See Visual Studio Microsoft.Practices.Unity assembly, 587 Microsoft.Practices.Unity. Configuration assembly, 590 Microsoft.Web.Administration assembly, 112 Microsoft.XmlHttp object, 843 MigrateAnonymous event, 299–300 minifiers, 314 MobileCapabilities class, 77 mocks, 640 mode attribute, 74–75 Mode attribute, 81 model defined, 616 role in MVC, 617 role in MVP, 620 Model-View-Controller, 616–618, 620 Model-View-ViewModel, 615, 621–623 mod_mono module, 27 modular code, 571–572 Module Pattern, 849 modules, 646–647 Modules property, 154, 646 MongoDB, 614 Moq, 640 .msi files, 43 MSUnit, 638 multicolumn layouts, 485 multipart/form-data submissions, 249–251 multiserver environments settings, 88 multitiered architecture, 594– 595. See alsothree-tiered architecture MultiView control, 266–268, 373–374 MVC pattern, 616–618 vs. MVP pattern, 620 MVP pattern, 14, 619–621 implementing, 623–636 interface, 625 vs. MVC pattern, 620

    navigation, 632–636 presenter, creating, 626–632 presenter isolation, 641 testability of code, 636–642 view, abstracting, 624–626 MVVM pattern, 615, 621–623 myHandler function, 930

    N name conflicts, avoiding, 221 NameObjectCollectionBase class, 154, 676 namespaces, linking to pages, 188 section, 91 naming containers, 221, 226–227 NamingContainer property, 226–227 native requests, defined, 37 NavigateUrl property, 517 navigation implementing, 632–636 linear and nonlinear, 397 through wizards, 405–409 navigation system, 351–357 localizing, 360 SiteMap class, 355–356 site map configuration, 357–360 site map information, 352–353 SiteMapPath controls, 356–358 site map providers, 354–355 navigation workflow, defining, 633–634 nesting forms, 370–371 master pages, 330–333 .NET Framework ASP.NET modules, 646–647 Code Contracts API, 578 configuration scheme, 63 culture, setting, 308–309 Dynamic Language Runtime component, 335 exception handling, 270–272 graphic engine, 137 Managed Extensibility Framework, 584–587 predefined server controls, 236 .NET Framework root folder, 786 NetFrameWorkConfigurationKey container, 115 Netscape, 900 NETWORK SERVICE account, 38, 783 privileges of, 785–786 NHibernate, 612

    None authentication, 789 NorthScale Memcached Server, 755 NoSQL solutions, 614, 745 Nothing permission set, 103 NotifyDependencyChanged method, 738, 741 null identities, 32 null reference exceptions, 270 numRecompilesBeforeAppRestart attribute, 56

    O object caching, 463 object model. See also DOM defined, 600 updatable, 842 tags, server-side, 654–655 Object type, 846 ObjectCreating event, 463 ObjectDataSource control, 458, 459–469 caching support, 463 data retrieval, 460–461 deleting data, 465–468 existing business and data layers and, 463 paging, setting up, 464–465 properties of, 459–460 updating data, 465–468 ObjectDisposing event, 464 object-oriented design, 599 object-oriented programming. SeeOOP object/relational mapper, 610–613 OCP, 575–576 omitVaryStar attribute, 79 OnClientClick property, 258 OnCompletionCallback method, 147 one-click attacks, 780 security against, 193 onload event, 906 order of running, 907 OOP, 4, 569 in JavaScript, 846–847 substitution principle and, 576 Open Authorization (oAuth), 20 Open Data (oData), 20 Open/Closed Principle (OCP), 575–576 OpenID, 76 OpenMachineConfiguration method, 112

    951

    952

    OpenWebConfiguration method OpenWebConfiguration method, 111 OperationContract attribute, 884 optimized internal serializer, 696 originUrl attribute, 102, 787 O/RM, 610–613 Code-Only mode, 611 Entity Framework, 611–612 Linq-to-SQL, 610–611 NHibernate, 612 SQL code of, 612 out-of-band HTTP requests, 841–842 output cache execute now capabilities, 32 saving pages to, 33 output cache profiles, 79–80 output cache providers, 79, 776–777 output caching, 28, 33, 58–59, 669, 721, 755–777. See also caching caching profiles, 774–775 capabilities of, 758–759 configuration settings, 79 configuring, 58 dependency object, adding, 762–763 IIS kernel caching, 761–762 of multiple versions of page, 765–768 @OutputCache directive, 759–760 page output duration, 761 page output location, 760–761 of portions of page, 768–774 postbacks, dealing with, 766 post-cache substitution, 775–776 server-side caching, 761 sharing output of user controls, 772–773 of static vs. interactive pages, 766 of user controls, 770–775 validation of page, 764–765 varying by control, 770–772 varying by custom string, 767–768 varying by headers, 767 varying by parameters, 765 @OutputCache directive, 759–760 CacheProfile attribute, 774 Shared attribute, 773 VaryByCustom attribute, 767–768

    VaryByParam attribute, 765–766 section, 774 OutputCacheProvider class, 79 overloaded constructors, 583

    P packaging files, 43–51 settings, 43–51 page caching, 665–666. See also caching output caching, 669 Page class, 36, 119, 190–208 AddOnPreRenderCompleteAsync method, 202–203 Async attribute, 201–202 context properties, 193–194 controls-related methods, 195–197 Dispose method, 215 eventing model, 199 events of, 198–199 intrinsic objects, 191 LoadPageStateFromPersistenceMedium method, 215 as naming container for controls, 190 ProcessRequest method, 210 rendering methods, 194–195 SavePageStateToPersistenceMedium method, 215 script-related methods, 197–198 ViewStateUserKey property, 192–193 worker properties, 191–193 page classes derived, 12 intermediate, 13 page composition, 319–345 content pages, processing, 329–334 content pages, writing, 323–328 master pages, 320–324 master pages, processing, 329–334 master pages, programming, 333–336 styling pages, 336–344 page controller entities code-behind classes, 12–13. See also code-behind classes implementation of, 5

    Page Controller pattern, 11–14, 156, 618 effectiveness of, 14 HTTP handler components, 11 revisiting, 14 server-side definitions, 5 page development error handling, 269–285 page localization, 303–312 page personalization, 285–303 resources, adding to pages, 312–317 @Page directive, 180–185 Async attribute, 201–202 configuration settings, 89–92 EnableViewStateMac attribute, 713, 715 page behavior attributes, 182–184 page compilation attributes, 181–182 page output attributes, 184–185 page execution external page, embedding, 661–662 helper methods, 660–663 server-side redirection, 663 page handler factory, 177–179 page life cycle, 11–12, 119, 169, 174, 209–215 in ASP.NET MVC, 22 client data, processing, 211 finalization, 214–215 InitComplete events, 210 Init events, 210 LoadComplete event, 213 Load events, 211 managing, 177 markup, generating, 215 Page class events, 198–200 postback, 212–213 PreInit event, 209 PreLoad event, 211 PreRenderComplete event, 214 PreRender event, 214 restructuring, 14 setup, 209–212 Unload event, 215 page localization, 185, 303–312 page methods, 895–897 objects accessable from, 897 page objects, creation of, 178 page output, dependency object, 762–763 page personalization, 285–303

    permission sets, configuring page processing. See alsopartial rendering browser-led model, 840–841 page requests context information, 175 processing, 174–179 reason for processing, 209 page usability, 344–364 PageAsyncTask object, creating, 201 pageBaseType attribute, 90 PageHandlerFactory class, 144, 177–178 Page_Load event profile properties, initializing, 295 presenter instance in, 626–627 pageLoaded event, 873 pageLoading event, 873 PageMethods class, 896 page-output caching. Seeoutput caching PageRequestManager client object, 872 PagerSettings class, 441 pages, 6 advertisem*nt banners on, 262–263 asynchronous pages, 121, 201–209 batch mode compilation, 169 behavior of, 757 cacheability of, 757, 758–762 content pages. Seecontent pages culture, setting, 309 debugging, 284–285 download experience, optimizing, 312–317 dynamic compilation, 52 embedding external pages in current page, 661–663 error handling for, 272–278 errors, mapping to, 278–282 header information, 241 heaviness of, 10, 17 HTML forms on, 200 initialization code, 905–906 interaction model, 17 invoking, 170–173 layout of, 320. See alsomaster pages life cycle of. Seepage life cycle master pages. Seemaster pages namespaces, linking to, 188 page behavior, 182–184

    partial rendering, 865. See alsopartial rendering passing values between, 379 performance and quality analysis, 317 placeholders on, 265–266 postbacks. Seepostbacks processing directives, 179–190 protecting access to, 784 recompilation of, 170 rendering, 214–215 requested, representation of, 172–173 resources, adding, 312–317 run-time modules, 170–171 script files, linking to, 312–314 scripts, adding to, 858–859 serving to user, 329–330 sharing of output, 773 single server-side form support, 200 size thresholds, 714 styling, 336–344 testing for usability, 361–364 themes, applying to, 340–341 titles of, 348 unloading, 215 updating asynchronously, 16 usability, 319, 344–364 user controls, converting to, 769 user controls, linking to, 189–190 view state, enabling and disabling, 227 view state, persisting, 200 XML documents, injecting into, 264–265 Pages-for-Forms model, 16–17 section, 89–92 child sections, 91–92 controlRenderingCompatibilityVersion attribute, 232 paging data, 451–453 setting up for, 464–465 paradigm shifts, 15 parameters, varying output caching by, 765 ParseControl method, 197 parser errors, 269 partial caching, 768–774 partial classes, 173 code-behind classes, 173 partial rendering, 19–20, 851–879 asynchronous postbacks, concurrent, 877–878

    benefits and limitations of, 876–877 error handling, 857 example of, 861 migrating pages to, 865 polling and, 872, 878–879 vs. postbacks, 860, 878–879 postbacks, detecting from child controls, 866–868 postbacks, triggering, 868–869 refresh conditions, 866 script loading, 858–859 ScriptManager control, 852–860 ScriptManagerProxy control, 857–858 UpdatePanel control, 860–866 partial trust permission set changing name of, 103 PartialCaching attribute, 770 partial-trust applications, 103 partition resolvers, 704 partitioned cache topologies, 746 partitioned cache with H/A, 746 Passport authentication, 76 PasswordRecovery control, 832–833 passwords changing, 833–834 managing, 812 retrieving or resetting, 832–833 Path attribute, 69 path property, 895 pathInfo placeholder, 165 patterns. See design patterns Patterns & Practices group, 587 pending operations, canceling, 876 percentagePhysicalMemoryUsedLimit attribute, 78 performance analyzing pages for, 317 caching and, 733, 759, 761 closures and prototypes and, 849 DHTML and, 843 download experience, optimizing, 312–317 exception handling and, 270 nesting pages and, 330 site precompilation and, 52 Substitution control calls and, 776 view state size and, 713–715 PerformDataBinding method, overriding, 535, 540 permission sets, configuring, 103

    953

    954

    permissions, for applications permissions, for applications, 788–789 PermissionSetName attribute, 103 per-request impersonation, 785 PersistenceMode attribute, 558 personalization, 285–303 anonymous user information, migrating, 299–300 anonymous user profiles, 294–295 interaction with page, 292–300 personalization events, 298–299 profile database, creating, 292–294 profile providers, 300–303 user profiles, creating, 285–292 user-specific information, 299 Personalize event, 298 picture viewer handler, 128–133 PictureViewerInfo class, 129 pipeline events, wiring up, 151–153 PipelineRuntime objects, invoking, 34 PlaceHolder controls, 265–266, 557 placeholders, 320–321, 323, 333 contents of, 326 default content, 323–324 POCO code generator, 601 policy files and security levels, mappings between, 97–98 polling, 739, 742 partial rendering and, 872, 878–879 timer-based, 878–879 pollTime attribute, 80 port 80, 27 port numbers, changing, 100 positional filters, 911 PostAcquireRequestState event, 33, 650 PostAuthenticateRequest event, 32, 650 PostAuthorizeRequest event, 32, 650 postback controls, 387 postBackElement property, 873 postbacks, 4–6, 766 destination of, 365 detecting from child controls, 866–868 full, 869–870 handling, 5, 212–213 HTML server controls for, 244 partial rendering and, 860, 865

    replacing with AJAX calls, 10 via scripts, 374 SEO and, 350 through submit buttons, 374 to target page, 374–376. See alsocross-page posting Timer control for, 878–879 triggering, 258, 868–869 user interface, disabling, 874–875 PostBackTrigger object, 870 PostBackUrl property, 374 post-cache substitution, 775–776 posted data processing, 211 retrieving, 369 saving, 250–251 testing, 363 posted names, matching to control IDs, 211 posting acceptor, 250 PostLogRequest event, 33, 651 PostMapRequestHandler event, 32, 650 POST method, 365 posting forms with, 367 PostReleaseRequestState event, 33, 651 PostRequestHandlerExecute event, 33, 650 PostResolveRequestCache event, 32, 155–156, 650 PostUpdateRequestCache event, 33, 651 precedence of themes, 341 precondition attribute, 109, 126 preconditions, 578 predictable IDs, 91 PreInit events, 209 PreLoad event, 211 Preload method, 62 prependTo function, 921 PreRender event, 214, 230 PreRenderComplete event, 214 asynchronous handlers for, 201 Begin/End asynchronous handlers for, 201 page processing, 202, 203 PreRequestHandlerExecute event, 33, 650 PreSendRequestContent event, 33, 150 PreSendRequestHeaders event, 33, 150 presentation layer, 269, 593, 596. See alsopages in AJAX, 880

    DAL, invoking from, 608 design patterns for, 615–623 navigation workflow, binding to, 636 presentation logic, 217 Presentation Model (PM) pattern, 621 presenter creating, 626–632 data retrieval, 628–629 defined, 619 instance of, getting, 626–627 navigation and, 632–636 Refresh method, 628 role in MVVM pattern, 622 role of, 621 service layer, connecting to, 629–630 sharing with Windows applications, 631–632 testing, 639–642 testing in isolation, 641–642 __PREVIOUSPAGE hidden field, 374 PreviousPage property, 375 @PreviousPageType directive, 376–377 principal objects, custom, 804–806 priority, cached item, 726, 730–731 privateBytesLimit attribute, 78 process model configuration settings, 92–95 IIS integrated mode and, 95 optimizing, 94 process recycling, 28, 55–56 configuration file changes and, 65 event log entries for, 57 unexpected restarts and, 56–58 processing, 14 separating from rendering, 9–10 processing directives, 179–190 @Assembly directive, 185–187 @Implements directive, 189 @Import directive, 188 @Page directive, 181–185 @Reference directive, 189–190 syntax, 180 typed attributes, 180–181 section, 92–95 processRequestInApplicationTrust attribute, 102

    request handlers, determining ProcessRequest method, 36, 121– 122, 138, 210 HttpRuntime, 174–175 IHttpHandler, 174, 178, 190 profile API, 108 access layer, 300 storage layer, 300 for Web site projects, 286–287 profile class, 291–292 defining, 287–289 Profile property, 288, 295 attributes of, 96 profile providers, 300–303 configuring, 300–302 connection strings, 301 custom, 302–303 functionality of, 96 SQL Express, 286 section, 96–97, 286–287 ProfileEventArgs class, 299 ProfileModule, 298–299 Programming Microsoft ASP.NET MVC (Esposito), 26, 268 ProgressTemplate property, 871 properties of Control class, 218–228 defined, 413 of Web controls, 253–254 section, 286 property values, varying output caching by, 770 protected members, 173 protection providers, choosing, 115–116 ProtectSection method, 114 protocol stack, 29 prototype object, 848–849 prototype property, 847 prototypes, 848–849 Provider property, 808 ProviderBase class, 813 providerName attribute, 106 providers browser-capabilities providers, 346–348 browser providers, 77, 78 defined, 300 encryption providers, 107, 115–116 membership providers, 88 output cache providers, 79 profile providers, 96, 300–303 registering, 347 role providers, 97 site map providers, 100, 354–355 store providers, 99

    Providers property, 815 section, 99, 301 proxy cache, 755–756 proxy classes, PageMethods class, 896 proxy methods, JavaScript, 893–895 public methods, invoking, 886

    Q queries filter function, 914 filters, 911–914 find function, 914 selectors, 909–911 visibility operators, 915–917 query results. Seewrapped sets queryable objects, 414–415

    R RAD, 4, 437 designer data bindings, 218 paradigm, 569, 615 RadioButtonList control, 424– 425, 427 radio buttons, 259–260 RaisePostBackEvent method, 213, 387 RaisePostDataChangedEvent method, 212, 245 Random Number Generator (RNG) cryptographic provider, 687 RangeValidator control, 380, 386 Rapid Application Development. SeeRAD Raven, 614 raw data, passing, 17 reader/writer locking mechanisms, 683–684 reading methods, synchronization mechanism, 678 ready function, 906–907 multiple calls to, 907 order of running, 907 readyState property, 906 readyStateChange event, 906 reauthorization, 663 Red Gate SmartAssembly, 284 redirect attribute, 81 RedirectFromLoginPage method, 794, 796 redirects, 634–635

    RedirectToLoginPage method, 796 @Reference directive, 189–190 refreshing. Seeupdating conditional, 866–870 RegisterAsyncTask method, 206–208 RegisterHiddenField method, 261 RegisterInstance method, 588 RegisterRoutes method, 160 RegisterType method, 587 RegisterXXX methods, 855–856 regular expressions, validating, 385 RegularExpressionValidator control, 380, 385 release script files, 859–860 ReleaseHandler method, 145 ReleaseRequestState event, 33, 650 Remote Scripting (RS), 841 removal callbacks, 726 defining, 729–730 Remove method, 727 Render method, 231, 520–521 overriding, 528 RenderControl method, 230 Renderer class, 575 rendering browser-sensitive, 234–235 child controls for, 528–532 cross-browser rendering, 344–348 custom controls, 520–522, 527–533 legacy and CSS-friendly modes, 232–234 separating from processing, 9–10 SimpleGaugeBar control, 527–533 templates, 560–561 rendering engine, entry point into, 231 rendering methods, 194–195 RenderingCompatibility property, 233 Repeater control, 221, 416, 428–430 RepeaterItem class, 429 RepeaterItemCollection class, 429 replay attacks, 803, 804 replicated cache topologies, 746 Repository pattern, 609–610 Representational State Transfer (REST), 879–897 request handlers, determining, 32

    955

    956

    request life cycle request life cycle, 22 events in, 32–34 handlers, writing, 36–37 Request object Browser property, 77 request property, 873 request routing, 119–120 Request.Headers collection, 275 RequiredFieldValidator control, 380, 382, 386–387 RequireJS, 313 requirements churn, 567 RequirementsMode property, 889 requireSSL attribute, 84, 803–804 reset buttons, 247 ResetPassword method, 812 Resolve method, 588–589 ResolveRequestCache event, 32, 650 resolver types, 103 resource assemblies, defined, 304 resources adding to pages, 312–317 custom resources, 126–127 declarative vs. programmatic, 306 defined, 304 embedding, 195 forbidden, preventing access to, 143 global resources, 304–305, 307 lifetime of, 648 localizable, 304–308 local resources, 304–305, 308 mapping to handlers, 171–172 retrieving, 659–660 served by IIS, 128 $Resources expression builder, 307 $Resources expressions, 359 ResourceUICultures property, 315 response bodies, sending, 33 response filters, 651, 666–667 Response.Redirect method, 275, 663 REST, 879–897 consuming, 893 HTTP verbs and, 879 JavaScript proxy for, 893–895 webHttpBinding model, 883 RESX files, 304 culture information in, 308 editing, 306 site map information in, 359–360 ReturnUrl parameter, 799

    RewritePath method, 104, 158, 658–659 Rich Internet Application (RIA) services, 20 rigid software, 567–568 Rijndael encryption, 799 role management, 97, 817–821 LoginView control, 830–832 role management API, 108, 817–819 role manager HTTP module, 820 role manager providers, 300 section, 97 RoleProvider class, 820–821 role providers, 820–821 built-in, 821 custom, 821 role management, 97 roles defined, 817 login templates based on, 831–832 securing access with, 358 Roles class, 806, 807, 819–820 methods of, 819 properties of, 820 Route class, 160 route handlers, 159 tasks of, 156 route parameters, 159 accessing programmatically, 162 RouteData property, 162 routes, 158–159 default values, 162 defining, 160 HTTP handler for, 155 processing order, 162 storing, 160 structure of, 163–164 RouteTable class, 160 RouteValueDictionary type, 163 routing API, 104 RowUpdating event, 443, 455 RSA-based protection provider, 107, 115 runAllManagedModulesForAllRequests attribute, 109 runat attribute, 7, 197, 217 runat=”server” attribute, 200 for HTML controls, 235 for Web controls, 253 runManagedModulesForWebDavRequests attribute, 109 runtime compilation, 52 runtime environment, 27 ASP.NET MVC, 22–24

    asynchronous handlers, dealing with, 146–147 configuration settings for, 71–73, 85–87, 89–92 of early ASP.NET, 28–29 of early IIS, 29 of IIS, 30 IIS 5.0, 28 Windows 2000, 28 runtime errors, 269. See alsoerror handling runtime event handling, 119. See alsoHTTP handlers runtime page processing modules, 170–173

    S Same Origin Policy (SOP), 850, 881, 929 sandboxing, 789 SaveAs method, 251 SaveControlState method, 719 SavePageStateToPersistenceMedium method, 215 SaveViewState method, 214 overriding, 539 saving posted files, 250 scalability, 744. See alsodistributed cache ScaleOut StateServer, 755 scavenging, 731 schema-less storage, 614 scope application scope, 119 machine scope, 119 script code emitting in client pages, 198 for page postbacks, 213 script files aggregating, 315 embedded vs. inline, 316–317 linking to pages, 312–314 localized, 314–315 minifying, 314 moving to bottom of page, 312–313 script interceptors, 20 script maps, 127 script resource management, 852 tags, 312, 858, 929–930 defer attribute, 313 scriptable services, 880–889 scripting engines, 901–902 script-led requests, JSON for, 890–893

    server-side events ScriptManager control, 315, 851, 852–860 events of, 856 on master pages, 865 methods of, 854–855 properties of, 852–854 ScriptManagerProxy control, 852, 857–858, 865 ScriptModule HTTP module, 897 ScriptReference class, 859 ScriptResource.axd HTTP handler, 859 scriptResourceHandler element, 107 scripts composite, 859 debug files, 859–860 globalization, 860 loading, 858–859 Page class methods related to, 197–198 postbacks via, 374 release files, 859–860 Scripts collection, 858 ScriptService attribute, 886 search for files, 130 on input forms, 368–369 search engine optimization. SeeSEO search engines, expressive URLs and, 156 element, 67 section handlers, specifying, 116 element, 67–68 SectionInformation object ProtectSection method, 114 UnprotectSection method, 114 Secure Sockets Layer (SSL), 782 authentication tickets, securing with, 803–804 secured sockets, authentication over, 803–804 security application trust levels and, 786–789 ASP.NET pipeline level, 781, 784 ASP.NET security context, 781–791 authentication methods, 789–791 claims-based identity, 821–825 cookieless sessions and, 690–691 default ASP.NET account, changing, 784–786 error handling, 81

    filtering user input, 135 Forms authentication, 791–806 HTTP error handling and, 281 HttpOnly attribute and, 85 IIS level, 781–783 input validation, 674 JavaScript callers and, 880 membership API, 806–821 planning for, 779 role management, 817–821 server controls for, 825–835 of session state data, 699 threats, types of, 779–780 trust level and policy file mappings, 97–98 of view state, 192–193, 712–713 worker process level, 781, 783–784 Security Token Service (STS), 824 security trimming, 358 implementing, 354 security zones, 786 section, 97–98 Select buttons, 505–506 selected item templates, 505–507 SelectedIndexChanged event, 425 SelectedItem property, 424 SelectedItemTemplate property, 505 selection in ListView control, 505–507 selective updates, 19–20 selectors, 909–911 compound, 910–911 SelectParameters collection, 462 Selenium, 363 self-logging exceptions, 284 semi-dynamic content, caching, 58 sendCacheControlHeader attribute, 79 sensitive data, securing, 780 SEO, 348–351 ASP.NET Web Forms and, 350–351 cookieless sessions and, 691 measuring, 351–352 meta tags, 349 page titles, 348 query strings, 349 redirects, 349 Server.Transfer and, 275 subdomains, 349 separation of concerns (SoC). See SoC serialization of session state, 695–697, 710

    XML format, 890 server attacks, 779 server caching, 761 post-cache substitution and, 776 server controls, 4–5, 7–8. See alsoControl class; controls adaptive rendering, 230–232 in AJAX, 267–268, 851 browser-sensitive rendering, 234–235 control containers, 226–227 control state, 214, 718–719 CSS-friendly markup, 232–234 ctIXX string IDs, 223 custom controls, 513–562 data-bound, 411. See alsodata binding data source controls, 456–468 HTML and CSS friendly, 337 HTML server controls, 217, 235–252 identifying, 220–226 instances of, 172 literal controls, 252 name conflicts, avoiding, 221 naming containers, 221 programming, 7 RAD designer data bindings, 218 role of, 217 security-related, 825–835 skins for, 220, 340–342 Static option, 224 template definitions for, 340 themes, 220, 235, 337, 340–341 validation of, 381–382 view state, 227–228 view state, enabling or disabling for, 715–717 visibility of, 228 Web controls, 217, 253–268 server forms, 365 Server object, 660–663 server processes, memory limits, 94 server transfers, 378–379 server variables, 673 ServerChange event, 245, 248–249 ServerClick event, 247 servers machinewide settings, 70 view state, storing on, 719–720 server-side controls runat=server attribute, 200 view state information, 200 server-side events, 212–213

    957

    958

    server-side expressions, syntax server-side expressions, syntax, 690 server-side forms, 240 server-side tags, single, 200 server-side handlers, 6 server-side programming, 839 server-side redirection, 663 server-side validation, 387–388 in wizards, 405 Server.Transfer method, 275, 378 ServerValidate event, 384 service layer defined, 602 methods in, 604 presenter, connecting to, 629–630 Service Layer pattern, 602 Service Locator pattern, 582 services, scriptable, 880–889 session cookies, 687–688. See alsocookies session hijacking, 690, 780 session ID, 687–692 custom, 708–710 default generation behavior, 708 encrypting, 690 generating, 687 session ID managers, 708–710 Session object, 680–681 behavior and implementation, 98 removal of values from, 694–695 session providers, out-of-process, 753 session state, 680–704. See alsoHttpSessionState class access to, 680, 699 best practices for, 710 concurrent access, 684 configuring, 98–100, 691–692 customizing management of, 704–710 errors on page and, 695 expiration of, 706 extensibility model for, 680 HTTP handler access to, 141 InProc mode issues, 694–695 lifetime of, 693–695 loss of, 694–695 management timeline, 683 persisting data to remote servers, 695–699 persisting data to SQL Server, 699–704

    remote, 695–699 serialization and deserialization, 695–697 Session_End event, 693–694 session ID, assigning, 687–692 Session_Start event, 693 session-state HTTP module, 680–684 state client manager, 681–682 synchronizing access to, 683–686 Web farm/garden scenarios, 703 session state store, 705. See alsostate providers Session_End event, 686, 693–694 SessionIDManager class, 708 deriving from, 709 sessions abandoning, 686 cookieless, 688–691 identifying, 687–692 lifetime of, 693 out-of-process, 695–697 Session_Start event, 693 section, 98–100, 691–692 attributes of, 692 SQL Server, setting as state provider, 700–701 SessionStateModule, 119, 680–684 SessionStateStoreData class, 707 SessionStateStoreProviderBase class, 705–706 SessionStateUtility class, 707 SetAuthCookie method, 798 SetCacheability method, 666 SetExpires method, 666 SetPropertyValue property, 290 settings inheritance, 63, 90 packaging, 43–51 SetVaryByCustom method, 768 shadow-copy feature, 84 shadowCopyBinAssemblies attribute, 84 sharding, 612 Shared attribute, 773 SharedCache, 754 ShouldHook helper function, 153 show function, 915–917 shutdownTimeout attribute, 84 SignOut method, 795 sign-outs, 795–796 Silverlight compatibility with other applications, 632

    WCF service configuration in, 885 SimpleGaugeBar control, 522–527 color support, 526–527 extending, 533–543 object model definition, 523 object model implementation, 523–526 output of, 529 properties of, 523 rendering, 527–533 ruler, 526, 530–531 using, 532–533 SimpleHandlerFactory class, 142, 144 Single Responsibility Principle (SRP), 573–574 single-form model, 365–366, 368. See alsoinput forms site map providers, 100, 354–355 default, 352 section, 100 site maps configuring, 357–360 defining, 352–353 localizing, 359–361 multiple, 357–358 securing access with roles, 358 site navigation API, 352–358 configuration settings, 100 site precompilation, 52–55 benefits of, 52 deployment precompilation, 53–55 in-place precompilation, 53 target directory support, 53, 54 site replication tools, advantages of, 42 site-level configuration settings, 108–110 SiteMap class, 355–356 elements, 353 SiteMapPath controls, 356–358 SiteMapProvider class, 354 SkinID property, 342 skins, 338–341 applying, 341–342 for server controls, 220 sliding expirations, 723, 726, 731–732 SoC, 10, 571–573 in ASP.NET MVC, 23 favoring, 14 layers and, 593 MVC pattern and, 617 Socket class, 102 software, rigid, 567–568

    System.ApplicationException class software dependencies, 568 software design, 565 abstraction, 575–576 big ball of mud, 566–567 cohesion and coupling, 569–571 mainatainability, 565 methodologies, 595 object-oriented design, 599 principles of, 569–572 requirements churn, 567 security planning, 779 separation of concerns, 571–573 SOLID principles, 573–583 structured writing, 615 symptoms of deterioration, 567–569 test-driven development, 638 three-tiered architecture, 593–595 from use-case, 624 software design team limited skills, 566–567 member turnover, 567 software modules cohesion of, 570 coupling of, 570–571 low coupling between, 575 software reuse, 568 software workarounds, 568–569 SOLID principles, 573–583 Dependency Inversion principle, 580–583 Interface Segregation principle, 579–580 Liskov’s principle, 576–578 Open/Closed Principle, 575–576 Single Responsibility Principle, 573–574 Sort buttons, 511 sorting data, 453–454 expressions, 453 lists, 511 source code of content pages, 325 deploying, 40 for derived classes, generating, 172 parsing, 170 source files dynamic compilation of, 189 generating, 611 tags, 388 sprites, 315–316 SQL Azure, 613

    SQL Express, 286 SQL injection, 780 SQL Server cache dependency, 743–745, 762 hosting identity access, 703 persisting session state to, 699–704 session state data store, creating, 701–703 SqlCacheDependency class, 80, 743–745 SqlCommand object, 744 SqlDependency attribute, 762 SqlRoleProvider, 821 SQLServer mode, 99 SQLServer provider, 695, 705 src attribute, 858 SRP, 573–574 canonical example, 574 SSL, 782 authentication tickets, securing with, 803–804 StackOverflow site, 267 startMode attribute, 60 state client managers, 681–682 state information. See also view state detecting changes, 212–213, 248–249 persisting, 33 releasing, 33 retrieving, 32 state management . See also view state application state, 676–679 best practices, 710 cookies, 675 levels of, 675 session state, 680–710 view state, 710–720 state providers ASP.NET, 697–699 custom, 704–708 expiration callback support, 706 expiration mechanisms, 706 locking mechanisms, 706 out-of-process, 695–699 partition resolvers, 704 registering, 707 SQL Server, 700–704 writing, 707 StateBag class, 711–712 methods of, 711–712 properties of, 711 stateful behavior postbacks for, 6

    view state and, 6–7 StateServer mode, 100 StateServer provider, 695, 705 static files, IIS serving of, 128 Static option, 224 static properties in global.asax file, 655 static requests, processing, 29 static resources behavior of, 757–758 images, 133 StaticSiteMapProvider class, 354 statusCode attribute, 81 S3, 613 StopRoutingHandler class, 164 storage of HTTP requests, 673 intermediate, 721 local, 923 of output caching, 776–777 schema-less storage, 614 store providers, 692 for session-state settings, 99 stored procedures, 612 stream classes, creating, 666–667 strings, lengths of, 696 stubs, 640 Style class, 254–255 style information adding to pages, 337–345 themes, 337 style properties, 357 of Web controls, 254–255 style sheet files, external, linking to, 242 style sheets, 339. See alsoCSS defined, 338 style sheet themes, 338, 340 StyleSheetTheme attribute, 340, 341 styling pages, 336–344 submit buttons, 213, 247 SubSonic, 600 Substitution control, 775–776 .svc resources, 881–882 swapText function, 921 synchronization of application state operations, 678–679 of cache, 736 with Copy Web Site feature, 42 synchronous handlers, 121–127. See alsoHTTP handlers SYSTEM account, 781 system classes, 12 System.ApplicationException class, 272

    959

    960

    System.Configuration namespace System.Configuration namespace, 63 configuration management classes in, 110 section, 101 System.Drawing namespace, use of classes in, 140 section, 67 section, 107–108 section, 71–105 subgroup, 73 HTTP handlers, registering in, 124 important sections in, 71–73 section, 108–110 HTTP handlers, registering in, 125 reading and writing in, 112 System.Web.UI.HtmlControls namespace, 237 System.Web.UI.Page class, 12, 36, 172 ProcessRequest method, 36 System.Web.UI.WebControls namespace, 253

    T T4 templates, 600 Table Module pattern, 597, 598 DAL and, 606

    tag, 232 table-based interfaces, 480–485 tables, for multicolumn layouts, 485 tabular layouts, 480–485 alternate item rendering, 483–484 HTML tables, 484–485 item template, 481–483 layout template, 480–481 tag-based selectors, 910 section, 91–92 tasks, asynchronous execution, 201 TDD, 23, 638 Telerik JustCode, 270 template containers, defining, 558–559 template definitions, for controls, 340 template properties attributes, 557–558 defining, 557–558

    setting, 559–560 TemplateControl class, 190 Eval method, 438 templated fields, 450–451 templates for custom controls, 556–561 defined, 434 insulating in separate file, 557 ListView support of, 473 login templates, 831 rendering, 560–561 role-based, 831–832 T4 templates, 600 for wizards, 400 temporary ASP.NET files folder, 786 permissions on, 784 test doubles, 640 test harnesses, 638 testability of Web Forms, 636–642 test-driven development (TDD), 638 with ASP.NET MVC, 23 testing CacheDependency objects, 742 code-behind classes, 361 DAL interfacing and, 609 presenter classes, 639–642 test names, 639 unit testing, 637–638 for usability, 361–364 writing tests, 637 text inserting as literals, 260 localized text, 306–307 text boxes, multiline, 245 text controls, 260–261 TextBox class, interfaces of, 260 theme attribute, 340 Theme attribute, 341 Themeable attribute, 235 ThemeList controls, 343 themes, 319, 337–341 applying, 340–341 changing, 209 vs. CSS, 357 customization themes, 338 defined, 337 enabling and disabling, 342–343 loading dynamically, 343 precedence of, 341 for server controls, 220, 235 skins, 341–342 structure of, 339–340 style sheet themes, 338

    thread pool, free threads in, 86 threads asynchronous handlers and, 147–148 impersonation and, 784–785 minimum settings for, 94–95 three-tiered architecture, 593–595 business logic layer, 596–605 design model, 595 tickets, authentication, 792–793 getting and setting, 798 securiing, 803–804 storage in cookies, 799–800 tiled layouts, 487–493 grouping items, 487–489 group item count, 489–491 group separators, 489 populating dynamically, 491–493 Timer control, 878–879 ToInt32 method, 131 topology of distributed cache, 746 section, 100–101 Trace.axd handler, 129 tracing, 100–101 Transaction Script (TS) pattern, 597–598 Transfer method, 663 Transform attribute, 50 transformation files, 50–51 transition events, defined, 404 TransmitFile method, 669 tree of controls building, 209 unique names in, 190 trigger function, 918 Triggers collection, 869 triggers of postbacks, 868–869 trust levels, 786–789 configuration settings, 101–104 and policy files, mappings between, 97–98 section, 101–104 code access security permissions, 787 elements, 97 try/catch/finally blocks, 270 wrapping code in, 278 typed attributes, 180–181 TypeName attribute, 376

    U UICulture property, 315 unbind function, 918–919

    users UniqueID property, 211, 220 unit testing, 637–638 base classes and, 656 unit test classes, 639 Unity, 587–592 declarative configuration, 589–590 dependencies, resolving, 588–589 lifetime managers, 590–592 types and instances, registering, 587–588 Unload event, 215 unobtrusive JavaScript, 918 UnprotectSection method, 114 update callbacks, 726 Update method exceptions thrown in, 868 signature, 868 update operations, 466–468 in ListView control, 499–501 modifying objects, 468 parameters for, 466 UpdateMode property, 866–867 UpdatePanel control, 851, 860–865 conditional refreshes, 866–870 contents of, 865 example of, 861 feedback during updates, 870–876 full postbacks from, 869–870 in master pages, 864–865 vs. panel controls, 860–861 populating, 863–864 postbacks, triggering, 868–869 properties of, 862 UpdateProgress control for, 871 UpdateProgress control, 870–872 events of, 872–873 UpdateRequestCache event, 33, 651 updating concurrent calls, 877–878 conditional refreshes, 866–870 pending operations, aborting, 876 progress screen, 871–872 refresh conditions, 866 user interface, disabling, 874–875 Updating event, 468 uploading files control, 261–262 Uri class, 673 url attribute, 104 URL authorization, 791 URL encoding, 661 URL Rewrite Module, 37

    URL rewriting, 155, 157–158, 349, 658–659 drawback of, 158 vs. URL routing, 159 URL routing, 155–165 constraints on, 162, 164 vs. HTTP handlers, 165 preventing for defined URLs, 164–165 vs. URL rewriting, 159 in Web Forms, 36, 160–165 URL routing engine, 119–120, 155, 157–159 URLAuthorizationModule HTTP module, 791 section, 104 urlMetadataSlidingExpiration attribute, 84 UrlRoutingModule class, 155 URLs for advertisem*nts, 262–263 derived classes, linking to, 172 for embedded resources, 195 and endpoints, mappings between, 104 expressive URLs, 156–157 for hyperlinks, 447 for images, 133, 259 logic and parameters in, 156 mangling, 690 mapping to ASPX pages, 36 navigating to, 243–244 preventing routing for, 164–165 resolving, methods for, 195–197 route data in, 156 usability, 344–364 cross-browser rendering, 344–348 navigation system, 351–357 SEO, 348–351 site map configuration, 357–360 testing for, 361–364 UseDeviceProfile, 691, 801 useHostingIdentity attribute, 703 user account management, 806 user authentication, 784, 794–795 configuration settings, 74–76 user controls cacheability of, 770 caching in cacheable pages, 773–774 caching output of, 770 vs. custom controls, 513 description of, 768–769 dynamically loading, 557 inserting into pages, 769 master pages, 329. See alsomaster pages

    pages, linking to, 189–190 sharing output of, 772–773 Static option, 224 strongly typed instances of, 189 user credentials, collecting, 794 user input filtering, 135 validation of, 379–396 user interface disabling, 874–875 dynamic, 18–19 iterative controls for, 427–432 table-based interfaces, 480–485 for Web pages, 3 user profiles in action, 296–298 for anonymous users, 294–295, 299–300 automatically saving, 97 configuration settings, 96–97 creating, 285–292 grouping properties, 290 interaction with page, 292–300 profile database, creating, 292–294 profile providers, 300–303 properties, accessing, 295–296 storage of data, 286 user-specific information in, 299 for Web Application Projects, defining, 286 for Web site projects, 285 UserControl class, 321 user-defined code, invoking, 245 userIsOnlineTimeWindow attribute, 88 UserIsOnlineTimeWindow property, 808 user-mode caching, 58 users adding and creating, 806, 809– 810, 834–835 anonymous identification feature and, 73–74 authenticating, 793, 810–811. See alsoauthentication authentication state, 829 authorization of, 76–77 feedback for, 870–876 information about, storing, 106 managing, 811–812 reauthorization of, 663 roles, 817 UseSubmitBehavior property, 213, 258

    961

    962

    val function

    V val function, 923 Validate method, 379, 388 ValidateRequest attribute, 674 ValidateUser function, 810–811 validation of input fields, 248 of new records, 504–505 of cached pages, 764–765 validation attribute, 87 validation controls, 379–396 BaseValidator class, 380–381 client-side validation, 393–394 CompareValidator control, 382–383 cross-page posting and, 395–396 CustomValidator control, 383–385 error information, displaying, 388–389 ForeColor property, 381 generalities of, 379–382 and input controls, linking, 381–382 multiple controls, 380 multiple validators, 390–391 properties of, 380–381 RangeValidator control, 386 RegularExpressionValidator control, 385 RequiredFieldValidator control, 386–387 server-side validation, 387–388 validation groups, 394–395 validation summary, 391–393 element, 109 ValidationGroup property, 394–395 validationKey attribute, 87 [ValidationProperty] attribute, 385 ValidationSummary control, 380, 391–393 Validators collection, 379 value, cached item, 725 VaryByControl attribute, 770–772 VaryByCustom attribute, 767 VaryByHeader attribute, 767 VaryByHeaders property, 767 VaryByParam attribute, 759–760, 765 VaryByParams property, 765 verbs attribute, 76 VerifyRenderingInServerForm method, 195, 365 view abstracting, 624–626

    in ASP.NET MVC, 21–22 autonomous views, 616 defined, 616 role in MVC, 618 role in MVP, 620 XAML-based, 623 view controls, 266–268, 411, 432–434 DataKeyNames property, 421 DetailsView control, 432 FormView control, 433 GridView control, 433 ListView control, 433–434 programmatic control in, 476 view state, 4–7, 710–720 authentication checks for, 713 of controls, 227–228 control state, 718–719 cross-page posting and, 374–375 disabling, 715–717 encrypting, 712–713 encryption and decryption keys, 87 functionality of, 716 information stored in, 710 issues with, 712–715 methods of, 711–712 page performance and, 713–715 persisting, 200 programming, 715–720 properties of, 711 restoring, 210 saving to storage medium, 214 security of, 192–193, 712–713 SEO and, 350 on server, 719–720 size of, 7, 10, 227, 713–715 StateBag class, 711–712 tracking, 210 truncation of, 90–91 when to use, 717 writing, 711 ViewState class, 676 ViewState container classes, saving in, 539 control proeprties in, 536–538 property values, storing, 539 __VIEWSTATE hidden field, 215, 712 restoring contents of, 210 ViewState property, 710–712 ViewStateEncryptionMode property, 713 ViewStateMode property, 227– 228, 716

    ViewStateUserKey property, 192–193, 713 Virtual Accounts, 39 virtual directories, configuring properties, 43 virtual folders for running applications, 645 virtual members, safe use of, 578 VirtualPath attribute, 376 viscosity, 569 visibility operators, 915–917 Visual Basic, 3 Visual Studio Add Config Transform option, 51 adding content pages to projects, 324 Build|ASP.NET Configuration menu item, 302 deploy packages, building, 45–46 designers, 615 exception handling, 270 Mark As IIS Application On Destination check box, 47 MSUnit, 638 Package/Publish SQL tab, 46 Package/Publish Web tab, 45–46 resources files, adding, 304 site precompilation, 52, 55 T4 templates, 600 Table Module pattern and, 598 web.config transformations, 49–51 Web Deployment Tool, 44–45 Web project deployment, 40 Web setup applications, creating, 42–43 Web Site Administration Tool (WSAT), 809 XCopy capabilities, 40–41 Visual Studio Development Server, 48 Visual Studio Publish Wizard, 48 Visual Studio 2010, 20 Visual Studio 2010 Coded UI Tests, 363 Visual Studio 2010 Ultimate, 615 Vlissides, John, 575 VSDOC file, 904

    W WAPs, 40 data model definition, 290–292 personalization data in, 295–296

    Download from Wow! eBook

    web.config file WAPs (continued) user profiles, building, 286 web.config transformations, 49 warm-up. Seeapplication warm-up WatiN, 362–364 WCF services, 882 in AJAX pages, 881–885 ASP.NET compatibility mode, 887, 889 DataContract attribute, 891–892 method execution requests, 887 WDT, 44–45 building, 45–47 capabilities of, 45 contents of, 47 installing, 44 Web application folders, configuring, 43 Web Application Projects. SeeWAPs Web applications. See also applications autostarting, 38–39 grouping, 29 IIS settings, specifying, 48 initialization tasks, 38 installing, 39 machinewide settings, 69, 70 per-directory settings, 68 presentation layer, 269. See alsopages publishing in Visual Studio, 46–47 responsiveness of, 8 root web.config file, 69 Web attacks common types of, 779–780 fending off, 779. See alsosecurity Web browsers. See browsers Web cache, 755–756 Web controls, 217, 253–268. See also controls AdRotator controls, 262–263 AJAX and, 267–268 base class, 253 button controls, 257–258 Calendar controls, 263–264 check box controls, 259–260 core controls, 256–257 correct rendering of, 195 file upload control, 261–262 hidden field control, 261–262 hyperlink controls, 258–259 image button controls, 259 image controls, 259

    methods of, 255–256 PlaceHolder control, 265–266 properties of, 253–254 radio button controls, 259–260 runat=”server” attribute for, 253 styling, 254–255 text controls, 260–261 user interface, 527 view controls, 266–267 Xml control, 264–265 Web deployment, 40. See alsoapplication deployment Web Deployment Tool or Web Deploy (WDT), 44–47 Web development ASP.NET for, 3 ASP.NET MVC for, 4 tools for, 19 Web farms/gardens, session state and, 703 Web Forms, 3–14 in action, 5 alternatives to, 21–26 base class, 36 code testability, 636–642 vs. Data-for-Data model, 17 effectiveness of, 11, 14 HTTP handlers, determining, 35 moving away from, 15–19 MVC pattern and, 618 MVP pattern and, 621 MVVM pattern and, 622 navigation in, 634–636 opting out of built-in features, 25 Page Controller pattern, 11–14, 618 page postbacks, 4–5 page weights, 10 postback events, 5 presentation layer patterns, 615–623 abstraction layer, 14 runtime environment, 27 runtime stack, 23 Selective Updates model, 20 server controls, 4–5 strengths of, 4–8 testability of, 10 UI focus, 26, usability of, 11 view state, 4–5 weaknesses of, 8–10 Web frameworks, 18–19 AJAX built into, 19–20 Web methods, defining, 896 Web pages. See alsoASP.NET pages image references, 133

    markup mix, 3 Web Platform Installer, 44 Web servers, 27. See alsoIIS extensions of, 120 functionality of, 27 redesign of, 29 uploading files to, 249–251 Web Setup Projects creating, 42–43 Web application folders, 43 Web Site Administration Tool (WSAT), 292–293, 809 for role management, 818 Web site projects (WSPs), 40 Copy Web Site function, 40–41 data model definition, 286–287 personalization data in, 295–296 user profiles, defining, 285 Web site root folder, 786 Web sites development skill set for, 3 integration testing, 49 interface elements, 319 JSONP-enabled, 930 navigation system, 351–357 page composition, 319–345 rich client sides, 839 root web.config file, 69 testing for usability, 361–364 usability, 344–364 visual idea for, 319 Web user controls, use of, 557 web.config file. See also individual section names additional files, 64 assemblies, editing, 186 centralized files, 69 for classic and integrated IIS 7 working modes, 109 for configuration, 63 current environment settings in, 49 custom build configurations, 51 debug, release, and test versions, 49–51 editing, 50, 116–117, 170 global settings, replicating in, 70 section, encrypting, 87 section, 68 numRecompilesBeforeAppRestart attribute, 56 section, 774 processing of, 64–65

    963

    964

    WebConfigurationManager class web.config file (continued) remote session state, enabling in, 698 root file, 64 sections in, declaring, 68 writing to, 65 WebConfigurationManager class, 110, 111 WebControl class, 253, 514 vs. Control class, 519 deriving controls from, 513 section, 104–105 web.debug.config file, 49–50 WebGet attribute, 882 @WebHandler directive, 141–142 webHttpBinding model, 883 WebInvoke attribute, 883 WebMatrix IDE, 25 WebMethod attribute, 886, 895 web.release.config file, 49–50, 51 WebRequest class, 102 WebResource.axd handler, 859 web.sitemap file, 352 WIF, 76 claims and, 822 downloading, 824 Windows authentication, 76, 782, 790–791 limitations of, 791 Windows CardSpace, 791 Windows Communication Foundation (WCF), 603 Windows event log, logging exceptions in, 277 Windows Identity Foundation, (WIF), 76, 822, 824 Windows Server AppFabric, 747–753 Windows service always running, 38 Windows System folder, 786 WindowsTokenRoleProvider, 821 Wizard control, 266, 374, 397–402 events of, 401 main properties, 400 style properties, 399–400 suffixes, 400–401 templates for, 400 WizardNavigationEventArgs structure, 406, 407 WizardNavigationEventHandler delegate, 406 wizards, 397–409 adding steps to, 402–405 canceling navigation events, 407–408 finalizing, 408–409 headers, 398 input steps, 403–404 input validation, 404 jumping to steps, 401 navigating through, 405–409 navigation bar, 398 programming interface, 400–402 server-side validation, 405 sidebar, 398, 404–405 steps, types of, 402–403 structure of, 397–399 style of, 399–400 templates, 400

    view, 398 WizardStep class, 402 WizardStepType enumeration, 402 workarounds, 568–569 worker process ASP.NET standalone, 28–29 identity of, 781, 783 identity of, changing, 784–786 IIS native, 29 incoming requests, fielding, 149 recycling, 55, 59 worker properties, of Page class, 191–193 worker threads, number of, 94–95 World Wide Web Consortium (W3C), 339 proxy component standard, 842 updatable DOM standard, 842 wrapped sets, 905, 908–914 CSS classes, working with, 917 enumerating content, 908–909 operating on, 908–909, 915–919 visibility operators, 915–917 WriteFile method, 669 WriteSubstitution method, 776 WSPs, 40–41, 286–287, 295–296 w3wp.exe, 29 WWW publishing service, 29

    X XCopy, 40–43 Visual Studio capabilities, 40–41 xdt elements, 50 XHTML, ASP.NET support for, 3 XHTML rendering mode, designating, 105 section, 105 XML advertisem*nt files, 262–263 data, cache dependency for, 739–742 vs. JSON, 892–893 as serialization format, 890 Xml controls, 264 XML documents, embedding in pages, 264–265 XML encryption, 107 for section, 87 XmlDataCacheDependency class, 739–740 implementing, 740–741 XmlHttpRequest object, 16, 840–843 Same Origin Policy, 850 using, 844–845 XmlSiteMapProvider class, 352, 358 XslTransform class, 264, 265

    Y Yooder, Joseph, 566 YSlow, 317 YSOD (yellow screen of death), 272

    About the Author Dino Esposito is a software architect and trainer ­living near Rome and working all around the world. Having started as a C/C++ developer, Dino has ­embraced the ASP.NET world since its beginning and has contributed many books and articles on the subject, helping a generation of developers and ­architects to grow and thrive. More recently, Dino shifted his main focus to ­principles and patterns of software design as the typical level of complexity of applications—most of which were, are, and will be Web ­applications—increased beyond a critical threshold. Developers and architects won’t go far today without creating rock-solid designs and a ­ rchitectures that span from the browser presentation all the way down to the data store, through layers and tiers of services and workflows. Another area of growing interest for Dino is mobile software, specifically crossplatform mobile software that can accommodate Android and iPhone, as well as Microsoft Windows Phone 7. Every month, at least five different magazines and Web sites in one part of the world or another publish Dino’s articles, which cover topics ranging from Web development to data access and from software best practices to Android, Ajax, Silverlight, and JavaScript. A prolific author, Dino writes the monthly “Cutting Edge” column for MSDN Magazine, the “CoreCoder” columns for DevConnectionsPro Magazine, and the Windows newsletter for Dr.Dobb’s Journal. He also regularly contributes to popular Web sites such as DotNetSlackers—http://www.dotnetslackers.com. Dino has written an array of books, most of which are considered state-of-the-art in their respective areas. His more recent books are Programming ASP.NET MVC 3 (Microsoft Press, 2011) and Microsoft .NET: Architecting Applications for the Enterprise (Microsoft Press, 2008), which is slated for an update in 2011. Dino regularly speaks at industry conferences worldwide (such as Microsoft TechEd, Microsoft DevDays, DevConnections, DevWeek, and Basta) and local technical conferences and meetings in Europe and the United States. In his spare time (so to speak), Dino manages software development and training activities at Crionet and is the brains behind some software applications for live scores and sporting clubs. If you would like to get in touch with Dino for whatever reason (for example, you’re running a user group, company, community, portal, or play tennis), you can tweet him at @despos or reach him via Facebook.

    965

    For Visual Basic Developers Microsoft® Visual Basic® 2010 Step by Step

    Microsoft Visual Studio® Tips 251 Ways to Improve Your Productivity

    Michael Halvorson ISBN 9780735626690

    Sara Ford ISBN 9780735626409

    Teach yourself the essential tools and techniques for Visual Basic 2010—one step at a time. No matter what your skill level, you’ll find the practical guidance and examples you need to start building applications for Windows and the Web.

    Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build, Second Edition Sayed Ibrahim Hashimi, William Bartholomew ISBN 9780735645240 Your practical guide to using, customizing, and extending the build engine in Visual Studio 2010.

    This book packs proven tips that any developer, regardless of skill or preferred development language, can use to help shave hours off everyday development activities with Visual Studio.

    Parallel Programming with Microsoft Visual Studio 2010 Donis Marshall ISBN 9780735640603

    The roadmap for developers wanting to maximize their applications for multicore architecture using Visual Studio 2010.

    Programming Windows® Services with Microsoft Visual Basic 2008 Michael Gernaey ISBN 9780735624337

    The essential guide for developing powerful, customized Windows services with Visual Basic 2008. Whether you’re looking to perform network monitoring or design a complex enterprise solution, you’ll find the expert advice and practical examples to accelerate your productivity.

    microsoft.com/mspress

    Dev Visual Basic_ResPg_eVer_02.indd 1

    8/23/10 9:19 PM

    Collaborative Technologies— Resources for Developers Inside Microsoft® SharePoint® 2010 Ted Pattison, Andrew Connell, and Scot Hillier ISBN 9780735627468

    Get the in-depth architectural insights, taskoriented guidance, and extensive code samples you need to build robust, enterprise contentmanagement solutions.

    Programming Microsoft Dynamics® CRM 4.0 Jim Steger, Mike Snyder, Brad Bosak, Corey O’Brien, and Philip Richardson ISBN 9780735625945

    Apply the design and coding practices that leading CRM consultants use to customize, integrate, and extend Microsoft Dynamics CRM 4.0 for specific business needs.

    Programming for Unified Communications with Microsoft Office Communications Server 2007 R2 Rui Maximo, Kurt De Ding, Vishwa Ranjan, Chris Mayo, Oscar Newkerk, and the Microsoft OCS Team ISBN 9780735626232 Direct from the Microsoft Office Communications Server product team, get the hands-on guidance you need to streamline your organization’s real-time, remote communication and collaboration solutions across the enterprise and across time zones.

    Microsoft .NET and SAP Juergen Daiberl, Steve Fox, Scott Adams, and Thomas Reimer ISBN 9780735625686

    Develop integrated, .NET-SAP solutions— and deliver better connectivity, collaboration, and business intelligence.

    microsoft.com/mspress

    Dev CollabTech_ResPg_eVer_02.indd 1

    8/23/10 9:16 PM

    Best Practices for Software Engineering Software Estimation: Demystifying the Black Art

    Code Complete, Second Edition

    Steve McConnell ISBN 9780735605350

    Steve McConnell ISBN 9780735619678

    Amazon.com’s pick for “Best Computer Book of 2006”! Generating accurate software estimates is fairly straightforward—once you understand the art of creating them. Acclaimed author Steve McConnell demystifies the process—illuminating the practical procedures, formulas, and heuristics you can apply right away.

    Widely considered one of the best practical guides to programming—fully updated. Drawing from research, academia, and everyday commercial practice, McConnell synthesizes must-know principles and techniques into clear, pragmatic guidance. Rethink your approach—and deliver the highest quality code.

    Simple Architectures for Complex Enterprises

    Agile Portfolio Management Jochen Krebs ISBN 9780735625679

    Agile processes foster better collaboration, innovation, and results. So why limit their use to software projects— when you can transform your entire business? This book illuminates the opportunities—and rewards—of applying agile processes to your overall IT portfolio, with best practices for optimizing results.

    Roger Sessions ISBN 9780735625785

    Why do so many IT projects fail? Enterprise consultant Roger Sessions believes complex problems require simple solutions. And in this book, he shows how to make simplicity a core architectural requirement—as critical as performance, reliability, or security—to achieve better, more reliable results for your organization.

    The Enterprise and Scrum

    ALSO SEE

    Ken Schwaber ISBN 9780735623378

    Software Requirements, Second Edition

    Extend Scrum’s benefits—greater agility, higher-quality products, and lower costs—beyond individual teams to the entire enterprise. Scrum cofounder Ken Schwaber describes proven practices for adopting Scrum principles across your organization, including that all-critical component—managing change.

    Karl E. Wiegers ISBN 9780735618794

    More About Software Requirements: Thorny Issues and Practical Advice

    Agile Project Management with Scrum

    Ken Schwaber ISBN 9780735619937

    Solid Code

    Donis Marshall, John Bruno ISBN 9780735625921

    Karl E. Wiegers ISBN 9780735622678

    Software Requirement Patterns Stephen Withall ISBN 9780735623989

    microsoft.com/mspress

    Dev BestPrac_ResPg_eVer_03.indd 1

    8/23/10 9:11 PM

    For C# Developers Microsoft® Visual C#® 2010 Step by Step

    Microsoft XNA® Game Studio 3.0: Learn Programming Now!

    John Sharp ISBN 9780735626706

    Rob Miles ISBN 9780735626584

    Teach yourself Visual C# 2010—one step at a time. Ideal for developers with fundamental programming skills, this practical tutorial delivers hands-on guidance for creating C# components and Windows–based applications. CD features practice exercises, code samples, and a fully searchable eBook.

    Now you can create your own games for Xbox 360® and Windows—as you learn the underlying skills and concepts for computer programming. Dive right into your first project, adding new tools and tricks to your arsenal as you go. Master the fundamentals of XNA Game Studio and Visual C#—no experience required!

    CLR via C#, Third Edition

    Windows via C/C++, Fifth Edition

    Jeffrey Richter ISBN 9780735627048

    Jeffrey Richter, Christophe Nasarre ISBN 9780735624245

    Dig deep and master the intricacies of the common language runtime (CLR) and the .NET Framework. Written by programming expert Jeffrey Richter, this guide is ideal for developers building any kind of application—ASP.NET, Windows Forms, Microsoft SQL Server®, Web services, console apps—and features extensive C# code samples.

    Get the classic book for programming Windows at the API level in Microsoft Visual C++® —now in its fifth edition and covering Windows Vista®.

    Programming Windows® Identity Foundation

    Microsoft® ASP.NET 4 Step by Step

    Vittorio Bertocci ISBN 9780735627185

    George Shepherd ISBN 9780735627017

    Get practical, hands-on guidance for using WIF to solve authentication, authorization, and customization issues in Web applications and services.

    Ideal for developers with fundamental programming skills—but new to ASP.NET—who want hands-on guidance for developing Web applications in the Microsoft Visual Studio® 2010 environment.

    microsoft.com/mspress

    Dev C#_ResPg_eVer_02.indd 1

    8/23/10 9:13 PM

    What do you think of this book? We want to hear from you! To participate in a brief online survey, please visit:

    microsoft.com/learning/booksurvey

    Tell us how well this book meets your needs­—what works effectively, and what we can do better. Your feedback will help us continually improve our books and learning resources for you. Thank you in advance for your input!

    Stay in touch! To subscribe to the Microsoft Press® Book Connection Newsletter—for news on upcoming books, events, and special offers—please visit: microsoft.com/learning/books/newsletter

    Recommend Documents

    Programming Microsoft ASP.NET 4 - PDF Free Download (33)

    Published with the authorization of Microsoft Corporation by: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebas...

    Programming Microsoft ASP.NET 4 - PDF Free Download (34)

    Programming Microsoft LINQ in Microsoft .NET Framework 4®Paolo Pialorsi Marco Russo Published with the authorizat...

    Programming Microsoft ASP.NET 4 - PDF Free Download (35)

    Download from Wow! eBook Download from Wow! eBook Programming Microsoft LINQ in Microsoft .NET Framework 4®Paol...

    Programming Microsoft ASP.NET 4 - PDF Free Download (36)

    Published with the authorization of Microsoft Corporation by: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebas...

    Programming Microsoft ASP.NET 4 - PDF Free Download (38)

    PUBLISHED BY Microsoft Press A Division of Microsoft Corporation One Microsoft Way Redmond, Washington 98052-6399 Copyr...

    Sign In

    Our partners will collect data and use cookies for ad personalization and measurement. Learn how we and our ad partner Google, collect and use data.

Programming Microsoft ASP.NET 4 - PDF Free Download (2024)
Top Articles
Latest Posts
Article information

Author: Pres. Carey Rath

Last Updated:

Views: 6077

Rating: 4 / 5 (41 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Pres. Carey Rath

Birthday: 1997-03-06

Address: 14955 Ledner Trail, East Rodrickfort, NE 85127-8369

Phone: +18682428114917

Job: National Technology Representative

Hobby: Sand art, Drama, Web surfing, Cycling, Brazilian jiu-jitsu, Leather crafting, Creative writing

Introduction: My name is Pres. Carey Rath, I am a faithful, funny, vast, joyous, lively, brave, glamorous person who loves writing and wants to share my knowledge and understanding with you.