Migrating to ASP.NET 2 - Part 3 - Migrating Business Logic, Content Pages and User Controls

This is the third part of a (long running) article series about migrating a .NET 1.1 Web site to ASP.NET 2.0. This installment focuses on the way I migrated my business logic, content pages and user controls.

Read Part 1 - Introduction
Read Part 2 - Setup and Master Pages
Read Part 3 - Business Logic, Content Pages and User Controls
Read Part 4 - Implementing Custom Providers for Membership, Role Management and Profiles
Read Part 5 - It's the little things...


When I started this article series and said this would be a "long running article series" I had no idea it would take such a long time for new articles to arrive. It's been almost a year since I published part 2 about Master Pages. My excuse? I spent the past year investigating .NET 2.0 and writing my new book ASP.NET 2.0 Instant Results, out in March 2006. Now the book is done, I can focus on writing some content for my own web site again.

In this article, I'll continue where I left about a year ago: a conversion of my own web site here at Imar.Spaanjaars.Com. Part two ended with the setup of the project and the implementation of a single Master Page. With the Master Page done, the next step is migrating the business logic, content pages and user controls from my 1.1 site to the new one. Fortunately, the 1.1 site isn't too large, so migration is pretty simple. Instead of relying on upgrade tools that try to upgrade existing ASPX pages and User Controls, I decided to upgrade manually, copying the pages one by one from the 1.1 site to my new ASP.NET 2.0 project. This also allowed me to clean up any obsolete or messy code where necessary.

While this manual upgrade worked for me because I have only a handful of ASPX pages and User Controls in my site, manually upgrading a huge site isn't an option. For those of you who want to upgrade larger .NET 1.x sites to 2.0, check out the new Web Application Project that allows you to build .NET 2.0 applications using VS.NET 2003 style projects. Scott Guthrie has also posted upgrade tutorials for C# and VB.NET.

During my manual conversion, I ran into some issues, but nothing major. I'll describe each of the problematic areas and the way around them. Before I dig into that, let 's take a look at how things used to be.

How Things Used to Be in my 1.x Web Site

When I developed the first edition of my web site, back in 2003, I already separated all of my business logic into a separate C# Class Library project. My web site in turn had a reference to the DLL for this project so it could use all of its public classes and methods. The update of the look & feel of my web site in late 2004 didn't change the code model, but only changed the appearance of the site.

My 1.1 site also features a number of ASPX pages, all based on a custom page templating solution that you saw in part two of this series. I have around 50 pages in the entire site, with only around 20 of them with complex dynamic contents and code; the others are simply static files with a few user controls embedded to control layout out and look and feel.

Most of the dynamic pages contain one or more user controls to add additional content to each page. For example, the QuickDocId.aspx page (that you're reading right now) has a ContentDetails User Control to display the summary and the rating stuff in the left bar. These controls are loaded dynamically in the Code Behind of each file, mainly because of the template solution the site uses. In the new site, I am adding these controls to the content pages directly, resulting in less code, and better to manage pages (for example, because I'll get design-time support on the controls).

The user controls itself have little markup in the ASPX part of the page. Most of their output is generated in the Page_Load event of each control, where a long string is built up with the required markup, bases on some internal information. For example, the ContentDetailsPrintOnly.ascx control (that is visible only when you print a page on my web site) has the following ugly looking code to display some text about the article:

message = "<table cellpadding=\"2\" cellspacing=\"0\" border=\"0\" width=\"100%\">";
message += "<tr><td><strong>QuickDocId</strong></td><td>" 
        + oContent.ID + "</td></tr>";
message += "<tr><td><strong>Full URL</strong></td><td>" 
        + fullURL + "</td></tr>";
message += "<tr><td><strong>Written by</strong></td><td>" 
        + oContent.CreateMemberName + "</td></tr>";
message += "<tr><td><strong>Date Posted</strong></td><td>" 
        + oContent.CreateDateTime + "</td></tr>";
lblDetails.Text = message;

In the new setup, I intend to fix this kind of code and replace it with proper server-side controls.

Upgrading the Business Layer

Reusing this Class Library in my new ASP.NET 2.0 was very simple. I chose to add an existing project to my solution and pointed it to the old 1.1 version of my business layer. The Visual Studio Conversion Wizard kicked it and converted the entire project to the new V2 format. It converted my project without a single error or warning, allow me to use the project right away in my web site. There was one issue in my business layer that needed my attention. The business layer sort of doubles as a data access layer in that most of the 20+ class it contains talk directly to the database. This isn't really that bad, as I have no intention to reuse my business classes in another application. However, from a design and best practices perspective it would probably better to move the data access code to a separate layer. Also, the data access code uses the old (and sort of deprecated) "Microsoft Data Access Application Block V2" (DAAB) which has been superseded by the Enterprise Library. It contains code that looks similar to this to execute a stored procedure and get a DataReader:

SqlParameter[] arrParms = new SqlParameter[1];

