ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Learning ASP.NET for the ASP developer - Part 2

by Neel Mehta
10/25/2004

Part 2: Delving Deeper

This week, in Part 2 of our tutorial, we will continue our investigation of ASP.NET from a classic ASP developer's perspective. Last week, In Part 1, we showed how ASP.NET allows us to cleanly separate presentation markup from server side script code. Today, we will delve deeper and demonstrate how an ASP.NET page is put together. Finally, in the concluding article of this three part tutorial, we will build upon the concepts of the first two parts and illustrate how you can build large-scale web sites using ASP.NET.

In the first part of this tutorial, I motivated ASP.NET through tags such as <asp:DataGrid> and </asp:DataGrid> without going into much detail about their structure. This was deliberate, as I wanted to give you an overall appreciation of the concepts without getting into mundane details that take away from the development of the main ideas. You also may have observed some additional code in our examples above which we glossed over earlier. In this part, though, it's time to get formal with the implementation. Later, we will also look at a few more presentation tags to give you a glimpse of the richness of ASP.NET.

Page preliminaries

The first thing to note is that any ASP.NET page that is requested from a server has an .aspx extension. I make the qualification about the page that's requested because, just as in classic ASP, there are certain "include" pages which have different extensions. These pages, however, cannot be accessed directly.

In ASP.NET, as opposed to classic ASP, a web page is represented as a Page class. While this may at first seem an unnecessary complication, we'll soon see that having a Page class actually provides an efficient way to think about the structure of a page because it's the "container" for all the elements which appear on the page. The Page class is globally available on the page. Hence, you don't have to explicitly reference it when accessing its properties and methods.

Page directives set the attributes on this object. Thus, within an ASP.NET page, the very first thing you typically write is the "Page" directive. This directive is where you specify the scripting language and several other options, such as whether you want to see detailed error messages. We will discuss a few of these options. The general format for this directive is given below (text between [ ... ] is optional):

 <%@ Page attribute="value" [attribute="value"...]%> 

A comprehensive list of the Page attributes is available from the book, ASP.NET in a Nutshell book as well as from the Microsoft web site.

Related Reading

ASP.NET Cookbook
The Ultimate ASP.NET Code Sourcebook
By Michael A. Kittel, Geoffrey T. LeBlond

The Language attribute is used to specify the scripting language that will be used. ASP.NET supports several languages, including Microsoft's new language, C# (pronounced C-sharp), which was developed specifically for ASP.NET. In our examples until now, we used Visual Basic (VB), but later we will transition to C#.

The Debug attribute, when set to "True", gives detailed error reports when a bug is encountered. While no one enjoys bugs in their code, you will find these error messages quite informative, often giving you explicit corrections.

The Trace attribute allows you to write statements for debugging without having to remove them from your code when you are done. Recall that in classic ASP, you would use the "Response.Write/Response.End" pair of statements to display the value of variables. The downside about the classic ASP approach is that once you had fixed the bug, you had to go back to your code and remove these statements so that the page could be processed normally. With the Trace feature enabled, you can write the variables to a Trace object which are then displayed in the browser at the bottom of your page. Thus, you don't have to create "artificial" debug statements to test the program flow. The ServerVariables are also shown, which can provide useful information in your debugging efforts. Since the Trace object is only displayed when this feature is enabled, it's easy to turn off by setting the Trace attribute to "false" without impacting the rest of the code. Another advantage of retaining your debug statements is that you can equally easily turn it back on if you need to debug your page again.

Below is an example of how to specify a Page directive with the Trace and Debug attributes:

 <%@ Page Language="vb" Debug="True" Trace="True" %> 

Note that you once you have finished debugging and testing your code, you should set the Trace and Debug attributes to "False" to prevent detailed error messages being shown in production, in case your code encounters a bug.

With the Page directive behind us, we then specify the "script" block similar to classic ASP. Whereas in classic ASP, we typically used the <% ... %> tags to enclose any server side code, in ASP.NET we use <script> ... </script>." To distinguish between the <script> and </script> tags used in any Javascript code on the page, a runat="server" attribute is used to unambiguously specify that the code this tag encloses is evaluated by the server. In fact, the runat="server" attribute is used on any tag that needs to be accessed by the server side script code.

Once you have defined the script tags in classic ASP, you can immediately begin writing your code. In ASP.NET, however, all statements must go within functions. The only statements that are valid outside any function are those that define global variables. You cannot simply start writing code as you did in classic ASP. You can either write your own functions and then call them in your code or you can use functions that are triggered on certain events. For example, the "Page_Load" function is invoked when all the elements on the page have been "loaded." The "Page_Init" function, on the other hand, is initiated when the page is requested. You typically use one of these functions to get things started. Then, once your code has begun processing, you can write other functions to carry out additional tasks.

Finally, in ASP.NET, all tags have to be well-formed. Thus, you cannot write <asp:DataGrid id="DataGrid1" runat="server"> without properly closing the asp:DataGrid. Instead, you need to close it either with </asp:DataGrid> or <asp:DataGrid ... />.

Thus, the beginning of the code in ASP.NET takes the following initial form:


<%@ Page Language="vb" Debug="True" Trace="True" %>
<script runat="server">

  Sub Page_Load()

  End Sub ' end of Page_Load
</script>
<html>
  <head>
  </head>
  <body>
  </body>
</html>
    

In classic ASP, the six intrinsic classes, Application, ObjectContext, Request, Response, Server, and Session are the top-level classes. In ASP.NET, the Page class is the top level one. The classic ASP intrinsic objects have been replaced by the HttpApplicationState, HttpContext, HttpRequest, HttpResponse, HttpServerUtility, and the HttpSessionState classes, respectively. In addition, a few more have been also added.

Thus, for example, the classic ASP Request class is now the HttpRequest class in ASP.NET. Fortunately, though, the HttpRequest class is exposed as the Request property of the Page class. As a result, your classic ASP code for the Request class will be unchanged when migrated to ASP.NET. The same is true for the HttpResponse class as well as the HttpSessionState class.

In addition to the Page class, ASP.NET has numerous classes which you can use in your code. These can save you a lot of work. There are classes to access the file system, dynamically draw images, access databases, screen scrape other pages, and several other tasks which are quite cumbersome to code in classic ASP. We'll talk about how to access other classes in your code. However, the bigger question is how do you know which classes you need to use in the first place. We'll address this question later in the third part of the tutorial, where we outline a tool for ASP.NET development.

To access the members of a particular class, you use the fully qualified name for that class. Unlike classic ASP, there is no need to use a Server.CreateObject construct in your code. You can simply use the corresponding class directly in your code. So, for example, if you want to access your web server's file system folders, you would use the System.IO class in your code just as simply as using, say, session variables. There is no need to first create a File Scripting object, as in classic ASP, nor the need to register any external components, as is also often used in classic ASP. In ASP.NET, you can use these classes just like you would the intrinsic objects in classic ASP.

As I mentioned earlier, because, for example, the HttpRequest class is exposed as the Request property of the Page class, you do not need to explicitly refer to the Page class in your code and can simply use statements like stringvar = Request("myvar") in your code. But, when you use other classes, such as the System.IO class, you have to use the fully qualified name to access their properties and methods. So, if you want to get all the files in a folder, you can use the following code, which will return an array of the files: System.IO.Directory.GetFiles(Server.MapPath("DIRECTORY_PATH")).

If you are doing a lot of file system lookups, you will have to qualify the method or propery with the mind-numbing System.IO prefix every time. Fortunately, ASP.NET provides a great "shortcut," which is implemented using Namespaces. That is, you can instruct ASP.NET to bring in or import external class assemblies into your code. Importing namespaces allows you to reference the properties and methods of the imported classes just like you do for the Page class. In other words, you can drop the System.IO and instead use just Directory.GetFiles(Server.MapPath("DIRECTORY_PATH")). This makes your code a lot more readable, and thus, easier to maintain.

