Referencing Multiple Versions of the Same Assembly in a Single Application
We've all been there — our application depends on a version of assembly that's incompatible with the version that's referenced by the NuGet package we've just installed. The situation gets worse when the conflict occurs between two third-party NuGet packages. What can we do about it? What does Kentico do about it?
The Problem Called "Dependency Hell"
Sometimes, we get to a point where different parts of our application depend on different versions of the same DLL. And by "the same", we mean assemblies with the same simple assembly name. In many cases, when there were no breaking changes between the versions, this situation doesn't pose a problem. We can easily instruct the app to use only one of the DLLs, even without rebuilding the app. We call this assembly binding redirection and, in fact, it has been a standard approach for NuGet since version 1.2 (March 2011).
The real problem arises when the DLLs are incompatible — the public types and members of the API differ, etc.
If you're lucky, the developer of the third-party component used semantic versioning and you can tell whether the DLLs are backwards compatible or not just from looking at their version numbers. If you're not that lucky, you'll have to either compare the APIs manually or test your application for any runtime errors related to missing types or members.
Either way, it can happen that you need to reference both of the DLLs and, because they share the name, only one can end up in your bin folder. Renaming one of the DLLs wouldn't help as it would prevent the assembly binder from finding it.
System.IO.FileLoadException : Could not load file or assembly 'Newtonsoft.Json, Version=18.104.22.168, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
We don't want to end up with an exception like this, so what now?
There are more ways of loading multiple versions of the same assembly into a single application domain. All of them require the assemblies to be strong-named to avoid identity conflicts.
In terms of using third-party DLLs, Kentico is no exception. We use a lot of well-known libraries. That's also the reason why we introduced the concept of CMSDependencies several versions ago. We try not to conflict with the DLLs you may be using in your custom libraries and spare you the work that goes with using different versions of the same assemblies, so we pulled out these libraries from the bin folder and put them aside to a separate folder in the web project. From a technical perspective, as you have probably guessed, we're using the AppDomain.AssemblyResolve event approach, which gives us enough room for maneuver.
You may be asking whether the CMSDependencies will be available if you integrate with the Kentico API from an external application. The answer is yes if you use our official Kentico.Libraries NuGet package.
In this article, I tried to outline the main directions you can take when facing problems with conflicting assembly versions. There's much more to the loading logic, which I haven't covered in this article. For more information, I recommend the following MSDN reading:
I'll be very glad if you share your experience in the comments. Have you tried any of the suggested solutions? Did it work for you?