drohm, Fri 12/23/05 06:52 AM

I just posted about NUnit 2.2.4 being released and it seems they just released a newer version, 2.2.5 that has several bug fixes discovered in the 2.2.4 release.  It replaces 2.2.4 as their recommended download.

Grab it
here.

drohm, Sat 12/17/05 11:43 AM

The good folks over at NUnit released version 2.2.4 yesterday.  They have seperate downloads for each version of the .NET Framework available.  This is a production release so it's been tested pretty well and is a recommended version.

Grab the latest version here.

I just read on Scott Guthrie's blog about the CSS Properties window Visual Studio add-on.  This is the coolest add-on that allows you to view stylesheet properties in a seperate window.  Although the built-in style builder enables you to create and edit in-line styles, there's no way to edit the styles that are inherited from a linked style sheet - the new CSS Properties window provides this capability.  It allows you to edit both in-line styles and styles in linked style sheets.

Give it a try here.

As promised, I finished my article on customizing MSBuild with the Web Deployment Projects add-in.  Using MSBuild with Web Deployment Projects, the Visual Studio add-in from Microsoft, developers can customize their build and deployment strategies.  The article walks you through setting this up and how you can customize the MSBuild file and create custom Tasks.  You can read it here.

I'll follow-up this article with another on accessing a source control database, doing several different web server tasks, and creating some more cool custom utility tasks for MSBuild.

A couple of weeks ago I started using the tool released from Microsoft to help deployments with ASP.NET projects (Web Deployment Projects).  Because web projects don't create or maintain project files anymore, customizing builds and deployments isn't as straight-forward as a windows forms project or a class library project.  The “Publish Web Site” feature inside Visual Studio 2005 is good for a quick turn-around, but not the ideal solution for a dependable and repeatable deployment scenario.  This is where Web Deployment Projects can help make your life a lot easier.  A long the way, I dabbled in custom task building and realized how much fun it could be.  In this article I'll walk you through setting up a sample web project and then creating the Web Deployment project.  I'll then customize the build and also show you how to create a custom task and include and consume it in the build file.

First things first, if you haven't downloaded the Web Deployment Projects add-in from Microsoft, do so now here.  Install the .exe normally.  Once that is done, open Visual Studio 2005 and create an ASP.NET web site project.  Next, right-click on the web project in Solution Explorer and you’ll notice a new menu item (3rd item down in the context menu) called ‘Add Web Deployment Project…’  It should look like this:

Select ‘Add Web Deployment Project…’ and you should get the following dialog box:

Enter a descriptive name and choose a destination for the new deployment project.  Once that is complete the new project will show up in Solution Explorer with no files or directories underneath it.  It’s important to note here that you can create as many deployment projects for a web site as you need.  You can also have multiple web projects in a solution, each with a separate deployment project.

Build and Deployment Options

There are two ways you can modify the build and deployment options: with the GUI interface or directly with the MSBuild project file.  I’ll show how you can modify the project file later, but first I’ll go over the GUI options. 

Right-click on the deployment project and choose “Property Pages”.  The first thing to mention here is the Configuration Manager.  With the Configuration Manager, you can create multiple configurations for the build, each with its own independent settings and options.  By default, it comes with two configurations: Debug and Release.  For example, you can add a ‘Staging’ configuration for a test server deployment scenario that performs behaviors specific for that configuration.

Another suggestion in regards to the Configuration Manager is the project selections for each configuration.  For your Debug configuration, it might be beneficial to select the web project for building and deselect the deployment project.  The majority of the time when doing development you’ll not want to do any deployments, as this could wreak havoc on your test server.

For the Release configuration, do the opposite and deselect the web project for building and select the deployment project.

There are four Property pages, Compilation, Output Assemblies, Signing, and Deployment.  I won’t go over each and every option, but I will point out the more important ones. 

Compilation Page:

The options given on this dialog allow you to decide which precompile mode to use.  You can allow the precompiled site to be updatable or not updatable.  Allowing the precompiled site to be updatable is similar to how web projects were built with VS2003.  The .aspx/.ascx control definitions and html source is included in the deployed site.  The benefit is that the source files are, like the name implies, updatable.  The downside to this is that the pages will incur a dynamic compile performance hit when the pages are accessed.  If you choose to not make the site updatable, then the .aspx/.ascx control definitions and html source is stripped out of those files and compiled with the main assembly (The files are simply placeholders with no content).  This has a number of benefits, including a more efficient version of the application, eliminates the need for a first-time dynamic compile performance hit when your application is run, allows you to protect more of the intellectual property of your site, and enables you better compile-time checking of your application.

