Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions BUILDGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,38 @@ dotnet build -t:TestSqlClientFunctional -p:TestFramework=net10.0

### Packaging Projects

#### Consumer family-version check

The Logging package owns `ValidateSqlClientFamilyPackageVersions` and ships it in
both `build/` and `buildTransitive/`. NuGet imports it into direct consumers and
transitive PackageReference consumers (unless build assets are excluded).
Installed Logging, Abstractions, SqlClient, Extensions.Azure, and AKV packages
must match the resolved Logging version exactly, including prerelease labels.
Optional family packages are not required; independently versioned
Microsoft.SqlServer.Server and native SNI packages are excluded. The target uses
resolved compile/runtime package metadata or legacy `packages.config`, not
assembly versions or declared version ranges, and skips design-time builds.
Missing version metadata or a missing Logging anchor is not guessed.

**SQLCLIENT001** means installed family versions differ. Upgrade them together;
historical extension `1.0.0` versions are not automatically mapped to driver
`7.0.0`/`7.0.1`. The emergency escape hatch is
`-p:SqlClientEnforceFamilyVersions=false` (or that property in the consumer
project). It suppresses this check only, not incompatibilities.
Ordinary project references do not propagate NuGet buildTransitive assets:
Project-mode repository builds are not validated by this consumer target.

Run the focused synthetic and isolated NuGet consumer regression suite with:

```powershell
pwsh -NoProfile -File src/Microsoft.Data.SqlClient.Internal/Logging/test/RunVersionChecks.ps1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulmedynski - Use dotnet tool run pwsh to ensure correct tooling.

```

The suite packs the real Logging project, creates small family-package fixtures
in a private local feed, and verifies automatic imports for both direct and
transitive package consumers. It uses a unique directory under Logging/test and
removes that directory on completion; no SQL Server is required.

Just like building and testing the various projects in this repository, packaging the projects into NuGet packages is
also handled by `build.proj`. From the root of your repository, run `dotnet build` against `build.proj` with a pack target,
following this pattern:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<Project>
<!-- Logging owns the consumer check and ships it in build/ and buildTransitive/.
Only installed members of the lockstep family are checked. SqlServer.Server and native
SNI are independently versioned. Project references are not package-version evidence. -->
<PropertyGroup>
<SqlClientEnforceFamilyVersions Condition="'$(SqlClientEnforceFamilyVersions)' == ''">true</SqlClientEnforceFamilyVersions>
</PropertyGroup>

<Target Name="ValidateSqlClientFamilyPackageVersions"
AfterTargets="ResolvePackageAssets;ResolveReferences"
Condition="'$(SqlClientEnforceFamilyVersions)' == 'true' And '$(DesignTimeBuild)' != 'true'">
<ItemGroup>
<!-- Collect first, then use qualified metadata so framework/project references without
NuGet metadata cannot cause MSB4096. Include RID-specific runtime-only assets. -->
<_SqlClientResolvedAsset Include="@(ResolvedCompileFileDefinitions);@(ReferencePath);@(RuntimeCopyLocalItems);@(RuntimeTargetsCopyLocalItems)" />
<_SqlClientFamilyAsset Include="@(_SqlClientResolvedAsset)"
Condition="'%(_SqlClientResolvedAsset.NuGetPackageId)' == 'Microsoft.Data.SqlClient' Or
'%(_SqlClientResolvedAsset.NuGetPackageId)' == 'Microsoft.Data.SqlClient.Extensions.Abstractions' Or
'%(_SqlClientResolvedAsset.NuGetPackageId)' == 'Microsoft.Data.SqlClient.Extensions.Azure' Or
'%(_SqlClientResolvedAsset.NuGetPackageId)' == 'Microsoft.Data.SqlClient.Internal.Logging' Or
'%(_SqlClientResolvedAsset.NuGetPackageId)' == 'Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider'">
<FamilyVersion>%(_SqlClientResolvedAsset.NuGetPackageVersion)</FamilyVersion>
<FamilyId>%(_SqlClientResolvedAsset.NuGetPackageId)</FamilyId>
</_SqlClientFamilyAsset>
<_SqlClientLoggingVersion Include="@(_SqlClientFamilyAsset)"
Condition="'%(_SqlClientFamilyAsset.FamilyId)' == 'Microsoft.Data.SqlClient.Internal.Logging' And '%(_SqlClientFamilyAsset.FamilyVersion)' != ''" />
</ItemGroup>
<PropertyGroup>
<!-- Compare resolved version strings, including prerelease labels, not ranges or assembly
versions. Missing package metadata is not assigned a guessed version. -->
<_SqlClientExpectedFamilyVersion>@(_SqlClientLoggingVersion->'%(FamilyVersion)'->Distinct())</_SqlClientExpectedFamilyVersion>
</PropertyGroup>
<Error Code="SQLCLIENT001"
Condition="'%(_SqlClientFamilyAsset.FamilyVersion)' != '' And '$(_SqlClientExpectedFamilyVersion)' != '' And '%(_SqlClientFamilyAsset.FamilyVersion)' != '$(_SqlClientExpectedFamilyVersion)'"
Text="SqlClient family version mismatch: %(_SqlClientFamilyAsset.FamilyId) is %(_SqlClientFamilyAsset.FamilyVersion), but Logging is $(_SqlClientExpectedFamilyVersion). Upgrade all installed family packages together, including prerelease labels. Emergency opt-out: SqlClientEnforceFamilyVersions=false." />

<!-- Legacy packages.config consumers import build/ explicitly through NuGet installation.
Read installed versions from the manifest, independently of assembly reference metadata. -->
<XmlPeek XmlInputPath="$(MSBuildProjectDirectory)/packages.config"
Query="/packages/package[@id='Microsoft.Data.SqlClient.Internal.Logging']/@version"
Condition="Exists('$(MSBuildProjectDirectory)/packages.config')">
Comment on lines +39 to +41
<Output TaskParameter="Result" PropertyName="_SqlClientConfigExpectedVersion" />
</XmlPeek>
<XmlPeek XmlInputPath="$(MSBuildProjectDirectory)/packages.config"
Query="/packages/package[(@id='Microsoft.Data.SqlClient.Internal.Logging' or @id='Microsoft.Data.SqlClient.Extensions.Abstractions' or @id='Microsoft.Data.SqlClient' or @id='Microsoft.Data.SqlClient.Extensions.Azure' or @id='Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider') and @version!='$(_SqlClientConfigExpectedVersion)']"
Condition="'$(_SqlClientConfigExpectedVersion)' != ''">
<Output TaskParameter="Result" ItemName="_SqlClientConfigMismatch" />
</XmlPeek>
<Error Code="SQLCLIENT001"
Condition="'@(_SqlClientConfigMismatch)' != ''"
Text="SqlClient family version mismatch in packages.config: %(_SqlClientConfigMismatch.Identity), but Logging is $(_SqlClientConfigExpectedVersion). Upgrade all installed family packages together, including prerelease labels. Emergency opt-out: SqlClientEnforceFamilyVersions=false." />
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
<ItemGroup>
<None Include="$(RepoRoot)/tools/icons/dotnet.png" Pack="true" PackagePath="/" />
<None Include="PackageReadme.md" Pack="true" PackagePath="README.md" />
<None Include="../build/Microsoft.Data.SqlClient.Internal.Logging.targets"
Pack="true" PackagePath="build/;buildTransitive/" />
</ItemGroup>

<!-- CodeGen Targets ================================================= -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,33 @@ This package provides **ETW EventSource tracing and diagnostics** for [Microsoft

## Supportability

### SqlClient family version validation

Logging ships a consumer MSBuild target in `build/` and `buildTransitive/`.
NuGet imports it for direct package consumers and transitive PackageReference
consumers. After package/reference resolution it compares installed
`Microsoft.Data.SqlClient`, `Microsoft.Data.SqlClient.Extensions.Abstractions`,
`Microsoft.Data.SqlClient.Extensions.Azure`, and
`Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` package versions
against the **resolved Logging package version**. Optional packages are not required.
`Microsoft.SqlServer.Server` and native SNI packages are excluded.

Versions must match exactly, including prerelease labels; a mismatch fails the
build with **SQLCLIENT001**. Upgrade all installed family packages together.
Historical extensions numbered `1.0.0` are not mapped to driver `7.0.0`/`7.0.1`.
The check uses resolved compile/runtime asset metadata for PackageReference and
installed versions in `packages.config` for legacy consumers. It does not check
assembly versions, restore ranges, or API compatibility. Assets missing version
metadata and builds without a Logging version anchor are skipped. Design-time
builds are skipped.

For emergency recovery, set `SqlClientEnforceFamilyVersions=false` in the
consumer project or pass `-p:SqlClientEnforceFamilyVersions=false`. This disables
only this build check; it does not make mixed versions compatible. Excluding the
package's build assets also prevents target import. Ordinary project references
do not propagate NuGet buildTransitive targets, so this check does not enforce
in-tree Project-mode versions.

This package supports:

- .NET Standard 2.0 (compatible with .NET Framework 4.6.1+, .NET Core 2.0+, and .NET 5+)
Expand Down
Loading
Loading