Search code examples
c#asp.net.net.net-assembly

How does .NET resolve System.Net.Http at compile time when the referenced assembly version differs from a runtime assembly redirect?


I'm working on a .NET project for a web application, that has the following setup to reference System.Net.Http


.csproj

targets .NET Framework 4.8

references assembly version 4.1.1.3

references a nuget package dll local to the project:

..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll

The assembly version of the .dll for the package is 4.1.1.3


app.config

specifies a runtime redirect:

<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.0.0.0"/>

When I build the project, it builds successfully. System.Net.Http is not in the project bin folder, and the GAC has the following version of System.Net.Http:

v4.0_4.0.0.0

There are no obvious runtime errors when running the web application. Ignoring the fact that this setup looks messy and incorrect, I'd like to understand how .NET is resolving the compilation.

My understanding of what happens in .NET during compilation and runtime is this:

At compile time:

  • The compiler checks the project file to see which version of the assembly is referenced.
  • The compiler checks the GAC (Global Assembly Cache) to see if the referenced version of the assembly is available.
  • If the referenced version of the assembly is not available in the GAC, the compiler will look for the assembly in the project's reference paths.
  • If the compiler cannot find the referenced version of the assembly, it will generate an error.

At runtime:

  • The CLR (Common Language Runtime) tries to load the assembly that is referenced in the project file.
  • If the CLR cannot find the referenced version of the assembly, it will look for a binding redirect.
  • If the CLR finds a binding redirect, it will load the version of the assembly that is specified in the binding redirect.
  • If the CLR does not find a binding redirect, it will generate an error.

What I'm specifically unclear on is:

  • If the compiler checks the project file to see which version of the assembly is referenced, then how does the compiler determine that no .dll needs to be added to the project bin?
  • Is the setup potentially allowing code that's specific to System.Net.Http 4.1.1.3 to successfully compile which could then fail at runtime because only v 4.0.0.0 of System.Net.Http will ever be loaded at runtime?

Solution

  • When you build, the compiler uses so called reference assemblies (which contain no executable code, just stubs). The nuget package specifies which assemblies should be referenced from the SDK's reference assemblies and not from the package itself (and therefore not copied to \bin), and that is what you see in the References node in VS. E.g. System.Net.Http reference assembly is located at path "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.8\System.Net.Http.dll". During run time these assemblies are loaded from GAC after applying binding redirects.

    On other target frameworks like .net core, a package can either rely on some assemblies being part of the shared framework, or otherwise provide these assemblies as part of build output.

    And the answer to your second question is: yes, theoretically version-specific code could fail due to a binding redirect. But I believe automatic binding redirects are generated when you reference multiple assembly version from different projects and libraries, comprising you application, so you have to settle on some particular version to build with.