An interesting conversation occurred this morning on Twitter between Aaron Powel, Sebastiaan Janssen, Peter Gregory and myself about how we structure our Visual Studio projects when working with Umbraco, so I thought I’d share how I currently set mine up. I’m not saying this is the right solution, as I feel there are a number of flaws in it, but so far it has served me well.
Folder Structure

Above is an example of the folder structure I will usually follow, which is broken down as follows:
- Assets - All creative files, and copy docs
- Db - Database backups and SQL scripts
- Lib - Library assembleys. I will usually copy the Umbraco dlls into a sub folder in Lib.
- Src – Contains all my source code and web folder.
- ProjectName.Web – My Visual Studio web project folder.
- Web – My actual web folder containing the Umbraco web files, merged with my web projects output.
- Tools - Contains useful build tools which I will get on to shortly.
I will usually host my .sln file within the root of the folder structure for easy access.
Project Structure
Within Visual Studio, I will create folders for all the core Umbraco folders, plus one for Config, which is not exactly what you think it is, but I’ll get on to that shortly.
The next step is to set up the projects post build event which does 2 things. Firstly, copies over all the relevant projet files to the Web folder, and secondly, merges my config files, but I’ll get on to that shortly, I promise =).
REM #################################################
REM # Copy files
REM #################################################
xcopy /s /y "$(ProjectDir)Css\*.css" "$(WebProjectOutputDir)\..\Web\css\"
xcopy /s /y "$(ProjectDir)Images\*.jpg" "$(WebProjectOutputDir)\..\Web\images\"
xcopy /s /y "$(ProjectDir)Images\*.gif" "$(WebProjectOutputDir)\..\Web\images\"
xcopy /s /y "$(ProjectDir)Images\*.png" "$(WebProjectOutputDir)\..\Web\images\"
xcopy /s /y "$(ProjectDir)MasterPages\*.master" "$(WebProjectOutputDir)\..\Web\masterpages\"
xcopy /s /y "$(ProjectDir)Scripts\*.js" "$(WebProjectOutputDir)\..\Web\scripts\"
xcopy /s /y "$(ProjectDir)UserControls\*.ascx" "$(WebProjectOutputDir)\..\Web\usercontrols\"
xcopy /s /y "$(ProjectDir)Xslt\*.xslt" "$(WebProjectOutputDir)\..\Web\xslt\"
xcopy /s /y "$(ProjectDir)favicon.ico" "$(WebProjectOutputDir)\..\Web\"
xcopy /s /y "$(TargetDir)*.dll" "$(WebProjectOutputDir)\..\Web\bin\"
REM #################################################
REM # Merge config files
REM #################################################
REM FOR /F %%a IN ('DIR /B $(WebProjectOutputDir)\..\Web\Config\*.config') DO (
REM CALL "$(SolutionDir)Tools\Scripts\XmlConfigMerge.bat" $(SolutionDir) $(WebProjectOutputDir)\..\Web\Config\ $(ProjectDir)Config\ %%~na %%~xa $(ConfigurationName)
REM )
REM CALL "$(SolutionDir)Tools\Scripts\XmlConfigMerge.bat" $(SolutionDir) $(WebProjectOutputDir)\..\Web\ $(ProjectDir)Config\ Web .config $(ConfigurationName)
IIS
Personally, I prefer to set up my websites in IIS, rather than using Casini. I won’t go through this step, as it’s not really doing anything special, but one thing I do tend to do, is set up a host header in my hosts file, again nothing special, but I will share a .bat script I have pinned to my quick launch for easy editing.
start /d C:\Windows\System32\ notepad.exe C:\Windows\System32\drivers\etc\hosts
Debugging
During development, I will generally have a browser window open at all times with the site running, so to debug, I will usually use “Attach to Process” in Visual Studio (Ctrl + Alt + P) to attach to the w3wp.exe process. From there you can run through breakpoints as per usual.
Config Merging
Ok, so now to the semi-interesting bit I promised =), Config merging.
One of the main reasons I chose the project structure I use is to allow total separation of my project specific files, from the Umbraco web files. By separating it in this way, I should, in theory, be able to take a fresh Umbraco install, run the post build events, and have a fully setup client website (In theory). This should also help with upgrades, where I can grab a new version, copy everything over, and trial the website out. The difficulty for me came with config files.
What I didn’t want to do was to copy the whole config file over, as I couldn’t be sure the configs would always stay the same, plus it’s just a waste (a small waste granted, but a waste none the less), plus I wanted to have different configs for different machines and different build configurations.
To solve this problem, I use a tool called XmlConfigMerge which allows you to merge XML config files together. This way I only have to store in my solution the differences in the config files, and not the whole config file itself. To allow for different configs per machine and build process, I simply use the built-in Visual Studio $(ConfigurationName) macro, along with the DOS %computername% global variables to automatically select which config files to merge. It does this based on the name of the config file, which would be one of the following.
- ConfigName.config
- ConfigName.BuildConfiguration.config
- ConfigName.BuildConfguration.ComputerName.config
Looking back at my post build command, you’ll now see the 2 statements at the bottom, the first of which loops through all files in the config folder, and the second specifically merges the web.config. These commands get passed to the following .bat script.
@echo off SET SLN_DIR=%1 SET DST_FILE=%2%4%5 SET TMP_FILE=%2%4_tmp%5 SET SRC_FILE1=%3%4%5 SET SRC_FILE2=%3%4.%6%5 SET SRC_FILE3=%3%4.%6.%COMPUTERNAME%%5 COPY "%DST_FILE%" "%TMP_FILE%" /y IF EXIST "%SRC_FILE1%" ( "%SLN_DIR%Tools\XmlConfigMerge\XmlConfigMergeConsole.exe" "%TMP_FILE%" -m "%SRC_FILE1%" ) IF EXIST "%SRC_FILE2%" ( "%SLN_DIR%Tools\XmlConfigMerge\XmlConfigMergeConsole.exe" "%TMP_FILE%" -m "%SRC_FILE2%" ) IF EXIST "%SRC_FILE3%" ( "%SLN_DIR%Tools\XmlConfigMerge\XmlConfigMergeConsole.exe" "%TMP_FILE%" -m "%SRC_FILE3%" ) CALL "%SLN_DIR%Tools\Scripts\CopyIfNewer.bat" "%TMP_FILE%" "%DST_FILE%" DEL "%TMP_FILE%" /f /q
What this script does, is copy the source config to a temp file, then checks for existence of global, build specific and user specific configs in the config folder and merges them with the temp config file. Finaly, a check is made to see if the temp file was changed, and if so, overwrites the existing config file. The script for checking whether the file has changed is as follows:
@echo off echo Comparing two files: %1 with %2 if not exist %1 goto File1NotFound if not exist %2 goto File2NotFound fc %1 %2 /w if %ERRORLEVEL%==0 GOTO NoCopy echo Files are not the same. Copying %1 over %2 copy %1 %2 /y goto END :NoCopy echo Files are the same. Did nothing goto END :File1NotFound echo %1 not found. goto END :File2NotFound copy %1 %2 /y goto END :END echo Done.
Conclusion
As I said at the beginning, this is far from the perfect solution. It is one that I have adapted over time, having worked in multi-developer environments. Some of the flaws right now are that the build process can take a while with all the config merging, and because all of your source files are separate, you are currently forced to do a build to copy over any files to the Web folder. I’m sure I will continue to change this as I go along, but hopefully this may be handy for people starting out.
If you have any comments on my setup, or would like to share your own, please drop me a comment below.
Why not just use MSDeploy in VS 2010.
Check out this awesome demonstration and post about MSDeploy by Hanselman:
http://www.hanselman.com/blog/WebDeploymentMadeAwesomeIfYoureUsingXCopyYoureDoingItWrong.aspx
Cool.
As I say, it’s something I’ve built up over time. Maybe it’s time for a review =)
Matt
I used a set up similar to this for a fair while too, but used MSBuild to do the copying as it was easier to control for different build targets and environments.
That said I don’t like this kind of set up because of the following:
* It’s not possible to modify UI files in VS and have the changes visible until you recompile. That’s a real pain when you’re tweaking a user control or debugging a XSLT
* It adds time to a build, sure it may not be a lot of time (depending on the number of files) but when you’re changing 1 line of .NET and you have to compile, wait for all the unchanged files to be copied, and then refresh through an app pool reset it just gets tiresome. And if you’re working with a solution that is 5+ projects you don’t want to add anything to the build time that you don’t need
* You have to run VS as an admin to debug which I don’t like. I don’t like having to give any application which isn’t an installer admin rights. I shouldn’t need to do that, I should be able to run as a non-privileged user and still do all that I want
It is true that when you’re running through cassini you loose the ability to view the site without VS running, but I’ve always worked in a company where we had dedicated dev servers for that kind of thing.
Hey Aaron
These are pretty much the reasons why I’m currently looking to review it.
Trying some new ideas out on a new project, so will probably have a follow-up post some time in the future.
Thanks for your comments though, all very valid.
Matt
You can easily get around the slowdown of copying files to the target. We use pretty much the same approach as described by Matt, but with one difference: We use Robycopy for the file copy.
Robocopy is quite a handy tool as it will compare the files for copy and only copy those that changed from the last copy. Meaning that if you change just the one file that’s all that gets copied to the target.
We’ll probably still go over til MSBuild at some point as I want the build system to handle tagging releases in SVN and versioning the assemblies for release but for the basic build/copy scenario it works quite well with the .cmd file
Cool, will definatley check Robocopy out.
That’s kinda what I was trying to get my batch file to do, but admittedly, it doesn’t do a great job at it =)
Matt
Hi Matt,
Great post. I too use a similar setup, although it’s been a while since I’ve had the opportunity to work with Umbraco.
To get round the ‘changes visible until you recompile’ issue I wrote a small VB macro for VS 2008. I hooked this up to the Ctrl-Shift-S keyboard shortcut. Email me if you want a copy of the macro.
However, Robocopy may be a more robust solution.
Cheers,
Chris
An interesting read, thanks. I’m always looking at ways to make managing Umbraco sites easier, as I suspect no one has hit on a solution that they’re fully satisfied with.
My current preference when setting up sites is to simply copy a fresh Umbraco build into an empty Web Application Project and exclude any folders that I won’t be touching (/umbraco etc). I use git and have built up a decent’ish .gitignore file so nothing that’s part of Umbraco gets put under source control. So far that seems to be working for me the best, though i’d be interested to know if people think this is a bad idea!
I’ve tried variations of putting my files in a separate VS project and using post-build events, but I got frustrated with having to build every time I wanted to make minor UI changes.
Hey Rick,
Thats pretty much the layout that I’m trying out now, so will let you know how I get on.
I think another of the reasons for my current structure is also that of me using SVN in the past, which dots .svn folders everywhere, so having a separate web folder made it easier for uploads.
Matt
Matt,
Sometime on, how are you doing things now? I like the idea of having stuff in a Web Application Project (primarily so that I can use MSDeploy), but also don’t like the requirement to have to build & copy even minor XSLT/Razor changes.
I have changed somewhat how I work now, working straight out of the website folder. I gets around not having to build to copy css files etc, but I don’t think it’s as flexible when working in teams (Before I joined HQ, I was a sole developer, so that wasn’t a problem for me).
I’ll see if I can get around to writing an update blog post, with a comparison of the 2 techniques. I do still think this is a valid setup though.