During the past several years, Microsoft's Active Server Pages (ASP) Web-development technology has become one of the most-used tools for developing dynamic, data-driven Web applications. Now, Microsoft has introduced ASP.NET, a successor to ASP that dramatically improves on ASP's performance, reliability, and speed of development. Just how different is the new ASP.NET Web-development technology from the ASP that developers know and (mostly) love? In terms of migrating existing pages, ASP.NET isn't all that different, and much of your current ASP code will run fine under ASP.NET. You can also run ASP pages side by side with ASP.NET applications (although ASP and ASP.NET applications don't share application or session states), so you don't have to migrate all at once. But at its heart, ASP.NET is as different from ASP as a Hemi 'Cuda is from a VW Beetle. Both of them can get you where you need to go, but one is vastly more powerful than the other and gives you more options for customization. Part of that power comes from ASP.NET's tight integration with Microsoft's next-generation data-access technology, ADO.NET, which gives you robust and high-performance data binding.
What else makes ASP.NET so much more powerful than ASP? For starters, it's built on Microsoft's new .NET platform, a set of technologies that provides a modernized, language-independent replacement for the Win32 APIs. Some characteristics of ASP.NET are
- use of fully compiled, instead of interpreted, languages
- simplified programming model similar to that of Visual Basic (VB)
- a wealth of built-in functionality in the .NET Framework Class Library
- improved performance and scalability, manageability, code separation, customization, and reuse
- new, more powerful database and XML programming model
Let's look at these new features and improvements and explore how you can take advantage of them in your ASP.NET applications. Also, see the sidebar "Namespaces and Assemblies," page 38, for definitions of some terminology that's new to ASP.NET.
Fully Compiled Languages and the CLR
One of the most important differences between ASP and ASP.NET is their underlying technology. ASP is implemented as an Internet Server API (ISAPI) DLL (asp.dll) that intercepts and processes requests for .asp files. The DLL parses through the requested file and interprets any ASP code it finds, then returns the combined result of any static HTML the file contains and any output from executing the interpreted code. ASP developers who want to control the output their pages send to the browser are largely limited to the methods and properties of ASP's Response object.
ASP.NET is also implemented as an ISAPI DLL (aspnet_isapi.dll). This DLL intercepts requests to Microsoft IIS for ASP.NET pages (these pages use the .aspx extension) and passes them to the ASP.NET worker process, which fulfills the request.
Underlying the new technologies of ASP.NET is the .NET Common Language Runtime (CLR), which works with all .NET languages. With ASP.NET, you don't have to write and compile VB code that requires a VB runtime or write Visual C++ (VC++) code that requires the Microsoft Foundation Classes (MFC) runtime. All your application's executable code, whether you write it in VB.NET, C#, JScript.NET, Managed C++, or another .NET language, is ultimately executed by a single runtime, the CLR. All the language compilers in the .NET world compile your code into language-neutral intermediate language (IL). The CLR then executes the IL, just-in-time (JIT)—compiling the IL code into processor-specific binary code. One distinct advantage of this model is that it lets you create, in any .NET language, classes that any other .NET language's classes can inherit. Another advantage is that this model enables cross-language debugging.
Simplified Programming Model
ASP.NET also introduces a new Web-programming model that's based on the highly successful—and highly productive—VB model. In this new model, instead of working with static HTML code and scripts within a page, you can use ASP.NET's new Server Controls. Server Controls are tag-based controls, similar to VB controls (e.g., TextBox, Label), that provide simple programmability and built-in support for managing their own state. Server Controls provide programmability on the server while rendering cross-browser—compatible HTML to the client, so any HTML 3.2—compatible browser can use these controls (note that some controls can also optionally render Dynamic HTML—DHTML—content for uplevel browsers).
In the Visual Studio.NET (VS.NET) environment, you can place controls visually on the page in much the same way as you can in VB. Then, you can write server-side code to handle the events these controls expose. Listing 1, shows an ASP.NET page that creates Label, TextBox, and Button Server Controls. Note that you must enclose Server Controls within a set of <form> tags with the runat="server" attribute if you want to take full advantage of ASP.NET features such as automatic postback handling and automatic management of control state.
You implement Server Controls just as you do other HTML tags, except that you preface them with the asp: namespace indicator. Two important attributes that Server Controls use are the id attribute, which specifies a name by which your server-side code can refer to the control, and the runat attribute, which is required for all Server Controls (and is always set to the literal server). Figure 1 shows the output of Listing 1.
ASP.NET also exposes server-side events that developers can write event handlers for, including Page_Load and Page_Unload. ASP.NET automates the process of handling postbacks—pages that post to themselves—so one page can both display input forms and process the form input through code you write. ASP.NET pages expose an IsPostBack property that lets you check whether the current request is the result of a postback. Listing 2 expands on the code in Listing 1, adding a handler for the Page_Load event that responds to a postback.
VB developers will probably notice just how VB-like ASP.NET code is. In fact, you could write an event handler for the Click event (analogous to the OnClick event of a VB CommandButton) of the button just like you would in VB, instead of running your code in the Page_Load event handler, because the Button Server Control automatically submits the page. Figure 2 shows the output of Listing 2 after I entered my name in the TextBox and clicked Submit.
ASP's built-in functionality is limited to a small set of server-side COM components that developers can use for a variety of utility functions. These components include the Ad Rotator (which randomly cycles through a specified set of banner ads), the Browser Capabilities component, the FileSystemObject object, and ADO. These components are useful, but until ASP.NET, Web developers who needed access to additional functionality or the Win32 APIs have been forced to purchase third-party components or create their own COM wrappers.
ASP.NET Web applications can take full advantage of the .NET platform, including all the functionality that the .NET Class Library provides. The .NET Class Library is a set of classes in the System and Microsoft namespaces that provides all platform functionality for the .NET platform. The classes are divided into hierarchical namespaces based on function, so locating the class you need is fairly easy. (For example, the Page class that provides the functionality behind each ASP.NET page resides in the System.Web.UI namespace.)
Some of the functionality available from the .NET Class Library includes the ability to
- send SMTP email through the SMTPMail class (in the System.Web.Mail namespace)
- create custom images, using the Bitmap class (in System Drawing)
- access files by using the classes in the System.IO namespace, including the File, Directory, and TextReader classes
- perform advanced content caching, using the Cache class (in System.Web.Cache; the Page class exposes it as the Cache property)
Listing 3 shows the code that uses the SMTPMail class to let you send email. This code assumes that you've installed and properly configured the Microsoft SMTP service on the Web server. It also assumes that you've installed the GrocerToGo sample MSDE database that's available with the .NET Framework software development kit (SDK) Samples on the Web server and added a few entries to the Customers table.
The code in Listing 3 uses the @ import directive to import the System.Web.Mail, System.Data, and System.Data.SqlClient namespaces. This directive lets you refer to the members of these namespaces—including the MailMessage, SMTPMail, SqlConnection, SqlCommand, and SqlDataReader classes—simply by class name. You need one @ import statement for each namespace you're importing into your page. The code uses a series of ASP.NET Server Controls to collect user input, then processes that input in the Page_Load event when the client posts the page back, including retrieving email addresses from the Customers table of the GrocerToGo sample database if the Send To Mail List? checkbox is selected. The C# code in Page_Load creates an instance of the MailMessage class called myMail, sets the necessary properties (you need to substitute a valid email address for the From property), adds the list of email addresses from the database if necessary, then uses the SMTPMail.Send method to send the mail. (Note that if you're using a server other than the local server, you can use the SMTPServer property of the SMTPMail class to specify which server to send the mail from.) Figure 3 and Figure 4, show the output of the web.config ASP.NET configuration file in Listing 4, before and after a user sends mail.
ASP.NET also features dramatic improvements in performance and scalability. Performance improvements come both from the fact that the languages used are compiled, rather than interpreted, and from Microsoft's rewrite of the ASP execution engine. ASP.NET improves scalability by supporting two new session-state modes (through a new ASP.NET State NT Service or through SQL Server). Both modes let you share session state across multiple servers within a Web farm, breaking the limitation that the ASP session state imposed. Using the ASP.NET State service, you can assign a single server to manage session state for an entire farm of ASP.NET servers. Using the ASPState SQL Server database (which you can set up with the InstallSqlState.sql batch in the .NET Framework installation directory), you can share session-state information across a Web farm. You can further improve the reliability of session state by using clustering for the SQL Server that's managing session state.
ASP.NET also provides substantial manageability improvements through a new configuration system. Management of ASP.NET configuration is provided through hierarchical XML-based files. Each server has a master configuration file, machine.config, which contains the default settings for all .NET applications on that machine. Each Web application can have one or more files called web.config (each in its own folder) that can override the settings of the machine.config file (or any web.config files in a parent folder). In this way, you can easily set machinewide and application-wide configuration settings, such as session-state configuration, while retaining granular control over such settings as authentication and authorization, error handling, and page attribute defaults. Listing 4 shows a typical web.config file.
A major drawback of ASP is that ASP applications tend to become a mess of spaghetti code and include files, making maintenance and debugging a nightmare. ASP.NET has some substantial improvements that help you avoid this problem. Although you can write spaghetti code with ASP.NET, these improvements mean that you don't have to.
ASP developers need to be aware of one change that will break some existing ASP pages you migrate: ASP.NET doesn't let you put functions inside the <% %> render blocks. You need to place all server-side functions within a <script> block with the runat="server" attribute. Listing 5, shows both the ASP syntax and the required syntax for ASP.NET.
The elimination of functions in render blocks is helpful because it encourages developers to separate executable code from HTML presentation. But the most important method in ASP.NET for separating code from HTML is a new feature called codebehind. Codebehind lets you move your programmatic logic from the original .aspx files, where it would be subject to spaghettification and mangling by graphic designers who don't understand the importance of the <% %> tags, into separate class files. In ASP.NET, you use the src or the codebehind attribute of the @ Page directive to tell ASP.NET how to locate a codebehind file. Src tells ASP.NET to find the source file and compile it dynamically. Codebehind (the default in VS.NET) requires you to precompile your codebehind class into an assembly stored in your application's bin subfolder (the default location for assemblies in a Web application). The inherits attribute tells ASP.NET that the current page should inherit from the codebehind class file, which lets ASP.NET compile the page and codebehind through the CLR into one assembly. Listing 6, shows how the page in Listing 2 would look if you used codebehind. Listing 7, shows the codebehind file.
Judicious use of codebehind can help you create applications that are easier to understand at a glance, easier to maintain, and easier to debug. However, using this feature to its best advantage requires discipline and some up-front design work.
One area in which Web developers have long lagged behind their VB and Java counterparts is in overall productivity, partly because ASP lacks the easy extensibility that VB gains through controls and that Java and C++ gain through inheritance. ASP.NET remedies this deficiency.
ASP.NET lets you extend existing Server Controls by having your custom control class inherit from the class the control is based on, then override its functionality or extend it with additional properties and methods. You place custom Server Controls in an ASP.NET page by using tags, just as with the built-in Server Controls, but you must register the custom Server Controls by using the @ Register directive in your ASP.NET page. Listing 8 shows the @ Register directive's syntax for a custom Server Control. Listing 9 shows the syntax for implementing the Server Control registered in Listing 8. The tag name myTag in Listing 9 is derived from the myTag class in the myNS namespace.
A developer who wants to quickly make a snippet of HTML, code, or Server Controls available for reuse without the overhead of creating and compiling a new class can take advantage of ASP.NET User Controls. User Controls are simply files with the .ascx extension that you place within an ASP.NET page by using a tag, then register just as you do with custom Server Controls. As Listing 10 shows, the @ Register directive for User Controls is a little different from the directive for Server Controls. The code for implementing this User Control is the same as the code in Listing 9.
What's in It for Database Developers?
By now, you're probably asking, What can ASP.NET do about data access? ASP.NET covers data access pretty well, too. Where ASP has ADO—a set of COM components that provides a simple programming model for OLE DB data sources—ASP.NET has ADO.NET, which lets developers access databases (and other data sources). You access the data sources either directly through managed providers (the .NET equivalent of OLE DB providers) or indirectly through the OLE DB managed provider if no native managed provider is available for the desired data source. ADO.NET includes a set of classes that enable data access, let you manipulate data, and give you the ability to read data from (and write data to) XML files or streams. I cover ADO.NET at a fairly high level; for more information, see Dino Esposito, "ADO.NET: A Bridge to the Future," June 2001.
ADO.NET consists of the classes contained in the System.Data, System.Data.Common, System.Data.OleDb, System.Data.SqlClient, and System.Data.SqlTypes namespaces. Although ADO.NET is ADO's successor in terms of data access, they have some significant differences. Let's look at the key differences between the two.
Datasets. The most commonly used ADO object, Recordset, has an implicit or explicit connection to a specific database unless you explicitly disconnect. As a result, using ADO directly in ASP pages can result in poor scalability. ADO.NET provides an abstraction layer between a data source and queried data in the form of the DataSet object. A DataSet contains and provides for the manipulation of data but is in no way tied to the data source the data was drawn from. You can think of a DataSet as an in-memory cache of data, including the tables, columns, relationships, and constraints for the queried data.
Databinding. An important benefit of ADO.NET's separation of the DataSet object from the underlying data source is that this separation lets you perform databinding (binding a dataset to one or more ASP.NET Server Controls to automate the display and updating of data) without hurting scalability. Your business tier can simply pass a DataSet back to your ASP.NET page, which can then bind that DataSet to an ASP.NET DataGrid or other Server Control that supports databinding. Listing 11, shows databinding to a DataGrid.
DataReaders. In addition to the DataSet object, ADO.NET has the DataReader object, which behaves like a forward-only/read-only cursor. When you simply need to return a list of data and iterate over that data once (for example, to display the data in an ASP.NET page), the DataReader provides a faster, lighter-weight method than using a DataSet. Unlike the DataSet, DataReaders are specific to a particular data source. When you create a DataReader for SQL Server, use the SqlDataReader class, which is in the System.Data.SqlClient namespace. Listing 4 showed the use of a SqlDataReader, which I used to retrieve email addresses for a mail application.
Managed Providers. In ADO, OLE DB providers perform data access. These providers can be either native providers for a specific data source or the generic OLE DB provider for ODBC in conjunction with the appropriate ODBC driver for the DataSource object. ADO.NET works similarly in that data access occurs through managed providers, which are the .NET equivalent of OLE DB providers. ADO.NET ships with two built-in providers. The SQL Server managed provider's classes reside in the System.Data.SqlClient namespace; the OLE DB managed provider's classes are in the System.Data.OleDb namespace. When you're using the OLE DB managed provider, you must also use the appropriate OLE DB provider for the desired data source. (Note that using the OLE DB provider for ODBC will probably result in suboptimal performance, given the number of layers involved in each request.)
In both the SQL Server and OLE DB managed providers, the key to abstracting the DataSet object from the underlying data source is the DataAdapter. You can use DataAdapters, which are specific to a given data source, to pull data from a database into a DataSet or to update a database with modified data from a DataSet.
An important difference between the SQL Server managed provider and the OLE DB managed provider is that the SQL Server managed provider communicates directly with SQL Server, using its Tabular Data Stream (TDS) native wire protocol, rather than through OLE DB or ODBC. This direct communication results in improved performance, so using the native managed provider whenever possible is a good idea.
XML Support. The DataSet object intrinsically supports reading and writing from a wide variety of XML sources, such as XML files, streams of XML, and XMLReaders (a class of the System.Data.XML namespace). Therefore, you can easily save a DataSet's contents to a file or read an XML file into a DataSet, then update a database with that data.
With a technology as new as ASP.NET, the decision about whether to migrate new projects or existing applications involves a trade-off between the advantages that the new technology offers and the costs of migration. The costs of migrating to ASP.NET applications are mostly a matter of the cost of modifying existing applications. The .NET Framework runs comfortably on most current hardware (the .NET Framework SDK system requirements recommend a 133MHz or better Pentium processor and 128MB or more of RAM), and there is no licensing cost associated with ASP.NET. However, if you choose to develop with VS.NET, you need to take into account its licensing cost.
ASP.NET offers so many compelling benefits that for most new projects, ASP.NET will be an obvious choice. But what about existing applications? The decision about whether to migrate an existing application largely rests on whether ASP meets that application's performance, scalability, and other needs. If it does, you might be better off simply running the application as is, because you can run both ASP and ASP.NET applications on the same server without worrying about one affecting the other.
However, if you're having trouble with performance, scalability, or any other factor that the improvements in ASP.NET address, the trouble of migrating might be worthwhile. Remember that because ASP and ASP.NET can run side by side, the decision needn't be all-or-nothing. You might be able to migrate your application page by page, keeping in mind that ASP and ASP.NET don't share the Application and Session collections. So if you use these collections for managing state, you need to write code to pass this state information between the part of your application that's running in ASP and the part that's running in ASP.NET.
What kind of challenges can you expect during migration? Several changes are likely to cause problems, some of them specific to ASP.NET, and some language-specific. In the former category are the following:
- ASP.NET supports only one language per page, whereas ASP lets you use multiple languages in the same page.
- As I mentioned, ASP.NET doesn't support functions in <% %> render blocks.
- In ASP, the Request(), Request.QueryString(), and Request.Form() functions return arrays of strings. In ASP.NET, they each return a string.
The latter category includes these changes:
- ByVal is the default parameter type for VB.NET. Your code won't work if it doesn't specify ByVal or ByRef for a method parameter but expects the receiving method to treat the parameter as ByRef.
- Unlike VB 6.0 and earlier, VB.NET doesn't use Set and Let.
- You must use parentheses for calling Sub procedures in VB.NET.
ASP pages that use COM components might also present problems because these pages don't work directly in ASP.NET. You can make them work, however, by using the .NET COM Interoperability layer. Through a utility called tlbimp.exe, this layer lets you import a COM object's type library to create a .NET assembly, then use that assembly to call the COM component from an ASP.NET page.
ASP.NET offers a great deal of added functionality, both natively and through the .NET Framework Class Library. It also offers many improvements in performance, customization, reuse, maintainability, and scalability that truly make this new technology the Hemi 'Cuda of the Web-development world.