Newtonsoft Json.NET version incompatibility (DLL hell)
Asked Answered
N

1

23

Newtonsoft.Json release incompatible versions with same strong name, only changing the File version.

According to MSDN:

Assemblies that have the same strong name should be identical.

Because of this our application breaks if other applications, outside our control, put different version of Newtonsoft.Json.dll into the GAC.

Is there any way to force .NET to load a specific version we need?

Update:

Let me explain the problem more in depth:

In .NET, as far as I know, there is no mechanism to resolve assemblies before the CLR tries to resolve them and fails.

There is only the AppDomain.AssemblyResolve event and it fires only when the assembly is not resolved. Usually it is enough.

But in case with Newtonsoft.Json it does not fail to resolve the assembly but it simply loads the wrong one.

This happens because Newtonsoft.Json release incompatible versions with same strong name.

Example:

Let's say our application compiled against N.J.dll (assembly version 1.0, file version 1.0)

Then some other application, put other incompatible version of same dll into the GAC N.J.dll (assembly version 1.0, file version 1.1)

Because they only change file version and does not change assembly version, this two assemblies have the same strong name.

So for our application .NET trying to resolve N.J.dll (assembly version 1.0) it sees the dll in GAC and loads it. (Because .NET always prefer assembly from GAC for assembly from "bin" folder)

But assembly that loaded is wrong one. It has file version 1.1 and it is not compatible with version 1.0.

Because both assemblies has same assembly version, .NET does not see any difference beetween them. But then when it actualy try to resolve some class or member inside, it fails because it was changed in version 1.1.

And the whole application fails with unpredictable errors.

And the worst thing is that even if my application does not put newtonsoft.json.dll into the GAC, some other application, outside of my control, will put different version of newtonsoft.json.dll into GAC - my application will break with unpredictable exceptions.

So my question is, can I some how load correct assembly first, before .NET load wrong one?

Update

https://github.com/JamesNK/Newtonsoft.Json/issues/615 https://github.com/JamesNK/Newtonsoft.Json/issues/1001

Issues with this problem was closed with comment that show that the author of Newtonsoft.Json does not understand .NET versioning and why this is important.

Notch answered 24/7, 2015 at 11:6 Comment(4)
It happened even with Microsoft before - J# was rereleased with assembly version unchanged. If you detect it during setup, you may ask user to install proper version of assembly in GAC. The other option is to produce your own binary from same sources and assign it strong name as you wish (if license permits)Erlandson
Problem is even bigger. Microsoft itself uses Newtonsoft Json.NET in its libraries and SDKs. So anything that use them could broke at any time with very strange errors.Notch
This finally explains a bunch of unexpected exceptions I have received in an MVC web API application half a year ago. The app was using Json.Net with higher version than the version bundled with the WebAPI itself. The application worked fine immediately after deployment, until IIS decides to suspend it (after about 30 mins of no usage). When someone accesses the application after suspended, IIS attempts to start it up again, but somehow messes up the Json.Net version and my working application died notoriously. I did not have Json.Net in GAC, only used NuGet to manage my dependencies.Nanon
You are correct that newtonsoft does not update AssemblyVersion along with AssemblyFileVersion. Usually their file or product version matches their package version. Both file and product versions are win32 concepts. So, for packages 6.0.4 and 6.0.8 for example they use the same assembly versions and ,I believe, also ensure they are perfectly backward/forward compatible. Only change must be just bug fixes. So, this should not ideally break your applications. But I agree they should not be following such practice.Chrisom
N
3

The assembly loader will only probe for missing assemblies, i.e. assemblies that are not already loaded. If you deploy the DLL to your application install folder then load it explicitly when your application starts up, then the assembly loader will not attempt to load it again from the GAC.

You can load the assembly explicitly using the Assembly.LoadFrom method.

See https://msdn.microsoft.com/en-us/library/dd153782(v=vs.110).aspx for more information.

Neurosurgery answered 29/9, 2016 at 0:24 Comment(6)
Yes but it is very hard to achieve. Because it need to happen before any type that uses given assembly is initialized. It need to be very early and I do not know any event or entry point that reliably happens that earlyNotch
What type of application is it? Is this a web site, console app... There is almost always a startup place where you can put this code but where exactly depends on the type of application.Neurosurgery
You could also create a loader app. The loader app would create a new app domain, explicitly load the dlls for your application into the new app domain before calling the entry point of your application to run it. This is the most extreme solution I can imagine, and it is almost certainly not necessary for your situation.Neurosurgery
Another option is to dynamically bind to the Newtonsoft assemblies rather than creating a static binding. This would involve loading the Type by name and finding the constructor and the methods you want to call using reflection. This approach is only practical if you only use Newtonsoft in a few places in your application.Neurosurgery
LoadFrom() and LoadFile() won't necessarily load requested assembly from file. If an assembly with its identity is in the GAC, the GAC copy will be used instead.Diplegia
I don't see anything in the documentation to support your assertion Max, in fact learn.microsoft.com/en-us/dotnet/api/… supports my answer. If you used this method in your application and still got the GAC version it is because your code referenced a type within the assembly before you explicitly loaded it.Neurosurgery

© 2022 - 2024 — McMap. All rights reserved.