arrParms[0] = new SqlParameter("@id", SqlDbType.Int);
arrParms[0].Direction = ParameterDirection.Input;
arrParms[0].Value = id;

SqlDataReader drCategory = SqlHelper.ExecuteReader(
     Helpers.DefaultConnectionString, CommandType.StoredProcedure, 
     "sprocCategorySelectItem", arrParms);

if (drCategory.Read())
  description = drCategory.GetString(1);
  contentTypeID = drCategory.GetInt32(2);

As you can see, it uses a custom SqlHelper class to access the database.

While this helper class isn't really optimized for .NET 2, it also never caused me any problems in the 1.1 version of the web site. Upgrading all the classes in the business layer meant rewriting a lot of code, without any direct benefits. I also expect that some of the 20+ classes will become obsolete when I am done with the entire conversion. Some of these classes deal with user and role management, something I intend to replace with (custom) Membership and Role providers. To avoid a lot of useless work, I decided to leave the business layer as is: let all methods work with the SqlHelper class and over time remove whatever classes and methods have become obsolete. New code will target ADO.NET 2.0 classes, instead of creating more "old code".

Upgrading Content Pages

Converting the content pages was pretty simple. For each page in my 1.1 site, I created a new one in the new site and based it on my central Master Page. I then manually copied the code from the markup and code behind from the 1.1 file into the new page. Once all pages had been copied, I tried to recompile the site and received a number of warnings, mostly related to "obsolete" methods or classes. For example, the namespace System.Web.Mail should no longer be used, and you're advised to use System.Net.Mail instead. Fixing this issue also forced me to rewrite some of the code that sends out e-mail, because of the new behavior of some of the mail classes. For example, the To address of a MailMessage is no longer a simple string, but a MailAddress object. All in all, the fixes were minimal and I ended up with the following code:

MailMessage myMessage = new MailMessage();

myMessage.From = new MailAddress(fromAddress, fromName ); myMessage.To.Add(new MailAddress(toAddress, toName )); myMessage.Subject = mySubject; myMessage.Body = myMessageBody; myMessage.IsBodyHtml = true; SmtpClient myClient = new SmtpClient(); myClient.Send(myMessage);

Notice that you no longer have to specify an SMTP server (although you could if you wanted to). The SmtpClient class is able to read from the web.config file to get information about the SMTP server you want to use:

    <smtp deliveryMethod="Network">
      <network host="smtp.yourprovider.com" port="25"/>

Other warnings I got were related to the obsolete ConfigurationSettings class. Instead of using ConfigurationSettings.AppSettings.Get("SomeKey") you're now advised to use ConfigurationManager.AppSettings.Get("SomeKey") to get the value for the requested key from the web.config file. A simple Find & Replace action took care of this.

Another thing I had to do was upgrade some of the server controls my 1.1 pages were using. For example, some pages used a DataGrid control. While this control still runs fine in .NET 2.0, it's better to replace it with the new GridView control. This new control has a lot of new features that make it much easier to work with. Although at this stage I am not going to use these new features, I still changed the DataGrid control to a GridView control, if only to take advantage of its new XHTML compatibility and accessibility features. Replacing the control was as simple as changing <asp:DataGrid> into <asp:GridView> and changing the <asp:*Column> columns to <asp:*Field> columns. All the other stuff just kept working.

The remainder of the changes I made to the markup and Code Behind of all my pages didn't have much to do with .NET 2, but were merely optimizations I could have done in .NET 1.x as well. The core of the site is over 3 years old, and I learned a lot since that time. This whole conversion process was a good opportunity to fix a few nasty bugs, and to optimize and clean some dirty and old code.

Upgrading User Controls

Converting the user controls in my site was similar to converting the content pages. For each control in the 1.1 site, I created a new one in the new site and copied over the markup and the code from the Code Behind files. Most things worked straight away, although I did spend quite a lot of time optimizing the controls. Instead of building up long strings with content that was displayed in an <asp:Literal> control, I added a number of true server controls to each user control (like HyperLinks) and set their public properties in the Code Behind. Again, this isn't necessarily related to ASP.NET 2.0 but was merely a much needed optimization I could (and should) have implemented in the 1.x version of the site as well.

What's Next

By migrating the business logic, content pages and User Controls, I reproduced the bare minimum in functionality: you can now view content on the new web site. However, there is still a lot to do, including:

  • Recreate Login functionality
  • Rebuild Management section (to manage the content in the site)
  • Implement Membership features (including sign up)
  • Make use of new .NET 2 features
  • Upgrade / Migrate public Web Services

All of these feature will be implemented in the new 2.0 site over the next few months. In the next installment of this article series, I'll spend some time discussing small but useful new features in ASP.NET like ValidationGroups, DefaultButtons and base classes. Future articles will dig deeper into the other topics. Although I don't have a strict planning for the series, I promise it won't be another year before I post the next article.

Where to Next?

Wonder where to go next? You can post a comment on this article. You can read existing comments below or you can post a comment yourself on this article .

Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.