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

Reference transitive not working well when app and libs has different targetFramework, and lib use an transitive reference from special targetFramework of an multi-targetFramework PackageReference/ProjectReference #3103

Open
yyjdelete opened this issue Apr 9, 2019 · 4 comments
Milestone

Comments

@yyjdelete
Copy link

yyjdelete commented Apr 9, 2019

Sdk Version: 3.0.100-preview3-010431, 2.1.504
Run ConsoleApp5 in https://github.com/yyjdelete/test_dotnet_indirect_dependency
And see an MissingMethodException and an FileNotFoundException for System.Data.SqlClient, Version=4.5.0.0

This can also happen with PackageReference. I reference Dapper from an ClassLibrary with netstandard2.0, and the main App is net472, and failed to found System.Data.SqlClient, Version=4.5.0.0.

Project struct:

ConsoleApp5(net472)
|--ClassLibrary1(netstandard2.0)
---|--ClassLibrary2(netstandard2.0;net472)
------|--System.Data.SqlClient(when netstandard2.0)

Expected:
The FileNotFoundException not happen and the System.Data.SqlClient, Version=4.5.0.0 is include in the output bins.
The below behavior may be not expected, but it's the current behavior for project struct(dependencies) in VS2019. (Rider follow the actual behavior for project struct)

ConsoleApp5(net472)
|--ClassLibrary1(netstandard2.0)
---|--ClassLibrary2(netstandard2.0)
------|--System.Data.SqlClient(netstandard2.0)

Actual:

ConsoleApp5(net472)
|--ClassLibrary1(netstandard2.0)
---|--System.Data.SqlClient(missing from the output of ConsoleApp5)
---|--ClassLibrary2(net472)
@dasMulli
Copy link
Contributor

dasMulli commented Apr 9, 2019

You seem to expose a different public API from ClassLibrary2 when compiled for different target frameworks.

This will cause MissingMethodExceptions due to every project selecting the "nearest" target framework for project and package references.

Especially exposing different public APIs between a .NET Standard and .NET Framework build is dangerous since .NET Standard is compatible with .NET Framework and you may end up in such a situation with intermediate .NET Standard projects/packages. It is less of a problem when exposing different API surfaces between .NET Core and .NET Framework TFMs (but may still exist due to the .NET Framework compatibility when you target e.g. netcoreapp2.2;net46 and reference from a netcoreapp2.1 project).

@yyjdelete
Copy link
Author

yyjdelete commented Apr 9, 2019

The MissingMethodException is just a part of test-case here and not the main issue in my code.

The main issue I hit is that ClassLibrary2 has different dependcy for different targetFramework(System.Data.SqlClient 4.6.0 for netstandard2.0). And ClassLibrary1 inherit the dependcy from ClassLibrary2(netstandard2.0) and use it. But ConsoleApp5 choose to use ClassLibrary2(net472) without the dependcy, and the inherited dependcy is
not considered as the dependcy of ClassLibrary1, so it's dropped silently and not used for ConsoleApp5. And get an FileNotFoundException when use new ClassLibrary1.Class2().Test() in ConsoleApp5

Reference the below project from an App with net472 and execute new ClassLibrary1.Class2().Test() to see the Exception, and this also happen if the below project itself is an .nupkg.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Dapper" Version="1.60.6" />
  </ItemGroup>

</Project>
using System;
using System.Data.SqlClient;

namespace ClassLibrary1
{
    public class Class2
    {
        public void Test()
        {
            using (var x = new SqlConnection())
            {
            }
        }
    }
}

And the expected behavior in #3103 (comment) is the current behavior of project struct of VS2019, if it's not expected, it's another issue.
Update: I see Rider does use the same behavior as dotnet-sdk for project struct, which show ClassLibrary2(net472) for ConsoleApp5.

image

@yyjdelete
Copy link
Author

Try to add an ConsoleApp2 using old-style csproj, and add an ProjectReference ClassLibrary1.

  1. Unlike the new-style csproj, see the building output using ClassLibrary2(ns2.0). But System.Data.SqlClient is always missing in output bins, even if ClassLibrary2 is changed to reference System.Data.SqlClient without Condition(not missing for new-style csproj in the case).

  2. Add another old-style csproj ClassLibrary3(net472) with PackageReference:Newtonsoft.Json, and add ProjectReference to ConsoleApp2. Unlike what happened when reference new-style csproj , see Newtonsoft.Json is copy to the output if used in ClassLibrary3(seems indirect reference is not allowed in old-style csproj, that you can't use Newtonsoft.Json in ConsoleApp2).

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{D961573D-C904-4B7E-996E-48C0F37598B6}</ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>ConsoleApp2</RootNamespace>
    <AssemblyName>ConsoleApp2</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj">
      <Project>{d16e7a90-74ac-487f-8db6-9ffb45841f98}</Project>
      <Name>ClassLibrary1</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

@yyjdelete yyjdelete changed the title Indirect dependency may broken when the library and app has different targetFramework Reference transitive not working well when app and libs has different targetFramework, and lib use an transitive reference from special targetFramework of an multi-targetFramework PackageReference/ProjectReference Apr 10, 2019
@vslynko
Copy link

vslynko commented Apr 19, 2019

may this microsoft/dotnet#860 be related?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants