Chapter 34. Building a Code Sample Website

<feature>

IN THIS CHAPTER

</feature>

The goal of this final chapter is to present an entire ASP.NET 3.5 application that takes advantage of many of the new features of the .NET 3.5 Framework. In this chapter, we build a code sample website that also includes a simple blog.

There are two motivations behind this chapter. First, most of the code samples in this book are very brief. The code samples are intended to illustrate a particular point in the fewest lines of code possible. There are challenges, however, that you do not encounter until you build a full-blown web application. My hope is that you can apply the lessons I learned while building this application to an application you are building (in other words, learn from my pain).

The second motivation for writing this chapter is to tie together many of the technologies discussed in this book. This is a long book. Not many people read it from end to end (I thank the crazy few readers who manage to do it). My hope is that you’ll study the application presented in this chapter and become more interested in new .NET 3.5 technologies such as LINQ to SQL and the ASP.NET AJAX Extensions. (You might even use the VirtualPathProvider class someday.)

In the first part of this chapter, I provide you with a walkthrough of the pages contained in the sample application. Next, we examine more closely how different features of the website are implemented. In particular, we examine how data access and form validation are implemented in the sample website. We also discuss the Ajax features of the sample site. Finally, we discuss how the website enables “live” code samples.

Note

The entire code sample website is included on the CD that accompanies this book. The source code for the sample website is included in both C# and VB.NET.

The sample website database is preloaded with the code listings from the first three chapters of this book.

Overview of the Sample Website

The primary purpose of the website is to act as a code sample website for .NET code. Anyone who visits the website can post a new code sample entry. A code sample entry can consist of one or more code sample files. The author of the code sample entry can provide a description of each of the files. Furthermore, the author can add one or more tags to the code samples to categorize and describe their purpose.

Visitors to the website can browse the existing code sample entries. They can rate code samples when they view them. Furthermore, they can copy the code samples so they can use the samples in their own applications.

The website also includes a simple blog. The administrator of the website can post blog entries about the code samples or about any other topic. Visitors to the website can add comments to a blog entry.

The website exposes both an ATOM and RSS feed. If someone wants to subscribe to the blog, that person can subscribe to either the ATOM or RSS feed.

The home page of the website displays a code cloud and a list of recent blog entries (see Figure 34.1). The code cloud consists of a distinct list of all the code entry tags. The more code entries that share a tag, the larger the tag appears in the code cloud. In the figure, you’ll notice that a lot of code samples are related to validation. If you click a tag in the code cloud, you are transferred to a page that contains a list of code samples associated with the tag.

The home page of the sample website.

Figure 34.1. The home page of the sample website.

The home page also contains a list of three recent blog entries. A summary of each blog entry appears in the home page. You can click a blog entry to view the full entry.

In this section, you are provided with a walkthrough (with lots of screen shots) of the process of adding both blog entries and code entries.

Creating Blog Entries

Only a member with the Administrators role can post new blog entries. If you want to add a new blog entry, you must log in by clicking the Login link that appears at the top of every page.

Note

I’m set up as the administrator for the website by default. If you log in using the username Stephen and password secret, you will be logged in as an administrator.

You can create a new administrator account (and delete the Stephen account) by using the Website Administration Tool. After opening the sample site in Visual Web Developer, select the menu option Website, ASP.NET Configuration. When the Website Administration Tool opens, select the Security tab to manage users and roles.

After you log in, you can post a new blog entry by clicking the {Add Blog Entry} link that appears under the current list of blog entries on the home page. Clicking this link transfers you to the page displayed in Figure 34.2.

Adding a new blog entry.

Figure 34.2. Adding a new blog entry.

When you create a new blog post, you complete the following fields:

  • Title—The title of the blog post.

  • Introduction Text—The introduction text appears on the home page of the website as a summary of a blog entry. The introduction text is also used by the ATOM and RSS feeds.

  • Post—The full blog entry post.

  • Is Pinned—When this box is checked, the blog entry appears before all other blog entries on the home page.

