6

I have a solution with 2 projects: Proj1 and Proj2, where Proj1 is the startup project.

Proj1 references Proj2 (in order to call Proj2's class) and it has existing code that also references a 3rd party DLL called A, versioned at 1.0.0.0.

Proj2 references the same 3rd party DLL A, but it references it at version 2.0.0.0, since the class in this project needs newer implemenation that was not available in 1.0.0.0.

So far I've tried the following: 1. Switched "Specific Version" to true when referencing A 2. Added a folder called "v2Folder" in Proj2 and added A v2.0.0.0 to it, set its "Copy to Output Directory" to Copy Always 3. Added "probing path" to app.config to point to the sub-folder with the v2.0.0.0 DLL

What I want is to see A v1.0.0.0 in the normal \bin\ folder, and A v2.0.0.0 in \bin\v2Folder, and I expect that when I run my Proj1.exe, Proj1's old code will still call A v1.0.0.0's methods, and only call A v2.0.0.0's methods when calling what is implemented by Proj2.

The problem is, when I build my solution, v1.0.0.0 got replaced by v2.0.0.0, the build log has something like "No way to resolve conflict between "A, Version=2.0.0.0, Culture=neutral, PublicKeyToken=blah" and "A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=blah". Choosing "A, Version=2.0.0.0, Culture=neutral, PublicKeyToken=blah" arbitrarily.".

Can someone help?

codetc
  • 147
  • 1
  • 3
  • 5
  • Thank you Adriano for your suggestion. My problem is that I want my app to use both versions in different parts of the code, ie I want to keep both Version 1 and 2. – codetc Jul 18 '12 at 22:06
  • Why not upgrade Proj1 to use newer version of DLL "A"? Using different versions of the same DLL is not recommended and not actually possible without hacking with the AssemblyResolver. Here is link describing how NuGet might be able to help you out in this situation: http://blog.davidebbo.com/2011/01/nuget-versioning-part-1-taking-on-dll.html – Jesse Webb Jul 18 '12 at 22:07

3 Answers3

6

It's achievable even if the two dll versions have the same public token.

Here the steps to achieve this:

  • Ensure that the two version of the dll will be copied to the target directory
    • Add the two dll's as content items of the project
    • Enable local copy for both
  • Ensure that the two version of the dll will be referenced at compile time
    • Add the two dll's in the project as references
    • Disable local copy for both

Just add the references is not enough, since only the newer one will be copied (even if you enable local copy for both). This give a project tree like this:

Project tree

  • Ensure that the two version of the dll can be distinguished at compile time
    • Add an alias for at least on of the references

Aliases field position

  • Reference the libraries in the code using extern alias (see @drf response)

Example code

At this point you can compile but you still have problems at run-time. To fix those:

Auto-generate binding redirects checkbox position

  • Edit app.config to add an assemblyBinding.
    • assemblyIdentity is the concerned dll.
    • bindingRedirect map a version range (oldVersion) to a fixed version (newVersion).
    • codeBase map a fixed version to a file path (href).

bindingRedirect newVersion and codeBase version must match and match the version of the used dll.

Here, it's all about the dll assembly version, not the file version app.config example

Here is the program output:

Console output

This hack source code is available here.

Edit: As sharpiro commented, there is still a warning when the project is built, this is related to this msbuild bug for witch this answer is a workaround.

Orace
  • 7,822
  • 30
  • 45
  • 1
    I know we're supposed to avoid comments like these, but thank you for this step-by-step solution and example depot. It's so useful. – ysalmi May 25 '20 at 07:38
  • Good answer and solution, but unfortunately it will create 2 build warnings whenever the referencing project is built – Sharpiro Jun 04 '20 at 16:24
  • @Sharpiro what does they looks like ? Edit : Msbuild bug : https://github.com/Microsoft/msbuild/issues/715 – Orace Jun 04 '20 at 16:56
2

This may be possible using the extern alias feature. When you compile, include an alias to the DLL files, e.g.:

csc.exe ... /reference:AV1=v1.0.0.0/A.dll /reference:AV2=v2.0.0.0/A.dll

(This can be set in Visual Studio as well by changing the aliases property of the reference.)

In the cs files you could use extern alias to reference the namespaces:

extern alias AV1;
extern alias AV2;
// using statements

This allows you to reference each version independently:

var v1foo = new AV1::Foo();
var v2foo = new AV2::Foo();

For projects that use only one of the DLLs, you can include a reference to the desired version.

// ProjA cs file
extern alias AV1;
using Foo = AV1::Foo; // alternately, path to namespace
...
var foo = new Foo(); // from version 1 of library


// ProjB cs file
extern alias AV2;
using Foo = AV2::Foo; // alternately, path to namespace
...
var foo = new Foo(); // from version 2 of library

This allows both versions of the DLL to be referenced independently in the same solution. (MSDN Reference.)

drf
  • 8,461
  • 32
  • 50
  • Thank you drf for your sample code. I tried this out just now, except I left Proj1 alone (so its alias is still at "global"), and only specified Proj2's code to use a "extern alias AV2". I've also added a "probing privatePath="v12;"" in my app.config so that it knows where to load the v12 DLLs from. At run-time, I'm still getting a "FileLoadException" saying "Could not load file or assembly 'A, Version=2.0.0.0, ....". – codetc Jul 19 '12 at 21:40
  • To fix the run time issue, you should disable Automatic Binding Redirection https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/how-to-enable-and-disable-automatic-binding-redirection and create a custom dependentAssembly block in App.config like this https://github.com/Orace/Hacks.NET/blob/master/OneLibraryTwoVersions/App.config – Orace Nov 20 '19 at 09:44
0

Rename 3rd party dll version 2.0 to name A_2.dll, and add reference to renamed dll.

Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
  • will not work since the internal namspaces and class names remain the same and this will lead to naming collisions – Robot Mess Sep 19 '14 at 13:34