runtime: System.ComponentModel.Annotations version is inconsistent

Detailed repro steps so we can see the same problem

1.The target framework of my project is net472

  1. the target framework for the package is netstandard2.0

3.the package depend on System.ComponentModel.Annotations

4.When my project restores the package(System.ComponentModel.Annotations), the net461 package is restored

5.NServiceBus.DataAnnotations restores the package(System.ComponentModel.Annotations),the netstandard2.0 package is restored

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 9
  • Comments: 78 (51 by maintainers)

Most upvoted comments

@Varorbc This issue had the problem that many people were having the same type of symptoms for very different root causes, so it became hard to manage, as some solutions posted worked for some folks but not for others. Instead, what we opted with is to close this issue and have people hitting similar symptoms to log new individual issues with repro steps so we can investigate isolated cases and fix each one (if needed) so we are not mixing together many different root causes.

This is the second search result in Google for “System.ComponentModel.Annotations”.

@ViktorHofer @joperezr This explanation doesn’t make any sense to me. Please link to the many different root causes, as this issue has cost me over 40+ hours of development time over the last 3 years since I adopted .NET Core full-time. And I am just ONE developer.

It would be very helpful if you explained to us what information you need to collect to fix this problem.

The CORE problem seems to be that the underlying DLL has to target every platform known to the .NET Ecosystem, as the following screenshot of nuget.org System.ComponentModel.Annotations demonstrates:

image

I say this is the CORE problem because I suspect the “magic” the dotnet SDK team has done with MSBuild to generate a .deps.json file doesn’t work when there are this many target frameworks and the DLL has to be re-deployed any time any of these target frameworks change.

In my case, I used the following PowerShell script to determine which version of System.ComponentModel.Annotations I was building against: https://github.com/jzabroski/PowerShell-1/blob/master/GetFileProperties.ps1

I then got the ProductVersion attribute from the file, which was: 4.6.26515.06 @BuiltBy dlab-DDVSOWINAGE059 @Branch release/2.1 @SrcCode https://github.com/dotnet/corefx/tree/30ab651fcb4354552bd4891619a0bdd81e0ebdbf

Tracking backward from this commit, I see there was a type-forward added by @ViktorHofer here: https://github.com/dotnet/corefx/commit/e3f74deb999571d3f34a6b2b8011caebcf06dbfc#diff-df03b8a58b886901569279ef9bdb1d90R13 on on Oct 27, 2017. Traveling back to nuget.org (because I cannot easily infer which nuget packages shipped this commit), I can see it was shipped no earlier than 12/12/2017 via System.ComponentModel.Annotations 4.4.1. Curiously, this version of the nupkg mentions git commit hash 0f6d0a02c9cc2e766dd543ff24135f16e9a971e4. So, I went back to the corefx/tree and went to https://github.com/dotnet/corefx/tree/0f6d0a02c9cc2e766dd543ff24135f16e9a971e4 and then the commit https://github.com/dotnet/corefx/commit/0f6d0a02c9cc2e766dd543ff24135f16e9a971e4, where I can see that pkg/Microsoft.Private.PackageBaseline/packageIndex.json was updated to include 4.4.1. At this point, I’m not sure what packageIndex.json does, but I found Microsoft.Private.PackageBaseline which says:

Package used by CoreFx build infrastructure to represent the latest package versions in a release. When using NuGet 3.x this package requires at least version 3.4.

Looking at the version history for this package, it stopped being maintained on nuget.org as of 5/9/2017. Hypothesis: This package should never have been on nuget.org, because its designed for the sole consumption of the CoreFX build infrastructure. This may be causing some problems with versions of System.ComponentModel.Annotations or other dll’s prior to 5/9/2017, if the build infrastructure depended on its dependencies also being published to nuget.org. - Just a guess.)

Jumping back, trawling through the commit history from the 4.4.1 release commmit hash, I can see that Viktor’s typeforward fixes made it into this version: https://github.com/dotnet/corefx/commits/0f6d0a02c9cc2e766dd543ff24135f16e9a971e4/src/System.ComponentModel.Annotations

Given that Viktor implemented TypeForwardedFrom - I wonder if I can somehow implement TypeForwardedToAttribute on top of his TypeForwardedFrom. It seems like an awful idea, and I doubt there is much regression testing in the compilerservices stack on such scenarios, but hey, damned if you do, damned if you don’t.

Separately, digging through the THOUSANDS of linked issues, I found this simple explanation by @joperezr 👍. I’ve altered his quote slightly so that it’s clearer:

[System.ComponentModel.Annotations] won’t work because this specific library is not inbox, its Oob [(out-of-box)].

I also found this quote by Immo, on 10//2017 in an issue tagged dotnet-fx-compat:

I had heard that all these System.* dlls were going to go away in netstandard 2.0 but that doesn’t seem to be the case

Yes and no. If you compile against .NET Standard 2.0, your code will only be compiled against netstandard.dll. However, for backwards compatibility with .NET Standard 1.x the other DLLs are still needed.

In general, .NET Standard dependencies were never meant to be deployed by the application. The idea is that they are built into the .NET implementation you’re running on. Since we had a business goal to make .NET Framework 4.6.1 work with .NET Standard 2.0, we had to provide these additional files as part of the application. This will stop being the case starting with .NET Framework 4.7.1. In all other .NET implementations (.NET Core, Mono, Xamarin, UWP) they are already built-in.

Putting these two comments together, there are TWO types of .NET Standard dependencies:

“InBox .NET Standard” “OOB (Out-of-box) .NET Standard”

For .NET Framework applications, you will always run into these System.ComponentModel.Annotations inconsistency issues, because:

  1. System.ComponentModel.Annotations is “OOB .NET Standard” library
  2. Applications have to provide these DLLs for .NET Framework 4.6.1 to .NET Framework 4.7.0, because they’re always “OOB”.
  3. As of .NET Framework 4.7.1, .NET Standard 2.0 assemblies should be “inbox”.
  4. However, System.ComponentModel.Annotations is still not “inbox”, as per @joperezr comment - no idea why that’s the case.
  5. You can confirm System.ComponentModel.Annnotations is still not “inbox”, by going to C:\Program Files\dotnet\packs\NETStandard.Library.Ref\2.1.0\ref\netstandard2.1 and seeing that it’s not there.
  6. In fact, as the following PowerShell script shows, it’s not in any dotnet.exe pack prior to .NETCoreApp3.0:
    get-childitem 'C:\Program Files\dotnet\packs\' -recurse | where name -eq "System.ComponentModel.Annotations.dll"
    
    
    Directory: C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    -a----        9/13/2019   2:25 AM          27000 System.ComponentModel.Annotations.dll
    
    
    Directory: C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1
    
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    -a----       11/15/2019   8:33 AM          27208 System.ComponentModel.Annotations.dll
    
    and then cross-referencing that list with the list of runtimes you have installed:
    dotnet.exe --list-runtimes
    
    Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.2.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
    Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.2.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
    Microsoft.NETCore.App 1.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 1.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 1.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 1.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.2.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
    Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
    

Further, now let’s suppose this assembly WAS added in .NET Framework 4.7.1 and you don’t need to include it. Now any Nuget package published AFTER .NET Framework 4.7.1 SHOULD exclude this package as a PackageReference, otherwise you’re screwed. This appears to be the current problem with FluentMigrator, and there is no build warning telling us about this problem: https://github.com/fluentmigrator/fluentmigrator/blob/9d80faaae71de0d326b2f226083be3d8d7a5c9d2/src/FluentMigrator.Abstractions/FluentMigrator.Abstractions.csproj#L45

Further, as a dirty hack, you can use PowerShell 5.0 to quickly check the actual Assembly Version attribute on the dll in your deployment folder:

[System.Reflection.Assembly]::LoadFrom("D:\source\path\to\bin\Debug\net48\System.ComponentModel.Annotations.dll")
GAC    Version        Location
---    -------        --------
False  v4.0.30319     D:\source\path\to\bin\Debug\net48\System.ComponentModel.Annotations.dll

@joperezr @danmosemsft I would like to point out that I tried changing everything to package references and so did the customer. It still failed. So at least in my experience telling people to do this is not tha answer.

I also “solved” the problem using this code. But it is more a avoidance than a solution. Assembly loading is damn slow now. Am I the only one who finds it somehow ridiculous that this issue is unresolved over months and months while development of the assemblies continues? This must be a really, really deeply hidden problem where no developer dares to dig that a development machinery like MS cannot solve it and needs stackoverflow to offer a solution 😉

@SimonCropp I just tried that project locally with the latest VS, and I see that: 1) System.ComponentModel.Annotations.dll (version 4.2.1.0) is copied to the output folder, and 2) An app.config file binding redirect is generated successfully with the following contents:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

That binding redirect is causing the app to work just fine at runtime, I get the following output: image

@joperezr : Did you notice that earlier six other issues were closed with a reference to this issue. So why wait for new future issues ? One can also reopen these six issues. Maybe you don’t realize it, but the underlying problems causing this issue make it relatively difficult and time consuming to have solutions that include both .Net Framework and .Net Core projects, even de facto impossible in many cases. So solving the underlying problems causing this issue should in my opinion be a high priority for your company. That seems however to be difficult because in all likelihood many teams within your company will probably be involved. That is probably also the reason the problem is already there for over two years and will most likely still be there after migrating to .Net Core 3.0.

This problem looks to have been mis-diagnosed as a framework problem initially. I believe that the main problem is that there is a version mismatch between netstandard2.0 in the lib and ref folders. The ref folder has a version of 4.2.1.0, but the lib folder has 4.2.0.0. Version 4.4.0 of this package had both folders referenceing version 4.2.0.0, but since 4.5.0 onwards (including all of the 5.0 releases) there is a version mismatch.

This is what’s causing packages.config based projects to require a binding redirect to fix the problem

