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.