3

To our surprise, the following statement does not throw an exception!

Assembly.Load("System.Data, Version=4.0.99.99, Culture=neutral, PublicKeyToken=b77a5c561934e089");

The GAC contains only the version 4.0.0.0 - there is no 4.0.99.99. Note that trying to load 4.1.0.0 fails. We observed this behavior for .NET framework assemblies only.

Why is that so? Are there any default assembly binding redirects for .NET framework assemblies in place? Is there some other magic going on?

To my knowledge, strongly-named assemblies should only be loaded if the exact same version is found.

rkhb
  • 14,159
  • 7
  • 32
  • 60
D.R.
  • 20,268
  • 21
  • 102
  • 205
  • It certainly appears so. If you set up an event handler for `AppDomain.CurrentDomain.AssemblyResolve`, the above won't trigger the event handler; but if you change the assembly minor version (e.g. to `4.1.99.99`), or the public key token, the event will be triggered. From that I'd guess that the two last-significant parts of the version number is ignored. – stakx - no longer contributing Mar 13 '15 at 16:38
  • Is this documented? We observe this behavior ONLY for .NET framework assemblies. Normally, you would expect this behavior if binding redirects are defined -> are there any default binding redirects for .NET framework assemblies? – D.R. Mar 13 '15 at 16:39
  • I haven't checked, but perhaps `System.Data` version 4.0 has a [publisher policy file](https://msdn.microsoft.com/en-us/library/7wd6ex19%28v=vs.110%29.aspx#BKMK_Redirectingassemblyversionsbyusingpublisherpolicy) that does the redirect. – stakx - no longer contributing Mar 13 '15 at 16:42
  • Check your app.config or web.config for any binding redirects. Some Nuget packages add runtime binding settings that might be responsible for this. –  Mar 13 '15 at 16:46
  • @Amy: Thanks, but of course we already checked that. We even created a minimal example solution to be absolutely sure. – D.R. Mar 13 '15 at 16:46
  • @stakx: Yes, PP files could also be the reason behind this, however, we haven't found any documented "default publisher policy files". – D.R. Mar 13 '15 at 16:47
  • Don't just check `app.config` and `web.config`. Look in [`machine.config`](http://stackoverflow.com/questions/2325473/where-is-machine-config) too. – mason Mar 13 '15 at 16:47
  • @mason: Already done, not a single redirect in there. – D.R. Mar 13 '15 at 16:48
  • The CLR plays games with .NET Framework assembly versions in an effort to stay compatible. Most visible is how CLR v4 can load an assembly that targets CLR v2 and contains references to 2.0 versions, automatically mapping them to their 4.0 versions. Retargetable assemblies are also the feature that makes portable class libraries work. There is a *lot* more to it, some of it is visible from the CoreCLR source code. Note how System.Data is referenced in coreclr/src/inc/fxretarget.h. The exact semantics are murky and take a while to reverse-engineer, treat like "Feature, not a bug". – Hans Passant Mar 13 '15 at 16:49
  • @D.R.: Have you tried Fusion Log Viewer (`fuslogvw`)? ("Fusion" appears to be the internal name of .NET's assembly binding & loading mechanism.) – stakx - no longer contributing Mar 13 '15 at 16:50
  • @HansPassant: Looks like your comment points into the right direction. However, retargetable assemblies are required for changes in the major/minor version, the assembly-loading behavior we observe concentrates on fix/build version only. Why is that so? Do you have any documentation where this is descibed in detail? :-) – D.R. Mar 13 '15 at 16:52

1 Answers1

2

As Hans Passant mentioned in the comments, the CLR contains a retargeting mechanism for framework assemblies that redirects references to old framework libraries to newer ones. You can see that mechanism at work by looking at the Fusion Log (using fuslogvw.exe and configuring it to log all binds to disk), which contains the information "Version redirect found in framework config":

LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\fabian.schmied\Desktop\Temp\ConsoleApplication3\ConsoleApplication3\bin\Debug\ConsoleApplication3.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: Version redirect found in framework config: 2.0.0.0 redirected to 4.0.0.0.
LOG: Post-policy reference: System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
LOG: Found assembly by looking in the GAC.

In your sample, the Fusion log contains an equivalent line:

LOG: Version redirect found in framework config: 4.0.99.99 redirected to 4.0.0.0.

From that, I conclude that the same mechanism comes into play here. It seems that all references to System.Data up to 4.0.65534.65534 are redirected to 4.0.0.0.

(For versions starting from 4.1.0.0, the retargeting no longer kicks in.)

Fabian Schmied
  • 3,885
  • 3
  • 30
  • 49
  • I also tried locating this mechanism in the CoreCLR. The concrete log message is defined as `ID_FUSLOG_FX_CFG_VER_REDIRECT`, but I can't see where this message is actually issued (https://github.com/dotnet/coreclr/search?utf8=%E2%9C%93&q=ID_FUSLOG_FX_CFG_VER_REDIRECT). – Fabian Schmied Mar 16 '15 at 08:55
  • 1
    If someone wants to dig into it, https://github.com/dotnet/coreclr/blob/cbf46fb0b6a0b209ed1caf4a680910b383e68cba/src/inc/fxretarget.h seems to be a good place to start. (There is also a legacy mechanism in https://github.com/dotnet/coreclr/blob/cbf46fb0b6a0b209ed1caf4a680910b383e68cba/src/binder/compatibility.cpp; but that doesn't seem to affect `System.Data`.) – Fabian Schmied Mar 16 '15 at 08:56
  • Do keep in mind that CoreCLR source is unreliable to predict what the desktop CLR version looks like. The desktop version is much more elaborate, no source available for it beyond SSCLI20. Retargeting was added later. – Hans Passant Mar 16 '15 at 11:34