You specify the namespaces you want imported after you specify the Page directive and before the <script> ... </script> tags. (To import multiple namespaces, use a separate <%@ import ... %> directive for each namespace.)


<%@ Page Language="vb" Debug="True" Trace="True" %>
<%@ Import Namespace="System.IO" %>
<script runat="server">
 Sub Page_Load()

 End Sub ' end of Page_Load
</script>
    

Connecting to a SQL server database

In this section, we outline, at a high level, how to connect to a SQL Server database and extract records. You can also, of course, connect to other databases. However, ASP.NET has optimized the connection to SQL Server. Detailed description of connecting to the database, or connecting to another database, is beyond the scope of this article.

Below is the code snippet we use to connect to a SQL Server database:


' SET UP THE SQL SERVER DATABASE CONNECTION
string ConnStr = "server='(local)'; trusted_connection=true; database='Demo'";
DataSet RecordSet = new DataSet(); 
     ' NEED TO IMPORT THE System.Data NAMESPACE TO USE THE DataSet TYPE 
     ' NOW PULL THE RECORDS FROM THE DATABASE
string SQL = "Select * from Books";
SqlDataAdapter RecordSetAdpt = new SqlDataAdapter(SQL,ConnStr);
RecordSetAdpt.Fill(RecordSet);  ' EXTRACTED RECORDS ARE IN RecordSet
    

In order for us to use the various SQL Server methods above, we have to first import the System.Data.SqlClient namespace. The records extracted are available in the RecordSet variable. Each extracted record is in a row in the RecordSet. If we have a DataGrid defined in the presentation block, we can simply assign the DataSource of the DataGrid to this RecordSet:

DataGrid1.DataSource = RecordSet.Tables(0).DefaultView 

As we saw in the first part of this tutorial, there is no need to individually go through each row of the record set and format the information in each record as in classic ASP. In ASP.NET, the formatting of the data in each field is taken care in the DataGrid's attributes in the presentation block. Finally, note that since we are using the DataSet class to define the RecordSet, we need to import the System.Data namespace.

Thus, the complete code listing to display a table of books is:

<%@ Page Language="vb" Debug="True" Trace="True" %>
<%@ import Namespace="System.Data" %>
<%@ import Namespace="System.Data.SqlClient" %>
<script runat="server">
  'System.Web.UI.Controls namespace for the DataGrid control
  'System.Data namespace for the Dataset class
  'System.Data.SqlClient namespace for SQL server classes
  Sub Page_Load()
 
  'Set up the SQL Server database connection
  Dim ConnStr As String = "server='(local)'; trusted_connection=true; database='Demo'"
  Dim RecordSet As DataSet
  RecordSet = new DataSet()
  'Now, pull the records from the database
  Dim SQL As String = "Select * from Books"
  Dim RecordSetAdpt As new SqlDataAdapter(SQL, ConnStr)
  RecordSetAdpt.Fill(RecordSet)
  
  
  'Set the data grid's source of data to this recordset
  DataGrid1.DataSource = RecordSet.Tables(0).DefaultView
  'Finally, bind this data to the control
  DataGrid1.DataBind()
  
End Sub ' end of Page_Load
</script>

  <html>
    <head>
    </head>
    <body>
       <form runat="server">
         <p>
            <asp:DataGrid id="DataGrid1" runat="server"></asp:DataGrid>
         </p>
       </form>
    </body>
  </html>

Other presentation tags

Obviously, not all content on a web page will fit neatly into tables. ASP.NET provides the ability to display all kinds of rich content on a page through assorted presentation tags. The basic concept, however, is similar to the way we associated records from a database table to the DataGrid tag. In fact, the concept is not unlike DHTML, except that whereas DHTML takes place on the client side, dynamically associating content with ASP.NET presentation tags is entirely on the server side.

