You have a classic ASP application that needs migrating to ASP.NET. You are comfortable working with classic ASP. but ASP.NET is fairly new to you. So what do you do? This article is the first in a series that looks at your options and proposes an approach to the migration that follows the path of least resistance.
The other articles in the series are
Classic ASP and ASP.NET are very different technologies, and until a few years ago, that difference really shone through the development model choices on offer: Web Forms and MVC. Web Forms was the first iteration of ASP.NET. It is as control-based event-driven development model that resonates most with Windows Forms developers. It offers a fairly steep learning curve for developers who are used to working with HTTP, HTML and CSS as most of these basic web development technologies are abstracted away behind event handlers, server controls and other platform-specific artefacts. MVC was introduced to provide developers with more control over the basic web technologies, as well as a separated architecture that lends itself very well to unit testing. It also brought its own learning curve which is also considered pretty steep by Microsoft.
A third model was introduced in 2011 - the Web Pages framework, which also saw the introduction of the Razor syntax. ASP.NET Web Pages is a page-centric development model just like classic ASP in that server-side logic and HTML markup co-exist in the same file which represents a page in a web site. Server-side code is separated from HTML, CSS and client-side code through the use of tokens. Server-sde code is written using C# or VB, the latter option being more familiar to classic ASP developers who are used to writing in VBScript. Although VBScript and VB are completely different languages, a lot of the syntax is identical, which makes migrating to ASP.NET Web Pages using VB a compelling option. This article will explore how code blocks migrate from classic ASP to Web Pages using Razor and VB. Future articles will look at data access and how to manage include files and other reusable code.
If you have decided to really bite the bullet and learn C# at the same time as moving across to ASP.NET, you can learn the basics of Razor syntax with C# at the official ASP.NET Web Pages tutorials.
To help with illustrating the migration, I am using the Classifieds sample from the code download that comes with Chapter 15 of Wrox' seminal ASP title: Beginning Active Server Pages 3.0 (ISBN: 978-0-7645-4363-0). I still have my copy of this fine book, and the code sample served as the inspiration for the sample site I built in my ASP.NET Web Pages book. One of the best features of the code in the download is that it is truly awful. Consequently, it serves beautifully to illustrate some of the challenges that can be faced in porting really old code across to the new platform.
The amount of work that you have to do up front depends largely on the reasons for your migration to ASP.NET. I'm going to take a 'just get it done' approach in this article, which will involve as little reworking of the original code as possible. The HTML will ony be altered where it has to be in order to work with the Razor parser.
You also have to think about the tools you will use for development. ASP.NET Web Pages can be authored in either Visual Studio or WebMatrix. WebMatrix is a free lightweight IDE which includes a number of template starter sites. At the moment, it only supports Web Pages up to version 2 (ASP.NET 4.0). It offers a limited set of development tools such as a code completion system and compile time checking. Visual Studio includes a free edition: Express for Web. It supports Web Pages 3 (ASP.NET 4.5) and includes a fuller range of tools such as debugging and stronger IntelliSense. It only offers one Razor Web Pages template which includes a fair amount of clutter. For that reason, you might want to start with the Empty Site template in WebMatrix and once it has been created, open that in Visual Studio. If you opt to start with Visual Studio, you can remove everything from the generated template site except the bin folder. Even then, there is probably a lot in the bin folder that you won't need. The shaded libraries below are optional:
If you start with the WebMatrix Empty Site option, you will need to delete the default.cshtml file that gets generated. Razor files end in either .cshtml or .vbhtml depending on the language used. It is possible to mix file types in a Razor Web Pages site, but you are going to work with VB only, so you need to create a default.vbhtml file (or index.vbhtml if your existing classic ASP site has an index.asp file).
Razor Syntax and Visual Basic
In classic ASP. code blocks are delimited using <% and %> tags. The default.asp file in the Wrox Classifieds site has only one code block at the top of the file:
<% Session.Abandon %> <BASEFONT FACE="Comic Sans MS" COLOR="DarkBlue"> <HTML>
(I did mention that the code was awful, didn't I?)
Razor code blocks are delimited are opened with @Code and terminated with End Code. The equivalent lines in a Visual Basic Razor file are as follows:
@Code Session.Abandon() End Code <BASEFONT FACE="Comic Sans MS" COLOR="DarkBlue"> <HTML>
ASP objects such as Session, Request, Response etc are more or less identical in ASP.NET, so the code in the existing asp file can be copied and pasted into the ASP.NET Web Pages site with just the amendments to the code block markers detailed above, and the links in the HTML (not shown here) need to be updated to point to .vbhtml files instead of .asp files.
The next piece of code to migrate is the contents of Registration.asp. Mostly, this is just HTML, but there is a block of VBScript at the top of the page:
<CENTER> <% If Request("Update") = "True" Then Response.Write "<H1>Wrox Classifieds<BR> Update User Registration</H1>" Else Response.Write "<H1>Wrox Classifieds<BR> New User Registration</H1>" End If %> </CENTER> <P> <% If Request("Update") = "True" Then Response.Write "Please change your registration information as listed below<P>" Else If Request("NotFound") = "True" Then Response.Write "<I>We were unable to locate your information. " & _ "Please take the time to register again.</I><P>" Else Response.Write "<CENTER>(If you're already registered with us, " & _ "then click the 'Login' link below.)</CENTER><P>" End If Response.Write "You only need to register with our system if you want to " & _ "bid on existing 'for-sale' items, or sell your own items " & _ "on these pages. <BR> " & _ "In order to use these services, please take a few minutes " & _ "to complete the form below. Once you have done that, " & _ "you will have full access to the system." End If %>
Calls to Response.Write in classic ASP result in the output being rendered within the document where the call is positioned. It doesn't work like that in ASP.NET - Response.Write will result in the output being injected at the beginning of the document in all cases. Response.Write is simply not used in ASP.NET for that reason.
The first If... Then... block within the <CENTER> tags is re-written as follows:
<CENTER> @If Request("Update") = "True" Then @<h1>Wrox Classifieds<br> Update User Registration</h1> Else @<h1>Wrox Classifieds<br> New User Registration</h1> End If </CENTER>
Selection and iteration key words, like If, For, For Each and so on, do not need @Code... End Code markers when they are used to determine what is rendered inline. You just need to prefix the opening line of the server code with an @ sign. Instead of using Response.Write to render markup, you place it within the selection statement and also prefix it with an @ sign. Note that prefixing the HTML markup with the @ sign is only necessary for VB. It is not needed when coding in C#. This point is an important one to bear in mind when looking for tutorials or sample code for Web Pages - the vast majority of which are in C#.
The next block of code poses some new problems. The Razor parsing engine requires HTML tags to be balanced when they are preceded by the @ character and are intended to be rendered to the browser. You can see this in the previous example where the opening <h1> tag is balanced by the closing </h1> tag. The block that still needs to be migrated has an opening <P> tag but no matching closing tags. This is legal for HTML 4.01 and under but confuses Razor, and won't validate for HTML5. So the opening <P> tag needs to be brought inside the code block and closed off:
@If Request("Update") = "True" Then @<p>Please change your registration information as listed below</p> Else @If Request("NotFound") = "True" Then @<p><i>We were unable to locate your information. Please take the time to register again.</i></p> Else @<p><center>(If you're already registered with us then click the 'Login' link below.)</center></p> End If @<text>You only need to register with our system if you want to bid on existing 'for-sale' items, or sell your own items on these pages. <br /> In order to use these services, please take a few minutes to complete the form below. Once you have done that, you will have full access to the system. you will have full access to the system.</text> End If
There is a block of text that isn't surrounded by HTML tags. Razor requires this to be denoted and separated form server-side code somehow, so it introduces the <text> tag. The tag itself is not rendered. It simply acts as a marker for Razor. An alternative, but messier option is to prefix each line of text with the @ sign followed by a colon:
@:You only need to register with our system if you want to @:bid on existing 'for-sale' items, or sell your own items @:on these pages. @:<br /> @:In order to use these services, please take a few minutes @:to complete the form below. Once you have done that, @:you will have full access to the system. @:you will have full access to the system.
This approach is better utilised for single lines of bare text.
The final change to this file involves the values that are applied to the various fields in the registration form. Here are a couple of examples:
<TR> <TD WIDTH=20% ROWSPAN=11> </TD> <TD>E-Mail Address:</TD> <TD><INPUT TYPE="Text" NAME="email" VALUE="<%= Session("EMailAddress")%>" SIZE="40"></TD> </TR> <TR> <TD>Given Name:</TD> <TD><INPUT TYPE="Text" NAME="GivenName" VALUE="<%= Session("GivenName")%>" SIZE="40"></TD> </TR>
If you want to render variables or the result of expressions to the browser wth Razor, you simply prefix them with the @ sign:
<TR> <TD WIDTH=20% ROWSPAN=11> </TD> <TD>E-Mail Address:</TD> <TD><INPUT TYPE="Text" NAME="email" VALUE="@Session("EMailAddress")" SIZE="40"></TD> </TR> <TR> <TD>Given Name:</TD> <TD><INPUT TYPE="Text" NAME="GivenName" VALUE="@Session("GivenName")" SIZE="40"></TD> </TR>
The existing login.asp page includes a couple of examples of inline expressions. Here's one of them that conditionally sets the action attribute of the form:
<FORM ACTION="CheckLogin.asp<% If Request("SecondTry")="True" Then Response.Write "?SecondTry=True" End If %>" METHOD="POST">
This can be replaced with the following:
<FORM ACTION="CheckLogin.vbhtml@(If(Request("SecondTry")="True", "?SecondTry=True", String.Empty))" METHOD="POST">
So far, three files have been migrated with minimum changes to the originals despite the strong urge I feel to tidy up the atrocious HTML - but the brief was to just get it done. A certain amount of tidying up can be achieved by pressing Ctrl + K, Ctrl + D. This will convert the HTML tags to lower case and ensure that all attribute values are surrounded by double quotes. It will also attempt to apply some indenting of nested elements. WebMatrix validates the document against HTML5 and produces an error list for you to work against should that be important to you.
All of the existing VBScript has converted over to VB with little to no changes required. But that is about to change as I look at Data Access in the next article in this series.