Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.ComponentModel.Annotations version is inconsistent #27975

Closed
Varorbc opened this issue Nov 21, 2018 · 78 comments
Closed

System.ComponentModel.Annotations version is inconsistent #27975

Varorbc opened this issue Nov 21, 2018 · 78 comments
Labels
area-Infrastructure-libraries question Answer questions and provide assistance, not an issue with source code or documentation.
Milestone

Comments

@Varorbc
Copy link
Contributor

Varorbc commented Nov 21, 2018

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

@Varorbc
Copy link
Contributor Author

Varorbc commented Nov 21, 2018

image

image

@karelz
Copy link
Member

karelz commented Nov 26, 2018

@Varorbc does it cause any problems? What are the symptoms?

@Varorbc
Copy link
Contributor Author

Varorbc commented Nov 26, 2018

Could not load file or assembly 'System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

@Varorbc
Copy link
Contributor Author

Varorbc commented Nov 26, 2018

my test demo https://github.com/Varorbc/Test

@SimonCropp
Copy link
Contributor

@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

@SimonCropp
Copy link
Contributor

the v 4.5 System.ComponentModel.Annotations nuget

file versions

image

assembly versions

image

@bricelam
Copy link
Contributor

bricelam commented Dec 11, 2018

I'm seeing similar issues using System.ComponentModel.Annotations package version 4.6.0-preview.18604.2.

  • .NET Standard class library is compiled against version 4.2.0.0
  • .NET Framework test project redirects versions 0.0.0.0-4.2.0.0 to 4.2.0.0
  • .NET Framework test project publishes version 4.2.2.0

cc @divega @ajcvickers

@bricelam
Copy link
Contributor

@ericstj Was an assumption supporting these lines broken in the past year?

<!-- Must match version supported by frameworks which support 4.2.* inbox.
     Can be removed when API is added and this assembly is versioned to 4.3.* -->
<AssemblyVersion Condition="'$(TargetsNetFx)' != 'true'">4.2.0.0</AssemblyVersion>
<IsPartialFacadeAssembly Condition="'$(TargetsNetFx)' == 'true'">true</IsPartialFacadeAssembly>

@ericstj
Copy link
Member

ericstj commented Dec 12, 2018

.NET Framework test project redirects versions 0.0.0.0-4.2.0.0 to 4.2.0.0

That's the problem. It sounds like the .NETFramework project hasn't referenced the package and is instead picking up the .netstandard reference. If you examine the assembly version of the .NETFramework reference assembly in the package you will see it has 4.2.2.0. /cc @joperezr

You should either reference the package in the NETFramework test project, or enable PackageReference so that transitive dependencies flow to the test project.

@SimonCropp
Copy link
Contributor

note if u crack open some of those assemblies they are purely type forwarding. the weird thing is they are often typeforwarding to types in an assembly with a diff assembly version.

eg 4.4.1\lib\net461\System.ComponentModel.Annotations.dll (assembly version 4.2) typeforwards to System.ComponentModel.DataAnnotations, Version=4.0.0.0

@bricelam
Copy link
Contributor

...or enable PackageReference...

We’re using PackageReference. I’ll dig a bit more...

@SimonCropp
Copy link
Contributor

yeah in my repros of this issue i was also using PackageReference

@bricelam
Copy link
Contributor

bricelam commented Dec 12, 2018

@SimonCropp All the type forwarding and mismatched versions are normal. There’s a lot of .NET history surrounding these tangled assemblies. Luckily when it’s all working correctly, most people can ignore the ugly details. 😆

@SimonCropp
Copy link
Contributor

@bricelam yeah i figured we were dealing some long history of technical debt here :)

@bricelam
Copy link
Contributor

bricelam commented Dec 12, 2018

cc @ryanbrandenburg (Who is seeing the same issue on another codebase)

@ajcvickers
Copy link
Member

See also dotnet/efcore#13268

I have been trying for months to get answers on this. The response keeps being that it's going to be documented how to make it work. But we haven't seen any documentation yet, and both myself and customers have been tearing their hair out trying to make it work.

@ajcvickers
Copy link
Member

See also dotnet/efcore#13956

@danmoseley
Copy link
Member

