dotnet / standard

This repo is building the .NET Standard
3.07k stars 428 forks source link

Issue with resolving between .NET Standard 2.0 and .NET Standard 2.1 #1787

Closed rexcfnghk closed 2 years ago

rexcfnghk commented 3 years ago

Summary

.NET Standard version is not resolved correctly when a transitive dependency targets multiple .NET Standard versions.

Environment

Minimal reproducible example

bug.zip

This is a three-project solution:

The project dependency chain is: TestProject1 -> ClassProject1 -> ClassProject2

A method (ClassProject2Test1) on ClassProject2 is hidden behind a #if NETSTANDARD2_0 preprocessor directive.

The issue

The unit test failed with a MissingMethodException

[xUnit.net 00:00:00.38]     TestProject1.UnitTest1.Test1 [FAIL]
  Failed TestProject1.UnitTest1.Test1 [2 ms]
  Error Message:
   System.MissingMethodException : Method not found: 'Void ClassLibrary2.Class2.Class2Test1()'.
  Stack Trace:
     at ClassLibrary1.Class1.Class1Test1()
   at TestProject1.UnitTest1.Test1() in C:\Users\rex\source\netstandard-bug-repro\TestProject1\UnitTest1.cs:line 12

Expected result

The compiler should have resolved the chain as:

TestProject1 (.NET Core 3.1) -> ClassProject1 (.NET Standard 2.0) -> *ClassProject2 (.NET Standard 2.0)*

Actual result

The compiler resolved the chain as:

TestProject1 (.NET Core 3.1) -> ClassProject1 (.NET Standard 2.0) -> *ClassProject2 (.NET Standard 2.1)*

Which causes the exception because the offending method, ClassProject2Test1, was hidden by the #if NETSTANDARD2_0 preprocessor directive.

John0King commented 3 years ago

Well, I think this is the correct behavior, since .NET Standard 2.1 is higher than .NET Standard 2.0 , and .Net Core 3.1 can use .NET Standard 2.1, this is because .NET Standard 2.1 has much more api than .NET Standard 2.0 (the Span<T> and more) so library that target .NET Standard 2.1 can more optimization (for example , use Span<T> related type and api)

rexcfnghk commented 3 years ago

Well, I think this is the correct behavior, since .NET Standard 2.1 is higher than .NET Standard 2.0 , and .Net Core 3.1 can use .NET Standard 2.1, this is because .NET Standard 2.1 has much more api than .NET Standard 2.0 (the Span<T> and more) so library that target .NET Standard 2.1 can more optimization (for example , use Span<T> related type and api)

But from the project dependency hierarchy, it should be clear that ClassProject1 targets .NET Standard 2.0 only so shouldn't ClassProject2 be more restrictive by targeting .NET Standard 2.0 instead of 2.1?

terrajobst commented 2 years ago

A method (ClassProject2Test1) on ClassProject2 is hidden behind a #if NETSTANDARD2_0 preprocessor directive.

This is a violation of normal versioning rules. If you multitarget for A and B (and B > A) than B must not have fewer APIs than A. Replace .NET Standard with .NET Core and then it should be easy to understand:

The application (TestProject1) will run on .NET Core 3.0. That means it will use the netcoreapp2.0 build of ClassProject1 (because it's the only option) and the netcoreapp3.0 of ClassProject2 (because it's the better match).

Does this make sense?

Closing as by-design.