How to resolve “Could not load file or assembly … or one of its dependencies” and why this exception thrown in .Net?

When we use multiple versions of same assembly, it is more likely to encounter this runtime exception. It is easy to resolve provided that the new assembly version is backward compatible.

Before going into how to resolve this issue, let’s understand how runtime locates the assembly. If you just want to know the solution, you can skip this section, however, I recommend to go through it once to understand why we get this exception.

How Run-time locates assembly

Consider the scenario where I am using Newtonsoft.Json in my project. I am also referencing some library that is also using Newtonsoft.Json but with different version. When you build solution there can be one Newtonsoft.JSON.dll in bin/debug or bin/release folder.

fuslogvw.exe is assembly binding log viewer (installed with Visual studio), run it from developers command prompt with admin privileges. Go to debug (under bin) directory of project.

Here is sample log of probing (relevant parts are made bold):

LOG: This bind starts in default load context.
LOG: Using application configuration file: F:\visual studio 2017\Projects\TestBindingApp\TestBindingApp\bin\Debug\TestBindingApp.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Post-policy reference: Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///F:/visual studio 2017/Projects/TestBindingApp/TestBindingApp/bin/Debug/Newtonsoft.Json.DLL.
LOG: Assembly download was successful. Attempting setup of file: F:\visual studio 2017\Projects\TestBindingApp\TestBindingApp\bin\Debug\Newtonsoft.Json.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
WRN: Comparing the assembly name resulted in the mismatch: Major Version
ERR: The assembly reference did not match the assembly definition found.
ERR: Run-from-source setup phase failed with hr = 0x80131040.
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated

This log gives insights into how assembly is located. More details are available at Microsoft Docs — How runtime locates the assembly. Below is summary:

  1. Determine the correct assembly version: app/web config, publisher policy and machine.config are examined for this.
  2. Cached Assembly: Check the same assembly bound before then use it, if it failed then current request is failed immediately without any further steps.
  3. GAC: Assembly is checked in Global Assembly Cache, it must be strongly named.
  4. Code base or Probing: First checks in location in <codeBase> element. If not present, then probing is done in Application base considering culture, Name and private path of probing element. The Application base is location from executable is run

Then coming to question why error of assembly cannot be loaded? It is failing in one of the above steps. More often the first step — the assembly is not found matching the version.

Consider the scenario: where -> means references here:

Lib A references Lib C v1

Lib B references Lib C v2

Lib A references Lib B

Then after building the project there would be one version of Lib C in Application base so Lib A can’t find other version.

Can you guess which version of Lib C would be present in Application base?

It is Lib C v1. The reason is dependency resolver follows 4 rules

Lowest applicable version (e.g. Version 2.2 for ≥2.2 and if 2.2 is not available and 2.3 is next then 2.3)

Wildcard (e.g. 2.3.9 for 2.3.* — latest 2.3.x version)

Nearest wins (as in this scenario — Lib C v1 directly referenced by Lib A. Thus, this rule can result in a downgrade of the package version)

Cousin dependency (lowest maximum version that satisfies all requirements. This is case when different package versions are referred to at the same distance in the graph from the application). Example, Lib A referencing Lib B1 & Lib B2. Both B1 & B2 in turn referencing different version of Lib C.

We understood which version the application gets and how runtime probes it. Now, lets look into possible solutions:

Solution

There are two solutions:

  1. Upgrade NuGet package of Lib C in Lib A to v2
  2. Adding binding Redirect element in config file

Let’s see binding redirect solution first.

Binding Redirect:

The fix is easy —add <bindingRedirect> element in app.config or web.config. Below is sample fix for above log:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<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>
</assemblyBinding>
</runtime>

This specifies which version of assembly to use instead of old version. It is not necessarily requires later version be specified in newVersion, earlier version can be provided as well in newVersion. So, for any assembly reference containing Newtonsoft.JSON, it would now look for version 11 in this example.

Note that explicit assembly binding redirection in an application configuration file requires a security permission.

In Lib C example, since Lib C v1 is lower that Lib C v2, we will get same exception when that lib is attempted to load while executing code in Lib B (Lib B requires Lib C v2), in this case it is better to just update NuGet package of Lib C in A to v2.

Update NuGet Package:

Thus, another solution is to update NuGet package in all root project and then in subsequent referred project (if required) where same package is referred.

Hope, it helped to understand the mechanism and resolve the error. Thanks for reading, feel free to share and tap on clap button, if you enjoyed it.