More about 'Web Site' and 'ASP.Net Web Application' projects

12 December 2006

After my last post about switching between these two types I actually started using it and found a major flaw in my solution. The problem is the declaration of asp.net and html control variables. Is WS these are done magically in the background and are in a partial class file generated during run-time. In WAP you put them in default.aspx.designer.cs files. So after using WS for a while there is no easy way to synchronize the designer.cs files with the controls on the aspx page. It would be nice if Visual Studio could do this and it doesn't at least not in my version which reports "Warning 24 Generation of designer file failed: Object reference not set to an instance of an object." a lot if not always. I also need to be Visual Studio independent.

So I did some more research and found out about VS 2005 Web Deployment Projects, which allow you do compile and merge all the assemblies in a WS project into a single assembly, better yet it is using the command line tool 'aspnet.merge.exe' so you could do this without Visual Studio.

So here's how I now build my deployment package for the staging and production servers.

For development in Visual Studio or Notepad2 I'm now always using WS, it is so much easier not having to recompile all the time. When I done with testing I run a batch file to create a new zip file with the files I need to copy to the servers.

As usualy I'm using some 4NT specific features so the batches wont work with cmd.exe but you get the idea.

I first compile the site with aspnet_compiler.exe into a new directory

I then use aspnet_merger.exe to merge all the assemblies into a single one for which I can specify a name.

I then run a second batch which compares the new directory with the previous version and adds all changed files into a zip file which I can then upload to the live server.

I ran into some small problems:

1. Apparently you can specify an assembly.cs file in aspnet_merger to specify the meta data for the single assembly. This didn't work but I needed to combine two different files anyway because I am using a common SolutionInfo.cs for all the different assemblies.

2. After I create the compiled site and try to run it, I get an error "Could not load type '__ASPNET_INHERITS'." in global.asax. The file has a single line "<%@ application inherits="__ASPNET_INHERITS" %>" what the heck is that? I found out that global.asax is not even suppose to be there anymore because the file bin/App_global.asax.compiled points the runtime to the correct class in the single assembly. So I just deleted global.asax and it works fine.

Here's the batch file for a specific project:

@ECHO OFF

SETLOCAL

set app=admin.twee.net
set target=%tnHostsDir\Deployment\%app\compiled
set iisSite=/LM/W3SVC/236937994/Root/Topas

:: empty target
rmdir /S /Q %target

:: create new assemblyinfo.cs
copy %tnHostsDir\Topas\SolutionInfo.cs %tnHostsDir\Topas\TweeNet.Topas.UI.Web.Info.cs %tnHostsDir\Topas\TweeNet.Topas.UI.Web\App_Code\AssemblyInfo.cs

:: compile site
%tnDnDir\aspnet_compiler.exe -m %iisSite %target -u -nologo

:: merge assemblies
%tnBinDir\aspnet_merge.exe %target -o TweeNet.Topas.WebUI -copyattrs

:: clean bin directory
PUSHD %target\bin
del /Q /E SongTitles.* WinTopas.* topas.*.* *.xml nunit*.* TweeNet.Topas.Unittests.* *.pdb 
POPD
:: and some files in the root
PUSHD %target
del /Q /E *.user *.sln *.csproj PrecompiledApp.config global.asax
POPD

:: now call the main script to find files to be updated
CALL %tnHostsDir\scripts\DeploymentPackageCommon.cmd %app

ENDLOCAL

@ECHO ON

And here's a more generic one to find the updated files:

@ECHO OFF

:: TweeNet Topas Deployment Synchronizer

:: Description:
:: =============
:: Batch to find all updated files to be copied to the live server.
:: The is done by comparing a source and a master directory.
:: 1. Find out which files are newer in the source
:: 2. Create a batch file to list those files which is 
::    then used to copy them but also as a history log
::    to see which files have been updated.
:: 3. Copy the new files into a unique directory
:: 4. Zip the content of that directory
:: 5. Copy the new files into the master directory
:: 6. Manually upload and deploy the files on the live server.

:: Usage:
:: =======
:: TopasDeploySync.cmd appName
:: where appName is one of the applications we use.

