Title
ExcludeAssets="runtime" ignored by Microsoft.Data.SqlClient.SNI package - native DLLs copied unconditionally
Description
The Microsoft.Data.SqlClient.SNI package does not respect ExcludeAssets="runtime" on the parent Microsoft.Data.SqlClient package reference. Native SNI DLLs (Microsoft.Data.SqlClient.SNI.x64.dll, x86.dll, arm64.dll and their PDBs) are copied to the output directory even when runtime assets should be excluded.
Steps to Reproduce
- Create a .NET Framework 4.7.2 project
- Add package reference with runtime exclusion:
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.0.0" ExcludeAssets="runtime" PrivateAssets="all" />
- Build the project
- Check the output directory
Expected Behavior
With ExcludeAssets="runtime", no runtime DLLs or PDBs from Microsoft.Data.SqlClient or its dependencies should be copied to the output directory.
Actual Behavior
The following files are copied to the output directory despite the exclusion:
- Microsoft.Data.SqlClient.SNI.x64.dll
- Microsoft.Data.SqlClient.SNI.x64.pdb
- Microsoft.Data.SqlClient.SNI.x86.dll
- Microsoft.Data.SqlClient.SNI.x86.pdb
- Microsoft.Data.SqlClient.SNI.arm64.dll
- Microsoft.Data.SqlClient.SNI.arm64.pdb
Root Cause
The Microsoft.Data.SqlClient.SNI package (version 6.0.2) contains a .targets file at:
build/net462/Microsoft.Data.SqlClient.SNI.targets
buildTransitive/net462/Microsoft.Data.SqlClient.SNI.targets
This targets file unconditionally injects CopySNIFiles into the build process:
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
CopySNIFiles;
</BuildDependsOn>
<PrepareForRunDependsOn>
$(PrepareForRunDependsOn);
CopySNIFiles;
</PrepareForRunDependsOn>
</PropertyGroup>
This bypasses NuGet's ExcludeAssets mechanism entirely.
Investigation Results
Adding diagnostic MSBuild targets confirmed that NuGet correctly excludes the assets:
RuntimeCopyLocalItems: empty ✓
NativeCopyLocalItems: empty ✓
ReferenceCopyLocalPaths: empty ✓
However, the custom CopySNIFiles target runs directly through BuildDependsOn, ignoring the exclusion.
Workaround
Users must exclude build assets as well to prevent the targets file from being imported:
<PackageReference Include="Microsoft.Data.SqlClient"
ExcludeAssets="runtime;build;buildTransitive"
PrivateAssets="all" />
This is not ideal as it prevents all build-time functionality, not just the problematic copy behavior.
Suggested Fixes
Option 1: Add opt-out property (Quick fix)
<PropertyGroup>
<!-- Allow users to opt out of SNI file copying -->
<CopySNIFiles Condition="'$(CopySNIFiles)' == ''">true</CopySNIFiles>
</PropertyGroup>
<PropertyGroup>
<BuildDependsOn Condition="'$(CopySNIFiles)' == 'true'">
$(BuildDependsOn);
CopySNIFiles;
</BuildDependsOn>
<PrepareForRunDependsOn Condition="'$(CopySNIFiles)' == 'true'">
$(PrepareForRunDependsOn);
CopySNIFiles;
</PrepareForRunDependsOn>
</PropertyGroup>
Option 2: Use NuGet runtime assets (Best practice)
Remove the custom .targets file and properly package SNI DLLs as runtime assets using the runtimes/{rid}/native/ folder structure in the .nupkg. This would automatically respect ExcludeAssets="runtime".
Option 3: Check for exclusion markers
The targets file could check MSBuild properties or item metadata to detect if runtime assets should be excluded, though this is complex and fragile.
Environment
- Microsoft.Data.SqlClient version: 7.0.0
- Microsoft.Data.SqlClient.SNI version: 6.0.2 (transitive dependency for net462/net472)
- Target Framework: .NET Framework 4.7.2 (net472)
- SDK Style: Yes
- NuGet Package Management: Central Package Management
Additional Context
This issue affects scenarios where:
- Projects need compile-time references but not runtime deployment
- Projects want to control exactly which native DLLs are deployed
- Build output cleanliness is important
- Custom deployment strategies are used
Title
ExcludeAssets="runtime"ignored by Microsoft.Data.SqlClient.SNI package - native DLLs copied unconditionallyDescription
The
Microsoft.Data.SqlClient.SNIpackage does not respectExcludeAssets="runtime"on the parentMicrosoft.Data.SqlClientpackage reference. Native SNI DLLs (Microsoft.Data.SqlClient.SNI.x64.dll, x86.dll, arm64.dll and their PDBs) are copied to the output directory even when runtime assets should be excluded.Steps to Reproduce
Expected Behavior
With
ExcludeAssets="runtime", no runtime DLLs or PDBs from Microsoft.Data.SqlClient or its dependencies should be copied to the output directory.Actual Behavior
The following files are copied to the output directory despite the exclusion:
Root Cause
The
Microsoft.Data.SqlClient.SNIpackage (version 6.0.2) contains a.targetsfile at:build/net462/Microsoft.Data.SqlClient.SNI.targetsbuildTransitive/net462/Microsoft.Data.SqlClient.SNI.targetsThis targets file unconditionally injects
CopySNIFilesinto the build process:This bypasses NuGet's
ExcludeAssetsmechanism entirely.Investigation Results
Adding diagnostic MSBuild targets confirmed that NuGet correctly excludes the assets:
RuntimeCopyLocalItems: empty ✓NativeCopyLocalItems: empty ✓ReferenceCopyLocalPaths: empty ✓However, the custom
CopySNIFilestarget runs directly throughBuildDependsOn, ignoring the exclusion.Workaround
Users must exclude build assets as well to prevent the targets file from being imported:
This is not ideal as it prevents all build-time functionality, not just the problematic copy behavior.
Suggested Fixes
Option 1: Add opt-out property (Quick fix)
Option 2: Use NuGet runtime assets (Best practice)
Remove the custom
.targetsfile and properly package SNI DLLs as runtime assets using theruntimes/{rid}/native/folder structure in the.nupkg. This would automatically respectExcludeAssets="runtime".Option 3: Check for exclusion markers
The targets file could check MSBuild properties or item metadata to detect if runtime assets should be excluded, though this is complex and fragile.
Environment
Additional Context
This issue affects scenarios where: