dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.98k stars 4.66k forks source link

System.ComponentModel.Annotations version is inconsistent #27975

Closed Varorbc closed 4 years ago

Varorbc commented 5 years ago

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 commented 5 years ago

image

image

karelz commented 5 years ago

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

Varorbc commented 5 years ago

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

Varorbc commented 5 years ago

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

SimonCropp commented 5 years ago

@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 commented 5 years ago

the v 4.5 System.ComponentModel.Annotations nuget

file versions

image

assembly versions

image

bricelam commented 5 years ago

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

cc @divega @ajcvickers

bricelam commented 5 years ago

@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 commented 5 years ago

.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 commented 5 years ago

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 commented 5 years ago

...or enable PackageReference...

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

SimonCropp commented 5 years ago

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

bricelam commented 5 years ago

@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 commented 5 years ago

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

bricelam commented 5 years ago

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

ajcvickers commented 5 years ago

See also https://github.com/aspnet/EntityFrameworkCore/issues/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 commented 5 years ago

See also https://github.com/aspnet/EntityFrameworkCore/issues/13956

danmoseley commented 5 years ago

@ericstj any thoughts here?

bricelam commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

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

ajcvickers commented 5 years ago

@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 commented 5 years ago

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

joperezr commented 5 years ago

@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 commented 5 years ago

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

joperezr commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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

joperezr commented 5 years ago

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.

ajcvickers commented 5 years ago

@joperezr I was trying to make the repro from work https://github.com/aspnet/EntityFrameworkCore/issues/13268. I don't think NServiceBus was involved. I and the customer likely did something wrong.

If there is clear guidance somewhere on what should work, I'll point people to that and let it be. However, everything that has been mentioned here so far as already been suggested before and found not to work. That either means we're doing it wrong, or the guidance is wrong.

What I'm really looking for at this point is a clear place to point people for the guidance, and a clear place they can report issues when the guidance doesn't work.

SimonCropp commented 5 years ago

IMO no binding redirect should be needed when referencing a single version of one package.

ajcvickers commented 5 years ago

Another similar case reported here: https://github.com/aspnet/EntityFrameworkCore/issues/14368

obelixA commented 5 years ago

did just a little extension on my data model. adding the migration resulted in a wired problem with needed to configure the foreign key as nullable (see a former post on that problem) and now not being allowed to have the key nullable.

So I did a remove-migration -Context blahContext AND NOW GUESS WHAT: System.IO.FileLoadException: Die Datei oder Assembly "System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"... etc..

So now I need to undo the migration by hand?

ravenboilinux commented 5 years ago

is there an solution for this issue?

joperezr commented 5 years ago

@ravenboilinux if you have a minimal repro of the problem, we can take a look at it and see if one of the above solutions fix the issue in your case.

ravenboilinux commented 5 years ago

github repo

We have tried manually adding System.ComponentModel.Annotations package and switching to PackageRegerence both did not work to solve the issue

joperezr commented 5 years ago

That repo is not minimal unfortunately (only the code is already about 1.3 gb), IIRC it requires specific SDKs to be present on the machine to be even able to start to build.

ravenboilinux commented 5 years ago

test git repo

this is the bare repo that is needed to complete the test to throw the error. here is the Exception that I get Exception: System.TypeLoadException: Could not load type 'System.ComponentModel.DataAnnotations.Schema.InversePropertyAttribute' from assembly 'System.ComponentModel.Annotations, Version=4.2.1.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.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.DbContext.get_DbContextDependencies() at Microsoft.EntityFrameworkCore.DbContext.get_Model() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Linq.IQueryable.get_Provider() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include[TEntity,TProperty](IQueryable1 source, Expression1 navigationPropertyPath) at MGFConfiguration.MGFConfigurationProvider.Load() in E:\Code\IS\InnerSanctum\Servers\src\Config\MGFConfiguration\MGFConfigurationProvider.cs:line 30 at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList1 providers) at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build() at MGF_Photon.PhotonApplication.Setup() in E:\Code\IS\InnerSanctum\Servers\src\MGF.Photon\PhotonApplication.cs:line 114 at Photon.SocketServer.ApplicationBase.PhotonHostRuntimeInterfaces.IPhotonControl.OnPhotonRunning() at PhotonHostRuntime.PhotonDomainManager.PhotonPlainAppDomainBehavior.PhotonRunning() at PhotonHostRuntime.PhotonDomainManager.PhotonRunning()

