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.ValueTuple - compatibility issue between net 4.8 and net standard 2.0 #84618

Closed
martin-stepanek-swi opened this issue Apr 11, 2023 · 8 comments
Labels
area-System.Runtime needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity tracking-external-issue The issue is caused by external problem (e.g. OS) - nothing we can do to fix it directly

Comments

@martin-stepanek-swi
Copy link

martin-stepanek-swi commented Apr 11, 2023

Description

There's a specific scenario where in my opinion incorrect assembly is loaded.

Basically mix of GAC, nuget package, binding redirect, net 4.8 and net standard 2.0 projects results in:

Unhandled Exception: System.TypeLoadException: Method 'get_KeyParametersWithResolver' in type 'ValueTupleIssueDemo.DemoClass' from assembly 'ValueTupleIssueDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. at ValueTupleIssueDemo.Program.Main(String[] args)

Reproduction Steps

Steps to reproduce the issue

  1. Have System.ValueTuple netstandard1.0 dll in GAC
    (can be taken from https://www.nuget.org/packages/System.ValueTuple/4.5.0)
  2. Create console app targeting net48 (have auto generated binding redirects ON for the console app project.)
  3. Add nuget package Microsoft.Extensions.Configuration.Json v. 3.1.22 (version is not relevant, can be newer as well)
  4. Have class with property using System.ValueTuple like this:
internal class DemoClass
    {
        public IEnumerable<(string Name, Func<string, object> Resolver)> KeyParametersWithResolver { get; }

        public DemoClass()
        {
            KeyParametersWithResolver = new (string Name, Func<string, object> Resolver)[]
           {
                ("key", key => key)
           };
        }
    }
  1. Instantiate this class from Main in Program.cs
 DemoClass demoClass = new DemoClass();

Until now, program should be running just fine.

  1. Add ClassLibrary project targeting netstandard2.0 and reference it from console app project.
  2. Add abstract class including abstract property
 public abstract class BaseDemoClass
    {
        public abstract IEnumerable<(string Name, Func<string, object> Resolver)> KeyParametersWithResolver { get; }
    }
  1. As soon as you implement this abstract class in DemoClass like this:
  internal class DemoClass : BaseDemoClass
    {
        public override IEnumerable<(string Name, Func<string, object> Resolver)> KeyParametersWithResolver { get; }

        public DemoClass()
        {
            KeyParametersWithResolver = new (string Name, Func<string, object> Resolver)[]
           {
                ("key", key => key)
           };
        }
    }

You'll get:
Unhandled Exception: System.TypeLoadException: Method 'get_KeyParametersWithResolver' in type 'ValueTupleIssueDemo.DemoClass' from assembly 'ValueTupleIssueDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
at ValueTupleIssueDemo.Program.Main(String[] args)

Expected behavior

ValueTuple is loaded correctly (from mscorlib or net 4.7 dll with redirect to mscorlib)

Actual behavior

System.ValueTuple is loaded from GAC and program ends up with

Unhandled Exception: System.TypeLoadException: Method 'get_KeyParametersWithResolver' in type 'ValueTupleIssueDemo.DemoClass' from assembly 'ValueTupleIssueDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. at ValueTupleIssueDemo.Program.Main(String[] args)

Regression?

No response

Known Workarounds

  1. Delete generated binding redireciton
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
  1. Replace System.ValueTuple in GAC with net47 version, or remove it from GAC completely.
  2. Use Tuple instead of ValueTuple.

Configuration

.NET: net 4.8 + net standard 2.0 as target projects
OS: Tried with Windows 10 21H2 and Windows Server 2019 1809, also Windows Server 2016 all with .net framework 4.8 installed.

Other information

System.ValueTuple is from NET 4.7 part of mscorlib, up to that point package was needed. Here partial issue is that Microsoft.Extensions.Configuration.Json has dependency on System.Text.Json
which then refers to System.ValueTuple, assembly redirection is then generated automatically

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>

net47 and netstandard1.0 dll for System.ValueTuple have same signature that is token, culture, version etc. Only difference is that net47 version has forwarder
to mscorlib, while netstandard1.0 one has implementation of ValueTuple in it.

Because of the same signature and order of assembly binding, the dll from GAC is loaded.
IMPORTANT : but that is ONLY IN CASE where we implement class from net standard 2.0, without it the mscorlib is used directly.

Public repo with reproduced issue (you need to add System.ValueTuple in GAC yourself, see readme) :
https://github.com/martin-stepanek-swi/SystemValueTupleIssue

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 11, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Apr 11, 2023
@ghost
Copy link

ghost commented Apr 11, 2023

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

There's a specific scenario where in my opinion incorrect assembly is loaded.

Basically mix of GAC, nuget package, binding redirect, net 4.8 and net standard 2.0 projects results in:

Unhandled Exception: System.TypeLoadException: Method 'get_KeyParametersWithResolver' in type 'ValueTupleIssueDemo.DemoClass' from assembly 'ValueTupleIssueDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. at ValueTupleIssueDemo.Program.Main(String[] args)

Reproduction Steps

Steps to reproduce the issue

  1. Have System.ValueTuple netstandard1.0 dll in GAC
    (can be taken from https://www.nuget.org/packages/System.ValueTuple/4.5.0)
  2. Create console app targeting net48 (have auto generated binding redirects ON for the console app project.)
  3. Add nuget package Microsoft.Extensions.Configuration.Json v. 3.1.22 (version is not relevant, can be newer as well)
  4. Have class with property using System.ValueTuple like this:
internal class DemoClass
    {
        public IEnumerable<(string Name, Func<string, object> Resolver)> KeyParametersWithResolver { get; }

        public DemoClass()
        {
            KeyParametersWithResolver = new (string Name, Func<string, object> Resolver)[]
           {
                ("key", key => key)
           };
        }
    }
  1. Instantiate this class from Main in Program.cs
 DemoClass demoClass = new DemoClass();

Until now, program should be running just fine.

  1. Add ClassLibrary project targeting netstandard2.0 and reference it from console app project.
  2. Add abstract class including abstract property
 public abstract class BaseDemoClass
    {
        public abstract IEnumerable<(string Name, Func<string, object> Resolver)> KeyParametersWithResolver { get; }
    }
  1. As soon as you implement this abstract class in DemoClass like this:
  internal class DemoClass : BaseDemoClass
    {
        public override IEnumerable<(string Name, Func<string, object> Resolver)> KeyParametersWithResolver { get; }

        public DemoClass()
        {
            KeyParametersWithResolver = new (string Name, Func<string, object> Resolver)[]
           {
                ("key", key => key)
           };
        }
    }

You'll get:
Unhandled Exception: System.TypeLoadException: Method 'get_KeyParametersWithResolver' in type 'ValueTupleIssueDemo.DemoClass' from assembly 'ValueTupleIssueDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
at ValueTupleIssueDemo.Program.Main(String[] args)

Expected behavior

ValueTuple is loaded correctly (from mscorlib or net 4.7 dll with redirect to mscorlib)

Actual behavior

System.ValueTuple is loaded from GAC and program ends up with

Unhandled Exception: System.TypeLoadException: Method 'get_KeyParametersWithResolver' in type 'ValueTupleIssueDemo.DemoClass' from assembly 'ValueTupleIssueDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. at ValueTupleIssueDemo.Program.Main(String[] args)

Regression?

No response

Known Workarounds

  1. Delete generated binding redireciton
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
  1. Replace System.ValueTuple in GAC with net47 version, or remove it from GAC completely.
  2. Use Tuple instead of ValueTuple.

Configuration

.NET: net 4.8 + net standard 2.0 as target projects
OS: Tried with Windows 10 21H2 and Windows Server 2019 1809, also Windows Server 2016 all with .net framework 4.8 installed.

Other information

System.ValueTuple is from NET 4.7 part of mscorlib, up to that point package was needed. Here partial issue is that Microsoft.Extensions.Configuration.Json has dependency on System.Text.Json
which then refers to System.ValueTuple, assembly redirection is then generated automatically

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>

net47 and netstandard1.0 dll for System.ValueTuple have same signature that is token, culture, version etc. Only difference is that net47 version has forwarder
to mscorlib, while netstandard1.0 one has implementation of ValueTuple in it.

Because of the same signature and order of assembly binding, the dll from GAC is loaded.
IMPORTANT : but that is ONLY IN CASE where we implement class from net standard 2.0, without it the mscorlib is used directly.

Public repo with reproduced issue (you need to add System.ValueTuple in GAC yourself, see readme) :
https://github.com/martin-stepanek-swi/SystemValueTupleIssue

Author: martin-stepanek-swi
Assignees: -
Labels:

area-System.Runtime, untriaged, needs-area-label

Milestone: -

@vcsjones vcsjones removed the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 11, 2023
@tannergooding tannergooding added needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration and removed untriaged New issue has not been triaged by the area owner labels Jul 21, 2023
@tannergooding
Copy link
Member

CC. @ericstj, could you provide input/insight into this scenario? It seems like types are failing to unify here.

@ericstj
Copy link
Member

ericstj commented Jul 24, 2023

I cannot reproduce this on a clean machine.

This part of the repro is invalid:

Have System.ValueTuple netstandard1.0 dll in GAC
(can be taken from https://www.nuget.org/packages/System.ValueTuple/4.5.0)

It sounds to me like someone installed an OOB copy of System.ValueTuple in the GAC and installed a version that didn't match the framework that was installed on the machine. That same package provided a better/correct asset for this machine which should have been used.

The assemblies must have the same identity for multi-targeting to work. We rely on asset selection to determine the "correct" one for the framework being targeted. Here the "correct" one is from the net47 folder in the package.

Removing the bindingRedirect makes things work because .NETFramework's unification table will force the version to the one inbox. bindingRedirects will override the unification table, however, so that's why you're able to force a different version than is inbox.

Probably we can report a bug here against MSBuild - it should not be generating bindingRedirects when the assembly is part of the framework. It should recognize that the a newer version is in the framework and not emit a bindingRedirect nor allow an app-local copy. FWIW the dotnet cli does this correctly, but the VS-installed MSBuild seems to produce the wrong result cc @rainersigwald @dsplaisted.

@ericstj ericstj added this to the Future milestone Aug 14, 2023
@jkotas jkotas added the tracking-external-issue The issue is caused by external problem (e.g. OS) - nothing we can do to fix it directly label Nov 1, 2023
@jeffhandley
Copy link
Member

@PranavSenthilnathan For this issue, if it can still be reproduced using the VS-installed MSBuild as @ericstj described, please file an issue against dotnet/msbuild to capture the scenario with an accurate title and a minimal repro if feasible.

@PranavSenthilnathan
Copy link
Member

I can't repro this issue. When I follow the steps and build, the generated binding redirects in the output do not contain System.ValueTuple. @martin-stepanek-swi please let me know if you still have this issue.

@PranavSenthilnathan PranavSenthilnathan added needs-author-action An issue or pull request that requires more info or actions from the author. and removed needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration labels Nov 18, 2024
Copy link
Contributor

This issue has been marked needs-author-action and may be missing some important information.

Copy link
Contributor

This issue has been automatically marked no-recent-activity because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will remove no-recent-activity.

Copy link
Contributor

This issue will now be closed since it had been marked no-recent-activity but received no further activity in the past 14 days. It is still possible to reopen or comment on the issue, but please note that the issue will be locked if it remains inactive for another 30 days.

@dotnet-policy-service dotnet-policy-service bot removed this from the Future milestone Dec 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Runtime needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity tracking-external-issue The issue is caused by external problem (e.g. OS) - nothing we can do to fix it directly
Projects
None yet
Development

No branches or pull requests

8 participants