:: Requirements:
:: ==============
:: 4NT.exe 7.0 or higher is required for this
:: Robocopy.exe Version XP010 or higher
:: Windows NT 5.0 or higher

:: Assumptions:
:: ============= 
:: - All related files are in X:\hosts
:: - The source files are in X:\hosts\Deployment\appname\compiled
:: - The folders X:\hosts\Deployment\appname\wwwroot exists
:: - in the source \bin\TweeNet.Topas.Business.dll exists

:: History:
:: =========
:: Version 1.0 /  4-Oct-2006 Singapore
:: Version 1.1 / 12-Dec-2006 Singapore

:: Credits:
:: =========
:: written by Peter Hahndorf, contact me at www.twee.net/contact/ 

SETLOCAL

SET scriptVersion=1.0.10.0
SET scriptName=TopasDeploySync.cmd

:: we pass in the folder name for the application
SET appName=%1

:: the second parameter is optional for the hosts directory
:: if not specified we use \hosts on the current drive
iff %# == 2 then
	SET hostDir=%2
else
	SET hostDir=%@LEFT[1,%_CWD]:\hosts   
endiff

:: the third parameter is optional for the wwwroot directory
:: if not specified we use wwwroot on the current drive
iff %# == 3 then
	SET wwwrootDir=%3
else
	SET wwwrootDir=wwwroot   
endiff

:: sourceDir is the folder with the latest files
SET sourceDir=%hostDir\Deployment\%[appName]\compiled
:: masterDir the folder with the last updated version
SET masterDir=%hostDir\Deployment\%[appName]\wwwroot

:: get the version from the main assembly in the source
SET TopasVersion=%@VERINFO[%[sourceDir]\bin\TweeNet.Topas.Business.dll]

iff "%@files[%[sourceDir]\bin\TweeNet.Topas.Business.dll]" == "0" then
	@ECHO No Version information found at ^n%[sourceDir]\bin\TweeNet.Topas.Business.dll
	EXIT /B 15
endiff

:: if we have run this before, we need a new unique deployement directory
 iff "%@files[%hostDir\Deployment\%[appName]\%[TopasVersion]\]" == "0" then
	:: first time, deploy directory is simple the name and version
	SET uniqueToken=
 else
	:: Standard named directory is already there, use a different one
	:: winticks is pretty unique
	SET uniqueToken=_%_WINTICKS
 endiff

:: use the unique token we got above to build the name
SET DeployDir=%hostDir\Deployment\%[appName]\%[TopasVersion]%uniqueToken
:: the zip file to use, u- stands for update, f- would be full
:: we use the same unique token as above
SET zipFile=u-%[appName]-vs%[TopasVersion]%[uniqueToken].zip 

:: the batch file we create and use to copy the files to be updated
SET batchFile=%hostDir\Deployment\%[appName]\TopasSync_vs%[TopasVersion].cmd

:: temp file to store list of files to be copied
SET tempFile=%TEMP\%[appName]-vs%[TopasVersion].temp
:: temp file to redirect robocopy output into
SET tempFileSuppressOutput=%TEMP\%[appName]-vs%[TopasVersion]_output.temp

:: display some info
@ECHO ^n%scriptName Version %scriptVersion^n
@ECHO Create Deployment package for %[appName] %TopasVersion
@ECHO Source: %sourceDir
@ECHO Target: %DeployDir
@ECHO Master: %masterDir

:: get all files that are relevant and different, we use the fabulous robocopy to do this:
:: the /N* switches suppresses output.
:: the /L switch means we don't actually copy anything, just get a list of the files.
:: we only copy files that we really want on the live server, so loads of filters
robocopy.exe %[sourceDir] %[masterDir] /S /R:3 /W:10 /XX /L /NC /NS /NJH /NJS /NP /NDL *.aspx *.asmx *.ascx *.asax *.gif *dll *.jpg *.xml *.xsl? *.js *.ico *.css *.master *.skin /XD old obj topas /XF nunit.framework.dll TweeNet.*.xml TweeNet.Topas.UnitTests.dll Microsoft.Web.Atlas.dll WebDev.WebHost.dll app.css get_aspx_ver.aspx /Log:%tempFile >%[tempFileSuppressOutput]

:: we also want some *.exe files in the root
:: robocopy.exe %[sourceDir] %masterDir /R:3 /W:10 /XX /L /NC /NS /NJH /NJS /NP /NDL *.exe /Log+:%tempFile >>%[tempFileSuppressOutput]

:: count the number of lines, which represents the number of files.
:: if we have files in both directory, we get one extra blank line
SET numberOfNewFiles=%@LINES[%tempFile]

:: we now have a file with the list of all files to be copied

:: built content of the new batch file
@ECHO :: Topas Deployment Batch File >%batchFile
@ECHO :: created by %scriptName Version %scriptVersion >>%batchFile
@ECHO ::       created: %_ISODATE >>%batchFile
@ECHO :: Topas Version: %TopasVersion >>%batchFile
@ECHO ::   Application: %[appName] >>%batchFile ^n

@ECHO :: Files to be copied: ^n >>%batchFile
:: go through every line of the file and just output the filename
for /f %a in (@%tempFile) echo :: %@replace[%sourceDir,, %a] >>%batchFile

:: the copy command expects existing directories, robocopy can create them
:: but needs directory as input, not files, so we have to remove the file names
:: which is a bit messy with 4NT commands. dummy.xyz is the file to copy, 
:: it never exist, so robocopy just creates directories for us.
@ECHO ^n::Create directory structure with robocopy:>>%batchFile
for /f %a in (@%tempFile) echo robocopy.exe %@replace[%@filename[%a],,%a] ^t %@replace[%@filename[%@replace[%sourceDir,%DeployDir, %a]],,%@replace[%sourceDir,%DeployDir, %a]] dummy.xyz /NC /NS /NJH /NJS /NP /NDL >>%batchFile

:: this creates the actual copy commands
@ECHO ^n::Copy the files to deployment >>%batchFile
for /f %a in (@%tempFile) echo copy /Q %a ^t %@replace[%sourceDir,%DeployDir, %a] >>%batchFile

:: we also want to copy the files to the master directory to bring it up to date.
:: first directories, which may not be there
@ECHO ^n::Create directory structure with robocopy:>>%batchFile
for /f %a in (@%tempFile) echo robocopy.exe %@replace[%@filename[%a],,%a] ^t %@replace[%@filename[%@replace[%sourceDir,%masterDir, %a]],,%@replace[%sourceDir,%masterDir, %a]] dummy.xyz /NC /NS /NJH /NJS /NP /NDL >>%batchFile

:: and the files themselves
@ECHO ^n::Copy the files to master>>%batchFile
for /f %a in (@%tempFile) echo copy /Q %a ^t %@replace[%sourceDir,%masterDir, %a] >>%batchFile

:: delete the temp file
DEL /Q %tempFile

:: execute the sync batchfile we just created
:: this does all the copying, we don't want any output
CALL %batchFile >>%[tempFileSuppressOutput]

:: delete the temp file for outpur suppression
DEL /Q %[tempFileSuppressOutput]

:: check whether there were some new files
:: if not the Deploy directory has not been created
 iff "%@files[%DeployDir]" == "0" then
	:: delete the empty batch file
	DEL /Q %batchFile
	:: tell the user about this
	@ECHO ^nNo new files to be deployed
 else
	:: we have some new files
	:: now zip the target
	:: move into the directory above the content,
	:: this way we get a nice structure in the zip file.
	pushd %DeployDir
	:: create a new zip file
	zip -r -q %zipFile *.*
	
	:: copy the zip file to the news folder for easy upload
	copy /Q %zipFile %tnNewsDir
	
	:: move the batch file into the folder
	move /Q %batchFile %DeployDir
	
	:: back to our previous location
	popd
	
	:: tell the user we're done
	@ECHO ^N %numberOfNewFiles new files found
	@ECHO ^N Deployment package ready for upload at^N %tnNewsDir\%zipFile

 endiff

ENDLOCAL

Pages in this section

Categories

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