Why is this NuGet dependency missing when compiling .NET Framework project depending on .NET Standard?
Asked Answered
H

2

10

I have a Visual Studio solution, with 3 projects.

The top level is a .NET Framework 4.6.1 Console App (Project A). It depends on a .NET Framework 4.6.1 Class Library (Project B). Project B depends on a .NET Standard 2.0 Class Library (Project C).

I have some code in Project C that uses System.Data.SqlClient (NuGet package version 4.6.1).

Due to the following known issue https://github.com/dotnet/sdk/issues/901 I have also added System.Data.SqlClient as a NuGet dependency to Project B (the .NET Framework Class Library).

This is Scenario 1, and when the solution is built, System.Data.SqlClient is copied to the /bin/Debug folder of Project A and the the application runs successfully.

The code for Scenario 1 is here https://github.com/JamesDunlop/TestDependencyFlowsNetStandard

However, for Scenario 2, I now ADD a project reference to Project A such that it now also directly references/depends-on Project C (i.e. the .NET Standard Class Library), as well as Project B. This mimics what I will need to do in a legacy application.

Clean, and rebuild and run. System.Data.SqlClient is now missing from the /bin/Debug folder of Project A, and at run time there is an exception "System.IO.FileNotFoundException: 'Could not load file or assembly 'System.Data.SqlClient"

Why does the System.Data.SqlClient not get copied to /bin/Debug ?

Note that I have chosen NOT to migrate the .NET Framework projects to PackageReferences in order to resolve the issue https://github.com/dotnet/sdk/issues/901, since I need to implement this in a large legacy ASP.NET solution where it is not feasible.

I would expect that adding a reference to Project C would have little effect, other than (as observed) it results in a lot more type-forwarding DLLs being copied to the /bin/Debug folder. But I would not expect System.Data.SqlClient to now be missing.

Herr answered 21/6, 2019 at 6:21 Comment(3)
"2 The versions listed here represent the rules that NuGet uses to determine whether a given .NET Standard library is applicable. While NuGet considers .NET Framework 4.6.1 as supporting .NET Standard 1.5 through 2.0, there are several issues with consuming .NET Standard libraries that were built for those versions from .NET Framework 4.6.1 projects. For .NET Framework projects that need to use such libraries, we recommend that you upgrade the project to target .NET Framework 4.7.2 or higher." (docs.microsoft.com/en-us/dotnet/standard/net-standard). Maybe some string to pull onSeljuk
Thank you for the suggestion @Seljuk . I have tried upgrading the .NET Framework projects to 4.7.2, but unfortunately the issue as stated above still remains.Herr
Adding the System.Data.SqlClient NuGet package also to project A should solve it. For scenario 2, the MSBuild log shows that it tries to resolve this dependency from project C, which is missing there because a .NET Standard project doesn't copy its dependencies to the bin folder. For scenario 1, MSBuild resolves it from project B, which succeeds.Boswall
B
5

I'll repeat my comment above here, as it is considered valid as an answer.

The MSBuild log, with its build output verbosity set to level detailed, gives more insights in what happens.

Scenario 1 (A referencing B, B referencing C)

The build log shows that project A successfully resolved its System.Data.SqlClient dependency from the \bin\debug folder of project B and copies it locally.
(As project B is a .NET Framework class library, its NuGet dependencies do get copied to its bin folder.)

Dependency "System.Data.SqlClient, Version=4.5.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
  Resolved file path is "C:\...\TestDependencyFlows.Library\bin\Debug\System.Data.SqlClient.dll".

Scenario 2 (A referencing B and C, B referencing C)

The build log mentions that project A tries to resolve its System.Data.SqlClient dependency from the NET Standard project C (and some wellknown folders), but not anymore from project B.
(Because project C is a NET Standard project, it doesn't copy its NuGetdependencies to its bin folder.)
All these attempts fail with the message that the file doesn't exist at these locations.

Dependency "System.Data.SqlClient, Version=4.5.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
  Could not resolve this reference. Could not locate the assembly "System.Data.SqlClient, Version=4.5.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". 
  Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
  For SearchPath "C:\...\TestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0".
      Considered "C:\...\TestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0\System.Data.SqlClient.winmd", but it didn't exist.
      Considered "C:\...\TTestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0\System.Data.SqlClient.dll", but it didn't exist.
      Considered "C:\...\TestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0\System.Data.SqlClient.exe", but it didn't exist.
      ...

A solution could be to add the System.Data.SqlClient NuGet package also to project A.

Boswall answered 26/6, 2019 at 5:43 Comment(0)
H
1

pfx comment on the original question essentially answers the question.

In Scenario 1, we see this in the build log

3>  Dependency "System.Data.SqlClient, Version=4.5.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
3>      Resolved file path is "C:\dev\testing\TestDependencyFlowsNetStandard\TestDependencyFlows.Library\bin\Debug\System.Data.SqlClient.dll".

For Scenario 2, MSBuild does not attempt to use the bin directory of Project B and the build log shows this as noted by pfx

3> For SearchPath "C:\dev\testing\TestDependencyFlowsNetStandard\TestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0".
3>          Considered "C:\dev\testing\TestDependencyFlowsNetStandard\TestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0\System.Data.SqlClient.winmd", but it didn't exist.
3>          Considered "C:\dev\testing\TestDependencyFlowsNetStandard\TestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0\System.Data.SqlClient.dll", but it didn't exist.
3>          Considered "C:\dev\testing\TestDependencyFlowsNetStandard\TestDependencyFlows.Library.NetStandard\bin\Debug\netstandard2.0\System.Data.SqlClient.exe", but it didn't exist.

This insight provides the solution to fix Scenario 2.

Using the solution in this answer, by adding <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> to a <PropertyGroup> in the .csproj for the .NET Standard Project C, System.Data.SqlClient is copied to the bin directory of that .NET Standard project and MSBuild is then able to locate and copy it to the final build output folder.

EDIT - the suggestion above does not work, since when running a real application the DLL that is pushed to the output directory is for the wrong platform and is incompatible with .NET Framework.

As further info, the build logs that pfx refers to can be obtained within the Visual Studio IDE as per this post

pfx suggestion of adding System.Data.SqlClient to Project A works, but this was something I wanted to avoid because in the real monolithic legacy app there are many equivalents to Project A where it would need to be added (rather than simply adding it to the next level up Project B).

Herr answered 26/6, 2019 at 0:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.