Nuget DLL Hell: Could not load file or assembly System.Runtime

0

After upgrading Application Insights to the latest version today, because our site is no longer reporting callstacks, our .NET Framework 4.6.1 ASP.NET site now crashes on initialization due to:

System.IO.FileLoadException: 'Could not load file or assembly 'System.Runtime, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)'

I have fought a very similar problem before in the past for another job. The key difference being that the issue was caused by a Nuget package pulling in .NET Core or Standard. Thus, there were multiple .NET DLLs with the same name being copied to the bin folder (System.Net.Http in that case). In that situation, it was as "easy" as never updating that Nuget package again...

Although I think the situation is slightly different here, "not upgrading" isn't an option anymore. Application Insights no longer reporting callstacks kinda defeats the purpose of the whole thing!

Since it's System.Runtime, not a Nuget package, there are no dependency version tags to nuke in the config files; although even that would have worked, that seems like a temporary solution anyway.

I theorized maybe there is now a .NET Standard of .NET Core hidden in the project somewhere, but I see no NetStandard reference, and I don't know how to tell if Core is being dragged in somehow. I have not had the pleasure (?) of working with Standard or Core yet to know more off the top of my head. I'll keep Googling...

I thought it was because we have a single .NET 3.5 DLL (with zero Nuget or DLL references besides vanilla .NET Framework 3.5's System, System.Runtime.Serialization, and System.Xml), being referenced by the ASP.NET project, though this has never been an issue before.

I am completely stumped and has stopped all development for the day. I have been upgrading and downgrading various packages, looking at their prerequisites, but I am not finding any hints.

c#
asp.net
.net
nuget
asked on Stack Overflow Jul 27, 2019 by Zoop

1 Answer

2

Two different Nugets want to pull in different versions of System.Runtime.dll. This is extremely common in a .NET project using Nugets that use .NET Standard. This is now the second time I've run into this problem stemming from System.Net.Http, because it's used by just about everyone.

Since System.Runtime is a .NET Framework DLL, Visual Studio will always grab it from the Program Files\Reference Assemblies folder, no matter what you do in Visual Studio or the CSProj.

In my case, for whatever reason, the version being copied to the bin folder was a reflection-only version of the desired DLL, meaning I could not simply use a <dependantAssembly> tag to force it to use the version that was in the bin folder. I had to first get a full-fledged version of System.Runtime.dll into the bin folder.

Short of updating to .NET Core, the only other idea I had (and confirmed by another Stack Overflow user) was to copy it over by hand in a post-build. It's awful, but it works with whatever conflicted DLL you have:

  1. You will likely have the conflicted DLL in your project's references, due to the Nuget package. My issue was harder because although System.Runtime is a prerequisite to another Nuget I was using, I guess because it's part of .NET Framework, it wasn't added as a Nuget package or a reference. If not, go pull the Nuget package yourself.
  2. Look up the path to the DLL in your packages folder. For example, mine was $(SolutionDir)packages\System.Runtime.4.3.1\lib\net462\System.Runtime.dll. You will want to swap your solution directory with the $(SolutionDir) macro, as I did.
  3. Edit your post build event command line in your project's solution properties:

    copy "$(SolutionDir)packages\System.Runtime.4.3.1\lib\net462\System.Runtime.dll" "$(TargetDir)System.Runtime.dll"

  4. Open the DLL in something that can see its version number and public key token, like ILSpy. In my case, the DLL is version 4.1.1.1 with public key token b03f5f7f11d50a3a. Note, viewing the DLL's properties in File Explorer does NOT show you the right version number.

  5. Open your app.config/web.config in a text editor. If you do not already have a <dependentAssembly> tag for your DLL in the configuration\runtime\assemblyBinding tag, add it. It should look something like this:

<dependentAssembly> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/> <bindingRedirect oldVersion="0.0.0.0-9.9.9.9" newVersion="4.1.1.1"/> </dependentAssembly>

  1. (continued) Replace the name with the DLL's file name without the .DLL extension. Replace the public key token with the value you discovered in ILSpy. Set oldVersion to a wide range. Set newVersion to be the version you found in ILSpy.
  2. Whenever you touch that nuget package in Nuget Package Manager, it may change your <dependentAssembly> tag! So, leave a text file, a sticky note, a neon-lit sign on your project, saying that if you ever mess with that Nuget package, you may need to edit the post-build and app.config/web.config.
  3. Cry.

If you're stuck on .NET Framework like I am, you may end up having to do this hack on many DLLs, as .NET Standard/Core become more pervasive, and thus more DLLs end up with the same name as their .NET Framework counterparts. We found that deploying this solution onto Azure did NOT work.

Otherwise, the only other solution is to avoid it altogether: upgrade only one Nuget package at a time and test thoroughly after each time. Nuget can be like Russian Roulette ;-). Although the above worked for us, we ended up rolling back and slowly and painfully upgrade until we found the culprits.

If you know a better way, I'd love to hear it.

answered on Stack Overflow Jul 28, 2019 by Zoop • edited Aug 2, 2019 by Zoop

User contributions licensed under CC BY-SA 3.0