The presentation tags are essentially of two types: tags that have a direct counterpart in HTML and those that don't. These presentation tags have the same structure as regular HTML, except that every tag must be properly closed. In ASP.NET, these tags are referred to as "controls." Thus, the tags associated with HTML elements are called HTML Controls, while the others are Web Controls. All tags have a runat attribute set to server, runat="server", to indicate that these tags can be programmatically accessed on the server. Also, all web controls need to be within <form runat="server"> ... </form> tags. (The name "form" seems at first to be a misnomer, but in the next part we'll shed some light as to why it's named as such.)

There are several web controls which you can use to present the content on your web page. You will have to make a determination as to which control best matches the content you want to present. In our example, the data grid was sufficient to present the book information in a tabular format. Each control, in turn, has attributes to further refine the HTML output produced. The list of controls and attributes are beyond the scope of this article. The list need not, however, be daunting. Microsoft has produced a development environment, Web Matrix, for building ASP.NET applications, complete with its own web server and SQL Server database (both can only be used for testing and not for production). Web Matrix enumerates the controls available together with their properties. In addition, it also includes some documentation to guide you when you are inserting controls on your page. More information on WebMatrix can be found at http://www.asp.net/webmatrix/default.aspx?tabIndex=4&tabId=46

We have already seen the power of the <asp:DataGrid> control. In this section, we'll just touch on two of the controls to give you a feel as to how to use them. One of the simplest types of web controls is the Label control. This control is similar to the HTML <span>. It lets you "reserve" an area on the web page in which you can then assign HTML content in the script block. The Text attribute of the Label control sets the content that gets inserted into the control. So, here's an example of how to use a Label control in an ASP.NET page:


<%@ Page Language="VB" %>
<script runat="server">
  Sub Page_Load()
    Label1.Text = "This <b>text</b> gets placed on the control"
  End Sub
</script>

  <html>
  	<head>
  	</head>
  	<body>
  		<form runat="server">
			 <asp:Label id="Label1" runat="server">Label</asp:Label>
  		</form>
  	</body>
  </html>
    

When this page is requested, the content within the <asp:Label> ... </asp:Label> tags in the presentation block will be replaced by the content that is set using the label's Text property in the server-side script block before being delivered. (Server-side script is always evaluated first.) Thus, in this case, the content within the Label control in the presentation block plays absolutely no part and could have been omitted altogether. In JavaScript and DHTML, by contrast, the content in the presentation section will be streamed to the browser but based on some action on client side, such as when an element is clicked or when the page loads, JavaScripts kicks-in and changes the text.

Note that since we are explicitly assigning content to the Text property, there is no need for data binding.

Another example of a control to illustrate the power of ASP.NET is in the validation of HTML form input fields. The following code listing shows how an input field can be restricted to accept only integer values between 10 and 20.


<%@ Page Language="VB" %>
<script runat="server">
	Sub Submit_Click(Sender As Object, e As EventArgs)
		' Code to execute when the user has submitted form
  	End Sub

</script>
  <html>
    <head>
    </head>
    <body>
    <form runat="server">
      <p>
         Enter value between 10 and 20 
      </p>
      <p>
      <asp:TextBox id="TextBox1" runat="server"></asp:TextBox>
      <asp:RangeValidator id="RangeValidator1" runat="server" 
	ErrorMessage="Entry out of range"
	ControlToValidate="TextBox1" 
	Type="Integer" 
	MaximumValue="20"
	MinimumValue="10">
      </asp:RangeValidator>
      </p>
      <p>
      <asp:Button id="Button1" onclick="Submit_Click" 
		runat="server" 
		Text="
		Submit">
      </asp:Button>
      </p>
    </form>
  </body>
</html>
 
    

