Tuesday, April 1, 2008

Dueling Assembly References - Slow Build Performance


This blog post summarizes what is ‘Dueling Assembly References’ and how it affects build performance in Web Site Project option.

Note: Before we go further, let me make sure that this only affects Web Site Projects and not Web Application Projects.

What is Dueling Assembly References:

In WSP, developers 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 below:

· .NET assemblies registered in the GAC – .Net Tab

· Class libraries built as projects in the same VS solution – Projects Tab

· COM components – COM Tab

· File-path based assemblies – Browse Tab

Here we’ll talk about Browse Tab - When we add a file-system based assembly using this tab, Web Sites will do below.

· Copy the assembly picked into the \bin directory of the project.

· (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 we build the solution, VS will automatically check those assemblies with .refresh files to see if a newer version of the original 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. However 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, VS 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).

Note: No .refresh files been added for dependent assemblies in the bin.

This dependency might give birth to ‘Dueling Assembly References’ probelem – See below:

Assume one Web Site Project contain references to two different assemblies say A.dll abd B.dll where each updated dynamically (using .refresh files). On the dependent hand assume, these two different dlls, in turn have dependencies on some assembly say C.dll.

There will be no dueling problem referenced assembly C.dll is the same version for both assemblies A.dll and B.dll. But it will cause problems if the AssemblyC.dll being used is different between the two.

How it will affect Build performance:

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:

· 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).

· 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. We can re-enable the auto-refresh behavior once you fix the shared assembly conflicts.

Why WAP do not have this problem:

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.

Thanks & Regards,

Arun Manglick || Tech Lead || +91 20 30230500 Ext: 620 | +91 9850901262

No comments:

Post a Comment