Common Gotcha: Slow VS 2005 Web Site Build Performance Because of “Dueling Assembly References”
September Update: For more details on how to optimize web project build performance in VS 2005, please review this blog post I did here.
Symptom:
When building a VS 2005 Web Site Project you experience very slow build times – where compilation seems to mysteriously pause for 30+ seconds (or even minutes), before continuing each time you do a build or F5 of the project.
This compilation performance slow-down might be the result of a problem I call “dueling assembly references”. This can occur when you setup multiple “automatic refresh enabled” file-system assembly references within your web site project to multiple assemblies that in turn share common assembly dependencies whose version numbers are different. The post below describes this in more detail and how to fix it.
Some background on how file-assembly references work:
Developers using VS 2005 Web Site Projects can add references to assemblies in multiple ways. When you right-click on a project and choose the “Add Reference” menu option, it provides a dialog like the one below that allows you to reference .NET assemblies registered in the GAC, class libraries built as projects in the same VS solution, COM components, and file-path based assemblies that you pick using the “Browse” tab of the “Add References” dialog:
When you add a file-system based assembly reference using the “Browse” dialog above, VS 2005 Web Sites will copy the assembly picked into the \bin directory of the project as well as (by default) add a .refresh file into the \bin directory that contains a relative path string that points back to the original assembly path location:
When you build the solution, VS will automatically check those assemblies with .refresh files to see if a newer version of the assembly is available, and if so automatically re-copy the assembly and re-compile the solution using it. This avoids you having to manually update the assembly yourself every time it changes (it also avoids you having to check-in the assembly under source control). If you don’t want this “automatic refresh” capability, you can delete the .refresh file – in which case VS will not check the timestamp nor update the assembly for you.
Because assemblies often have dependent assembly references, Visual Studio will also automatically copy dependent assemblies that a reference assembly requires into the \bin directory of the target web-site as well. For example: if you setup a file-based reference to ClassLibrary1.dll, which in turn uses an assembly called SharedLibrary.dll that resides in the same directory, Visual Studio will make sure both assemblies are copied into the Web Site’s \bin directory (note: if it didn’t do this then the app would fail to run).
Dueling Assembly Reference Problem
The problem I refer to as a “dueling reference” occurs when you setup multiple file-based references to assemblies from a VS 2005 Web Site Project that are each updated dynamically (using .refresh files), and which in turn have dependencies on different versions of a common shared assembly library.
For example, assume your web-site has a reference to AssemblyA.dll and AssemblyB.dll – which in turn each reference AssemblyC.dll. This will work without problems if the referenced AssemblyC.dll is the same version for both AssemblyA.dll and AssemblyB.dll. But it will cause problems if the AssemblyC.dll being used is different between the two:
In cases where AssemblyC.dll is different, VS ends up copying the AssemblyC.dll file twice for each build – since it continually thinks that the assembly has been updated (once for each reference). This ends up requiring all references to be recalculated by the compiler, and a full re-build to occur within the IDE. This will cause build performance to slow down dramatically, and will cause builds to appear to pause as VS does this reference recalculation and fix-up on every single build.
How to Fix This Problem
There are a couple of ways to fix this problem:
1) The “most correct” way to fix this issue is to make sure your class library references are built against the same version of any dependent assemblies. This is good to-do not just to fix the above build performance problem, but also because it decreases the likelihood of introducing hard to figure out bugs in your application (at runtime only one version of the shared assembly is going to be used –so if you don’t fix this at least one of your dependent assemblies will end up running against an assembly it wasn’t built/unit-tested with).
2) The “easiest quick fix” way to resolve this issue is to modify one or more of your assembly file-based references to not be “automatic refresh enabled”. You can do this by deleting the .refresh files within your \bin directory that produce that shared conflict. This will avoid having VS auto-update the assemblies, and so will prevent the dueling update conflict altogether. You might find it useful to quickly disable this behavior by deleting the .refresh files as a stop-gap, and then re-enable the auto-refresh behavior once you fix the shared assembly conflicts.
Note that VS 2005 Web Application Projects don’t have this dueling reference issue (since they don’t use .refresh files for file-assembly references). So you won’t have this build-performance problem with it (instead it will just pick one version of the shared assembly to use). However, you still want to be careful about cases where you have two components built against two separate versions of a shared assembly – since this can still cause hard to understand behavioral bugs at runtime that end up bypassing your carefully written unit-tests (which were run against a different version of the shared assembly).
Hope this helps,
Scott