Wrox Press
ASPToday
       1031 Articles
  in the Solutions Library
  Log off
 
 
 
VBToday Announcement!  
 
ASPToday Subscriber's Article Jonathan Goodyear
Back Seat Driver Part III
by Jonathan Goodyear
Categories: .NET Framework, Performance, Site Design
Article Rating: 4 (2 raters)
Published on January 31, 2003
 
Content Related Links Discussion Comments Index Entries Downloads
 
Abstract
This month, Jonathan discusses a flexible deployment mechanism for ASP.NET web applications in large enterprises, and illustrates how you can use the Replace method from the Regex object to do string replacements based on a regular expression.

I see that you're back for another dose of .NET reality. This month, I'm going to discuss (among other things), a more flexible deployment mechanism for ASP.NET web applications in large enterprises. As always, I need you to sound off with your opinions and input by posting comments and discussion items. First-timers and anybody else that needs to get up to speed with the way that this column works can click my name (or my beautiful mug shot) and read the introductions to my first two columns. I don't want to waste time repeating it here. Three...Two...One...Go!
 
 
Article

ASP.NET Deployment – Back to Basics

Some of you may remember that back when Visual Studio .NET was still in its early beta stages, it was pretty darn unstable. At the time, it was far more productive to develop ASP.NET applications using Notepad than it was to wrestle with the crash-prone Visual Studio .NET IDE. The common development paradigm (oops, I used the "p" word) was to point your ASPX page to the code-behind source code file that contained its associated code-behind class using the "Src" attribute of the @Page directive. You would then deploy both the ASPX page and the code-behind source code file to your production web server. The first time that each ASPX page was navigated to, the code-behind class would be compiled, and the page would execute.

Jump ahead to the Visual Studio .NET development paradigm (strike two on the "p" word…one more time, and the next round of drinks is on me). Visual Studio .NET links ASPX pages to their associated code-behind classes using the "CodeBehind" and "Inherits" attributes. Since Visual Studio .NET pre-compiles the entire web application into a single assembly, it is no longer necessary to deploy the actual code-behind source code files for your ASPX pages (though I have seen many people make that mistake). Frankly, I find this über-assembly approach to be a stupid idea, because it leads to several deployment problems. I will re-create a couple of them using an (intentionally simplified) example:

Joe, Molly, and Tom are building a web application that consists of three ASPX pages. These are JoesPage.aspx, MollysPage.aspx, and TomsPage.aspx. JoesPage.aspx allows you to select from a list of fruits and display the selected fruit in a label:

MollysPage.aspx asks you for your name, and echoes it back to you along with a salutation:

TomsPage.aspx displays a simple report of the authors in the pubs SQL Server database:

For this example, assume that Joe, Molly, and Tom are using Visual Source Safe (VSS) and have invested in a separate server that they use to compile their web application before deploying it to production.

The first problem that should be quite apparent, is that whenever a change is made to any part of the web application, the entire web application must be re-compiled and deployed. One of the first rules of software development is that "if it’s working, don’t mess with it". That tenet is clearly violated by Visual Studio .NET’s über-assembly approach. But let’s get past the ideological semantics for a moment. There are some more tangible problems to discuss.

In the example above, assume that both Joe and Molly need to make changes to their respective ASPX pages. Joe needs to add an additional fruit to his list of available fruits (for now, forget the fact that the list shouldn’t have been hard-coded). Molly needs to add a RequiredFieldValidator control to her web form to force the user to specify their name before clicking the Submit button. Joe finishes his modification on the first day, but Molly has some difficulty. Her page isn’t working right yet, but before leaving that night, she still checks her page back into VSS, so that it can be archived in the nightly tape backup process. The next day, Joe wants to deploy his fixed ASPX page, so he does a "Get" on the project from VSS to the build server and inadvertently includes Molly’s broken ASPX page in the build. If the problem with Molly’s page is rather innocuous, it stands a good chance of making it through QA (if there is such a process) and into production. What’s worse, once the problem is discovered, the entire migration will have to be rolled back, because it is all contained within the über-assembly. Tom’s code is being tossed around, and he didn’t even make any changes. Bummer.

Could this problem have been avoided? It sounds easy enough. Joe should have kept better track of which files to retrieve from VSS before compiling the web application on the build server. But what if your web application consists of hundreds (even thousands) of ASPX pages, with migrations pertaining to dozens of them each week (or day)? Eventually, either the wrong files will get migrated to the build server and compiled into your web application, or you will discover a critical problem with a piece of code that was intentionally migrated to production (perhaps Joe incorrectly assumes that a carrot is a fruit). When that occurs, you will be force to do a complete rollback of the migration, and you will have to attempt to reconcile your build server’s files to a point where a stable version of your web application can once again be compiled. Forget about doing a complete refresh from VSS, because there is no way to determine which files are ready to be compiled and migrated to production.

So, now that I have discredited the deployment method that comes out of the box with Visual Studio .NET, what method should you use? I recommend that you go back to basics. Before Visual Studio .NET, when code-behind source code files were used, you could easily rip and replace any piece of a web application without affecting any other piece. The biggest problems with that approach were that you had to deploy your source-code and it wasn’t pre-compiled (meaning that you took a performance hit when your page was first navigated to, and your code-behind class had to be compiled for the first time). The ideal deployment plan is to merge the best aspects of the old and the new.

First, as I alluded to in last month’s Back Seat Driver, you shouldn’t use Visual Studio .NET to compile your production web application. Instead, you should use batch compile scripts on a clean build server with only the .NET Framework SDK installed on it. That allows for the greatest flexibility with regard to build automation and custom configuration. If you go the batch compile script route, it is just as easy to compile each code-behind class into its own assembly as it is to compile the entire web application into a single assembly. Doing so will give you the benefit of pre-compiled code, while enabling the ability to do partial migration rollbacks (an absolute "must have" in fast-moving "migrate first, ask questions later" corporate intranet environments). Even better, you don’t end up re-compiling code that isn’t broken over, and over, and over again. Why take the risk that something will accidentally get thrown in there? Leave well enough alone.

In the case of Joe, Molly and Tom’s web application, you might use the following compile scripts (each in its own file with a ".BAT" extension):

csc.exe /t:library /out:JoesPage.dll JoesPage.aspx.cs >> results_JoesPage.txt
csc.exe /t:library /out:MollysPage.dll MollysPage.aspx.cs >> results_MollysPage.txt
csc.exe /t:library /out:TomsPage.dll TomsPage.aspx.cs >> results_TomsPage.txt

There are a few things that I should point out. First, before you can use the command-line compilers, you need to make sure that the directory containing the compiler executable itself ( csc.exe for C# in this case) is in your system PATH environment variable. If it isn’t, you can register all of the necessary environment variables easily by executing the "vcvars32.bat" file, which is located in the "/bin" directory of your .NET installation. Or, if your machine has Visual Studio .NET installed, it will be in the "C:\Program Files\Microsoft Visual Studio .NET\Vc7\bin" directory.

The second thing that I’d like to point out is that instead of having to place all of your compiler switches on a single command-line, you can opt to use a response file. Response files are specified using the "@" character on the command-line, and enable you to place each compiler switch on a separate line in the file. You can also add comments by prefixing a line with the "#" character. You can find out all about how to use response files by looking up "response files" in the .NET Framework SDK documentation.

Lastly, the ">>" characters in each compile script above export the output of the compile script to the file specified after them. This allows you to catch and fix problems with your compile script and/or your source code. The first execution of the compile script creates the output file, and subsequent compiles append their output to the same file (unless you deleted it).

It takes a bit longer to get this system up and running, but once you do, your deployment process will be streamlined and ready to handle the inevitable bumps in the road. My hope is that Microsoft will allow for greater compilation flexibility in future versions of Visual Studio .NET, including the ability to see what compile scripts it is running in the background when you tell it to compile. That way, you can get your compile process organized and ready in the Visual Studio .NET environment, which will allow you to set up your build server environment much more quickly and easily.

Game, Set, MatchEvaluator

The Regex object in the System.Text.RegularExpressions namespace has a Replace method that allows you to do string replacements based on a regular expression. What I found most useful about this method, though, is that you can also specify a MatchEvaluator delegate that will fire for each potential replacement, allowing you to determine at runtime what the replacement text will be, as well as keep track of which replacements were made. This makes it ideal for token decoding operations. For example, create a new ASPX page named TestMatchEvaluator.aspx, and add a TextBox named txtInput, a Label named lblOutput, and a Button named btnSubmit to it. You should end up with something like this:

Just inside your code-behind class declaration, declare an ArrayList object to hold your token replacement words:

private ArrayList al = new ArrayList();

In the Page_Load event, populate the token replacement words:

private void Page_Load(object sender, System.EventArgs e)
{
    al.Add("apples");
    al.Add("oranges");
    al.Add("bananas");
    al.Add("mangoes");
    al.Add("grapes");
}

In the Click event for the btnSubmit Button control, place the following code:

private void btnSubmit_Click(object sender,
    System.EventArgs e)
{
    lblOutput.Text = Regex.Replace(
        txtInput.Text,@"<<\d+>>",
        new MatchEvaluator(TokenEvaluator));
}

The code above calls the Replace() method of the Regex object and passes it a regular expression to match against, and a reference to a MatchEvaluator delegate. It assigns the result of the replacement operation to the Text property of the lblOutput Label control. The MatchEvaluator delegate (named TokenEvaluator in this example) contains the following code:

private string TokenEvaluator(Match regexMatch)
{
    int index = Convert.ToInt32(
        regexMatch.Value.Substring(
        2,regexMatch.Value.Length - 4));

    if(index < al.Count)
    {
        return al[index].ToString();
    }
    else
    {
        return "N/A";
    }
}

The TokenEvaluator delegate method above is called each time the Regex.Replace() method encounters a match for the specified regular expression. The first thing that the function above does is parse out the number from the rest of the token syntax. In this example, I have specified that a token is represented by a number, enclosed by two less-than signs and two greater-than signs. For example "<<3>>". The TokenEvaluator delegate method then uses that token number as an index into the ArrayList containing the token replacement words. If the token number is larger than the number of items in the ArrayList, the string "N/A" is returned. Otherwise, the proper replacement word is returned. An example implementation is below:

You can modify the example in this article to look up token replacement words in a database to simulate a more real-world scenario. You might even want to track how often each token is used for statistical purposes.

Until next month, buckle up, and be careful which road signs you follow.

Conversation Starters

1) Have you deployed an ASP.NET website to production? What strategy did you use? What problems did you encounter, and how did you solve them?

2) Have you used the Regex object and/or the MatchEvaluator delegate? Do you have any tips that you'd like to share?

 
 
 
Rate this Article
How useful was this article?
Not useful Very useful
Brief Reader Comments: Read Comments
Your name (optional):
 
 
Content Related Links Discussion Comments Index Entries Downloads
 
Back to top