@Bert-Proesmans For now, the thing to watch out for is linking a netstandard2.0 assembly from a Common package as a transitive dependency. If you then have a net48 entrypoint and the netstandard2.0 Common assembly exposes the nuget package ANYWHERE in its public API, you have a diamond dependency issue.

Obviously, can also happen with net461 and net472 but practically speaking, only net472 and net48 support netstandard2.0.

In the meantime this is unsolvable when combining net framework and net standard libraries, because each targetted framework uses a different annotations assembly which cannot be robustly redirected?

System.ComponentModel.Annotations uses attributes, which are public - there is no such thing as a private attribute. That’s why System.ComponentModel.Annotations is the most common offender. In other scenarios, you might get lucky and sidestep the problem because the C# compiler and MSBuild generate magic configurations that sidestep the problem.

In terms of fixing this problem? Well, I didn’t think that far, but I guess the simplest solution is for Microsoft to not close this issue and… actually fix the issue. Additionally, adopting a policy of not creating separate BuildNumbers across Frameworks is probably a good check for nuget.org - at least have a warning that this will waste developers a lot of time. But I’m not a computer scientist so maybe my idea of fixing this is not smart enough

Problem solved by using the code found here: https://stackoverflow.com/questions/50342416/azure-function-ef-core-cant-load-componentmodel-annotations-4-2-0-0 Many thanks to the author for sharing this.

It is really bad thing. almost 20 years ago, when we said Goodbye --COM dll hell and embraced brand dotnet… but right now what happened? the same thing… maybe worse. wish MS reconsiders the way for releasing production component(dlls).

@karelz

a library “X” targets System.ComponentModel.Annotations nuget but only has netstandard2.0 as a framework. to it has a ref to the version from System.ComponentModel.Annotations.dll from the netstandard part of the package. then a net472 application tries to use both System.ComponentModel.Annotations and “X” nugets. u now have a binding conflict.

Not to mention if you are trying to debug something in production and there are all kinds of version mistmacthes between nugets and what is in prod, it make it very difficult to rull out some kind of deployment/build pipeline issue

@karelz @ViktorHofer We are seeing this issue too and the root cause is exactly what @MatthewSteeples mentioned above. Can we fix the version mismatch in “ref” and “lib” directories for .NET Standard 2.0. We have a product using this dll and targeting .NET Standard 2.0. It will be great if we can get a fixed version. Can you reopen the issue and fix it?

I was able to dodge this with .NET 4.8 test projects and .NET Standard libraries by using multi-targeting of .NET Standard 2.1 and .NET 4.7.2 in my library projects (rather than .NET Standard 2.0 individually). In the dependencies of the test project in which Annotations was used, I set up my target frameworks to look like this:

<PropertyGroup> <TargetFrameworks>netstandard2.1;net472</TargetFrameworks> </PropertyGroup>

You’ll need a DirtyAssemblyResolveHelper in your test project and an assembly ModuleInit to create manual binding redirects. You can use Fody for that - a bit convoluted but as part of your build process, run Fody through all *.Tests.dll files https://github.com/Fody/ModuleInit

I had this problem for some time and tried all sorts to no avail. However, I discovered the version showing for Version 4.5.0 in the library reference properties is 4.2.1.0. When I changed the version to 4.4.1 via NuGet, the version in the properties changed to 4.2.0.0. This fixed it for me and was able to load the Swagger UI. I hope you will find this helpful.

@joperezr It’s a different day and the binding redirect is now taking effect - assembly load works as expected. Thanks for the pointer.

@joperezr My .Net Framework dll is invoked via COM by an unmanaged C++ app.

Interestingly, the binding redirect isn’t taking effect even after duplicating it in both the .exe.config and the machine.config.

The issue reproduces for me when the project referencing EF Core is .Net Framework (as opposed to .Net Standard, as before).

Hi @AaronRobinsonMSFT! Hope you’re well 😃 You’re correct - ComExtensions is my project name. COM interop is in the picture but I doubt that everyone experiencing this issue is using it.

@SimonCropp while compelling idea, unfortunately the reality is more complex than that 😦. You can have collisions between references. You can have collisions with Framework built-in versions. You can have collisions with assemblies in GAC, or publisher policies. Binding redirects are mechanism to deal with all these problems (or cause more). Addressing diamond dependencies is just one of the main scenarios. Rather than jumping to conclusions how we think it should work, let’s try to understand what is broken and why it is broken and let’s deal with that.

Assembly version isn’t wrong. Your NServiceBus.DataAnnotations dependency targets netstandard2.0 for which the assembly version of System.ComponentModel.Annotations is 4.2.0.0. That said, you are running on .NET Framework, for which this assembly has been patched in the package, and since it is patched, it has a new assembly version: 4.2.1.0. Because of this, NServiceBus.DataAnnotations will look for the 4.2.0 version of the assembly, but will get 4.2.1 instead. In short, this is all caused because you are using an assembly that was targeting .NET Standard, and running on .NET Framework, which is totally supported, but requires assembly redirection like this in some cases.