One thing I was looking within the System.ComponentModel.Annotations.dll using dotpeek, and I only see two class FxResources.System.ComponentModel.Annotations.SR, and System.SR, two Resource files, and two assembly ref(mscorlib, and,System.ComponentModel.DataAnnotations) but nothing else.

joperezr commented 5 years ago

I just tried your repro and it works fine for me:

image

One thing I was looking within the System.ComponentModel.Annotations.dll using dotpeek, and I only see two class FxResources.System.ComponentModel.Annotations.SR, and System.SR, two Resource files, and two assembly ref(mscorlib, and,System.ComponentModel.DataAnnotations) but nothing else.

Correct, the reason why you see that is because System.ComponentModel.Annotations.dll in .NET Framework is a facade that just type forwards to System.ComponentModel.DataAnnotations. That means that if you check with dotpeek, you should be able to see this:

image

Which means that when this type is trying to be loaded from this assembly, it will be instead looked for in your System.ComponentModel.DataAnnotations.dll installed on your GAC, which should have the type since .NET Framework 4.5+. My only suggestion here would be to make sure that your .NET Framework installation is correct, and that you do make sure that this type exists correctly in your GAC. One other thing you could try to diagnose this further, would be to use fuslogvw to log the bind to disk that is failing, so that we can figure out why it is not finding the type from the GAC.

ravenboilinux commented 5 years ago

Sorry after I posted that comment I saw the TypeForwardedToAttribute in the assembly properties, something that I am not use to. My .NET frameworks (from 3.5 to current version that vs allows) are all installed by the visual studio installer. The assembly that is has the type is in the GAC. How do you use fuslogvw?

SimonCropp commented 5 years ago

Any thoughts on this https://github.com/dotnet/corefx/issues/33643#issuecomment-452451857 ?

karelz commented 5 years ago

@SimonCropp why do you think that? Are you sure it is truth in practice?

SimonCropp commented 5 years ago

binding redirects are to resolve version conflicts. if u only ref one version, with no diamond dependencies, there should be no conflicts and no binding redirects.

karelz commented 5 years ago

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

joperezr commented 5 years ago

How do you use fuslogvw?

Here is a good link that should get you started: https://docs.microsoft.com/en-us/dotnet/framework/tools/fuslogvw-exe-assembly-binding-log-viewer . Basically fuslogvw is a very useful tool whenever you get errors with TypeLoadExceptions, AssemblyLoadExceptions, FileNotFoundExceptions and MemberNotFoundExceptions. It helps by telling you exactly which assemblies each process is loading and also will tell you where did it look for a certain assembly in case it wasn't found.

ravenboilinux commented 5 years ago

@joperezr I repaired my visual studio and the the .net frameworks and still get the error. I tried the fuslogvw(which is a useful tool that i did not know about( I found the problem the runtime is looking within C:\ProgramData\Red Gate.NET Reflector\DevPath for dlls, and it finds the System.ComponentModel.Annotation.dll however that version of the dll does not have the TypeForwardedToAttribute to the System.ComponentModel.DataAnnotations.dll. How do you reset or remove the DEVOVERRIDE so that it looks into the GAC

joperezr commented 5 years ago

If you are seeing that, it is most likely that you have DEVPATH environment variable defined. Here is some info about that: http://ntcoder.com/bab/2014/08/28/how-to-locate-net-assemblies-by-using-devpath/ Unsetting that variable might do the trick.

BTW, fuslogvw is a very powerful tool but it is dangerous if you don't remember to disable the logs after you are done using it. If you forget to do it, then you will eventually run into issues where your whole disk will be filled with binding logs.

a14907 commented 5 years ago

I have the same problem, any solution?

vincedan commented 5 years ago

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