Output Assemblies Page:

On this property page you get to choose which way the build will generate your assemblies.  You can have it generate a single assembly, similar to VS2003 and you can choose the name for that assembly.  You also can choose to have it create an assembly per folder or per page.  This gives you better flexibility with larger web sites.  You also have the option to specify the assembly version.

Deployment Page:

One of the really cool features on this page is the ability to replace the application’s web.config settings at build-time.  Any section of the web.config file can be replaced.  Couple this with the different VS build configurations and you can have a very robust deployment setup.  For your Debug configuration you can replace the connectionSettings section to use a separate configuration file and have the Release configuration use another.  Very nice.

You can optionally have the deployment create an IIS virtual folder for the output folder.  If the virtual folder already exists, nothing will be performed.

Editing the MSBuild file Directly

In some of my past jobs, I've had the pleasure of working on deployments using several different tools.  CruiseControl.NET and NAnt was by far the best deployment tools I’ve ever used.  They still are to this day some of the most innovative and flexible tools any developer can get their hands on - and they're free!  I won’t go into the reasons why continuous integration and a repeatable build/deployment process are important.  I believe that most experienced developers already believe in the concept and have bought into it, at least partially.  What I will go into is Microsoft’s newest build tool, MSBuild.  MSBuild allows you to customize the build process at a very fine, granular level.  It’s structured and operates very similar to NAnt.  For example, a common operation after building a web site would be to deploy the site to a web server on another machine.  You can do this with MSBuild by entering the following in the AfterBuild Target:

  400   <Target Name="AfterBuild">

  401     <!-- Copy web site -->

  402     <Copy DestinationFiles="@(PrecompiledOutput->'$(DeploymentFolder)\%(RecursiveDir)%(Filename)%(Extension)')"

  403           SourceFiles="@(PrecompiledOutput)" />

  404   </Target>


One of the things I set out doing with MSBuild right away was to mimic the build process I had setup with CruiseControl.NET.  The basic tasks involved for this process were:

  1. Build the application
  2. Generate the API documentation
  3. Archive the compiled build
  4. Deploy compiled application to the target web server
    1. Confirm web directory on web server exists
    2. Clean web directory of previous version
    3. Copy files to web directory
  5. Deploy documentation to target web server
    1. Confirm documentation web directory on web server exists
    2. Clean documentation web directory
    3. Copy documentation to web directory
  6. Restart IIS


One step that I’m omitting is the step to get the latest version of the application from source control.  I decided to skip that step for discussion in a future article.  To keep this article short, I’ll go over building, archiving, and deploying the site.  Once you grasp the fundamentals you’ll be able to apply this new knowledge to customizing your build even further.

The first step involved building the application.  Since I am signing my assemblies I’ll need to specify my Key file and other Assembly Attributes, along with a method to specify versioning.  In the GUI interface, on the Output Assemblies property page, you can specify versioning for your assemblies.  When you check the box for Versioning and specify an Assembly Version and File Version, a new ItemGroup is placed in the MSBuild file automatically.

  408   <ItemGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">

  409     <AssemblyAttribute Include="AssemblyFileVersion">

  410       <Value>1.0.0.0</Value>

  411     </AssemblyAttribute>

  412     <AssemblyAttribute Include="AssemblyVersion">

  413       <Value>1.0.0.0</Value>

  414     </AssemblyAttribute>

  415   </ItemGroup>


Along with the version information, you can specify other Assembly attributes like the following:

  418   <ItemGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">

  419     <AssemblyAttribute Include="AssemblyFileVersion">

  420       <Value>1.0.0.0</Value>

  421     </AssemblyAttribute>

  422     <AssemblyAttribute Include="AssemblyVersion">

  423       <Value>1.0.0.0</Value>

  424     </AssemblyAttribute>

  425     <AssemblyAttribute Include="AssemblyTitle">

  426       <Value>MyWeb</Value>

  427     </AssemblyAttribute>

  428     <AssemblyAttribute Include="AssemblyDescription">

  429       <Value>Company Web Site</Value>

  430       </AssemblyAttribute>

  431     <AssemblyAttribute Include="AssemblyCompany">

  432       <Value>MyCompany</Value>

  433     </AssemblyAttribute>

  434     <AssemblyAttribute Include="AssemblyCopyright">

  435       <Value>Copyright (c) MyCompany 2005</Value>

  436     </AssemblyAttribute>

  437   </ItemGroup>


