Problem referencing different versions of same assembly in Team Build (issue with file references)


 

Problem – Build two projects referencing the assembly of same name but of different version fails in Team Build

Scenario – Consider two projects (Proj1 and Proj2) that refer to the same assembly (lib.dll) of different versions (1.0.0.0 and 2.0.0.0). For example

$/TP1/Common/v1/lib.dll

$/TP1/Common/v2/lib.dll

$/TP1/Proj1/Proj1.sln

$/TP1/Proj1/Proj1/Proj1.csproj (file reference to ..\..\Common\v1\lib.dll,1.0.0.0)

$/TP1/Proj2/Proj2.sln

$/TP1/Proj2/Proj2/Proj2.csproj (file reference to ..\..\Common\v2\lib.dll,2.0.0.0) 

Investigation result – This works fine on desktop scenario but is broken inside Team Build.

The user has to make sure

  1. Specific Version” flag is be set for the file references
  2. User has common folder checked in at the correct location inside source control
  3. Private” flag (for copy local for the referenced assembly) is set to false.  

The undesirable effect for the above mentioned changes is that user will not be able to run the assembly from the drop site. For example in our scenario –

  • Build will be successful.
  • At the drop site you will have binaries for proj1 and proj2.
  • Assemblies will not be usable because they will need lib.dll (1.0.0.0 & 2.0.0.0) at drop site but we can not have two files with same name at drop site
  • If we set “Private” flag to true for proj1.csproj and proj2.csproj then also assemblies will not be usable because as part of build process for proj1.csproj, the lib.dll (1.0.0.0) will be copied to $(outdir) (binaries folder). The build for proj2.csproj will override the lib.dll (2.0.0.0). This will result in unpredictable results.
    • If lib.dll (both v1 and v2) contain same methods that does different things, then assemblies for proj2 and proj1 will show wrong behavior.
    • If lib.dll (both v1 and v2) contains different method signatures then application will crash.

Recommendations/workarounds

  1. Avoid using file references
  2. If you need to use file references – different version of same assembly, then you can not run the assembly directly from the drop site and you need to have custom steps to copy the assembly to separate folder (with correct versions of referenced assemblies)

Related [post 

 


Comments (5)

  1. Gautam says:

    Is this “and” or “or” i.e. any one will do the job or all 3 is required? I think either 1 or 3 should do it.

  2. Manish Agarwal says:

    (1) is always “recommended/required” with file references.

    (2) Is required if you want to run the executable from the drop site. This has no impact on the build.

    And/Or between (1) and (2) depends on the scenario. At the minimum (1) is required and (2) is optional provided the hint path of file reference is proper on build machine.

  3. While building inside VS, each project/solution is built by default into its own directory/sub-tree. However, in Team Build, all the projects are built by default under one directory. This directory is then copied to the drop location.

    When the reference is added, by default it has a property called CopyLocal is set to true. This mean build process (MSBuild) will copy the referenced assembly to the local folder where the binary is getting built so that you can deploy/run from this folder directly. On building from VS, since each project is building in separate folder, there is no clash between the two copies of the two versions of the lib.dll. However, in Team Build, since both the binaries are building in the same folder, the second binary see that the lib.dll already exists (copied by the first binary) and tries to use it.

    The workaround for this issue for this release of Team Build is –

    1) Either set SpecificVersion property of the reference to true for both. (Select the reference and do F4 to get the properties window in VS)

    2) Or, set CopyLocal property of the reference to false for both.

    With either workaround, since there is a clash, the run of the above built assembly from drop location will not work unless the right version of lib.dll is in GAC. If this is the requirement then you can have a post build step in each of these project to copy the lib.dll into a sub-folder (of different name like v1, v2) and have config file for your assembly with probing path. For more, refer http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/gngrfprobing.asp.

  4. Oliver Hausler says:

    In large solutions, it might well happen in practice, that some components point to different (older) versions of the same assembly than others. Sometimes it just happens because one component needs a new feature, which is implemented into a referenced assembly. This component than is linked to the upgraded assembly with the newer version number. If the same solution contains other components pointing to that same referenced assembly, it might be desired that it keeps pointing to the previous version, to avoid having to re-test all components.

    About best practices:

    Please comment your thoughts about changing the filename of assemblies, so it contains the current version, e.g. myassembly.1.0.0.0.dll, myassembly.1.2.0.0.dll, etc., and keeping versions side-by-side. That way, any component of a solution can consume from different versions of the assembly at the same time.

  5. Chris Coffman says:

    I have a slightly different problem i want the final drop to project outputs separated.

    i.e. sln1 has prj1 and prj2. i want the drop to (roughly) correspond to

    drop

      proj1binaries

      proj2binaries

    This makes packaging easier as i do not have to keep track of individual files in the drop location. There could even exist ( as noted above) two files of the same name. i.e. a kludgy way to obfuscate data in a dll. ( don’t ask 🙂 ) It seems like the BinariesRoot property should be able to be set for each project. Is it as simple as adding that property to the .??proj files?

    Thanks.

    I do not see an easy way to make this happen in the targets files.