.NET has a weird way of referencing dependencies. When one "assembly" (EXE or DLL) references another, there are four items stored:

  • The dependency's name
  • The dependency's version
  • The dependency's "culture"
  • The dependency's signing token

So, if my project references, say, Newtonsoft.Json version 10, the reference usually gets displayed as:

Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed

The .NET runtime looks for this exact dependency, and when it finds it, it loads that DLL into the running process. Yay!

This can cause problems. What happens if my project references Newtonsoft.Json version 10, but another project that depends on me wants to use a new feature from Newtonsoft.Json version 11?

When this happens, you get a runtime error that looks something like this:

System.IO.FileLoadException : Could not load file or assembly 'Newtonsoft.Json, Version=10.0.0.0, 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)

The most common solution to this is to redirect the assembly version. You can use app.config/web.config to tell the .NET runtime that when it looks for Newtonsoft.Json version 10, it should load version 11 instead. This looks something like this:

<dependentAssembly>
  <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
  <bindingRedirect oldVersion="10.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>

The application configuration actually accepts version ranges, so most people just put in a range for "any previous version", i.e.:

<dependentAssembly>
  <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>

This is a really cumbersome and annoying solution though, once you have more than a handful of dependencies. And if you try and use a .NET Standard 1.0-1.6 assembly from a .NET Framework assembly, you need an absolute heap of these redirects.

Fortunately, the SDK can generate these for you automatically. This is even the new default for projects which target .NET Framework 4.7.2 or higher.

If you want to opt into this behaviour, you can define the following MSBuild property in your project file, or in Directory.Build.props:

<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>

However, this only works for executable projects (Console Application, Windows Forms application, etc.). If you want this to work for other projects - for example, a Class Library that contains unit tests, you need to add one more MSBuild property:

<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>

With those two flags set, then as long as your project directly references the newer version of a transitive dependency, your program will load the newest version, even when your dependencies are built against an older version.