10

What is the best practice in the .NET world to manage transitive dependencies that cause version conflict ?

In details : Project A Depends on Project B which in turn depends on library C

also

Project A also depends on Project X which depends on a DIFFERENT and (potentially) incompatible version of library C.

A->B->Cv1.0
&
A->X->Cv2.0
where
Cv1.0 <> Cv2.0

  • Is there a way to make this work ?

  • Can it be done WITHOUT using the GAC ?

  • Can it be done even if B and X are in binary format only (source not accessible) ?

In other words is there a way where I can have Project B and X each using their own dependencies when used together in Project A without causing conflicts.

NOTE: I realize that ideally I should not have this problem at all but as reliance on external libraries expands this will be an unavoidable side effect. So I am wondering should it occur how best to deal with it.

reuben
  • 3,360
  • 23
  • 28
Newtopian
  • 7,543
  • 4
  • 48
  • 71
  • 1
    Ideally you should re-build or update B so that it depends on the current version of C. If you cannot do that you can try to re-map versions. http://stackoverflow.com/a/11126867/48082 This is not guaranteed to work! – Cheeso Jul 04 '12 at 21:38
  • 2
    Agreed that ideally one should clean up their projects to avoid the situation in the firt place but this is not always possible, hence the question. – Newtopian Jul 04 '12 at 21:49
  • 1
    remapping DLL in some way is good only if both versions are compatible with each-other. Worst case scenario here is that in the event they are not compatible is there a way to have each middle dependencies use the version it was made for and still perform the work the main project is expecting of them. – Newtopian Jul 04 '12 at 21:51
  • yes, you can also load multiple distinct versions of an assembly. To do that, use distinct AppDomains. http://stackoverflow.com/a/1189491/48082 – Cheeso Jul 04 '12 at 21:53
  • Interresting ! Seems a bit expensive (both in brain power and computing resources) but at least theoretically places a solution in the realm of the doable. I will check it out... you should make this an answer though, that way I (and maybe others) can up-vote it. – Newtopian Jul 04 '12 at 21:56
  • 2
    The CLR can load the 2 versions inside the AppDomain (I already observed that fact, I guess referencing through strong-naming is enough). But by not refactoring/recompiling external dependencies you expose yourself to a lot of hard trouble. Solution that will work on your machine might not work in other exotic production environment. – Patrick from NDepend team Jul 05 '12 at 13:37
  • Agreed that it is quite troublesome and certainly something to steer away from whenever possible. However this only pushes the unavoidable for a later time. I've seen the situation in Java World with the emergence of Maven, Git etc making sharing tidbits of code easier and easier. I encountered such situation on many occasion. The tooling did evolve to help in resolving such conflict but the problem remains largely unsolved. OSGI does resolve it but the overhead it imposes is just too big. – Newtopian Jul 05 '12 at 16:41
  • Just curious though, what do you imply with "exotic environments" is it different possible Windows or other implementations of .NET (Mono, Xamarin etc) – Newtopian Jul 05 '12 at 16:48

1 Answers1

5

There are a lot of similar questions On Stack Overflow. For e.g. Referencing 2 different versions of log4net in the same solution

Summary:

  1. Ensure that you deploy the assembly C in folders 1.0 and 2.0 respectively within the folder containing the main executable.
  2. Change app.config file and include something like following:
 <configuration>
   <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
       <assemblyIdentity name="C" publicKeyToken="C's public key token" />
        <codeBase version="version string for C1.0 e.g. 1.0.0.0" href="1.0\C.dll" />
        <codeBase version="version string for C2.0 e.g. 2.0.0.0" href="2.0\C.dll" />
       </assemblyIdentity>
      </dependentAssembly>
    </assemblyBinding>
   </runtime>
 </configuration>

you can get the public key token of C using sn -T C.dll

If v1.0 and v2.0 of C have different public key (though ideally they shouldn't) then include two dependentAssembly tags.

Community
  • 1
  • 1
Amit Mittal
  • 2,646
  • 16
  • 24
  • 1
    there are indeed many questions but none really answered my question. My understanding of assemblyBinding was that it allows to specify which DLL is used for which namespace and to redirect calls accordingly. Here each call path will ultimately target a single DLL. In the scenario I exposed we have two identical call path that must target two different DLL. The only difference is that each is called from a different "parent" DLL. That is unless I did not understand correctly the purpose of assemblyBinding. – Newtopian Jul 05 '12 at 12:37
  • Tha said I guess that if the publisher of the two dependencies (B and X in the example above) were to specifically hardwire the DLL dependency (C above) versions when producing their libraries everything would just work (provided both C Dll are uniquely identifiable files ie different name and/or paths). I will have to experiment a bit with assemblyBinding see what can be done – Newtopian Jul 05 '12 at 12:41
  • If C is strongly named, above should work and should be able to load correct version of C for B and X. If it is not strongly named but still has a version (as specified through AssemblyVersion attribute) then you can experiment with removing the publicKeyToken and see if it still works. If that does not work, I think only hope would be AssemblyResolve event. I will try to check all these ifs and buts and share the results. – Amit Mittal Jul 05 '12 at 14:09
  • Thanks Amit for you time and effort on this, Curious to see what you will find – Newtopian Jul 05 '12 at 16:33