The key steps are to specify a runat="server" attribute on the HTML input form field so that it can be accessed using its ID in the script block and also to specify the ControlToValidate on the RangeValidator control. The valid range, as well as other presentation information, such as the error message if an entry is invalid, are also set as attributes. The <asp:RangeValidator> ... </asp:RangeValidator> will cause an implicit server side script evaluation.

There is no explicit JavaScript you have to write in your code in order for the range validation. When this page is requested, the server will automatically stream the appropriate HTML together with the necessary JavaScript for the validation.

You may have noticed a new control, asp:Button. The asp:Button is a web control which is similar to the standard Submit type of an HTML input field, <input type="submit" value="Submit">. The primary difference is that the asp:Button's onClick attribute will invoke a function (in this case, the Submit_Click(...) function) on the server side, whereas the onClick attribute on the standard <input ... > HTML tag will invoke a client-side JavaScript function. The Submit_Click(...) function is where you would write code to process the form inputs, post to a database, etc. (This code is for illustration purposes only. Hence, we have left this function empty.)

The above code to validate an input field gives the following familiar form when viewed in a browser:

Input Field Example

If you view the source of this page on the browser, you will see a lot of JavaScript in spite of the fact that you did not explicitly write any JavaScript for field validation. This code is automatically inserted by ASP.NET based on your attribute settings.

There are more advanced ways to validate HTML form input fields. For example, the <asp:RegularExpressionValidator>... </asp:RegularExpressionValidator> allows you to base form validation on regular expressions, thereby giving complete control on the types of entries you want to limit your site's users to. You use this validator in essentially the same way as the range validator.

Another control that you may also find useful is the control used to automatically create a list of checkbox options. In these cases, you typically have a set of selections which you want to show as a list of checkbox options. In classic ASP, you would have to write a loop to go through the selection set and write the corresponding HTML. In ASP.NET, like the DataGrid example in the first part of this tutorial, you can simply assign the selection options to the "DataSource" of the <asp:CheckBoxList> ... </asp:CheckBoxList> control. And, once every control's data source has been set, use the DataBind( ) method to actually populate the controls with the appropriate data.

Again, you will undoubtedly observe that there are several control and attributes to precisely define how content can be rendered, and you must be wondering if there is a lot of memorization to use ASP.NET effectively. In fact, the above code was created by "drag and drop" using Microsoft's new ASP.NET development environment, Web Matrix. As a result, you do not have to memorize the various attributes. Rather, it's more important to know how ASP.NET is structured so that you can use Web Matrix effectively.

Summary

In this second part of the tutorial, our main thrust was to review how an ASP.NET page is structured. Understanding the structure allowed us to write a complete ASP.NET web page. We also saw how to connect to a SQL Server database. Finally, we reviewed a few other controls to display rich content. Specifically, we saw that by using some prebuilt controls, we could eliminate writing any JavaScript for validating HTML form input fields. In the next and concluding part of this tutorial, we will build upon the concepts discussed in the first two parts and show how to build large scale web sites. Additionally, we will also demonstrate how a cumbersome task in classic ASP becomes relatively straightforward in .NET.

Related links and references

  1. Page Directive: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconPage.asp
  2. Web Matrix: http://www.asp.net/webmatrix/default.aspx?tabIndex=4&tabId=46
  3. ASP.Net in a Nutshell,  G.Andrew Duthie, Matthew MacDonald, http://www.oreilly.com/catalog/aspdotnetnut/index.html, O'Reilly.com, June 2002
  4. ASP.NET Cookbook, Michael A. Kittel. Geoffrey T. LeBlond, http://www.oreilly.com/catalog/aspnetckbk/index.html, O'Reilly.com, August 2004

Neel Mehta has a doctorate from the University of Pennsylvania and is an ardent fan of .NET and sees it as one of the few technologies that will be around for some time.


Return to ONDotnet.com.

Copyright © 2009 O'Reilly Media, Inc.