@ericstj any thoughts here?

@bricelam
Copy link
Contributor

bricelam commented Jan 4, 2019

Got to the bottom of our issue: We were using the the Microsoft.NETFramework.ReferenceAssemblies package at build time then our tests compiled code via Roslyn but didn't reference the assemblies in Microsoft.NETFramework.ReferenceAssemblies.

@joperezr
Copy link
Member

joperezr commented Jan 4, 2019

@danmosemsft the problems here as @ericstj already pointed out are due to using packages.config instead of PackageReference. All of the instances that I've investigated around this issue are caused by transitive dependencies not flowing because of the use of packages.config. In order to fix this issue, users need to either manually add a reference to System.ComponentModel.Annotations package to their application, or they need to switch their project to use PackageReference instead which would flow the transitive dependencies.

@danmoseley
Copy link
Member

My bad, I somehow missed that comment. If there is no action for us, we can close this right?

@ajcvickers
Copy link
Member

@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.

@SimonCropp
Copy link
Contributor

isnt this repro already using packagrereferences https://github.com/dotnet/corefx/issues/33643#issuecomment-441834688 ?

@joperezr
Copy link
Member

joperezr commented Jan 7, 2019

@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

@Varorbc
Copy link
Contributor Author

Varorbc commented Jan 7, 2019

@joperezr Binding redirection is certainly possible, but fixing the assembly version is the best approach

@joperezr
Copy link
Member

joperezr commented Jan 8, 2019

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.

@ajcvickers
Copy link
Member

I would also like to point out that I and the customer also tried adding binding redirects everywhere possible and still could not make this work.

@joperezr
Copy link
Member

joperezr commented Jan 8, 2019

Oh I see I did get a repro with the NServiceBus test, so the Test2 project. That happens because by default we don't generate dll config files so there is no way to set the binding redirect in that case. In order to force generate the dll config with the binding redirect, just add <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> property to that project and try to build and launch it again. This time, everything should work normally.

@ajcvickers
Copy link
Member

@joperezr Yep, tried that too. Didn't work for me.

@joperezr
Copy link
Member

joperezr commented Jan 8, 2019

The only reason why that wouldn't work would be if the NServiceBus host doesn't honor the dll config when starting up an AppDomain. If you launch(and enable) the fuslogvw utility you should be able to see when the error happens, to see if the host looked at the dll config or not.

@bvandehey
Copy link

bvandehey commented Apr 1, 2019

Thanks. I have found a workaround. At least in my situation, the issue is with ef.exe. I wouldn't think I should need a binding redirect for that program to create a migration. And if one is needed, it should come in the nuget package.

This can be easily reproduced using the following steps:

git clone https://github.com/SynchroFeed/SynchroFeed.Core.git
cd SynchroFeed.Core\
git checkout efcore-bug
start SynchroFeed.Core.sln (to start Visual Studio 2017)

From Package Manager within Visual Studio 2017:
Add-Migration Initial -project SynchroFeed.Command.Catalog