So far so good, but the versioning ability is severely limited.  What if my company likes to version the build on the date and then increment the revision number for each build?  For example, 1.0.51212.5 indicates the build number as the current date, 12/12/2005, and the revision number as the 5.  There isn’t a built-in property that we can use to customize this, but luckily there is an open-source community library available that allows us to do what we want.  The MSBuild Community Tasks Project has a number of tasks that allow us to customize our build process.  I highly recommend this library and will reference it in this article.  In order to take advantage of this library, download and install it.  Once the installation is done, you’ll notice a new directory: C:\Program Files\MSBuild\ MSBuildCommunityTasks.  You’ll need to reference this path with the UsingTask tag so that you can use the specific task you need in the library.  For example, we’re going to use the Version task so my UsingTask tag will look like the following:

  439   <UsingTask AssemblyFile="C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll"

  440             TaskName="MSBuild.Community.Tasks.Version"/>


Now I can reference that task in the BeforeBuild target like so:

  443   <Target Name="BeforeBuild">

  444     <!-- Generate assembly version -->

  445     <Version RevisionType="Increment" VersionFile="BuildNumber.txt">

  446       <Output PropertyName="Major" TaskParameter="Major" />

  447       <Output PropertyName="Minor" TaskParameter="Minor" />

  448       <Output PropertyName="Build" TaskParameter="Build" />

  449       <Output PropertyName="Revision" TaskParameter="Revision" />

  450     </Version>

  451     <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)" />

  452     <CreateItem Include="AssemblyFileVersion" AdditionalMetadata="Value=$(Major).$(Minor).$(Build).$(Revision)">

  453       <Output TaskParameter="Include" ItemName="AssemblyAttributes" />

  454     </CreateItem>

  455     <CreateItem Include="AssemblyVersion" AdditionalMetadata="Value=$(Major).$(Minor).$(Build).$(Revision)">

  456       <Output TaskParameter="Include" ItemName="AssemblyAttributes" />

  457     </CreateItem>

  458   </Target>


There is one important thing to note here.  I’m using the CreateItem tags to create the AssemblyFileVersion and AssemblyVersion attributes.  This means I’ll need to remove those two items from my original ItemGroup listed above.  Otherwise, I’ll get a duplicate item error when I go to build the project.

Next we need to archive the newly built application.  To do this, I’m going to use the Zip task that comes with the MSBuildCommunityTasks library.  I’ll need to add another UsingTask to reference the Zip task:

  461   <UsingTask AssemblyFile="C:\Program Files\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll"

  462             TaskName="MSBuild.Community.Tasks.Zip"/>


I’ll then place the task in the AfterBuild target:

  464   <Target Name="AfterBuild">

  465     <!-- Archive build -->

  466     <Zip ZipFileName="$(ArchiveDirectory)\$(ProjectName)_$(Major).$(Minor).$(Build).$(Revision).zip" Files="@(PrecompiledOutput)" />

  467   </Target>


Notice that I’m using a couple custom properties that I declared above in the build file.  The property $(ProjectName) simply stores the full project name so I can reference it here.  The property $(ArchiveDirectory) is where I will always place the zip file containing the latest build.  Also notice that the properties from our Version task are still available to us allowing us to accurately name our zip file.

Now we need to deploy the site to the deployment web server.  For this task, I'll use the copy task that I mentioned earlier.  Here is the command again for convenience:

  471   <Target Name="AfterBuild">

  472     <!-- Archive build -->

  473     <Zip ZipFileName="$(ArchiveDirectory)\$(ProjectName)_$(Major).$(Minor).$(Build).$(Revision).zip" Files="@(PrecompiledOutput)" />

  474     <!-- Copy web site -->

  475     <Copy DestinationFiles="@(PrecompiledOutput->'$(DeploymentFolder)\%(RecursiveDir)%(Filename)%(Extension)')" SourceFiles="@(PrecompiledOutput)" />

  476   </Target>


The details for the parameters of the Copy tasks are:

  • @(PrecompiledOutput) – This is one of the items built-in with Web Deployment Projects. 
  • $(DeploymentFolder) – Custom user parameter that I added.  You can add any number of custom parameters to the build file.

Notice that it is in the AfterBuild target.  This means it will get executed after the site has been built.  In my build file I’ve also added a conditional construct to dynamically set the $(DeploymentFolder) property based on the configuration used in the build.

  478   <Choose>

  479     <When Condition=" '$(Configuration)'=='Debug' ">

  480       <PropertyGroup>

  481         <DeploymentFolder>C:\Inetpub\MyWebDir</DeploymentFolder>

  482       </PropertyGroup>

  483     </When>

  484     <When Condition=" '$(Configuration)'=='Release' ">

  485       <PropertyGroup>

  486         <DeploymentFolder>\\webserver\Inetpub\MyProdWebDir</DeploymentFolder>

  487       </PropertyGroup>

  488     </When>

  489   </Choose>


We now have a signed assembly with the assembly attributes being set properly.

There is one limitation with the Version task that comes with the MSBuildCommunityTask library.  It doesn’t allow you to specify the date for the build number like we talked about earlier.  It will allow you to use an automatic numbering system for the build number, but it won’t satisfy our need for the date.  In order to do this, we’ll need to create a custom MSBuild task.  To do this, create a new class project in a seperate solution (I called my project BuildTasks).  Next, create a new class called Version.  Here is the class with an explanation below:

    1 using System;

    2 using System.IO;

    3 using Microsoft.Build.Utilities;

    4 using Microsoft.Build.Framework;

    5 

    6 namespace BuildTasks

    7 {

    8     public class Version : Task

    9     {

   10         #region Fields

   11 

   12         private string mFileName;

   13         private int mMajor;

   14         private int mMinor;

   15         private int mBuild;

   16         private int mRevision;

   17 

   18         #endregion

   19 

   20         #region Properties

   21 

   22         [Required]

   23         public string FileName

   24         {

   25             get { return mFileName; }

   26             set { mFileName = value; }

   27         }

   28 

   29         [Output]

   30         public int Major

   31         {

   32             get { return mMajor; }

   33             set { mMajor = value; }

   34         }

   35 

   36         [Output]

   37         public int Minor

   38         {

   39             get { return mMinor; }

   40             set { mMinor = value; }

   41         }

   42 

   43         [Output]

   44         public int Build

   45         {

   46             get { return mBuild; }

   47             set { mBuild = value; }

   48         }

   49 

   50         [Output]

   51         public int Revision

   52         {

   53             get { return mRevision; }

   54             set { mRevision = value; }

   55         }

   56 

   57         #endregion

   58 

   59         public override bool Execute()

   60         {

   61             bool bSuccess = true;

   62 

   63             try

   64             {

   65                 CalculateVersionNumber();

   66                 Log.LogMessage(MessageImportance.Normal, "Version {0}.{1}.{2}.{3}", mMajor, mMinor, mBuild, mRevision);

   67             }

   68             catch (Exception ex)

   69             {

   70                 // Log failure

   71                 Log.LogErrorFromException(ex);

   72                 Log.LogMessage(MessageImportance.High, "Failed to increment Version Number!");

   73                 bSuccess = false;

   74             }

   75 

   76             return bSuccess;

   77         }

   78 

   79         private void CalculateVersionNumber()

   80         {

   81             // Set default values

   82             mMajor = 1;

   83             mMinor = 0;

   84             mBuild = 0;

   85             mRevision = 0;

   86 

   87             // Calculate build number

   88             DateTime dDate = DateTime.Now;

   89             string _month = dDate.Month.ToString();

   90             string _day = dDate.Day.ToString();

   91             string _year = dDate.Year.ToString();

   92             _year = _year.Substring(_year.LastIndexOf("0"));

   93             mBuild = int.Parse(_year + _month + _day);

   94 

   95             // Get previous version

   96             if (System.IO.File.Exists(mFileName))

   97             {

   98                 string previousVersion = File.ReadAllText(mFileName);

   99                 string[] previousVersionNumbers = previousVersion.Split('.');

  100                 mMajor = int.Parse(previousVersionNumbers[0]);

  101                 mMinor = int.Parse(previousVersionNumbers[1]);

  102 

  103                 if (mBuild == int.Parse(previousVersionNumbers[2]))

  104                 {

  105                     mRevision = int.Parse(previousVersionNumbers[3]) + 1;

  106                 }

  107             }

  108 

  109             // Save new version to file

  110             File.WriteAllText(mFileName, string.Format("{0}.{1}.{2}.{3}", mMajor, mMinor, mBuild, mRevision));

  111         }

  112     }

  113 }


You’ll need to add a couple using statements for Microsoft.Build.Utilities and Microsoft.Build.Framework and have your class inherit from Task.  The Execute method is the entry point into the class and will return a boolean value indicating if the task completed successfully or not.  After you compile the project, you can reference the new Version task with the following UsingTask in the MSBuild file that points to the .dll of our task project.

  491   <UsingTask TaskName="BuildTasks.Version"

  492             AssemblyFile="D:\Development\Solutions\BuildTasks\BuildTasks\bin\Release\BuildTasks.dll"/>


The code itself is very straight-forward.  I call CalculateVersionNumber() from the Execute() method.  This method opens the version file thats specified by the task in the MSBuild file by the user.  It reads the file getting the previous version number.  If the previous build number is the same as the build number calculated here, no change will be made to the build number and the revision number will get incremented by 1.  I then save the new version number back to the version file for future builds.  The call to CalculateVersionNumber() is inside a Try...Catch which allows me to return true or false from the Execute() method.  This also allows me to write the appropriate output message to the Log.

Four of the public properties for the class are declared with the [Output] attribute to indicate that these properties will be available to the task in the MSBuild file.  The FileName property is marked with the [Required] attribute to indicate that this property is required by the task (it will not run without it).

Knowing the structure of the class, the base class to inherit from, and the methods to implement are the most important parts for creating a custom task.  You can see from this example, creating custom MSBuild Tasks is very easy and the possibilities are very much endless.

Summary

We’ve seen how to use the Web Deployment Projects add-in from Microsoft to customize building and deploying web projects in Visual Studio 2005.  Without this tool, the options are very limited and the process is very manual.  Alternatively, by editing the deployment project file, you can customize the build in any way imaginable.  In my next article, I’ll focus on source control, web server custom tasks, and other cool utility tasks that can be created and used in your build and deployment strategy.

The Enterprise Library team just released the December "interim community drop" of the Enterprise Library for .NET 2.0.  The announced that it was near-complete and only contains the source code, no documentation.  One major change included in this drop is the long-awaited configuration tool.

Get more information and download it here.

drohm, Thu 12/01/05 05:42 PM

I'm finally getting around to experimenting with .NET 2.0 and VS 2005.  One of the more interested features is MSBuild.  I'm currently reading up on it since the built-in support for web site deployments is serverely lacking.  One tool that I think everyone should download and use if you're doing any ASP.NET development is the Visual Studio 2005 Web Deployment Projects.  It's a Visual Studio 2005 add-in that adds the ability to create a deployment project to your solution for creating build and deployment options for your web projects.  Specifically (from their web site):

A Web Deployment Project creates and maintains an MSBuild project file, and is associated in a solution with a Web site project. A Web Deployment Project enables you to manage not only build configuration and merge options, but other tasks such as specifying changes for the application's Web.config file during compilation, changing connection strings, creating virtual directories, and performing other tasks at specific points in the deployment process.

I'm still learning MSBuild and this tool and will post again when I have a better grasp of how to best do deployments with 2.0.

drohm, Thu 12/01/05 09:35 AM

After getting CS installed, I decided to search for some nice skins.  There are a bunch out there created by some very creative people.  Here are some links to some really good skins:

Take the time to look around, there are tons of good looking skins to spice up your site.

drohm, Thu 12/01/05 03:06 AM

Took a bit of time, but I finally got CS 1.1 up and running.  A couple of things...if you're looking for a good hosting provider for .NET with a good reputation and great service, ORCS Web is the only one to look at.  Simply amazing customer service, 'nuff said.  Second, Community Server is great.  Tons of features and very good documentation.

More soon...