2

How can I <Import> a .targets file that is included in <PackageReference>d NuGet package in a non-SDK-style web application project that targets .NET 4.7.2?

Background

The web application project is not an SDK-style project. It references a class library project in the same solution. This class library project is an SDK-style project. It has a package reference to Microsoft.Data.SqlClient version 4.1.0, which depends on Microsoft.Data.SqlClient.SNI.runtime 4.0.0. The latter contains native binaries which are automatically copied to the bin directory of the project.

The native binaries are not copied to the bin directory of the web application project. This is probably because of this:

SNI is the native C++ library that SqlClient depends on for various network operations when running on Windows. In .NET Framework applications that are built with the MSBuild Project SDK, native DLLs aren't managed with restore commands. So a ".targets" file is included in the "Microsoft.Data.SqlClient.SNI" NuGet package that defines the necessary "Copy" operations.

The included ".targets" file is auto-referenced when a direct dependency is made to the "Microsoft.Data.SqlClient" library. In scenarios where a transitive (indirect) reference is made, this ".targets" file should be manually referenced to ensure "Copy" operations can execute when necessary.

Recommended Solution: Make sure the ".targets" file is referenced in the application's ".csproj" file to ensure "Copy" operations are executed.

Source

Not sure what "built with the MSBuild Project SDK" means exactly, but I think my scenario qualifies.

Now I'm trying to implement the recommended solution, but I cannot get it to work.

What I did

Step 1: add package reference to Microsoft.Data.SqlClient.SNI version 4.0.0:

<PackageReference Include="Microsoft.Data.SqlClient.SNI">
  <GeneratePathProperty>true</GeneratePathProperty>
  <Version>4.0.0</Version>
</PackageReference>

I added <GeneratePathProperty> because of step 2 below. According to the documentation this should make a property available that can be used to refer to files in the package:

Sometimes it is desirable to reference files in a package from an MSBuild target. In packages.config based projects, the packages are installed in a folder relative to the project file. However in PackageReference, the packages are consumed from the global-packages folder, which can vary from machine to machine.

To bridge that gap, NuGet introduced a property that points to the location from which the package will be consumed.

Step 2: import the targets file:

<Import Project="$(PkgMicrosoft_Data_SqlClient_SNI)\build\net46\Microsoft.Data.SqlClient.SNI.targets" Condition="Exists('$(PkgMicrosoft_Data_SqlClient_SNI)\build\net46\Microsoft.Data.SqlClient.SNI.targets')" />

You can see that the package contains this targets file using the NuGet package explorer (link). The PkgMicrosoft_Data_SqlClient_SNI property is named according to the documentation (linked above) and based on their example:

MSBuild properties and package identities do not have the same restrictions so the package identity needs to be changed to an MSBuild friendly name, prefixed by the word Pkg.

Step 3: verify the tool versions on our agent:

  • MSBuild 16.11.2.50704 (installed on the agent C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\bin)
  • NuGet 6.1.0.106 (installed via a pipeline task)

According to the documentation linked above, these fulfil the minimum requirements (MSBuild 16 and NuGet 5).

Step 4: run nuget restore on our build agent and then build the project using the Azure DevOps MSBuild@1 task with msbuildArguments: '/t:Build'.

Step 4: collect output and publish as an artifact.

Outcome:

The SNI files are not present in the output. When building locally with MSBuild 17.0.0.52104 (installed in 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\bin') I do see the SNI files.

Michiel van Oosterhout
  • 22,839
  • 15
  • 90
  • 132
  • This is the root cause: https://github.com/dotnet/SqlClient/issues/936 (SNI files are not copied to the publish directory used by the web application targets), with a work-around (add a target to copy `@(SNIFiles)` to `@(SNIFiles -> '$(WebProjectOutputDir)\bin\%(RecursiveDir)%(Filename)%(Extension)')`). – Michiel van Oosterhout May 04 '22 at 13:41

0 Answers0