This generates the following stack trace:
System.IO.FileLoadException: Could not load file or assembly 'System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) File name: 'System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.CoreConventionSetBuilder.CreateConventionSet() at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateConventionSet(IConventionSetBuilder conventionSetBuilder) at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator) at System.Lazy1.CreateValue()
at System.Lazy1.LazyInitValue() at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel() at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model() at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider() at Microsoft.EntityFrameworkCore.Internal.InternalAccessorExtensions.GetService[TService](IInfrastructure1 accessor)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func1 factory) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType) at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_01.b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)`

I'm using Windows 10, Visual Studio 2017 v15.9.10, .NET Framework 4.7.2, Microsoft.EntityFrameworkCore.SqlServer 2.2.3 and Microsoft.EntityFrameworkCore.Tools v2.2.3.

@st1pps
Copy link

st1pps commented Apr 9, 2019

I have the same problem with EF Core 2.2.3, EF 6 6.2.0 in a .NET 4.6.1 WPF application. Part of the code comes from a custom .NET Standard 2.0 dll.
I'll try to put together a 'working' minimal example asap.

@Poutche
Copy link

Poutche commented Apr 10, 2019

Same problem here. EF Core 2.2.4 on .NET Framework 4.61 in a winforms app. Occurs in Mycontext.MyDbSet.Load() (SqlExpress).

My first steps with Entity Framework... grrr... ;)

The Microsoft.EntityFrameworkCore.dll (version 2.2.4.0) has a reference to System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a (unresolved)...
App.Config:

@Poutche
Copy link

Poutche commented Apr 12, 2019

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.

@obelixA
Copy link

obelixA commented Apr 12, 2019

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 ;-)

@junkbondtrader
Copy link

junkbondtrader commented May 6, 2019

@joperezr fuslogvw gives me the following:

*** Assembly Binder Log Entry  (5/6/2019 @ 3:46:21 PM) ***

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Source\Application\Debug\Application.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 (Fully-specified)
LOG: Appbase = file:///C:/Source/Application/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = Application.exe
Calling assembly : Microsoft.EntityFrameworkCore, Version=2.2.4.0, Culture=neutral, PublicKeyToken=adb9793829ddae60.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: No application configuration file found.
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: System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///C:/Source/Application/Debug/System.ComponentModel.Annotations.DLL.
LOG: Attempting download of new URL file:///C:/Source/Application/Debug/System.ComponentModel.Annotations/System.ComponentModel.Annotations.DLL.
LOG: Attempting download of new URL file:///C:/Source/Application/Debug/System.ComponentModel.Annotations.EXE.
LOG: Attempting download of new URL file:///C:/Source/Application/Debug/System.ComponentModel.Annotations/System.ComponentModel.Annotations.EXE.
LOG: Attempting download of new URL file:///C:/Source/Application/ComExtensions/ComExtensions/bin/Debug/System.ComponentModel.Annotations.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\Source\Application\ComExtensions\ComExtensions\bin\Debug\System.ComponentModel.Annotations.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: System.ComponentModel.Annotations, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
WRN: Comparing the assembly name resulted in the mismatch: Build Number
ERR: The assembly reference did not match the assembly definition found.
ERR: Run-from-source setup phase failed with hr = 0x80131040.
LOG: Attempting download of new URL file:///C:/Source/Application/ComExtensions/ComExtensions/bin/Debug/System.ComponentModel.Annotations/System.ComponentModel.Annotations.DLL.
LOG: Attempting download of new URL file:///C:/Source/Application/ComExtensions/ComExtensions/bin/Debug/System.ComponentModel.Annotations.EXE.
LOG: Attempting download of new URL file:///C:/Source/Application/ComExtensions/ComExtensions/bin/Debug/System.ComponentModel.Annotations/System.ComponentModel.Annotations.EXE.
LOG: All probing URLs attempted and failed.

My .Net Framework 4.7.2 project (ComExtensions) uses PackageReference to consume a .Net Standard 2.0 project that has an indirect dependency on System.ComponentModel.Annotations 4.5.0 (Microsoft.EntityFrameworkCore.SqlServer 2.2.4 -> System.ComponentModel.Annotations 4.5.0).

The .Net Framework project has automatic binding redirection enabled and the .dll.config looks like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <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>

@joperezr
Copy link
Member

joperezr commented May 6, 2019

@junkbondtrader from what I can see, that binding redirect is not getting honored. Take a look at this line in particular:

LOG: No application configuration file found.

That basically means that the .dll.config was not loaded. If it was, then this would have worked as it clearly found your app-local 4.2.1.0 version of the assembly, but didn't unify to it because it didn't find a redirect.
@AaronRobinsonMSFT is this expected on ComExtensions projects? @junkbondtrader how is this particular component getting loaded? That might give us more info on why the binding redirect is not getting honored.

@yyjdelete
Copy link

Have an similar issues for use any(No build-time warns, so it's hard to see whether it's affacted or not) library which target only ns2.0 and depend an multi-target lib with different dependencies for different target, and then use it in net472 program.
And see no warns at build time, but an FileNotFoundException at runtime, seems transitive dependencies never works in that case, and no warns is shown as it does without transitive dependencies.
See dotnet/sdk/issues/3103

Dapper<-System.Data.SqlClient

Struct

Main(net472)
|-Lib1(netstandard2.0)//And use `System.Data.SqlClient.SqlConnection` transitive from Lib2 directly in the lib, or use any public API which export `System.Data.SqlClient`
	|-Lib2 or [Dapper](https://www.nuget.org/packages/Dapper)
		|-System.Data.SqlClient, Version=4.5.0.0(netstandard2.0)
		|-System.Data(net451, frameworkAssembly)

Wanted:

Main(net472)
|-Lib1(netstandard2.0)//And use `System.Data.SqlClient.SqlConnection` transitive from Lib2 directly in the lib, or use any public API which export `System.Data.SqlClient`
	|-Lib2(both ns2.0 and net451 is OK) or [Dapper](https://www.nuget.org/packages/Dapper)
	|-System.Data.SqlClient, Version=4.5.0.0(net451/net461)//this one is needed as it's an runtime package, the ns2.0 version always throw `PlatformNotSupportedException`

And see no build-time error, but System.Data.SqlClient.dll is lost for the main program and an runtime expection FileNotFoundException with System.Data.SqlClient, Version=4.5.0.0, lib1 can also be publish as an nuget package without warns but the same result at runtime.

Addin Issue:

Visual Studio also show an different dependencies tree struct(Dapper ns2.0+System.Data.Client instead of net451) for the reference as the sdk does for project.assets.json.

@AaronRobinsonMSFT
Copy link
Member

@joperezr I think the "ComExtensions" project mentioned above is one of @junkbondtrader's projects and not something we provide. If it is I am happy to look, but I have never heard of it.

@junkbondtrader
Copy link

junkbondtrader commented May 7, 2019

@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.

@junkbondtrader
Copy link

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

@tobbyioa
Copy link

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.

@ViktorHofer
Copy link
Member

@joperezr can you please help me out here and explain what the action item here is?

@Varorbc
Copy link
Contributor Author

Varorbc commented Jul 27, 2019

@ViktorHofer What is the solution to this issue?

@joperezr
Copy link
Member

@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.

@JRBonnema
Copy link

@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.

@jzabroski
Copy link
Contributor

@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: dotnet/corefx@e3f74de#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 dotnet/corefx@0f6d0a0, 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

@Bert-Proesmans
Copy link

@jzabroski very clear explanation, thank you.
I deduce from your future note that the duration until this gets fixed encompasses both framework updates and following ecosystem updates. So we'll have to wait at least months if not years?
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?

@jzabroski
Copy link
Contributor

@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

@Bert-Proesmans
Copy link

Thank you for the clarification.

Indeed, assembly redirects solve some cases but only for executables. The remaining issue for me personally is test projects: Library projects do not automatically receive nor seem to respect manually defined assembly redirects. At the moment my tests fail at runtime, while the deployed project works fine.
I converted one of my test projects into an executable one to keep moving forward. Sadly, I lost interaction through the Test Explorer in the process.
Is there a way to make executable test projects work with test explorer? I quickly browsed some search results but haven't really seen a solution, but will keep looking.

@jzabroski
Copy link
Contributor

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

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 5.0 milestone Jan 31, 2020
@derickanderson
Copy link

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>

@daiplusplus
Copy link

daiplusplus commented Apr 18, 2020

I was having the same issue with a PackageReference-style project targeting .NET Standard 2.0 that was being referenced by a Unit Test project (old-style csproj) that targeted .NET Framework 4.7.2.

In my case, my PackageReference was for System.ComponentModel.Annotations version 4.7.0. My solution would build fine, but whenever the DataAnnotations attributes were loaded/used on-demand then I'd get this exception:

System.IO.FileLoadException: Could not load file or assembly 'System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

I downgraded my NuGet package reference to System.ComponentModel.Annotations version 4.4.0 and the solution builds and runs fine.

What happened between version 4.4.0 and 4.7.0 that caused System.ComponentModel.Annotations to break in .NET Framework 4.7.2 Unit Test projects?

@MatthewSteeples
Copy link

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

@viralmodi
Copy link

viralmodi commented Dec 5, 2020

@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?

@ghost ghost locked as resolved and limited conversation to collaborators Jan 4, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Infrastructure-libraries question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests