Switch between a 'Web Site project' and an 'ASP.Net Web Application project'

9 December 2006

Visual Studio 2005 now supports two different asp.net project types, the original file system based 'Web Site' (WS) type and as a web-download but included in Service Pack 1 the 'ASP.NET Web Application project' (WAP) type which is pretty much the way we used to do asp.net projects in .Net 1.x.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/WAP.asp describes this type and shows the pros and cons between the two types. So far I as only using WAP because that's how I always did it and it was much easier to port my applications from 1.1 to 2.0. However I liked the fact 'dynamic compilation' of the code-behind files in WS. If you have a bigger site it takes a while to compile all your code-behind code into that single assembly, and you have to do that all the time.

I played with WS a little but because of the deployment model for my main site, I decided to stick with WAP.
But I thought it would be nice to use WS during development and WAP for test, staging and production sites. The asp.net runtime looks at the page directive of an requested aspx page. If it finds the 'CodeFile=' attribute it knows it has to compile the class in that file and deals with a WS project. So by simply changing all CodeBehind= to CodeFile I could convert my WAP project into a WS project and could use the benefit of the dynamic compilation of the aspx.cs files.

Switching between the two. To switch between the two types I came up with a little bit of scripting:

The main task the batch files does is to replace CodeBehind= with CodeFile= and back. I also had to rename the folder 'App_code' to something else like 'AppCode', otherwise you get two copies of the same classes.

One other thing I had to change in my site was an HttpModule which was part of the single assembly. I guess it has to be in a well known assembly because it has to be set up in web.config. So I just moved the class out into it's own little assembly.

I started by using a plain old batch file and some external tools like 4nt.exe and RxFind  as well as Windows scripting host but the next day I ported the whole thing to a Windows Powershell script.

The script supports both IIS and the Visual Studio internal web server because I use both of the at different times. I first delete all temporary asp.net files because otherwise we may end up with two copies of the same classes.

The I check for the existance of my single web assembly, if is exists, I want to move from WAP to WS, if not its the other way.

I then replace all codebehind with codefile or the other way around, it's pretty easy to do in Powershell.

At the end I start the Application poll or the web server process again.

The solution doesn't require Visual Studio at all, but so far I only tested on a Windows 2003 Server, when using XP you don't have AppPools, just kill the ddlhost.exe process hosting the asp.net runtime. Also because you stop an AppPool you need to be an administrator, which is a pain because of course we all never ever log on as one of those. I usually always have one console window that runs under an admin account so I run the batch there.

Here's the script, save it as switch.ps1

# Powershell script to switch an asp.net project from a 'Web Site' type
# to a 'Application Web Project' type and back.
# Peter Hahndorf
# Singapore 11-Dec-2006
# www.twee.net/contact

# Configuration
$AppPoolName="DefaultAppPool"   # The IIS Pool used for the app
$WwwRootDir="c:\hosts\Topas\Tweenet.Topas.UI.Web" # The path to the web files
$WebUIAssembly="TweeNet.Topas.UI.Web.DLL" # The Name of the assembly
$VirtualDirName="Topas" # The name of the virtual directory
$ProjectFileName="TweeNet.Topas.UI.Web.csproj" # Name of the project file
$useIIS = $false # set to $true when using IIS or $false when using Cassini

#======================================================================

# Build some paths
$DotNetDirectory = $env:windir + "\Microsoft.NET\Framework\v2.0.50727"
$assemblyfile = $WwwRootDir + "\bin\" + $WebUIAssembly

# a function to replace text, I thought I had to use
# .net for this, but it's all built in.
function ReplaceString([string]$fileName,[string]$find,[string]$replaceWith)
{
  (Get-Content $fileName) | foreach-object {$_ -replace $find, $replaceWith} | set-content $fileName
}

# function to loop through files
function SwitchFileContent([bool] $toApplication)
{

	$files = get-childitem  $WwwRootDir -Include *.as?x -Recurse
	
	foreach ($file in $files) 
	{
	   if ($toApplication)
	   {
		  ReplaceString $file.fullname " CodeFile=" " CodeBehind="
	   }
	   else
	   {
		  ReplaceString $file.fullname " CodeBehind=" " CodeFile="
	   }
	}

	$files = get-childitem  $WwwRootDir -Include *.master -Recurse
	
	foreach ($file in $files) 
	{
	   if ($toApplication)
	   {
		  ReplaceString $file.fullname " CodeFile=" " CodeBehind="
	   }
	   else
	   {
		  ReplaceString $file.fullname " CodeBehind=" " CodeFile="
	   }
	}

}

if ($useIIS)
{
	# get the IIS Application Pool
	$pool = get-wmiobject -namespace root/MicrosoftIISv2 -class IIsApplicationPool|where {$_.name -match $AppPoolName}
	
	# Stop the application Pool
	$pool.Stop()
}
else
{
	# don't show error if the process does not exist.
	$server = get-process -name webdev.webserver -ErrorAction SilentlyContinue
	
	if ($server -ne $null )
	{
		stop-process -name "WebDev.WebServer"
	} 

}

# Delete all temporary asp.net files for this app
$tempDir = $DotNetDirectory + "\Temporary ASP.NET Files\" + $VirtualDirName + "\"

if ((test-path $tempDir))
{
   Remove-Item $tempDir -Recurse -force
}

if ((test-path $assemblyfile))
{
   "Switch to Web Site Project..."

    # delete the web assembly
    Remove-Item $assemblyfile
	
	# Enable automatic compilation and additional classes in app_code
	$cmdLine = $WwwRootDir + "\AppCode\"
	Rename-Item -Path $cmdLine -newName "App_Code"
	
    SwitchFileContent $false
}
else
{
    "Switch to ASP.Net Web Application Project..."

    # replace the attributes
    SwitchFileContent $true

	# disable App_Code
	$cmdLine = $WwwRootDir + "\App_Code\"
	Rename-Item -Path $cmdLine -newName "AppCode"

	# Use MsBuild to compile the single web assembly
	$startInfo = new-object System.Diagnostics.ProcessStartInfo
	$startInfo.FileName = $DotNetDirectory + "\msbuild.exe "
	$startInfo.Arguments = $WwwRootDir + "\" + $ProjectFileName + " /t:Build /nologo"
	$startInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden

	$process = [system.Diagnostics.Process]::Start($startInfo)	

}

if ($useIIS)
{
	# Start the pool again
	$pool.Start()
}
else
{
	$startInfo = new-object System.Diagnostics.ProcessStartInfo
	$startInfo.FileName = $DotNetDirectory + "/WebDev.WebServer.exe"
	$startInfo.Arguments = " /port:80 /path:" + $WwwRootDir
	$startInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden

	$process = [system.Diagnostics.Process]::Start($startInfo)
}

"Done"



Pages in this section

Categories

ASP.Net | Community | Development | IIS | IT Pro | Security | SQL (Server) | Tools | Web | Work on the road | Windows