Notice that the input field for entering a blog post uses a rich text editor. The sample website uses the open source FCKeditor. You can download the FCKeditor from www.fckeditor.net. The FCKeditor displays a rich editor in the case of Internet Explorer and Firefox. A plain text editor is displayed when the page is requested using the Opera browser.

If you attempt to submit the form without completing a required field, validation errors are displayed using callouts (see Figure 34.3). Validation errors are not displayed until you actually click the Next button.

Receiving validation errors when submitting the Blog form.

Figure 34.3. Receiving validation errors when submitting the Blog form.

After you successfully complete the form for adding a new blog entry and click the Next button, you are redirected to a page that you can use to tag the new blog entry (see Figure 34.4). You can add as many tags to a blog entry as you desire. The tags enable users to cross-navigate among related blog entries. Blog entries that share the same tags are linked.

Tagging a blog entry.

Figure 34.4. Tagging a blog entry.

The TextBox for adding a new tag uses the AJAX Control Toolkit AutoComplete Extender control. As you type, matching tags are retrieved from the database. Using the AutoComplete extender makes it more likely that you’ll use the same tags for multiple blog posts.

When you are done adding all your tags, you can click the Finish button to finish the process of adding a new blog entry. You are redirected to the finished blog entry that the world sees.

After you create a blog entry, you always have the option of editing or deleting the entry. To edit or delete a blog entry, log in to the website using an Administrator account, navigate to the blog entry page, and click either the {Edit} or {Delete} link.

Creating Code Sample Entries

The main purpose of the code sample website is to enable people to post new code samples, browse existing code samples, and rate code samples. Any registered user can post a new code sample entry at the website.

Before you can post a new code sample, you must navigate to the main code sample page. Click the Code Samples link that appears at the top of any page. You’ll see the page in Figure 34.5.

The main code sample page.

Figure 34.5. The main code sample page.

The main code sample page displays a list of the top ten highest rated code samples, the top ten most viewed code samples, and the top ten most recent code samples. At the bottom of the page, you can click the Add New Code Sample link to add a new code sample.

After you click the Add New Code Sample link, you see the form in Figure 34.6. A code sample entry can contain one more code samples. Typically, a code sample consists of multiple files. The form in Figure 34.6 enables you to provide a description of the entire code sample entry.

Form for adding a new code sample entry.

Figure 34.6. Form for adding a new code sample entry.

After you provide a description for the code sample entry and click Next, you are redirected to a page that you can use to associate one or more code samples with the code sample entry (see Figure 34.7). For example, you might want to create both a VB.NET and C# version of a code sample.

Managing code samples associated with a code sample entry.

Figure 34.7. Managing code samples associated with a code sample entry.

If you click the Add Code Sample link, you can add a new code sample. The form for adding a new code sample contains the following fields:

  • File Name—The name of the code sample file (for example, SamplePage.aspx).

  • File Language—The programming language used for the code sample.

  • Description—The description of the code sample.

  • Code—The actual source code of the code sample.

  • Enable Try It—When this box is checked, the code sample can be executed “live” from the website. This field appears only for users in the Administrators role.

  • Try It Code—The code executed when a code sample is executed “live” from the website. This field appears only for users in the Administrators role.

The last two fields require some explanation. If you are a member of the Administrators role, you can enable users to execute a code sample. When you check the Enable Try It check box, a Try It link appears with the code sample that a user can click to run the code sample (see Figure 34.8).

Viewing a code sample with a Try It link.

Figure 34.8. Viewing a code sample with a Try It link.

Because, most likely, you’ll want to use a different database connection with the code that gets executed when a user clicks Try It, there is a separate text field that you can use to enter the Try It code. The source code entered into the Try It Code text field is never displayed to the public.

After you submit a code sample, you can click Next to move to a page that enables you to tag a code sample entry. You can associate a maximum of three tags with any code sample entry. The tags appear in the code cloud. They also appear at the bottom of each code entry to provide visitors to the website with a way to navigate between related code samples.

Data Access and Validation

In this section, you learn how data access and form validation are implemented for the sample website. Data access and form validation are implemented by taking advantage of new features of the .NET 3.5 Framework.

Using LINQ to SQL

All data access performed by the sample website is performed using LINQ to SQL. No explicit SQL code was written. Taking advantage of LINQ to SQL enabled me to dramatically reduce the amount of time and code required to build the website.

Note

LINQ to SQL is discussed in detail in Chapter 18, “Data Access with LINQ to SQL.”

Normally, when performing data access from a web application, you need to write an entire data access layer to bridge the divide between your application and your database. By taking advantage of LINQ to SQL, I could avoid writing a data access layer. Instead, I could concentrate on the real task that I needed to accomplish: writing the data access queries.

I used the Object Relational Designer to create my LINQ entities (see Figure 34.9). I ran into one issue when using the Designer. Several of the website database tables include a DateCreated column. This column has a default value generated by the SQL GetDate() function. However, the Object Relational Designer did not pick up on this fact and I received errors when performing inserts and updates. To work around this problem, I had to manually update the DateCreated property for each entity in the Object Relational Designer. Within the Object Relational Designer, you can select a property of an entity and modify that property in the Properties window. In the case of the DateCreated property, I had to assign the value True to the Auto Generated Value property (see Figure 34.10).

Using the Object Relational Designer.

Figure 34.9. Using the Object Relational Designer.

Modifying the DateCreated Auto Generated Value property.

Figure 34.10. Modifying the DateCreated Auto Generated Value property.

I created a separate partial class for each entity. I added the LINQ to SQL queries that I needed to the partial class. For example, Listing 34.1 contains some of the code for the partial CodeSample entity.

Example 34.1. CodeSample.cs (Partial)

public partial class CodeSample : EntityBase<CodeSample>
{
    public IEnumerable<CodeSample> SelectByEntryId(int entryId)
    {
        return Table.Where( s => s.EntryId == entryId );
    }

    partial void OnCreated()
    {
        this.LanguageId = -1;
    }
}

Notice that the class is declared as a partial class. The other half of the partial class is generated by the Object Relational Designer. You can find the Designer-generated half of the CodeSample partial class in the Superexpert.Designer.cs file in the App_Code folder.

The preceding partial class contains a method named SelectByEntryId. This method executes a LINQ to SQL query that returns all code samples associated with a certain entryId. The Table property used within the method is a property exposed by the base class (we discuss this base class in the next section).

Notice that the class also includes an OnCreated() method. Unfortunately, you can’t add a constructor to a LINQ to SQL partial class because the Object Relational Designer already creates a constructor. However, you can create the equivalent of a class constructor by handling the OnCreated() event. In the case of the CodeSample class, the OnCreated() event is used to provide a default value for the CodeSample.LanguageId property.

Note

All the entity partial classes can be found in the App_CodeEntities folder on the CD.

Handling Common Database Operations

When building the LINQ to SQL queries for the sample application, I noticed that I ended up writing almost the exact same queries for each entity. For example, I needed to write Select, Insert, Update, and Delete queries for both the Blog and the CodeSample entities. Whenever you find yourself writing duplicate code, you should stop yourself and determine whether there is a way to make the code more generic. In this case, I took advantage of a custom entity base class.

All the entity partial classes derive from the base EntityBase class. This class contains generic Get, Select, Update, Delete, and Insert methods. It also contains generic methods for sorting and paging database data.

For example, the Blog partial class is declared like this:

public partial class Blog : EntityBase<Blog>

Because the Blog class derives from the EntityBase class, it includes methods such as Get, Select, Insert, Update, and Delete for free. It inherits all the properties and methods of the EntityBase class (the EntityBase class is located in the App_CodeEntityBaseClasses folder).

Note

We discuss the EntityBase class in more detail in the last part of Chapter 18.

Creating a Single Insert and Update Form

The sample application includes forms for inserting and updating blog entries, inserting and updating code sample entries, and inserting and updating individual code samples. All the forms in the sample application follow the same pattern. A FormView that contains a single EditItemTemplate is used for displaying the form for both inserting and updating the form data.

For example, the page used for inserting and updating a blog entry is contained in Listing 34.2.

Example 34.2. Edit.aspx

<%@ Page Language="C#" MasterPageFile="~/Design/MasterPage.master"
  Title="Blog Post" %>
<script runat="server">

    /// <summary>
    /// Add default values for new blog entry
    /// </summary>
    protected void srcBlog_Updating
    (
      object sender,
      ObjectDataSourceMethodEventArgs e
    )
    {
        // If new blog entry, add user name
        Blog newBlog = (Blog)e.InputParameters[1];
        if (newBlog.Id == 0)
        {
            newBlog.AuthorUserName = User.Identity.Name;
        }
    }

    /// <summary>
    /// If no problems, then redirect to blog tags page
    /// </summary>
    protected void srcBlog_Updated(object sender, ObjectDataSourceStatusEventArgs e)
    {
        if (e.Exception == null)
        {
            Blog newBlog = (Blog)e.ReturnValue;
            Response.Redirect("~/Admin/BlogTags/Edit.aspx?blogId=" + newBlog.Id);
        }
    }

    /// <summary>
    /// If there was a problem, keep the form in edit mode
    /// and show validation errors
    /// </summary>
    protected void frmBlog_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
    {
        if (e.Exception != null)
        {
            e.KeepInEditMode = true;
            e.ExceptionHandled = true;
            ValidationUtility.ShowValidationErrors(this, e.Exception);
        }
    }

</script>
<asp:Content ID="Content1" ContentPlaceHolderID="cphMain" Runat="Server">

<asp:UpdatePanel ID="up1" runat="server">
<ContentTemplate>
<asp:FormView
    id="frmBlog"
    DataSourceID="srcBlog"
    DataKeyNames="Id,Version"
    DefaultMode="Edit"
    OnItemUpdated="frmBlog_ItemUpdated"
    Width="100%"
    Runat="server">
    <EditItemTemplate>

    <div class="field">
        <div class="fieldLabel">
        <asp:Label
            id="lblTitle"
            Text="Title:"
            AssociatedControlID="txtTitle"
            Runat="server" />
        </div>
        <div class="fieldValue">
        <asp:TextBox
            id="txtTitle"
            Text='<%# Bind("Title") %>'
            Columns="60"
            Runat="server" />
        </div>
        <div class="fieldValue">
        <super:EntityCallOutValidator
            id="valTitle"
            PropertyName="Title"
            Runat="server" />
        </div>
    </div>

    <div class="field">
        <div class="fieldLabel">
        <asp:Label
            id="lblIntroductionText"
            Text="Introduction Text:"
            AssociatedControlID="txtIntroductionText"
            Runat="server" />
        </div>
        <div class="fieldValue">
        <asp:TextBox
            id="txtIntroductionText"
            Text='<%# Bind("IntroductionText") %>'
            TextMode="MultiLine"
            Columns="60"
            Rows="4"
            Runat="server" />
        </div>
        <div class="fieldValue">
        <super:EntityCallOutValidator
            id="valIntroductionText"
            PropertyName="IntroductionText"
            Runat="server" />
        </div>
    </div>

    <div class="field">
        <div class="fieldLabel">
        <asp:Label
            id="lblPost"
            Text="Post:"
            AssociatedControlID="txtPost"
            Runat="server" />
        </div>
        <div class="fieldValue">
        <fck:FCKeditor
            id="txtPost"
            BasePath="~/FCKEditor/"
            ToolbarSet="Superexpert"
            Value='<%# Bind("Post") %>'
            Width="600px"
            Height="600px"
            runat="server" />
        </div>
        <div class="fieldValue">
        <super:EntityCallOutValidator
            id="EntityCallOutValidator1"
            PropertyName="Post"
            Runat="server" />
        </div>
    </div>

    <div class="field">
        <div class="fieldLabel">
        </div>
        <div>
        <asp:CheckBox
            id="chkIsPinned"
            Text="Is Pinned"
            Checked='<%# Bind("IsPinned") %>'
            Runat="server" />
        </div>
    </div>

    <div class="field">
        <div class="fieldLabel">
        </div>
        <div>
        <asp:Button
            id="btnNext"
            CommandName="Update"
            Text="Next"
            Runat="server" />
        </div>
    </div>

    </EditItemTemplate>
</asp:FormView>
</ContentTemplate>
</asp:UpdatePanel>

<super:EntityDataSource
    id="srcBlog"
    TypeName="Blog"
    SelectMethod="Get"
    UpdateMethod="Save"
    OnUpdating="srcBlog_Updating"
    OnUpdated="srcBlog_Updated"
    Runat="Server">
    <SelectParameters>
        <asp:QueryStringParameter Name="id" QueryStringField="blogId" />
    </SelectParameters>
</super:EntityDataSource>

</asp:Content>

The page in Listing 34.2 contains a FormView control bound to a DataSource control. The FormView control contains a single EditItemTemplate template. Notice that it does not include an InsertItemTemplate, even though the FormView is used both for inserting new blog entries and editing existing blog entries. By using a single template, you reduce the amount of code you must write and maintain by half.

Note

Most of the pages in the sample application use a DataSource control named the EntityDataSource control. We discussed the EntityDataSource control in Chapter 18. This control is derived from the ObjectDataSource control and provides default values for some of the ObjectDataSource control’s properties. The EntityDataSource control is contained in the App_CodeEntityBaseClasses folder on the CD.

The DataSource control includes a select QueryStringParameter that grabs an id value from the query string. The DataSource control calls the Get() method to grab an instance of the Blog entity when the page is first requested.

The Get() method is a method of the EntityBase class. It looks like this:

public static T Get(int? id)
{
  if (id == null)
    return new T();

  return Table.Single(GetDynamicGet(id.Value));
}

When a null ID is passed to the Get() method, it simply returns a new instance of the entity (in this case, the Blog entity). If the id parameter does have a value, on the other hand, the entity with a matching ID is retrieved from the database with the help of the GetDynamicGet() method.

Therefore, if you request the page in Listing 34.2 without passing an id parameter in the query string, a form that represents a new Blog entry is displayed. Otherwise, if you do pass an id parameter, a form for editing the existing Blog entity is displayed.

Handling Form Validation

The sample application discussed in this chapter does not use any of the standard ASP.NET validation controls. Validation is handled at the entity level. In other words, validation is performed in the business logic layer, where validation should be performed, instead of the user interface layer.

The EntityBase class includes an abstract (MustInherit) method named Validate(). Each of the entities implements this abstract method. All the validation logic is contained in the entity’s Validation() method.

For example, Listing 34.3 contains the Validation() method used by the Blog entity.

Example 34.3. Blog.cs (Partial)

public partial class Blog : EntityBase<Blog>
{
    /// <summary>
    /// Where all validation happens
    /// </summary>
    protected override void Validate()
    {
        // Required fields
        if (!ValidationUtility.SatisfiesRequired(Title))
            ValidationErrors.Add("Title", "Required");
        if (!ValidationUtility.SatisfiesRequired(IntroductionText))
            ValidationErrors.Add("IntroductionText", "Required");
        if (!ValidationUtility.SatisfiesRequired(Post))
            ValidationErrors.Add("Post", "Required");
    }
}

The Validate() method takes advantage of the ValidationUtility to check for several required fields. If any of the validation checks fails, an error message is added to the entity’s ValidationErrors collection.

Note

We discuss the ValidationUtility in Chapter 18. The ValidationUtility contains additional methods for validating property values against regular expressions stored in the web configuration file.

If you look closely at the EditItemTemplate contained in the FormView in Listing 34.2, you’ll notice that EntityCallOutValidator controls are associated with each TextBox. For example, the following EntityCallOutValidator control is associated with the txtTitle TextBox:

        <super:EntityCallOutValidator
            id="valTitle"
            PropertyName="Title"
            Runat="server" />

When the ValidationUtility.ShowValidationErrors() method is called in the frmBlog_ItemUpdated() event handler, any validation error that matches the value of an EntityCallOutValidator control’s PropertyName property is displayed.

The advantage of placing your validation logic in your business logic layer is that your validation logic is applied automatically wherever you use the entity. For example, if the Blog entity is used in multiple pages (or even multiple applications), you don’t need to rewrite the very same validation logic.

Note

The traditional advantage of placing your validation logic in the user interface layer is responsiveness. You don’t need to perform a postback to view validation error messages. However, Ajax is blurring this traditional divide between server and client. All the forms used in the sample application use the UpdatePanel control in order to make the forms more responsive.

Taking Advantage of Ajax

The sample application discussed in this chapter takes advantage of the Microsoft server-side AJAX controls. The UpdatePanel control is used with almost all the forms for inserting and editing data. The sample application also takes advantage of the ASP.NET AJAX Control Toolkit. Two controls from the Toolkit, the AutoCompleteExtender and the Rating control, are used to create a more interactive experience.

Using the UpdatePanel Control

Almost all the FormView controls used in the sample application are wrapped in an UpdatePanel control. When you submit a form, a disruptive postback is not performed. Instead, a sneaky postback is performed in the background by the UpdatePanel control.

The overall user experience is improved by the UpdatePanel control. For example, the UpdatePanel control creates the illusion that the validation error messages are being generated on the client when, in fact, the validation error messages are being generated by the server.

Note

We discuss the UpdatePanel control in detail in Chapter 31, “Using Server-Side ASP.NET AJAX.”

However, using the UpdatePanel control made debugging the sample application more difficult. The UpdatePanel control prevents normal error messages from being displayed in the browser. Furthermore, because an UpdatePanel control times out, stepping through code with the Visual Web Developer debugger in a page that contains an UpdatePanel is difficult.

My recommendation is that you don’t add UpdatePanel controls to a web application until after it is fully debugged.

Using the ASP.NET AJAX Control Toolkit

The sample application uses two controls from the ASP.NET AJAX Control Toolkit: the AutoCompleteExtender control and the Rating control.

Note

The ASP.NET AJAX Control Toolkit is discussed in Chapter 32, “Using the ASP.NET AJAX Control Toolkit.”

The AutoCompleteExtender control can be used to extend a TextBox control so that suggestions appear while you type (like in Google Suggest). The suggestions are retrieved from a web method. The web method can be defined in the page that contains the AutoCompleteExtender control, or the web method can be defined in a separate web service.

The AutoCompleteExtender control is used in multiple pages within the sample application. For example, it is used both in the page for editing blog tags (see Figure 34.11) and in the page for editing code sample tags. Listing 34.4 is extracted from the page for editing blog tags.

Receiving auto-complete suggestions as you type.

Figure 34.11. Receiving auto-complete suggestions as you type.

Example 34.4. AdminBlogTagsEdit.aspx (Partial)

    <asp:TextBox
        id="txtTag"
        AutoComplete="Off"
        Text='<%# Bind("Name") %>'
        Runat="server" />
    <ajaxToolkit:AutoCompleteExtender
        ID="AutoCompleteExtender1"
        ServiceMethod="GetSuggestions"
        TargetControlID="txtTag"
        MinimumPrefixLength="1"
        Runat="server" />

Notice that the TextBox control in Listing 34.4 includes an AutoComplete="Off" attribute. This attribute disables the built-in browser auto-complete (for Internet Explorer and Firefox) so that it does not interfere with the Ajax auto-complete.

In Listing 34.4, the AutoCompleteExtender is associated with the TextBox control through its TargetControlID property. The MinimumPrefixLength property configures the control to start displaying suggestions as soon as you type at least one character into the TextBox control. Finally, the AutoCompleteExtender control is set up to retrieve its suggestions from a web method named GetSuggestions(). This method is declared in the same page as the AutoCompleteExtender control. The code for the GetSuggestions() method is contained in Listing 34.5.

Example 34.5. AdminBlogTagsEdit.aspx (Partial)

    [System.Web.Services.WebMethod]
    public static string[] GetSuggestions(string prefixText, int count)
    {
        return BlogTag.GetSuggestions(prefixText, count);
    }

When a web method is declared in a page, it must be declared as a static method. Furthermore, it must be decorated with the WebMethod attribute.

The GetSuggestions() method in Listing 34.5 calls the GetSuggestions() method of the BlogTag entity to get existing blog tags that match the prefix from the database. The BlogTag.GetSuggestions() method is contained in Listing 34.6.

Example 34.6. BlogTag.cs (Partial)

public partial class BlogTag : EntityBase<BlogTag>
{
    public static string[] GetSuggestions(string prefixText, int count)
    {
        return Table.Where( t => t.Name.StartsWith(prefixText) )
          .Select(t => t.Name).Distinct().Take(count).ToArray();
    }
}

The other control from the ASP.NET AJAX Control Toolkit used in the sample application is the Rating control. This control is used to enable users to rate the quality of a code sample (see Figure 34.12).

Rating a code sample.

Figure 34.12. Rating a code sample.

The Rating control is declared with the following attributes in the CodeSamplesEntry.aspx page:

<ajaxToolkit:Rating
     ID="Rating1"
     BehaviorID="RatingBehavior1"
     CurrentRating="2"
     MaxRating="5"
     StarCssClass="ratingStar"
     WaitingStarCssClass="savedRatingStar"
     FilledStarCssClass="filledRatingStar"
     EmptyStarCssClass="emptyRatingStar"
     runat="server"
     style="float:left"
     Tag='<%# Eval("Id") %>'
     OnChanged="Rating1_Changed" />

When a user clicks the Rating control and selects a rating, the Rating control raises its Changed event. The Changed event is handled by the following event handler:

    protected void Rating1_Changed(object sender, RatingEventArgs e)
    {
        EntryRating.Insert( new EntryRating(){EntryId=Int32.Parse(e.Tag),
          Rating = Int32.Parse(e.Value)} );
    }

This event handler inserts a new EntryRating entity into the database that represents the user rating. The second parameter passed to this method is an instance of the RatingEventArgs class. Two properties of this class are used when inserting the new record: Value and Tag. The Value property represents the rating the user selected. The Tag property can represent any information you want to associate with the rating. When the Rating control is declared, the code sample Entry.Id is assigned to the tag.

Note

The Rating control performs a normal server postback when you select a particular rating.

Using the VirtualPathProvider Class

The VirtualPathProvider class was introduced into the ASP.NET 2.0 Framework. Not many people know about this class. The VirtualPathProvider class can be used to abstract the location of an ASP.NET file away from the file system.

I want visitors to the code sample website to be able to try the code samples “live.” Someone browsing the code samples should be able to click a Try It link and execute a code sample (see Figure 34.13).

Trying a code sample “live.”

Figure 34.13. Trying a code sample “live.”

In the sample application, code samples are stored in a database table. In order to get the Try It functionality to work, the application must be able to execute code samples directly from the database. This is exactly what the VirtualPathProvider class enables you to do. The code sample application takes advantage of the VirtualPathProvider class to execute ASP.NET pages directly from a database table.

The VirtualPathProvider class hijacks a particular file system path. Any path that starts with a directory named /virtual is passed to VirtualPathProvider. For example, when you click the Try It link next to a code sample, a request for a page at the following location is sent to the server:

/virtual/codesample/166/ShowAjaxValidator.aspx

Because this path starts with a directory named /virtual, the request gets routed to the VirtualPathProvider class, which grabs the file from the TryItCode column in the CodeSample database table. The file gets dynamically compiled by the ASP.NET Framework and served as a normal ASP.NET page.

Note

The VirtualPathProvider class is discussed in detail in Chapter 21, “Advanced Navigation.”

Summary

The goal of this chapter was to demonstrate how you can take advantage of several of the new features of the ASP.NET 3.5 Framework when building a web application. In this chapter, you were presented with an overview of a complex application written with ASP.NET 3.5. You learned about the code sample application.

In the first part of this chapter, you learned how data access and form validation are implemented in the sample application. My hope is that studying this application will convince you to stop writing SQL queries and start writing LINQ queries.

In the second part of this chapter, you learned how the sample application takes advantage of the new server-side Ajax features introduced into the ASP.NET 3.5 Framework. You learned how the sample application uses the UpdatePanel control to create more responsive forms. We also discussed two controls from the ASP.NET AJAX Control Toolkit used in the sample application: the AutoCompleteExtender and Rating controls.

Finally, we discussed one of the more obscure and overlooked classes contained in the ASP.NET Framework: the VirtualPathProvider class. You learned how the sample application uses the VirtualPathProvider class to execute ASP.NET pages directly from a database table.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset