From ce38e64ee73b7339f5aaff07a52fa1dd48f59133 Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Tue, 17 Mar 2026 15:43:07 -0700 Subject: [PATCH 01/22] Add release notes for 7.0 (#4053) --- .github/prompts/release-notes.prompt.md | 4 + CHANGELOG.md | 131 ++++++ release-notes/7.0/7.0.0.md | 410 ++++++++++++++++++ release-notes/7.0/README.md | 1 + .../Extensions/Abstractions/1.0/1.0.0.md | 30 ++ .../Extensions/Abstractions/1.0/README.md | 1 + release-notes/Extensions/Azure/1.0/1.0.0.md | 66 +++ release-notes/Extensions/Azure/1.0/README.md | 1 + .../Extensions/Logging/1.0/README.md | 3 + release-notes/Internal/Logging/1.0/1.0.0.md | 37 ++ release-notes/Internal/Logging/1.0/README.md | 11 + release-notes/README.md | 11 +- .../AzureKeyVaultProvider/7.0/7.0.0.md | 54 +++ .../AzureKeyVaultProvider/7.0/README.md | 1 + 14 files changed, 756 insertions(+), 5 deletions(-) create mode 100644 release-notes/7.0/7.0.0.md create mode 100644 release-notes/Extensions/Abstractions/1.0/1.0.0.md create mode 100644 release-notes/Extensions/Azure/1.0/1.0.0.md create mode 100644 release-notes/Internal/Logging/1.0/1.0.0.md create mode 100644 release-notes/Internal/Logging/1.0/README.md create mode 100644 release-notes/add-ons/AzureKeyVaultProvider/7.0/7.0.0.md diff --git a/.github/prompts/release-notes.prompt.md b/.github/prompts/release-notes.prompt.md index ca03933236..55cc7785e2 100644 --- a/.github/prompts/release-notes.prompt.md +++ b/.github/prompts/release-notes.prompt.md @@ -86,6 +86,10 @@ For each package that has relevant PRs in the milestone: - Look up dependencies using the Dependency Sources from the lookup table above. Resolve concrete versions from [Directory.Packages.props](Directory.Packages.props). - List dependencies per target framework. Use the project file's `` to determine which frameworks to list. - Omit the Contributors section for packages with no public contributors. + - **GA releases (all packages):** When the release is a stable (non-preview) version, structure the notes with two sections: + 1. **"Changes Since [last preview]"** — only the delta since the most recent preview of this package. + 2. **"Cumulative Changes Since [last stable]"** — all changes since the last stable release of this package, synthesized from all preview release notes plus the GA milestone. This applies to every package (MDS, AKV, Extensions.Azure, Abstractions, Internal.Logging, etc.), not just the core driver. Apply the cross-referencing from Step 3 to eliminate items already shipped in prior stable patch releases. + - **Preview releases:** Only include the delta since the previous release (preview or stable). No cumulative section is needed. 3. **Create or update the version README** at `/README.md`. Follow the existing format — see [release-notes/add-ons/AzureKeyVaultProvider/6.1/README.md](release-notes/add-ons/AzureKeyVaultProvider/6.1/README.md) for reference: diff --git a/CHANGELOG.md b/CHANGELOG.md index dc51c7d2bc..fedf1ad568 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,137 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) > **Note:** Releases are sorted in reverse chronological order (newest first). +## [Stable Release 7.0.0] - 2026-03-17 + +This section summarizes all changes across the 7.0 preview cycle for users upgrading from the latest 6.1 stable release. +See the [full release notes](release-notes/7.0/7.0.0.md) for detailed descriptions. + +Also released as part of this milestone: +- Released Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0. See [release notes](release-notes/Extensions/Abstractions/1.0/1.0.0.md). +- Released Microsoft.Data.SqlClient.Extensions.Azure 1.0.0. See [release notes](release-notes/Extensions/Azure/1.0/1.0.0.md). +- Released Microsoft.Data.SqlClient.Internal.Logging 1.0.0. See [release notes](release-notes/Internal/Logging/1.0/1.0.0.md). +- Released Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 7.0.0. See [release notes](release-notes/add-ons/AzureKeyVaultProvider/7.0/7.0.0.md). + +### Changed + +- **Breaking:** Removed Azure dependencies from the core package. Entra ID authentication (`ActiveDirectoryAuthenticationProvider` and related types) has been extracted into a new `Microsoft.Data.SqlClient.Extensions.Azure` package. The core `Microsoft.Data.SqlClient` package no longer depends on `Azure.Core`, `Azure.Identity`, or their transitive dependencies. Applications using Entra ID authentication must now install `Microsoft.Data.SqlClient.Extensions.Azure` separately. + ([#1108](https://github.com/dotnet/SqlClient/issues/1108), + [#3680](https://github.com/dotnet/SqlClient/pull/3680), + [#3902](https://github.com/dotnet/SqlClient/pull/3902), + [#3904](https://github.com/dotnet/SqlClient/pull/3904), + [#3908](https://github.com/dotnet/SqlClient/pull/3908), + [#3917](https://github.com/dotnet/SqlClient/pull/3917), + [#3982](https://github.com/dotnet/SqlClient/pull/3982), + [#3978](https://github.com/dotnet/SqlClient/pull/3978), + [#3986](https://github.com/dotnet/SqlClient/pull/3986)) + +- Two additional packages were introduced to support this separation: `Microsoft.Data.SqlClient.Extensions.Abstractions` (shared types between the core driver and extensions) and `Microsoft.Data.SqlClient.Internal.Logging` (shared ETW tracing infrastructure). + ([#3626](https://github.com/dotnet/SqlClient/pull/3626), + [#3628](https://github.com/dotnet/SqlClient/pull/3628), + [#3967](https://github.com/dotnet/SqlClient/pull/3967), + [#4038](https://github.com/dotnet/SqlClient/pull/4038)) + +- Deprecated `SqlAuthenticationMethod.ActiveDirectoryPassword` (ROPC flow). The method is now marked `[Obsolete]` and will generate compiler warnings. Migrate to `ActiveDirectoryInteractive`, `ActiveDirectoryServicePrincipal`, `ActiveDirectoryManagedIdentity`, or `ActiveDirectoryDefault`. + ([#3671](https://github.com/dotnet/SqlClient/pull/3671)) + +- Reverted public visibility of internal interop enums (`IoControlCodeAccess` and `IoControlTransferType`) that were accidentally made public during the project merge. + ([#3900](https://github.com/dotnet/SqlClient/pull/3900)) + +- Removed `Constrained Execution Region` error handling blocks and associated `SqlConnection` cleanup. + ([#3535](https://github.com/dotnet/SqlClient/pull/3535)) + +- Performance improvements across SqlStatistics timing, Always Encrypted scenarios, and connection opening: + ([#3609](https://github.com/dotnet/SqlClient/pull/3609), + [#3612](https://github.com/dotnet/SqlClient/pull/3612), + [#3732](https://github.com/dotnet/SqlClient/pull/3732), + [#3660](https://github.com/dotnet/SqlClient/pull/3660), + [#3791](https://github.com/dotnet/SqlClient/pull/3791), + [#3772](https://github.com/dotnet/SqlClient/pull/3772), + [#3554](https://github.com/dotnet/SqlClient/pull/3554)) + +- Allow `SqlBulkCopy` to operate on hidden columns. + ([#3590](https://github.com/dotnet/SqlClient/pull/3590)) + +- Updated UserAgent feature to use a pipe-delimited format, replacing the previous JSON format. + ([#3826](https://github.com/dotnet/SqlClient/pull/3826)) + +- Minor improvements to Managed SNI tracing to capture continuation events and errors. + ([#3859](https://github.com/dotnet/SqlClient/pull/3859)) + +### Added + +- Added `SspiContextProvider` abstract class and `SqlConnection.SspiContextProvider` property, enabling custom SSPI authentication for scenarios like cross-domain Kerberos negotiation and NTLM username/password authentication. + ([#2253](https://github.com/dotnet/SqlClient/issues/2253), + [#2494](https://github.com/dotnet/SqlClient/pull/2494)) + +- Continued refinement of packet multiplexing with bug fixes and stability improvements, plus new app context switches for opt-in control. + ([#3534](https://github.com/dotnet/SqlClient/pull/3534), + [#3537](https://github.com/dotnet/SqlClient/pull/3537), + [#3605](https://github.com/dotnet/SqlClient/pull/3605)) + +- Added support for enhanced routing, a TDS feature that allows the server to redirect connections to a specific server and database during login, enabling Azure SQL Hyperscale read replica load balancing. + ([#3641](https://github.com/dotnet/SqlClient/issues/3641), + [#3969](https://github.com/dotnet/SqlClient/pull/3969), + [#3970](https://github.com/dotnet/SqlClient/pull/3970), + [#3973](https://github.com/dotnet/SqlClient/pull/3973)) + +- Updated pipelines and test suites to compile the driver using the .NET 10 SDK. + ([#3686](https://github.com/dotnet/SqlClient/pull/3686)) + +- Added `SqlConfigurableRetryFactory.BaselineTransientErrors` static property exposing the default transient error codes list as a `ReadOnlyCollection`. + ([#3903](https://github.com/dotnet/SqlClient/pull/3903)) + +- Added app context switch `Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault` to set `MultiSubnetFailover=true` globally without modifying connection strings. + ([#3841](https://github.com/dotnet/SqlClient/pull/3841)) + +- Added app context switch `Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner` to let the client ignore server-provided failover partner info in Basic Availability Groups. + ([#3625](https://github.com/dotnet/SqlClient/pull/3625)) + +- Enabled `SqlClientDiagnosticListener` for `SqlCommand` on .NET Framework, closing a long-standing observability gap where diagnostic events were previously only available on .NET Core. + ([#3658](https://github.com/dotnet/SqlClient/pull/3658)) + +- Brought the 15 strongly-typed diagnostic event classes in the `Microsoft.Data.SqlClient.Diagnostics` namespace (e.g., `SqlClientCommandBefore`, `SqlClientConnectionOpenAfter`, `SqlClientTransactionCommitError`) to .NET Framework as part of the codebase merge. These types were originally introduced for .NET Core in 6.0. + ([#3493](https://github.com/dotnet/SqlClient/pull/3493)) + +- Enabled User Agent Feature Extension (opt-in via `Switch.Microsoft.Data.SqlClient.EnableUserAgent`). + ([#3606](https://github.com/dotnet/SqlClient/pull/3606)) + +- Added actionable error message when Entra ID authentication methods are used without the `Microsoft.Data.SqlClient.Extensions.Azure` package installed. + ([#3962](https://github.com/dotnet/SqlClient/issues/3962), + [#4046](https://github.com/dotnet/SqlClient/pull/4046)) + +### Fixed + +- Fixed a connection performance regression where SPN generation was triggered for non-integrated authentication modes (e.g., SQL authentication) on the native SNI path. + ([#3929](https://github.com/dotnet/SqlClient/pull/3929)) + +- Fixed `ExecuteScalar` to propagate errors when the server sends data followed by an error token. + ([#3912](https://github.com/dotnet/SqlClient/pull/3912)) + +- Fixed `NullReferenceException` in `SqlDataAdapter` when processing batch scenarios. + ([#3857](https://github.com/dotnet/SqlClient/pull/3857)) + +- Fixed reading of multiple app context switches from a single `AppContextSwitchOverrides` configuration field. + ([#3960](https://github.com/dotnet/SqlClient/pull/3960)) + +- Fixed an edge case in `TdsParserStateObject.TryReadPlpBytes` where zero-length reads returned `null` instead of an empty array. + ([#3872](https://github.com/dotnet/SqlClient/pull/3872)) + +- Fixed issue where extra connection deactivation was occurring. + ([#3758](https://github.com/dotnet/SqlClient/pull/3758)) + +- Fixed debug assertion in connection pool (no impact to production code). + ([#3587](https://github.com/dotnet/SqlClient/pull/3587)) + +- Prevented uninitialized performance counters escaping `CreatePerformanceCounters`. + ([#3623](https://github.com/dotnet/SqlClient/pull/3623)) + +- Fixed `SetProvider` to return immediately if user-defined authentication provider found. + ([#3620](https://github.com/dotnet/SqlClient/pull/3620)) + +- Fixed connection pool concurrency issue. + ([#3632](https://github.com/dotnet/SqlClient/pull/3632)) + ## [Preview Release 7.0.0-preview4.26064.3] - 2026-03-05 This update brings the below changes over the previous preview release: diff --git a/release-notes/7.0/7.0.0.md b/release-notes/7.0/7.0.0.md new file mode 100644 index 0000000000..f62178076b --- /dev/null +++ b/release-notes/7.0/7.0.0.md @@ -0,0 +1,410 @@ +# Release Notes + +## Stable Release 7.0.0 - 2026-03-17 + +This is the general availability release of **Microsoft.Data.SqlClient 7.0**, a major milestone for the .NET data provider for SQL Server. This release addresses the most upvoted issue in the repository's history — extracting Azure dependencies from the core package — introduces pluggable SSPI authentication, adds enhanced routing for Azure SQL Hyperscale, and delivers async read performance improvements. + +Also released as part of this milestone: +- Released Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0. See [release notes](../Extensions/Abstractions/1.0/1.0.0.md). +- Released Microsoft.Data.SqlClient.Extensions.Azure 1.0.0. See [release notes](../Extensions/Azure/1.0/1.0.0.md). +- Released Microsoft.Data.SqlClient.Internal.Logging 1.0.0. See [release notes](../Internal/Logging/1.0/1.0.0.md). +- Released Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 7.0.0. See [release notes](../add-ons/AzureKeyVaultProvider/7.0/7.0.0.md). + +## Changes Since [7.0.0-preview4](7.0.0-preview4.md) + +### Added + +- Added actionable error message when Entra ID authentication methods are used without the `Microsoft.Data.SqlClient.Extensions.Azure` package installed, guiding users to install the correct package. + ([#3962](https://github.com/dotnet/SqlClient/issues/3962), + [#4046](https://github.com/dotnet/SqlClient/pull/4046)) + +- Added Azure authentication sample application. + ([#3988](https://github.com/dotnet/SqlClient/pull/3988)) + +### Changed + +#### Other changes + +- Renamed the `Microsoft.Data.SqlClient.Extensions.Logging` package to `Microsoft.Data.SqlClient.Internal.Logging` to indicate it is for internal use only and should not be referenced directly by application code. + ([#4038](https://github.com/dotnet/SqlClient/pull/4038)) + +- Fixed non-localized exception strings. + ([#4022](https://github.com/dotnet/SqlClient/pull/4022)) + +- Codebase merge and cleanup: + ([#3997](https://github.com/dotnet/SqlClient/pull/3997), + [#4052](https://github.com/dotnet/SqlClient/pull/4052)) + +- Various test improvements: + ([#3891](https://github.com/dotnet/SqlClient/pull/3891), + [#3996](https://github.com/dotnet/SqlClient/pull/3996), + [#4002](https://github.com/dotnet/SqlClient/pull/4002), + [#4034](https://github.com/dotnet/SqlClient/pull/4034), + [#4041](https://github.com/dotnet/SqlClient/pull/4041), + [#4044](https://github.com/dotnet/SqlClient/pull/4044)) + +- Documentation improvements (including Entra ID branding updates): + ([#4021](https://github.com/dotnet/SqlClient/pull/4021), + [#4047](https://github.com/dotnet/SqlClient/pull/4047), + [#4049](https://github.com/dotnet/SqlClient/pull/4049)) + +- Updated Dependencies + ([#4045](https://github.com/dotnet/SqlClient/pull/4045)): + - Updated `Azure.Core` to v1.51.1 + - Updated `Azure.Identity` to v1.18.0 + - Updated `Azure.Security.KeyVault.Keys` to v4.9.0 + - Updated `Microsoft.Extensions.Caching.Memory` to v9.0.13 (.NET 9.0) + - Updated `Microsoft.IdentityModel.JsonWebTokens` to v8.16.0 + - Updated `Microsoft.IdentityModel.Protocols.OpenIdConnect` to v8.16.0 + - Updated `Microsoft.Bcl.Cryptography` to v9.0.13 (.NET 9.0) + - Updated `System.Configuration.ConfigurationManager` to v9.0.13 (.NET 9.0) + - Updated `System.Diagnostics.DiagnosticSource` to v10.0.3 + - Updated `System.Security.Cryptography.Pkcs` to v9.0.13 (.NET 9.0) + - Updated `System.Text.Json` to v10.0.3 + - Updated `System.Threading.Channels` to v10.0.3 + - Updated `System.ValueTuple` to v4.6.2 + +## Cumulative Changes Since [6.1](../6.1/README.md) + +This section summarizes all changes across the 7.0 preview cycle for users upgrading from the latest 6.1 stable release. + +### Changed + +#### Azure Dependencies Removed from Core Package + +*What Changed:* + +- The core `Microsoft.Data.SqlClient` package no longer depends on `Azure.Core`, `Azure.Identity`, or their transitive dependencies (e.g., `Microsoft.Identity.Client`, `Microsoft.Web.WebView2`). Azure Active Directory / Entra ID authentication functionality (`ActiveDirectoryAuthenticationProvider` and related types) has been extracted into a new `Microsoft.Data.SqlClient.Extensions.Azure` package. + ([#1108](https://github.com/dotnet/SqlClient/issues/1108), + [#3680](https://github.com/dotnet/SqlClient/pull/3680), + [#3902](https://github.com/dotnet/SqlClient/pull/3902), + [#3904](https://github.com/dotnet/SqlClient/pull/3904), + [#3908](https://github.com/dotnet/SqlClient/pull/3908), + [#3917](https://github.com/dotnet/SqlClient/pull/3917), + [#3982](https://github.com/dotnet/SqlClient/pull/3982), + [#3978](https://github.com/dotnet/SqlClient/pull/3978), + [#3986](https://github.com/dotnet/SqlClient/pull/3986)) +- Two additional packages were introduced to support this separation: `Microsoft.Data.SqlClient.Extensions.Abstractions` (shared types between the core driver and extensions) and `Microsoft.Data.SqlClient.Internal.Logging` (shared ETW tracing infrastructure). + ([#3626](https://github.com/dotnet/SqlClient/pull/3626), + [#3628](https://github.com/dotnet/SqlClient/pull/3628), + [#3967](https://github.com/dotnet/SqlClient/pull/3967), + [#4038](https://github.com/dotnet/SqlClient/pull/4038)) + +*Who Benefits:* + +- All users benefit from a significantly lighter core package. Previously, the Azure dependency chain pulled in numerous assemblies even for applications that only needed basic SQL Server connectivity. This was the [most upvoted open issue](https://github.com/dotnet/SqlClient/issues/1108) in the repository. +- Users who do not use Entra ID authentication no longer carry Azure-related assemblies in their build output. +- Users who do use Entra ID authentication can now manage Azure dependency versions independently from the core driver. + +*Impact:* + +- Applications using Entra ID authentication (e.g., `ActiveDirectoryInteractive`, `ActiveDirectoryDefault`, `ActiveDirectoryManagedIdentity`, etc.) must now install the `Microsoft.Data.SqlClient.Extensions.Azure` NuGet package separately: + +``` +dotnet add package Microsoft.Data.SqlClient.Extensions.Azure +``` + +- No code changes are required beyond adding the package reference. +- If an Entra ID authentication method is used without the Azure package installed, the driver now provides an actionable error message guiding users to install the correct package. + +### Added + +#### Pluggable Authentication with SspiContextProvider + +*What Changed:* + +- Added a public `SspiContextProvider` property on `SqlConnection`, completing the SSPI extensibility work begun in 6.1.0. Applications can now supply a custom SSPI context provider for integrated authentication, enabling custom Kerberos ticket negotiation and NTLM username/password authentication scenarios. + ([#2253](https://github.com/dotnet/SqlClient/issues/2253), + [#2494](https://github.com/dotnet/SqlClient/pull/2494)) + +*Who Benefits:* + +- Users authenticating across untrusted domains, non-domain-joined machines, or cross-platform environments where configuring integrated authentication is difficult. +- Users running in containers who need manual Kerberos negotiation without deploying sidecars or external ticket-refresh mechanisms. +- Users who need NTLM username/password authentication to SQL Server, which the driver does not provide natively. + +*Impact:* + +- Applications can set a custom `SspiContextProvider` on `SqlConnection` before opening the connection: + +```c# +var connection = new SqlConnection(connectionString); +connection.SspiContextProvider = new MyKerberosProvider(); +connection.Open(); +``` + +- The provider handles the authentication token exchange during integrated authentication. Existing authentication behavior is unchanged when no custom provider is set. See [SspiContextProvider_CustomProvider.cs](../../doc/samples/SspiContextProvider_CustomProvider.cs) for a sample implementation. +- **Note:** The `SspiContextProvider` is part of the connection pool key. Care should be taken when using this property to ensure the implementation returns a stable identity per resource. + +#### Async Read Performance: Packet Multiplexing (Preview) + +*What Changed:* + +- Continued refinement of packet multiplexing with bug fixes and stability improvements since 6.1.0, plus new app context switches for opt-in control. + ([#3534](https://github.com/dotnet/SqlClient/pull/3534), + [#3537](https://github.com/dotnet/SqlClient/pull/3537), + [#3605](https://github.com/dotnet/SqlClient/pull/3605)) + +*Who Benefits:* + +- Applications performing large async reads (`ExecuteReaderAsync` with big result sets, streaming scenarios, or bulk data retrieval). + +*Impact:* + +- Packet multiplexing ships behind two opt-in feature switches: + +```c# +AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour", false); +AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni", false); +``` + +- Setting both switches to `false` enables the new async processing path. By default, the driver uses the existing (compatible) behavior. + +#### Enhanced Routing Support + +*What Changed:* + +- Added support for enhanced routing, a TDS feature that allows the server to redirect connections to a specific server *and* database during login. + ([#3641](https://github.com/dotnet/SqlClient/issues/3641), + [#3969](https://github.com/dotnet/SqlClient/pull/3969), + [#3970](https://github.com/dotnet/SqlClient/pull/3970), + [#3973](https://github.com/dotnet/SqlClient/pull/3973)) + +*Who Benefits:* + +- Users connecting to Azure SQL Hyperscale environments that use named read replicas and gateway-based load balancing. + +*Impact:* + +- Enhanced routing is negotiated automatically during login when the server supports it. No application code changes are required. + +#### Support for .NET 10 + +*What Changed:* + +- Updated pipelines and test suites to compile the driver using the .NET 10 SDK. + ([#3686](https://github.com/dotnet/SqlClient/pull/3686)) + +*Who Benefits:* + +- Developers targeting .NET 10 on day one. + +*Impact:* + +- SqlClient 7.0 compiles and tests against .NET 10, ensuring compatibility. + +#### Strongly-Typed Diagnostic Events on .NET Framework + +*What Changed:* + +- Enabled `SqlClientDiagnosticListener` for `SqlCommand` on .NET Framework, closing a long-standing observability gap where diagnostic events were previously only emitted on .NET Core. + ([#3658](https://github.com/dotnet/SqlClient/pull/3658)) + +- Brought the 15 strongly-typed diagnostic event classes in the `Microsoft.Data.SqlClient.Diagnostics` namespace — originally introduced for .NET Core in 6.0 — to .NET Framework as part of the codebase merge. Both platforms now use the same strongly-typed event model. The types cover command, connection, and transaction lifecycle events: + - `SqlClientCommandBefore`, `SqlClientCommandAfter`, `SqlClientCommandError` + - `SqlClientConnectionOpenBefore`, `SqlClientConnectionOpenAfter`, `SqlClientConnectionOpenError` + - `SqlClientConnectionCloseBefore`, `SqlClientConnectionCloseAfter`, `SqlClientConnectionCloseError` + - `SqlClientTransactionCommitBefore`, `SqlClientTransactionCommitAfter`, `SqlClientTransactionCommitError` + - `SqlClientTransactionRollbackBefore`, `SqlClientTransactionRollbackAfter`, `SqlClientTransactionRollbackError` + + ([#3493](https://github.com/dotnet/SqlClient/pull/3493)) + +*Who Benefits:* + +- .NET Framework users subscribing to `SqlClientDiagnosticListener` events for observability, distributed tracing, or custom telemetry. These users now have parity with .NET Core, gaining IntelliSense, compile-time safety, and eliminating the need to access diagnostic payloads via reflection or dictionary lookups. + +*Impact:* + +- On .NET Framework, `SqlCommand` now emits the same diagnostic events that were previously only available on .NET Core. Subscribers to `DiagnosticListener` events (e.g., `Microsoft.Data.SqlClient.WriteCommandBefore`) receive the strongly-typed objects: + +```c# +listener.Subscribe(new Observer>(kvp => +{ + if (kvp.Value is SqlClientCommandBefore before) + { + Console.WriteLine($"Executing: {before.Command.CommandText}"); + } +})); +``` + +- The types implement `IReadOnlyList>` for backward compatibility with code that iterates properties generically. + +#### Other Additions + +- Added `SqlConfigurableRetryFactory.BaselineTransientErrors` static property exposing the default transient error codes list as a `ReadOnlyCollection`, making it easier to extend the default list with application-specific error codes. + ([#3903](https://github.com/dotnet/SqlClient/pull/3903)) + +- Added app context switch `Switch.Microsoft.Data.SqlClient.EnableMultiSubnetFailoverByDefault` to set `MultiSubnetFailover=true` globally without modifying connection strings. + ([#3841](https://github.com/dotnet/SqlClient/pull/3841)) + +- Added app context switch `Switch.Microsoft.Data.SqlClient.IgnoreServerProvidedFailoverPartner` to let the client ignore server-provided failover partner info in Basic Availability Groups. + ([#3625](https://github.com/dotnet/SqlClient/pull/3625)) + +- Enabled User Agent Feature Extension (opt-in via `Switch.Microsoft.Data.SqlClient.EnableUserAgent`). + ([#3606](https://github.com/dotnet/SqlClient/pull/3606)) + +### Changed + +#### Deprecation of `SqlAuthenticationMethod.ActiveDirectoryPassword` + +*What Changed:* + +- `SqlAuthenticationMethod.ActiveDirectoryPassword` (the ROPC flow) is now marked `[Obsolete]` and will generate compiler warnings. This aligns with Microsoft's move toward [mandatory multifactor authentication](https://learn.microsoft.com/entra/identity/authentication/concept-mandatory-multifactor-authentication). + ([#3671](https://github.com/dotnet/SqlClient/pull/3671)) + +*Who Benefits:* + +- Teams moving toward stronger, passwordless or MFA-compliant authentication. + +*Impact:* + +- If you use `Authentication=Active Directory Password`, migrate to a supported alternative: + +| Scenario | Recommended Authentication | +|----------|---------------------------| +| Interactive / desktop apps | `Active Directory Interactive` | +| Service-to-service | `Active Directory Service Principal` | +| Azure-hosted workloads | `Active Directory Managed Identity` | +| Developer / CI environments | `Active Directory Default` | + +- See [Connect to Azure SQL with Microsoft Entra authentication](https://learn.microsoft.com/sql/connect/ado-net/sql/azure-active-directory-authentication) for more information. + +#### Breaking Changes + +- Reverted public visibility of internal interop enums (`IoControlCodeAccess` and `IoControlTransferType`) that were accidentally made public during the project merge. + ([#3900](https://github.com/dotnet/SqlClient/pull/3900)) + +#### Other Changes + +- Removed `Constrained Execution Region` error handling blocks and associated `SqlConnection` cleanup. + ([#3535](https://github.com/dotnet/SqlClient/pull/3535)) + +- Performance improvements across SqlStatistics timing, Always Encrypted scenarios, and connection opening: + ([#3609](https://github.com/dotnet/SqlClient/pull/3609), + [#3612](https://github.com/dotnet/SqlClient/pull/3612), + [#3732](https://github.com/dotnet/SqlClient/pull/3732), + [#3660](https://github.com/dotnet/SqlClient/pull/3660), + [#3791](https://github.com/dotnet/SqlClient/pull/3791), + [#3772](https://github.com/dotnet/SqlClient/pull/3772), + [#3554](https://github.com/dotnet/SqlClient/pull/3554)) + +- Allow `SqlBulkCopy` to operate on hidden columns. + ([#3590](https://github.com/dotnet/SqlClient/pull/3590)) + +- Updated UserAgent feature to use a pipe-delimited format, replacing the previous JSON format. + ([#3826](https://github.com/dotnet/SqlClient/pull/3826)) + +- Minor improvements to Managed SNI tracing to capture continuation events and errors. + ([#3859](https://github.com/dotnet/SqlClient/pull/3859)) + +### Fixed + +- Fixed a connection performance regression where SPN generation was triggered for non-integrated authentication modes (e.g., SQL authentication) on the native SNI path. + ([#3929](https://github.com/dotnet/SqlClient/pull/3929)) + +- Fixed `ExecuteScalar` to propagate errors when the server sends data followed by an error token. + ([#3912](https://github.com/dotnet/SqlClient/pull/3912)) + +- Fixed `NullReferenceException` in `SqlDataAdapter` when processing batch scenarios. + ([#3857](https://github.com/dotnet/SqlClient/pull/3857)) + +- Fixed reading of multiple app context switches from a single `AppContextSwitchOverrides` configuration field. + ([#3960](https://github.com/dotnet/SqlClient/pull/3960)) + +- Fixed an edge case in `TdsParserStateObject.TryReadPlpBytes` where zero-length reads returned `null` instead of an empty array. + ([#3872](https://github.com/dotnet/SqlClient/pull/3872)) + +- Fixed issue where extra connection deactivation was occurring. + ([#3758](https://github.com/dotnet/SqlClient/pull/3758)) + +- Fixed debug assertion in connection pool (no impact to production code). + ([#3587](https://github.com/dotnet/SqlClient/pull/3587)) + +- Prevented uninitialized performance counters escaping `CreatePerformanceCounters`. + ([#3623](https://github.com/dotnet/SqlClient/pull/3623)) + +- Fixed `SetProvider` to return immediately if user-defined authentication provider found. + ([#3620](https://github.com/dotnet/SqlClient/pull/3620)) + +- Fixed connection pool concurrency issue. + ([#3632](https://github.com/dotnet/SqlClient/pull/3632)) + + +## Contributors + +We thank the following public contributors. Their efforts toward this project are very much appreciated. + +- [edwardneal](https://github.com/edwardneal) +- [ErikEJ](https://github.com/ErikEJ) +- [frankbuckley](https://github.com/frankbuckley) +- [MatthiasHuygelen](https://github.com/MatthiasHuygelen) +- [ShreyaLaxminarayan](https://github.com/ShreyaLaxminarayan) +- [tetolv](https://github.com/tetolv) +- [twsouthwick](https://github.com/twsouthwick) +- [Wraith2](https://github.com/Wraith2) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64, Windows ARM64) +- .NET 8.0+ (Windows x86, Windows x64, Windows ARM, Windows ARM64, Linux, macOS) + +### Dependencies + +#### .NET 9.0 + +- Microsoft.Bcl.Cryptography 9.0.13 +- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0 +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 +- Microsoft.Data.SqlClient.SNI.runtime 6.0.2 +- Microsoft.Extensions.Caching.Memory 9.0.13 +- Microsoft.IdentityModel.JsonWebTokens 8.16.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 8.16.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 9.0.13 +- System.Security.Cryptography.Pkcs 9.0.13 + +#### .NET 8.0 + +- Microsoft.Bcl.Cryptography 8.0.0 +- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0 +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 +- Microsoft.Data.SqlClient.SNI.runtime 6.0.2 +- Microsoft.Extensions.Caching.Memory 8.0.1 +- Microsoft.IdentityModel.JsonWebTokens 8.16.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 8.16.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 8.0.1 +- System.Security.Cryptography.Pkcs 8.0.1 + +#### .NET Standard 2.0 + +- Microsoft.Bcl.Cryptography 8.0.0 +- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0 +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 +- Microsoft.Data.SqlClient.SNI.runtime 6.0.2 +- Microsoft.Extensions.Caching.Memory 8.0.1 +- Microsoft.IdentityModel.JsonWebTokens 8.16.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 8.16.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 8.0.1 +- System.Security.Cryptography.Pkcs 8.0.1 +- System.Text.Json 10.0.3 +- System.Threading.Channels 10.0.3 + +#### .NET Framework 4.6.2+ + +- Microsoft.Bcl.Cryptography 8.0.0 +- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0 +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 +- Microsoft.Data.SqlClient.SNI 6.0.2 +- Microsoft.Extensions.Caching.Memory 8.0.1 +- Microsoft.IdentityModel.JsonWebTokens 8.16.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 8.16.0 +- System.Buffers 4.6.1 +- System.Diagnostics.DiagnosticSource 10.0.3 +- System.Memory 4.6.3 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Pkcs 8.0.1 +- System.Text.Json 10.0.3 +- System.Threading.Channels 10.0.3 +- System.ValueTuple 4.6.2 diff --git a/release-notes/7.0/README.md b/release-notes/7.0/README.md index e8bfcc25f3..70cfdc3835 100644 --- a/release-notes/7.0/README.md +++ b/release-notes/7.0/README.md @@ -4,6 +4,7 @@ The following Microsoft.Data.SqlClient 7.0 releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2026-03-17 | 7.0.0 | [Release Notes](7.0.0.md) | | 2026-03-05 | 7.0.0-preview4.26064.3 | [Release Notes](7.0.0-preview4.md) | | 2025-12-08 | 7.0.0-preview3.25342.7 | [Release Notes](7.0.0-preview3.md) | | 2025-10-16 | 7.0.0-preview2.25289.6 | [Release Notes](7.0.0-preview2.md) | diff --git a/release-notes/Extensions/Abstractions/1.0/1.0.0.md b/release-notes/Extensions/Abstractions/1.0/1.0.0.md new file mode 100644 index 0000000000..82157224ff --- /dev/null +++ b/release-notes/Extensions/Abstractions/1.0/1.0.0.md @@ -0,0 +1,30 @@ +# Release Notes + +## Stable Release 1.0.0 - 2026-03-17 + +This is the first stable release of `Microsoft.Data.SqlClient.Extensions.Abstractions`, providing shared types and interfaces between the core `Microsoft.Data.SqlClient` driver and its extension packages (e.g., `Microsoft.Data.SqlClient.Extensions.Azure`). + +## Changes Since [1.0.0-preview1](1.0.0-preview1.md) + +No changes to this package since preview1. + +## Cumulative Changes (First Stable Release) + +This is the first stable release of this package. The following summarizes all changes across the preview cycle. + +### Added + +- Shared abstraction types for the SqlClient extensions model. + ([#3626](https://github.com/dotnet/SqlClient/pull/3626), + [#3628](https://github.com/dotnet/SqlClient/pull/3628), + [#3967](https://github.com/dotnet/SqlClient/pull/3967)) + +## Target Platform Support + +- .NET Standard 2.0 + +### Dependencies + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 diff --git a/release-notes/Extensions/Abstractions/1.0/README.md b/release-notes/Extensions/Abstractions/1.0/README.md index 2e91111742..347ae77335 100644 --- a/release-notes/Extensions/Abstractions/1.0/README.md +++ b/release-notes/Extensions/Abstractions/1.0/README.md @@ -5,4 +5,5 @@ The following `Microsoft.Data.SqlClient.Extensions.Abstractions` | Release Date | Description | Notes | | :-- | :-- | :--: | +| 2026-03-17 | 1.0.0 | [Release Notes](1.0.0.md) | | 2026-03-05 | 1.0.0-preview1.26064.3 | [Release Notes](1.0.0-preview1.md) | diff --git a/release-notes/Extensions/Azure/1.0/1.0.0.md b/release-notes/Extensions/Azure/1.0/1.0.0.md new file mode 100644 index 0000000000..c371669493 --- /dev/null +++ b/release-notes/Extensions/Azure/1.0/1.0.0.md @@ -0,0 +1,66 @@ +# Release Notes + +## Stable Release 1.0.0 - 2026-03-17 + +This is the first stable release of `Microsoft.Data.SqlClient.Extensions.Azure`, providing Microsoft Entra ID (Azure Active Directory) authentication support for Microsoft.Data.SqlClient. This package was extracted from the core driver to address the [most requested feature](https://github.com/dotnet/SqlClient/issues/1108) — making the core package lighter by removing Azure dependencies. + +## Changes Since [1.0.0-preview1](1.0.0-preview1.md) + +### Changed + +- Renamed dependency from `Microsoft.Data.SqlClient.Extensions.Logging` to `Microsoft.Data.SqlClient.Internal.Logging`. + ([#4038](https://github.com/dotnet/SqlClient/pull/4038)) + +- Updated Dependencies + ([#4045](https://github.com/dotnet/SqlClient/pull/4045)): + - Updated `Azure.Core` to v1.51.1 + - Updated `Azure.Identity` to v1.18.0 + - Updated `Microsoft.Identity.Client` to v4.83.0 + +## Cumulative Changes (First Stable Release) + +This is the first stable release of this package. The following summarizes all changes across the preview cycle. + +### Added + +- Entra ID authentication provider (`ActiveDirectoryAuthenticationProvider`) supporting: + - `ActiveDirectoryInteractive` + - `ActiveDirectoryDefault` + - `ActiveDirectoryManagedIdentity` + - `ActiveDirectoryServicePrincipal` + - `ActiveDirectoryDeviceCodeFlow` + - `ActiveDirectoryWorkloadIdentity` + - `ActiveDirectoryPassword` (deprecated) + ([#3680](https://github.com/dotnet/SqlClient/pull/3680), + [#3902](https://github.com/dotnet/SqlClient/pull/3902), + [#3904](https://github.com/dotnet/SqlClient/pull/3904), + [#3908](https://github.com/dotnet/SqlClient/pull/3908), + [#3917](https://github.com/dotnet/SqlClient/pull/3917), + [#3982](https://github.com/dotnet/SqlClient/pull/3982), + [#3978](https://github.com/dotnet/SqlClient/pull/3978), + [#3986](https://github.com/dotnet/SqlClient/pull/3986)) + +## Target Platform Support + +- .NET Standard 2.0 +- .NET Framework 4.6.2+ + +### Dependencies + +#### .NET Standard 2.0 + +- Azure.Core 1.51.1 +- Azure.Identity 1.18.0 +- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0 +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 +- Microsoft.Extensions.Caching.Memory 8.0.1 +- Microsoft.Identity.Client 4.83.0 + +#### .NET Framework 4.6.2+ + +- Azure.Core 1.51.1 +- Azure.Identity 1.18.0 +- Microsoft.Data.SqlClient.Extensions.Abstractions 1.0.0 +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 +- Microsoft.Extensions.Caching.Memory 8.0.1 +- Microsoft.Identity.Client 4.83.0 diff --git a/release-notes/Extensions/Azure/1.0/README.md b/release-notes/Extensions/Azure/1.0/README.md index 19dc060723..dd0030f110 100644 --- a/release-notes/Extensions/Azure/1.0/README.md +++ b/release-notes/Extensions/Azure/1.0/README.md @@ -5,4 +5,5 @@ The following `Microsoft.Data.SqlClient.Extensions.Azure` | Release Date | Description | Notes | | :-- | :-- | :--: | +| 2026-03-17 | 1.0.0 | [Release Notes](1.0.0.md) | | 2026-03-05 | 1.0.0-preview1.26064.3 | [Release Notes](1.0.0-preview1.md) | diff --git a/release-notes/Extensions/Logging/1.0/README.md b/release-notes/Extensions/Logging/1.0/README.md index b31406665b..f5cbea90ea 100644 --- a/release-notes/Extensions/Logging/1.0/README.md +++ b/release-notes/Extensions/Logging/1.0/README.md @@ -1,5 +1,8 @@ # Microsoft.Data.SqlClient.Extensions.Logging 1.0 Releases +> **Note:** This package has been renamed to `Microsoft.Data.SqlClient.Internal.Logging` starting +> with the 1.0.0 stable release. See [Internal/Logging release notes](../../../Internal/Logging/1.0/1.0.0.md). + > **Note:** This package is for internal use by other Microsoft.Data.SqlClient packages only > and should not be referenced directly by application code. diff --git a/release-notes/Internal/Logging/1.0/1.0.0.md b/release-notes/Internal/Logging/1.0/1.0.0.md new file mode 100644 index 0000000000..5cbf4d886b --- /dev/null +++ b/release-notes/Internal/Logging/1.0/1.0.0.md @@ -0,0 +1,37 @@ +# Release Notes + +## Stable Release 1.0.0 - 2026-03-17 + +> **Note:** This package is for internal use by other Microsoft.Data.SqlClient packages only +> and should not be referenced directly by application code. + +This is the first stable release of `Microsoft.Data.SqlClient.Internal.Logging`, providing shared ETW EventSource tracing infrastructure for the Microsoft.Data.SqlClient package family. + +## Changes Since [1.0.0-preview1](../../../Extensions/Logging/1.0/1.0.0-preview1.md) + +### Changed + +- Renamed from `Microsoft.Data.SqlClient.Extensions.Logging` to `Microsoft.Data.SqlClient.Internal.Logging` to clarify that this package is for internal use only. + ([#4038](https://github.com/dotnet/SqlClient/pull/4038)) + +## Cumulative Changes (First Stable Release) + +This is the first stable release of this package. The following summarizes all changes across the preview cycle. + +### Added + +- Shared ETW EventSource tracing infrastructure for internal use by `Microsoft.Data.SqlClient` and its extension packages. + ([#3626](https://github.com/dotnet/SqlClient/pull/3626), + [#3628](https://github.com/dotnet/SqlClient/pull/3628), + [#3967](https://github.com/dotnet/SqlClient/pull/3967)) + + +## Target Platform Support + +- .NET Standard 2.0 + +### Dependencies + +#### .NET Standard 2.0 + +- None diff --git a/release-notes/Internal/Logging/1.0/README.md b/release-notes/Internal/Logging/1.0/README.md new file mode 100644 index 0000000000..15978c4843 --- /dev/null +++ b/release-notes/Internal/Logging/1.0/README.md @@ -0,0 +1,11 @@ +# Microsoft.Data.SqlClient.Internal.Logging 1.0 Releases + +> **Note:** This package is for internal use by other Microsoft.Data.SqlClient packages only +> and should not be referenced directly by application code. + +The following `Microsoft.Data.SqlClient.Internal.Logging` +1.0 releases have been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2026-03-17 | 1.0.0 | [Release Notes](1.0.0.md) | diff --git a/release-notes/README.md b/release-notes/README.md index 4e2c67bc33..a511a589bc 100644 --- a/release-notes/README.md +++ b/release-notes/README.md @@ -1,6 +1,6 @@ # Microsoft.Data.SqlClient Release Notes -The latest stable release is [Microsoft.Data.SqlClient 6.1](6.1). +The latest stable release is [Microsoft.Data.SqlClient 7.0](7.0). ## Release Information @@ -22,7 +22,7 @@ The latest stable release is [Microsoft.Data.SqlClient 6.1](6.1). # Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider Release Notes The latest stable release is -[Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 6.1](add-ons/AzureKeyVaultProvider/6.1). +[Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 7.0](add-ons/AzureKeyVaultProvider/7.0). ## Release Information @@ -54,14 +54,15 @@ The latest release is - [Microsoft.Data.SqlClient.Extensions.Azure 1.0](Extensions/Azure/1.0) -# Microsoft.Data.SqlClient.Extensions.Logging Release Notes + +# Microsoft.Data.SqlClient.Internal.Logging Release Notes The latest release is -[Microsoft.Data.SqlClient.Extensions.Logging 1.0](Extensions/Logging/1.0). +[Microsoft.Data.SqlClient.Internal.Logging 1.0](Internal/Logging/1.0). ## Release Information -- [Microsoft.Data.SqlClient.Extensions.Logging 1.0](Extensions/Logging/1.0) +- [Microsoft.Data.SqlClient.Internal.Logging 1.0](Internal/Logging/1.0) # Microsoft.SqlServer.Server Release Notes diff --git a/release-notes/add-ons/AzureKeyVaultProvider/7.0/7.0.0.md b/release-notes/add-ons/AzureKeyVaultProvider/7.0/7.0.0.md new file mode 100644 index 0000000000..6a6f31054b --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/7.0/7.0.0.md @@ -0,0 +1,54 @@ +# Release Notes + +## Stable Release 7.0.0 - 2026-03-17 + +## Changes Since [7.0.0-preview1](7.0.0-preview1.md) + +### Changed + +- Changed AKV Provider to target .NET Standard 2.0 instead of multi-targeting .NET Framework and .NET Core. Since the package contains no framework-specific code, this simplifies the package and reduces its footprint. + ([#4036](https://github.com/dotnet/SqlClient/pull/4036)) + +- Renamed dependency from `Microsoft.Data.SqlClient.Extensions.Logging` to `Microsoft.Data.SqlClient.Internal.Logging`. + ([#4038](https://github.com/dotnet/SqlClient/pull/4038)) + +- Updated Dependencies + ([#4045](https://github.com/dotnet/SqlClient/pull/4045)): + - Updated `Azure.Core` to v1.51.1 + - Updated `Azure.Security.KeyVault.Keys` to v4.9.0 + +## Cumulative Changes Since [6.1](../6.1/README.md) + +This section summarizes all changes across the 7.0 preview cycle for users upgrading from the latest 6.1 stable release. + +### Changed + +- Changed AKV Provider to target .NET Standard 2.0 instead of multi-targeting .NET Framework and .NET Core. Since the package contains no framework-specific code, this simplifies the package and reduces its footprint. + ([#4036](https://github.com/dotnet/SqlClient/pull/4036)) + +- Performance improvements for all built-in `SqlColumnEncryptionKeyStoreProvider` implementations, including `SqlColumnEncryptionAzureKeyVaultProvider`. + ([#3554](https://github.com/dotnet/SqlClient/pull/3554)) + +- Added dependency on `Microsoft.Data.SqlClient.Internal.Logging` to align with the new extensions infrastructure. + ([#3626](https://github.com/dotnet/SqlClient/pull/3626), + [#3628](https://github.com/dotnet/SqlClient/pull/3628), + [#3967](https://github.com/dotnet/SqlClient/pull/3967), + [#4038](https://github.com/dotnet/SqlClient/pull/4038)) + +- Updated Dependencies: + - Updated `Azure.Core` to v1.51.1 + - Updated `Azure.Security.KeyVault.Keys` to v4.9.0 + +## Target Platform Support + +- .NET Standard 2.0 + +### Dependencies + +#### .NET Standard 2.0 + +- Azure.Core 1.51.1 +- Azure.Security.KeyVault.Keys 4.9.0 +- Microsoft.Data.SqlClient (>= 7.0.0) +- Microsoft.Data.SqlClient.Internal.Logging 1.0.0 +- Microsoft.Extensions.Caching.Memory 8.0.1 diff --git a/release-notes/add-ons/AzureKeyVaultProvider/7.0/README.md b/release-notes/add-ons/AzureKeyVaultProvider/7.0/README.md index c63a0bc71f..22593ef05c 100644 --- a/release-notes/add-ons/AzureKeyVaultProvider/7.0/README.md +++ b/release-notes/add-ons/AzureKeyVaultProvider/7.0/README.md @@ -5,4 +5,5 @@ The following `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` | Release Date | Description | Notes | | :-- | :-- | :--: | +| 2026-03-17 | 7.0.0 | [Release Notes](7.0.0.md) | | 2026-03-05 | 7.0.0-preview1.26064.3 | [Release Notes](7.0.0-preview1.md) | From 86492a1fa139b001efbfb349a971acc30d601db5 Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Wed, 18 Mar 2026 09:20:05 -0700 Subject: [PATCH 02/22] Validate milestone assignment on pull requests. (#4055) * Validate milestone assignment on pull requests. * Update json checks to avoid jq. --- .github/workflows/check-milestone.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/check-milestone.yml diff --git a/.github/workflows/check-milestone.yml b/.github/workflows/check-milestone.yml new file mode 100644 index 0000000000..da4681fd7b --- /dev/null +++ b/.github/workflows/check-milestone.yml @@ -0,0 +1,24 @@ +name: Check Milestone + +on: + pull_request: + types: [opened, edited, synchronize, milestoned, demilestoned] + +jobs: + check-milestone: + name: Validate milestone + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: Check milestone is set + if: github.event.pull_request.milestone == null + run: | + echo "::error::This PR does not have a milestone set. Please assign a milestone before merging." + exit 1 + + - name: Check milestone is open + if: github.event.pull_request.milestone != null && github.event.pull_request.milestone.state != 'open' + run: | + echo "::error::Milestone '${{ github.event.pull_request.milestone.title }}' is ${{ github.event.pull_request.milestone.state }}. Please assign an open milestone." + exit 1 From 07a9280494d62a9a2381a1215abb6587c0a4379d Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Thu, 19 Mar 2026 16:27:39 -0500 Subject: [PATCH 03/22] Merge | Pack Mds Target, Wire PR/CI Pipelines to Build2.proj (#4033) * In progress work for pack target ... kinda working * Add config to genapi path. Remove unnecessary dependencies from genapi project * Fix generation of docs in ref project * Dump reference type specific builds Fix xml documentation file generation for implementation project * Generating a package file works!!! * Resync pipelines folder in solution * Add an assembly build number argument? ... it doesn't work * Wiring up build numbers through build2.proj * Build number argument * Maybe wiring it up?!? * Reinstate the assemblybuildnumber property * Build all of MDS, once * PR comments from copilot * Specify test results folder for CI builds * TargetFramework => TestFramework * Fixing a couple more comments * Couple more comments from copilot * downgrade back to 7.0.0 :) * I dunno, fixing some stuff, I guess. * Fix indenting * Back out changes to official pipelines * A couple more comments from CoPilot * Generate AKV documentation file during CI build ... idk why this is only a problem *now* * Comments from Copilot ... I'd love it if it could give the same comments across commits. --- build2.proj | 272 +++++++++++++----- .../templates/steps/ci-project-build-step.yml | 104 +------ .../templates/steps/run-all-tests-step.yml | 248 ++++++++-------- .../sqlclient-pr-package-ref-pipeline.yml | 1 + .../sqlclient-pr-project-ref-pipeline.yml | 1 + src/Microsoft.Data.SqlClient.sln | 87 +++--- .../MdsVersions.props | 35 +++ .../Microsoft.Data.SqlClient.csproj | 12 +- .../ref/Microsoft.Data.SqlClient.cs | 10 +- .../ref/Microsoft.Data.SqlClient.csproj | 17 +- .../src/Microsoft.Data.SqlClient.csproj | 20 +- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 16 +- .../src/Microsoft/Data/SqlDbTypeExtensions.cs | 6 +- .../src/Microsoft/Data/SqlTypes/SqlJson.cs | 22 +- .../src/Microsoft/Data/SqlTypes/SqlVector.cs | 20 +- tools/intellisense/TrimDocs.ps1 | 5 +- tools/props/Versions.props | 33 +-- tools/specs/Microsoft.Data.SqlClient.nuspec | 204 ++++++------- tools/targets/GenerateThisAssemblyCs.targets | 3 + 19 files changed, 615 insertions(+), 501 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/MdsVersions.props diff --git a/build2.proj b/build2.proj index a5fb6b89a2..b9b08f9a4a 100644 --- a/build2.proj +++ b/build2.proj @@ -6,6 +6,25 @@ + + + + + -p:BuildNumber=$(BuildNumber) + + - + + + + nuget @@ -97,6 +136,19 @@ -p:ReferenceType=Package + + + + -p:SigningKeyPath="$(SigningKeyPath)" + + - true - + true + --collect "Code coverage" --settings "$(RepoRoot)src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/CodeCoverage.runsettings" - + - $(RepoRoot)src/Microsoft.Data.SqlClient/ + $(RepoRoot)src/Microsoft.Data.SqlClient/ + $(RepoRoot)artifacts/Microsoft.Data.SqlClient/ + + + $(MdsSrcRoot)src/Microsoft.Data.SqlClient.csproj + $(MdsSrcRoot)ref/Microsoft.Data.SqlClient.csproj + $(MdsSrcRoot)notsupported/Microsoft.Data.SqlClient.csproj + + + $(MdsSrcRoot)tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj + $(MdsSrcRoot)tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj + $(MdsSrcRoot)tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj $(RepoRoot)tools/specs/Microsoft.Data.SqlClient.nuspec $(RepoRoot)tools/GenAPI/Microsoft.DotNet.GenAPI/ $(GenApiPath)Microsoft.DotNet.GenAPI.csproj - - - $(MdsRoot)tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj - $(MdsRoot)tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj - $(MdsRoot)notsupported/Microsoft.Data.SqlClient.csproj - $(MdsRoot)src/Microsoft.Data.SqlClient.csproj - $(MdsRoot)ref/Microsoft.Data.SqlClient.csproj - $(MdsRoot)tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj @@ -211,98 +266,163 @@ --> - - "$(DotNetPath)dotnet" build "$(GenApiProjectPath)" + + "$(DotnetPath)dotnet" build "$(GenApiProjectPath)" -p:Configuration=$(Configuration) - + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + - + - - "$(DotNetPath)dotnet" build "$(MdsNotSupportedProjectPath)" + + "$(DotnetPath)dotnet" build "$(MdsNotSupportedProjectPath)" -p:Configuration=$(Configuration) -p:GenApiPath="@(GenApiArtifactPath->'%(FullPath)')" - + $(SigningKeyPathArgument) + + + $(BuildNumberArgument) + $(PacakgeVersionMdsArgument) + + + $(ReferenceTypeArgument) + $(PackageVersionAbstractionsArgument) + $(PackageVersionLoggingArgument) + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + - - $(DotNetPath)dotnet build $(MdsRefProjectPath) + + "$(DotnetPath)dotnet" build $(MdsRefProjectPath) -p:Configuration=$(Configuration) + $(SigningKeyPathArgument) + + + $(BuildNumberArgument) + $(PacakgeVersionMdsArgument) + + $(ReferenceTypeArgument) $(PackageVersionAbstractionsArgument) - + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + - - $(DotNetPath)dotnet build $(MdsProjectPath) + + "$(DotnetPath)dotnet" build $(MdsProjectPath) -p:Configuration=$(Configuration) -p:TargetOs=Unix + $(SigningKeyPathArgument) + + + $(BuildNumberArgument) + $(PackageVersionMdsArgument) $(ReferenceTypeArgument) $(PackageVersionAbstractionsArgument) $(PackageVersionLoggingArgument) - + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + - - $(DotNetPath)dotnet build $(MdsProjectPath) + + "$(DotnetPath)dotnet" build $(MdsProjectPath) -p:Configuration=$(Configuration) -p:TargetOs=Windows_NT + $(SigningKeyPathArgument) + + + $(BuildNumberArgument) + $(PackageVersionMdsArgument) $(ReferenceTypeArgument) $(PackageVersionAbstractionsArgument) $(PackageVersionLoggingArgument) - + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + - - - + + + + + + + + + git rev-parse HEAD + + + $([System.Text.RegularExpressions.Regex]::Replace($(GitCommand), "\s+", " ")) + + + + + + + + $([System.Text.RegularExpressions.Regex]::Replace($(CommitId), "\s", "")) + + "$(NugetPath)" pack "$(MdsNuspecPath)" + -Symbols + -SymbolPackageFormat snupkg + -Version "$(PackageVersionMds)" + -OutputDirectory "$(MdsArtifactRoot)/$(ReferenceType)-$(Configuration)" + -properties "COMMITID=$(CommitId);Configuration=$(Configuration);ReferenceType=$(ReferenceType);AbstractionsPackageVersion=$(PackageVersionAbstractions);LoggingPackageVersion=$(PackageVersionLogging)" + + + $([System.Text.RegularExpressions.Regex]::Replace($(NuGetCommand), "\s+", " ")) + + + @@ -316,11 +436,11 @@ MdsFunctional-$(OS) $(LogFilePrefix)-$(TestFramework) - - $(DotNetPath)dotnet test "$(MdsFunctionalTestProjectPath)" + + "$(DotnetPath)dotnet" test "$(MdsFunctionalTestProjectPath)" -p:Configuration=$(Configuration) $(TestBlameArgument) - $(TestCollectArgument) + $(TestCodeCoverageArgument) $(TestFiltersArgument) $(TestFrameworkArgument) --results-directory "$(TestResultsFolderPath)" @@ -331,12 +451,12 @@ $(PackageVersionAbstractionsArgument) $(PackageVersionLoggingArgument) $(PackageVersionMdsArgument) - + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + @@ -346,11 +466,11 @@ $(LogFilePrefix)-$(TestFramework) $(LogFilePrefix)-$(TestSet) - - $(DotNetPath)dotnet test "$(MdsManualTestProjectPath)" + + "$(DotnetPath)dotnet" test "$(MdsManualTestProjectPath)" -p:Configuration=$(Configuration) $(TestBlameArgument) - $(TestCollectArgument) + $(TestCodeCoverageArgument) $(TestFiltersArgument) $(TestFrameworkArgument) $(TestSetArgument) @@ -362,12 +482,12 @@ $(PackageVersionAbstractionsArgument) $(PackageVersionLoggingArgument) $(PackageVersionMdsArgument) - + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + @@ -380,21 +500,21 @@ MdsUnit-$(OS) $(LogFilePrefix)-$(TestFramework) - - $(DotNetPath)dotnet test "$(MdsUnitTestProjectPath)" + + "$(DotnetPath)dotnet" test "$(MdsUnitTestProjectPath)" -p:Configuration=$(Configuration) $(TestBlameArgument) - $(TestCollectArgument) + $(TestCodeCoverageArgument) $(TestFiltersArgument) $(TestFrameworkArgument) --results-directory "$(TestResultsFolderPath)" --logger:"trx;LogFilePrefix=$(LogFilePrefix)" - + - $([System.Text.RegularExpressions.Regex]::Replace($(DotNetCommand), "\s+", " ")) + $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) - - + + diff --git a/eng/pipelines/common/templates/steps/ci-project-build-step.yml b/eng/pipelines/common/templates/steps/ci-project-build-step.yml index e496aa2b71..d43222bfd1 100644 --- a/eng/pipelines/common/templates/steps/ci-project-build-step.yml +++ b/eng/pipelines/common/templates/steps/ci-project-build-step.yml @@ -71,61 +71,25 @@ parameters: default: $(akvPackageVersion) steps: -- ${{ if or(eq(parameters.operatingSystem, 'Windows'), eq(parameters.operatingSystem, 'deferedToRuntime')) }}: + # Build MDS - ${{ if or(eq(parameters.build, 'MDS'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}: - task: MSBuild@1 - displayName: 'Restore [Win]' + displayName: 'Build Driver' condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) inputs: - solution: build.proj + solution: build2.proj msbuildArchitecture: x64 - msbuildArguments: >- - -t:restore + platform: '${{ parameters.platform }}' + configuration: '${{ parameters.buildConfiguration }}' + msbuildArguments: + -t:BuildMds -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - retryCountOnTaskFailure: 1 - - - ${{ if eq(parameters.build, 'allNoDocs') }}: - - task: MSBuild@1 - displayName: 'Build Driver (no docs) [Win]' - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: '${{ parameters.platform }}' - configuration: '${{ parameters.buildConfiguration }}' - msbuildArguments: >- - -t:BuildAllConfigurations - -p:ReferenceType=${{ parameters.referenceType }} - -p:GenerateNuget=false - -p:GenerateDocumentationFile=false - -p:BuildNumber=${{ parameters.buildNumber }} - -p:AssemblyBuildNumber=${{ parameters.assemblyBuildNumber }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - - - ${{ if or(eq(parameters.build, 'MDS'), eq(parameters.build, 'all')) }}: - - task: MSBuild@1 - displayName: 'Build Driver [Win]' - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - inputs: - solution: build.proj - msbuildArchitecture: x64 - platform: '${{ parameters.platform }}' - configuration: '${{ parameters.buildConfiguration }}' - msbuildArguments: - -t:BuildAllConfigurations - -p:ReferenceType=${{ parameters.referenceType }} - -p:GenerateNuget=false - -p:BuildNumber=${{ parameters.buildNumber }} - -p:AssemblyBuildNumber=${{ parameters.assemblyBuildNumber }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} + -p:BuildNumber=${{ parameters.buildNumber }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} + # Build AKV Provider - ${{ if or(eq(parameters.build, 'AkvProvider'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}: - task: MSBuild@1 displayName: 'Build AKV Provider' @@ -136,53 +100,10 @@ steps: platform: '${{ parameters.platform }}' configuration: '${{ parameters.buildConfiguration }}' msbuildArguments: >- - -t:BuildAkvProvider - -p:ReferenceType=${{ parameters.referenceType }} - -p:GenerateNuget=false - -p:BuildNumber=${{ parameters.buildNumber }} - -p:AssemblyBuildNumber=${{ parameters.assemblyBuildNumber }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:AkvPackageVersion=${{ parameters.akvPackageVersion }} - -- ${{ if or(eq(parameters.operatingSystem, 'Linux'), eq(parameters.operatingSystem, 'MacOS'), eq(parameters.operatingSystem, 'deferedToRuntime')) }}: - - ${{ if or(eq(parameters.build, 'MDS'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}: - - task: DotNetCoreCLI@2 - displayName: 'Build SqlClient [${{ parameters.operatingSystem }}]' - condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT')) - inputs: - command: custom - projects: build.proj - custom: build - arguments: >- - -t:BuildSqlClient - -p:ReferenceType=${{ parameters.referenceType }} - -p:TestEnabled=true - -p:GenerateNuget=false - -p:GenerateDocumentationFile=false - -p:Configuration=${{ parameters.buildConfiguration }} - -p:BuildNumber=${{ parameters.buildNumber }} - -p:AssemblyBuildNumber=${{ parameters.assemblyBuildNumber }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - verbosityRestore: Detailed - verbosityPack: Detailed - - - ${{ if or(eq(parameters.build, 'AkvProvider'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}: - - task: DotNetCoreCLI@2 - displayName: 'Build AKV Provider [${{ parameters.operatingSystem }}]' - condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT')) - inputs: - command: custom - projects: build.proj - custom: build - arguments: >- -t:BuildAkvProvider -p:ReferenceType=${{ parameters.referenceType }} -p:TestEnabled=true -p:GenerateNuget=false - -p:GenerateDocumentationFile=false -p:Configuration=${{ parameters.buildConfiguration }} -p:BuildNumber=${{ parameters.buildNumber }} -p:AssemblyBuildNumber=${{ parameters.assemblyBuildNumber }} @@ -192,3 +113,4 @@ steps: -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} verbosityRestore: Detailed verbosityPack: Detailed + diff --git a/eng/pipelines/common/templates/steps/run-all-tests-step.yml b/eng/pipelines/common/templates/steps/run-all-tests-step.yml index b1d0261abc..2c407f7d1e 100644 --- a/eng/pipelines/common/templates/steps/run-all-tests-step.yml +++ b/eng/pipelines/common/templates/steps/run-all-tests-step.yml @@ -81,168 +81,176 @@ steps: displayName: 'Run Unit Tests ${{parameters.msbuildArchitecture }}' condition: succeededOrFailed() inputs: - solution: build.proj + solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} platform: '${{parameters.platform }}' configuration: '${{parameters.buildConfiguration }}' ${{ if eq(parameters.msbuildArchitecture, 'x64') }}: msbuildArguments: >- - -t:RunUnitTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsUnit + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} + -p:TestResultsFolderPath=TestResults ${{ else }}: # x86 msbuildArguments: >- - -t:RunUnitTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsUnit + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} + -p:TestResultsFolderPath=TestResults - task: MSBuild@1 displayName: 'Run Flaky Unit Tests ${{parameters.msbuildArchitecture }}' condition: succeededOrFailed() inputs: - solution: build.proj + solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} platform: '${{parameters.platform }}' configuration: '${{parameters.buildConfiguration }}' ${{ if eq(parameters.msbuildArchitecture, 'x64') }}: msbuildArguments: >- - -t:RunUnitTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsUnit + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false ${{ else }}: # x86 msbuildArguments: >- - -t:RunUnitTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsUnit + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false continueOnError: true - task: MSBuild@1 displayName: 'Run Functional Tests ${{parameters.msbuildArchitecture }}' condition: succeededOrFailed() inputs: - solution: build.proj + solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} platform: '${{parameters.platform }}' configuration: '${{parameters.buildConfiguration }}' ${{ if eq(parameters.msbuildArchitecture, 'x64') }}: msbuildArguments: >- - -t:RunFunctionalTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsFunctional + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} + -p:TestResultsFolderPath=TestResults ${{ else }}: # x86 msbuildArguments: >- - -t:RunFunctionalTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsFunctional + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} + -p:TestResultsFolderPath=TestResults - task: MSBuild@1 condition: succeededOrFailed() displayName: 'Run Flaky Functional Tests ${{parameters.msbuildArchitecture }}' inputs: - solution: build.proj + solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} platform: '${{parameters.platform }}' configuration: '${{parameters.buildConfiguration }}' ${{ if eq(parameters.msbuildArchitecture, 'x64') }}: msbuildArguments: >- - -t:RunFunctionalTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsFunctional + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false ${{ else }}: # x86 msbuildArguments: >- - -t:RunFunctionalTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsFunctional + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false continueOnError: true - task: MSBuild@1 condition: succeededOrFailed() displayName: 'Run Manual Tests ${{parameters.msbuildArchitecture }}' inputs: - solution: build.proj + solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} platform: '${{parameters.platform }}' configuration: '${{parameters.buildConfiguration }}' ${{ if eq(parameters.msbuildArchitecture, 'x64') }}: msbuildArguments: >- - -t:RunManualTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsManual + -p:TestFramework=${{ parameters.targetFramework }} -p:TestSet=${{ parameters.testSet }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} + -p:TestResultsFolderPath=TestResults ${{ else }}: # x86 msbuildArguments: >- - -t:RunManualTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsManual + -p:TestFramework=${{ parameters.targetFramework }} -p:TestSet=${{ parameters.testSet }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} + -p:TestResultsFolderPath=TestResults retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }} - task: MSBuild@1 condition: succeededOrFailed() displayName: 'Run Flaky Manual Tests ${{parameters.msbuildArchitecture }}' inputs: - solution: build.proj + solution: build2.proj msbuildArchitecture: ${{parameters.msbuildArchitecture }} platform: '${{parameters.platform }}' configuration: '${{parameters.buildConfiguration }}' ${{ if eq(parameters.msbuildArchitecture, 'x64') }}: msbuildArguments: >- - -t:RunManualTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsManual + -p:TestFramework=${{ parameters.targetFramework }} -p:TestSet=${{ parameters.testSet }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false ${{ else }}: # x86 msbuildArguments: >- - -t:RunManualTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsManual + -p:TestFramework=${{ parameters.targetFramework }} -p:TestSet=${{ parameters.testSet }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false continueOnError: true - ${{ else }}: # Linux or macOS @@ -252,18 +260,17 @@ steps: condition: succeededOrFailed() inputs: command: custom - projects: build.proj + projects: build2.proj custom: msbuild arguments: >- - -t:RunUnitTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsUnit + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:Platform=${{ parameters.platform }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:Configuration=${{ parameters.buildConfiguration }} + -p:TestResultsFolderPath=TestResults verbosityRestore: Detailed verbosityPack: Detailed @@ -272,20 +279,19 @@ steps: condition: succeededOrFailed() inputs: command: custom - projects: build.proj + projects: build2.proj custom: msbuild arguments: >- - -t:RunUnitTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsUnit + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:Platform=${{ parameters.platform }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:Configuration=${{ parameters.buildConfiguration }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false verbosityRestore: Detailed verbosityPack: Detailed continueOnError: true @@ -295,18 +301,17 @@ steps: condition: succeededOrFailed() inputs: command: custom - projects: build.proj + projects: build2.proj custom: msbuild arguments: >- - -t:RunFunctionalTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsFunctional + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:Platform=${{ parameters.platform }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:Configuration=${{ parameters.buildConfiguration }} + -p:TestResultsFolderPath=TestResults verbosityRestore: Detailed verbosityPack: Detailed @@ -315,20 +320,19 @@ steps: displayName: 'Run Flaky Functional Tests' inputs: command: custom - projects: build.proj + projects: build2.proj custom: msbuild arguments: >- - -t:RunFunctionalTests - -p:TF=${{ parameters.targetFramework }} - -p:TestSet=${{ parameters.testSet }} + -t:TestMdsFunctional + -p:TestFramework=${{ parameters.targetFramework }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:Platform=${{ parameters.platform }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:Configuration=${{ parameters.buildConfiguration }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false verbosityRestore: Detailed verbosityPack: Detailed continueOnError: true @@ -338,18 +342,18 @@ steps: condition: succeededOrFailed() inputs: command: custom - projects: build.proj + projects: build2.proj custom: msbuild arguments: >- - -t:RunManualTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsManual + -p:TestFramework=${{ parameters.targetFramework }} -p:TestSet=${{ parameters.testSet }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:platform=${{ parameters.platform }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:Configuration=${{ parameters.buildConfiguration }} + -p:TestResultsFolderPath=TestResults verbosityRestore: Detailed verbosityPack: Detailed retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }} @@ -359,20 +363,20 @@ steps: condition: succeededOrFailed() inputs: command: custom - projects: build.proj + projects: build2.proj custom: msbuild arguments: >- - -t:RunManualTests - -p:TF=${{ parameters.targetFramework }} + -t:TestMdsManual + -p:TestFramework=${{ parameters.targetFramework }} -p:TestSet=${{ parameters.testSet }} -p:ReferenceType=${{ parameters.referenceType }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:Platform=${{ parameters.platform }} + -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} + -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} + -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} -p:Configuration=${{ parameters.buildConfiguration }} - -p:Filter="category=flaky" - -p:CollectCodeCoverage=false + -p:TestFilters="category=flaky" + -p:TestResultsFolderPath=TestResults + -p:TestCodeCoverage=false verbosityRestore: Detailed verbosityPack: Detailed continueOnError: true diff --git a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml index 663532b905..a575d2dd6a 100644 --- a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml @@ -44,6 +44,7 @@ pr: - tools/* - azurepipelines-coverage.yml - build.proj + - build2.proj - Directory.Packages.props - dotnet-tools.json - global.json diff --git a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml index 868b3010ff..dd8f34e58b 100644 --- a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml @@ -44,6 +44,7 @@ pr: - tools/* - azurepipelines-coverage.yml - build.proj + - build2.proj - Directory.Packages.props - dotnet-tools.json - global.json diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 0a80e8f0ca..b25ed21444 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -220,11 +220,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{4600328C-C13 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{4CAE9195-4F1A-4D48-854C-1C9FBC512C66}" ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\akv-official-pipeline.yml = ..\eng\pipelines\akv-official-pipeline.yml ..\eng\pipelines\dotnet-sqlclient-ci-core.yml = ..\eng\pipelines\dotnet-sqlclient-ci-core.yml ..\eng\pipelines\dotnet-sqlclient-ci-package-reference-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-ci-package-reference-pipeline.yml ..\eng\pipelines\dotnet-sqlclient-ci-project-reference-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-ci-project-reference-pipeline.yml - ..\eng\pipelines\dotnet-sqlclient-signing-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-signing-pipeline.yml ..\eng\pipelines\sqlclient-pr-package-ref-pipeline.yml = ..\eng\pipelines\sqlclient-pr-package-ref-pipeline.yml ..\eng\pipelines\sqlclient-pr-project-ref-pipeline.yml = ..\eng\pipelines\sqlclient-pr-project-ref-pipeline.yml ..\eng\pipelines\stress-tests-pipeline.yml = ..\eng\pipelines\stress-tests-pipeline.yml @@ -236,12 +234,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{3850810F-535E-443A-A9B4-7EE85B327E0A}" ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\common\templates\jobs\build-signed-package-job.yml = ..\eng\pipelines\common\templates\jobs\build-signed-package-job.yml ..\eng\pipelines\common\templates\jobs\ci-build-nugets-job.yml = ..\eng\pipelines\common\templates\jobs\ci-build-nugets-job.yml ..\eng\pipelines\common\templates\jobs\ci-code-coverage-job.yml = ..\eng\pipelines\common\templates\jobs\ci-code-coverage-job.yml ..\eng\pipelines\common\templates\jobs\ci-run-tests-job.yml = ..\eng\pipelines\common\templates\jobs\ci-run-tests-job.yml - ..\eng\pipelines\common\templates\jobs\run-tests-package-reference-job.yml = ..\eng\pipelines\common\templates\jobs\run-tests-package-reference-job.yml - ..\eng\pipelines\common\templates\jobs\validate-signed-package-job.yml = ..\eng\pipelines\common\templates\jobs\validate-signed-package-job.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{01FA4774-E594-4A2D-B839-EA7A56D09A8B}" @@ -251,22 +246,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{01FA47 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{EABE3A3E-D505-418A-B1B8-1B0AC4872F3D}" ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\common\templates\steps\build-all-configurations-signed-dlls-step.yml = ..\eng\pipelines\common\templates\steps\build-all-configurations-signed-dlls-step.yml ..\eng\pipelines\common\templates\steps\build-and-run-tests-netcore-step.yml = ..\eng\pipelines\common\templates\steps\build-and-run-tests-netcore-step.yml ..\eng\pipelines\common\templates\steps\build-and-run-tests-netfx-step.yml = ..\eng\pipelines\common\templates\steps\build-and-run-tests-netfx-step.yml ..\eng\pipelines\common\templates\steps\ci-project-build-step.yml = ..\eng\pipelines\common\templates\steps\ci-project-build-step.yml - ..\eng\pipelines\common\templates\steps\code-analyze-step.yml = ..\eng\pipelines\common\templates\steps\code-analyze-step.yml ..\eng\pipelines\common\templates\steps\configure-sql-server-linux-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-linux-step.yml ..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml ..\eng\pipelines\common\templates\steps\configure-sql-server-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-step.yml ..\eng\pipelines\common\templates\steps\configure-sql-server-win-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-win-step.yml - ..\eng\pipelines\common\templates\steps\copy-dlls-for-test-step.yml = ..\eng\pipelines\common\templates\steps\copy-dlls-for-test-step.yml - ..\eng\pipelines\common\templates\steps\esrp-code-signing-step.yml = ..\eng\pipelines\common\templates\steps\esrp-code-signing-step.yml ..\eng\pipelines\common\templates\steps\generate-nuget-package-step.yml = ..\eng\pipelines\common\templates\steps\generate-nuget-package-step.yml ..\eng\pipelines\common\templates\steps\override-sni-version.yml = ..\eng\pipelines\common\templates\steps\override-sni-version.yml ..\eng\pipelines\common\templates\steps\pre-build-step.yml = ..\eng\pipelines\common\templates\steps\pre-build-step.yml ..\eng\pipelines\common\templates\steps\prepare-test-db-step.yml = ..\eng\pipelines\common\templates\steps\prepare-test-db-step.yml - ..\eng\pipelines\common\templates\steps\publish-symbols-step.yml = ..\eng\pipelines\common\templates\steps\publish-symbols-step.yml ..\eng\pipelines\common\templates\steps\publish-test-results-step.yml = ..\eng\pipelines\common\templates\steps\publish-test-results-step.yml ..\eng\pipelines\common\templates\steps\run-all-tests-step.yml = ..\eng\pipelines\common\templates\steps\run-all-tests-step.yml ..\eng\pipelines\common\templates\steps\update-config-file-step.yml = ..\eng\pipelines\common\templates\steps\update-config-file-step.yml @@ -274,27 +264,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{EABE3A3E EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libraries", "libraries", "{75BAE755-3A1F-41F2-9176-9F8FF9FEE2DD}" ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\libraries\build-variables.yml = ..\eng\pipelines\libraries\build-variables.yml ..\eng\pipelines\libraries\ci-build-variables.yml = ..\eng\pipelines\libraries\ci-build-variables.yml - ..\eng\pipelines\libraries\common-variables.yml = ..\eng\pipelines\libraries\common-variables.yml - ..\eng\pipelines\libraries\mds-validation-variables.yml = ..\eng\pipelines\libraries\mds-validation-variables.yml - ..\eng\pipelines\libraries\mds-variables.yml = ..\eng\pipelines\libraries\mds-variables.yml - ..\eng\pipelines\libraries\variables.yml = ..\eng\pipelines\libraries\variables.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "variables", "variables", "{1C796A8E-3529-481B-9D4F-88C67D03DCA2}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\variables\akv-official-variables.yml = ..\eng\pipelines\variables\akv-official-variables.yml - ..\eng\pipelines\variables\common-variables.yml = ..\eng\pipelines\variables\common-variables.yml - ..\eng\pipelines\variables\esrp-signing-variables.yml = ..\eng\pipelines\variables\esrp-signing-variables.yml - ..\eng\pipelines\variables\onebranch-variables.yml = ..\eng\pipelines\variables\onebranch-variables.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{09352F1D-878F-4F55-8AA2-6E47F1AD37D5}" ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\jobs\build-akv-official-job.yml = ..\eng\pipelines\jobs\build-akv-official-job.yml ..\eng\pipelines\jobs\pack-abstractions-package-ci-job.yml = ..\eng\pipelines\jobs\pack-abstractions-package-ci-job.yml ..\eng\pipelines\jobs\pack-azure-package-ci-job.yml = ..\eng\pipelines\jobs\pack-azure-package-ci-job.yml + ..\eng\pipelines\jobs\pack-logging-package-ci-job.yml = ..\eng\pipelines\jobs\pack-logging-package-ci-job.yml ..\eng\pipelines\jobs\stress-tests-ci-job.yml = ..\eng\pipelines\jobs\stress-tests-ci-job.yml ..\eng\pipelines\jobs\test-abstractions-package-ci-job.yml = ..\eng\pipelines\jobs\test-abstractions-package-ci-job.yml ..\eng\pipelines\jobs\test-azure-package-ci-job.yml = ..\eng\pipelines\jobs\test-azure-package-ci-job.yml @@ -302,16 +279,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{09352F1D-8 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{AD738BD4-6A02-4B88-8F93-FBBBA49A74C8}" ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\steps\compound-build-akv-step.yml = ..\eng\pipelines\steps\compound-build-akv-step.yml - ..\eng\pipelines\steps\compound-esrp-dll-signing-step.yml = ..\eng\pipelines\steps\compound-esrp-dll-signing-step.yml - ..\eng\pipelines\steps\compound-esrp-nuget-signing-step.yml = ..\eng\pipelines\steps\compound-esrp-nuget-signing-step.yml - ..\eng\pipelines\steps\compound-extract-akv-apiscan-files-step.yml = ..\eng\pipelines\steps\compound-extract-akv-apiscan-files-step.yml - ..\eng\pipelines\steps\compound-nuget-pack-step.yml = ..\eng\pipelines\steps\compound-nuget-pack-step.yml - ..\eng\pipelines\steps\compound-publish-symbols-step.yml = ..\eng\pipelines\steps\compound-publish-symbols-step.yml ..\eng\pipelines\steps\install-dotnet.yml = ..\eng\pipelines\steps\install-dotnet.yml ..\eng\pipelines\steps\install-dotnet-arm64.ps1 = ..\eng\pipelines\steps\install-dotnet-arm64.ps1 - ..\eng\pipelines\steps\roslyn-analyzers-akv-step.yml = ..\eng\pipelines\steps\roslyn-analyzers-akv-step.yml - ..\eng\pipelines\steps\script-output-environment-variables-step.yml = ..\eng\pipelines\steps\script-output-environment-variables-step.yml EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.UnitTests", "Microsoft.Data.SqlClient\tests\UnitTests\Microsoft.Data.SqlClient.UnitTests.csproj", "{4461063D-2F2B-274C-7E6F-F235119D258E}" @@ -363,6 +332,9 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.Internal", "Microsoft.Data.SqlClient.Internal", "{C1D2E3F4-A5B6-7C8D-9E0F-1A2B3C4D5E6F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient", "{7289C27E-D7DF-2C71-84B4-151F3A162493}" + ProjectSection(SolutionItems) = preProject + Microsoft.Data.SqlClient\MdsVersions.props = Microsoft.Data.SqlClient\MdsVersions.props + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7E0602AC-7F0A-362A-D734-0FDDFCC600B5}" EndProject @@ -372,6 +344,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{E9D12A ProjectSection(SolutionItems) = preProject ..\eng\pipelines\stages\build-abstractions-package-ci-stage.yml = ..\eng\pipelines\stages\build-abstractions-package-ci-stage.yml ..\eng\pipelines\stages\build-azure-package-ci-stage.yml = ..\eng\pipelines\stages\build-azure-package-ci-stage.yml + ..\eng\pipelines\stages\build-logging-package-ci-stage.yml = ..\eng\pipelines\stages\build-logging-package-ci-stage.yml + ..\eng\pipelines\stages\build-sqlclient-package-ci-stage.yml = ..\eng\pipelines\stages\build-sqlclient-package-ci-stage.yml + ..\eng\pipelines\stages\generate-secrets-ci-stage.yml = ..\eng\pipelines\stages\generate-secrets-ci-stage.yml ..\eng\pipelines\stages\stress-tests-ci-stage.yml = ..\eng\pipelines\stages\stress-tests-ci-stage.yml EndProjectSection EndProject @@ -391,6 +366,48 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.GenAPI", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Cci.Extensions", "..\tools\GenAPI\Microsoft.Cci.Extensions\Microsoft.Cci.Extensions.csproj", "{2F900B8A-EA19-4274-9AE9-3E6A59856FCC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "onebranch", "onebranch", "{5EC4C165-25BB-4346-9DCC-5EFEEAC00C02}" + ProjectSection(SolutionItems) = preProject + ..\eng\pipelines\onebranch\sqlclient-non-official.yml = ..\eng\pipelines\onebranch\sqlclient-non-official.yml + ..\eng\pipelines\onebranch\sqlclient-official.yml = ..\eng\pipelines\onebranch\sqlclient-official.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{B3061B34-2A1C-49B9-A3A9-D4D37B4704B2}" + ProjectSection(SolutionItems) = preProject + ..\eng\pipelines\onebranch\jobs\build-signed-csproj-package-job.yml = ..\eng\pipelines\onebranch\jobs\build-signed-csproj-package-job.yml + ..\eng\pipelines\onebranch\jobs\build-signed-sqlclient-package-job.yml = ..\eng\pipelines\onebranch\jobs\build-signed-sqlclient-package-job.yml + ..\eng\pipelines\onebranch\jobs\publish-nuget-package-job.yml = ..\eng\pipelines\onebranch\jobs\publish-nuget-package-job.yml + ..\eng\pipelines\onebranch\jobs\validate-signed-package-job.yml = ..\eng\pipelines\onebranch\jobs\validate-signed-package-job.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{3654906C-6852-48AF-BC7E-B3A62F3E9DB9}" + ProjectSection(SolutionItems) = preProject + ..\eng\pipelines\onebranch\stages\build-stages.yml = ..\eng\pipelines\onebranch\stages\build-stages.yml + ..\eng\pipelines\onebranch\stages\release-stages.yml = ..\eng\pipelines\onebranch\stages\release-stages.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{45B9E5C3-DED5-4DB6-B1E6-4EAC4E68BA83}" + ProjectSection(SolutionItems) = preProject + ..\eng\pipelines\onebranch\steps\build-all-configurations-signed-dlls-step.yml = ..\eng\pipelines\onebranch\steps\build-all-configurations-signed-dlls-step.yml + ..\eng\pipelines\onebranch\steps\code-analyze-step.yml = ..\eng\pipelines\onebranch\steps\code-analyze-step.yml + ..\eng\pipelines\onebranch\steps\compound-build-csproj-step.yml = ..\eng\pipelines\onebranch\steps\compound-build-csproj-step.yml + ..\eng\pipelines\onebranch\steps\compound-esrp-dll-signing-step.yml = ..\eng\pipelines\onebranch\steps\compound-esrp-dll-signing-step.yml + ..\eng\pipelines\onebranch\steps\compound-esrp-nuget-signing-step.yml = ..\eng\pipelines\onebranch\steps\compound-esrp-nuget-signing-step.yml + ..\eng\pipelines\onebranch\steps\compound-nuget-pack-step.yml = ..\eng\pipelines\onebranch\steps\compound-nuget-pack-step.yml + ..\eng\pipelines\onebranch\steps\compound-pack-csproj-step.yml = ..\eng\pipelines\onebranch\steps\compound-pack-csproj-step.yml + ..\eng\pipelines\onebranch\steps\compound-publish-symbols-step.yml = ..\eng\pipelines\onebranch\steps\compound-publish-symbols-step.yml + ..\eng\pipelines\onebranch\steps\esrp-code-signing-step.yml = ..\eng\pipelines\onebranch\steps\esrp-code-signing-step.yml + ..\eng\pipelines\onebranch\steps\publish-symbols-step.yml = ..\eng\pipelines\onebranch\steps\publish-symbols-step.yml + ..\eng\pipelines\onebranch\steps\script-output-environment-variables-step.yml = ..\eng\pipelines\onebranch\steps\script-output-environment-variables-step.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "variables", "variables", "{E849E6FB-1AB0-4922-A060-0062EA669FD0}" + ProjectSection(SolutionItems) = preProject + ..\eng\pipelines\onebranch\variables\common-variables.yml = ..\eng\pipelines\onebranch\variables\common-variables.yml + ..\eng\pipelines\onebranch\variables\onebranch-variables.yml = ..\eng\pipelines\onebranch\variables\onebranch-variables.yml + ..\eng\pipelines\onebranch\variables\sqlclient-validation-variables.yml = ..\eng\pipelines\onebranch\variables\sqlclient-validation-variables.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -883,7 +900,6 @@ Global {01FA4774-E594-4A2D-B839-EA7A56D09A8B} = {E76A4ED5-9137-4E4B-AE91-7AEDB2683823} {EABE3A3E-D505-418A-B1B8-1B0AC4872F3D} = {E76A4ED5-9137-4E4B-AE91-7AEDB2683823} {75BAE755-3A1F-41F2-9176-9F8FF9FEE2DD} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} - {1C796A8E-3529-481B-9D4F-88C67D03DCA2} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} {09352F1D-878F-4F55-8AA2-6E47F1AD37D5} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} {AD738BD4-6A02-4B88-8F93-FBBBA49A74C8} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} {4461063D-2F2B-274C-7E6F-F235119D258E} = {0CC4817A-12F3-4357-912C-09315FAAD008} @@ -918,6 +934,11 @@ Global {1DB299CE-95EA-4566-84DD-171768758291} = {351BE847-A0BF-450C-A5BC-8337AFA49EAA} {583E2B51-A90B-4F34-AD8B-4061504E855E} = {351BE847-A0BF-450C-A5BC-8337AFA49EAA} {2F900B8A-EA19-4274-9AE9-3E6A59856FCC} = {351BE847-A0BF-450C-A5BC-8337AFA49EAA} + {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} + {B3061B34-2A1C-49B9-A3A9-D4D37B4704B2} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} + {3654906C-6852-48AF-BC7E-B3A62F3E9DB9} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} + {45B9E5C3-DED5-4DB6-B1E6-4EAC4E68BA83} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} + {E849E6FB-1AB0-4922-A060-0062EA669FD0} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} diff --git a/src/Microsoft.Data.SqlClient/MdsVersions.props b/src/Microsoft.Data.SqlClient/MdsVersions.props new file mode 100644 index 0000000000..e50e813f66 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/MdsVersions.props @@ -0,0 +1,35 @@ + + + + 7.0.0 + + 0 + + + $(MdsVersionDefault).$(BuildNumber.Split('.')[0]) + $(MdsPackageVersion.Split('-')[0]) + + + $(MdsVersionDefault).$(BuildNumber)-dev + + diff --git a/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj index 065e10dcd3..6f5fc44f59 100644 --- a/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj @@ -67,6 +67,14 @@ $(NoWarn);CS0618 + + + + $(MdsAssemblyVersion) + $(MdsAssemblyVersion) + $(MdsPackageVersion) + + - $(RepoRoot)artifacts/$(AssemblyName).notsupported/$(Configuration)/ + $(RepoRoot)artifacts/$(AssemblyName).notsupported/$(ReferenceType)-$(Configuration)/ @@ -86,7 +94,7 @@ --> $(RepoRoot)src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj - $(RepoRoot)artifacts/Microsoft.Data.SqlClient.ref/$(Configuration)/$(TargetFramework)/Microsoft.Data.SqlClient.dll + $(RepoRoot)artifacts/Microsoft.Data.SqlClient.ref/$(ReferenceType)-$(Configuration)/$(TargetFramework)/Microsoft.Data.SqlClient.dll public sealed class SqlConfigurableRetryFactory { - /// + /// public static System.Collections.ObjectModel.ReadOnlyCollection BaselineTransientErrors { get { throw null; } } /// public static SqlRetryLogicBaseProvider CreateExponentialRetryProvider(SqlRetryLogicOption retryLogicOption) { throw null; } @@ -885,7 +885,7 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public Microsoft.Data.SqlClient.SqlCredential Credential { get { throw null; } set { } } - /// + /// public SspiContextProvider SspiContextProvider { get { throw null; } set { } } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] @@ -1359,7 +1359,7 @@ public SqlDataAdapter(string selectCommandText, string selectConnectionString) { public event Microsoft.Data.SqlClient.SqlRowUpdatedEventHandler RowUpdated { add { } remove { } } /// public event Microsoft.Data.SqlClient.SqlRowUpdatingEventHandler RowUpdating { add { } remove { } } - /// + /// protected override void OnRowUpdated(System.Data.Common.RowUpdatedEventArgs value) { } /// protected override void OnRowUpdating(System.Data.Common.RowUpdatingEventArgs value) { } @@ -2181,7 +2181,7 @@ public sealed class SspiAuthenticationParameters { /// public SspiAuthenticationParameters( - string serverName, + string serverName, string resource, string userId = null, string databaseName = null, @@ -2201,4 +2201,4 @@ public SspiAuthenticationParameters( /// public string Password { get { throw null; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj index 447fb01182..dbdf7e9338 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj @@ -2,6 +2,15 @@ Microsoft.Data.SqlClient net462;net8.0;net9.0;netstandard2.0 + true + + + + + + $(MdsAssemblyVersion) + $(MdsAssemblyVersion) + $(MdsPackageVersion) @@ -24,7 +33,7 @@ is possible but also requires specifying the IntermediateOutputPath. So while it would be nice to have a flatter directory structure, it's more hassle than its worth. --> - $(ArtifactPath)$(AssemblyName).ref/$(Configuration)/ + $(ArtifactPath)$(AssemblyName).ref/$(ReferenceType)-$(Configuration)/ @@ -35,7 +44,9 @@ have the full documentation. --> - + powershell.exe pwsh @@ -43,7 +54,7 @@ $(PowerShellCommand) -NonInteractive -ExecutionPolicy Unrestricted - -Command "$(RepoRoot)tools\intellisense\TrimDocs.ps1 -inputFile '$(OutputPath)\Microsoft.Data.SqlClient.xml' -outputFile '$(OutputPath)\Microsoft.Data.SqlClient.xml'" + -Command "$(RepoRoot)tools\intellisense\TrimDocs.ps1 -inputFile '$(DocumentationFile)' -outputFile '$(DocumentationFile)'" diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj index d0713280e6..49f5677535 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj @@ -1,11 +1,21 @@ - + Microsoft.Data.SqlClient Debug;Release; - true - + true + + true + + + + + + $(MdsAssemblyVersion) + $(MdsAssemblyVersion) + $(MdsPackageVersion) + @@ -62,7 +72,7 @@ - $(ArtifactPath)$(AssemblyName)/$(Configuration)/$(NormalizedTargetOs)/ + $(ArtifactPath)$(AssemblyName)/$(ReferenceType)-$(Configuration)/$(NormalizedTargetOs)/ @@ -124,7 +134,7 @@ - + diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 0d6203c08d..8806df9432 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -845,7 +845,7 @@ private TdsOperationStatus TryCleanPartialRead() private void CleanPartialReadReliable() { AssertReaderState(requireData: true, permitAsync: false); - + TdsOperationStatus result = TryCleanPartialRead(); Debug.Assert(result == TdsOperationStatus.Done, "Should not pend on sync call"); Debug.Assert(!_sharedState._dataReady, "_dataReady should be cleared"); @@ -1054,7 +1054,7 @@ private TdsOperationStatus TryCloseInternal(bool closeReader) { Connection.RemoveWeakReference(this); // This doesn't catch everything -- the connection may be closed, but it prevents dead readers from clogging the collection } - + // IsClosed may be true if CloseReaderFromConnection was called - in which case, the session has already been closed if (!wasClosed && stateObj != null) { @@ -1073,7 +1073,7 @@ private TdsOperationStatus TryCloseInternal(bool closeReader) } } // @TODO: CER Exception Handling was removed here (see GH#3581) - + // DO NOT USE stateObj after this point - it has been returned to the TdsParser's session pool and potentially handed out to another thread // do not retry here @@ -1658,7 +1658,7 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf { remaining = 0; TdsOperationStatus result; - + int cbytes = 0; AssertReaderState(requireData: true, permitAsync: true, columnIndex: i, enforceSequentialAccess: true); @@ -2598,7 +2598,7 @@ virtual public SqlXml GetSqlXml(int i) return sx; } - /// + /// virtual public SqlJson GetSqlJson(int i) { ReadColumn(i); @@ -2606,7 +2606,7 @@ virtual public SqlJson GetSqlJson(int i) return json; } - /// + /// virtual public SqlVector GetSqlVector(int i) where T : unmanaged { if (typeof(T) != typeof(float)) @@ -3139,7 +3139,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met return (T)(object)data.String; } // the requested type is likely to be one that isn't supported so try the cast and - // unless there is a null value conversion then feedback the cast exception with + // unless there is a null value conversion then feedback the cast exception with // type named to the user so they know what went wrong. Supported types are listed // in the documentation try @@ -3909,7 +3909,7 @@ private TdsOperationStatus TryReadColumnHeader(int i) { throw SQL.InvalidRead(); } - + return TryReadColumnInternal(i, readHeaderOnly: true); // @TODO: CER Exception Handling was removed here (see GH#3581) } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlDbTypeExtensions.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlDbTypeExtensions.cs index 96244fb7a8..0b6780f900 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlDbTypeExtensions.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlDbTypeExtensions.cs @@ -6,16 +6,16 @@ namespace Microsoft.Data { - /// + /// public static class SqlDbTypeExtensions { - /// + /// #if NET9_0_OR_GREATER public const SqlDbType Json = SqlDbType.Json; #else public const SqlDbType Json = (SqlDbType)35; #endif - /// + /// #if NET10_0_OR_GREATER public const SqlDbType Vector = SqlDbType.Vector; #else diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlJson.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlJson.cs index 8d0ee74cc6..0c1047ee6d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlJson.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlJson.cs @@ -12,18 +12,18 @@ namespace Microsoft.Data.SqlTypes { - /// + /// public class SqlJson : INullable { // Our serialized JSON string, or null. private readonly string? _jsonString = null; - /// + /// public SqlJson() { } - /// + /// #if NET public SqlJson([StringSyntax(StringSyntaxAttribute.Json)] string? jsonString) #else @@ -45,8 +45,8 @@ public SqlJson(string? jsonString) _jsonString = jsonString; } - /// - public SqlJson(JsonDocument? jsonDoc) + /// + public SqlJson(JsonDocument? jsonDoc) { if (jsonDoc == null) { @@ -57,15 +57,15 @@ public SqlJson(JsonDocument? jsonDoc) _jsonString = jsonDoc.RootElement.GetRawText(); } - /// + /// public bool IsNull => _jsonString is null; - /// + /// public static SqlJson Null => new(); - /// - public string Value - { + /// + public string Value + { get { if (IsNull) @@ -77,7 +77,7 @@ public string Value } } - /// + /// public override string? ToString() { return _jsonString; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs index cf04ff8636..14e790e977 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs @@ -15,7 +15,7 @@ namespace Microsoft.Data.SqlTypes; -/// +/// public readonly struct SqlVector : INullable, ISqlVector where T : unmanaged { @@ -55,10 +55,10 @@ private SqlVector(int length) Memory = new(); } - /// + /// public static SqlVector CreateNull(int length) => new(length); - /// + /// public SqlVector(ReadOnlyMemory memory) { (_elementType, _elementSize) = GetTypeFieldsOrThrow(); @@ -101,16 +101,16 @@ internal string GetString() #region Properties - /// + /// public bool IsNull { get; } - /// + /// public static SqlVector? Null => null; - /// + /// public int Length { get; } - - /// + + /// public ReadOnlyMemory Memory { get; } #endregion @@ -206,7 +206,7 @@ private byte[] MakeTdsBytes(ReadOnlyMemory values) // The vector length is an unsigned 16-bit integer, little-endian. int length = BinaryPrimitives.ReadUInt16LittleEndian(rawBytes.AsSpan(2)); - + // The vector size is the number of bytes required to represent the vector in TDS. int size = TdsEnums.VECTOR_HEADER_SIZE + (_elementSize * length); @@ -237,6 +237,6 @@ private T[] MakeArray() return MemoryMarshal.Cast(dataSpan).ToArray(); #endif } - + #endregion } diff --git a/tools/intellisense/TrimDocs.ps1 b/tools/intellisense/TrimDocs.ps1 index c5e7eeb893..b09b8fc9d7 100644 --- a/tools/intellisense/TrimDocs.ps1 +++ b/tools/intellisense/TrimDocs.ps1 @@ -7,10 +7,11 @@ param ( [string]$outputFile="" ) +$ErrorActionPreference = 'Stop' + # Validate inputFile exists if (-not (Test-Path $inputFile)) { - Write-Host "XML File not found: $inputFile" - exit + throw "XML File not found: $inputFile" } [xml]$xml = Get-Content $inputFile diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 739055dbf0..858b41628b 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -29,38 +29,7 @@ - - - 7.0.0 - - $(MdsVersionDefault).$(BuildNumber)-dev - - - - - $(MdsVersionDefault).$(AssemblyBuildNumber) - - - 7.0.0.0 - - $(AssemblyFileVersion) - - - $(MdsPackageVersion) - + diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index cbea2140c7..2e80556c98 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -123,103 +123,111 @@ command line to indicate where the DLLs are. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/targets/GenerateThisAssemblyCs.targets b/tools/targets/GenerateThisAssemblyCs.targets index dc12b40322..6dfd670c5c 100644 --- a/tools/targets/GenerateThisAssemblyCs.targets +++ b/tools/targets/GenerateThisAssemblyCs.targets @@ -8,6 +8,9 @@ + + $(AssemblyVersion) + System From e65124a251a3a8805abf8aac1bf03d18928f0a0c Mon Sep 17 00:00:00 2001 From: samsharma2700 Date: Fri, 20 Mar 2026 09:29:02 -0700 Subject: [PATCH 04/22] Scope NuGet package signing to specific packages (#4058) --- .../onebranch/jobs/build-signed-csproj-package-job.yml | 1 + .../jobs/build-signed-sqlclient-package-job.yml | 1 + .../onebranch/steps/compound-esrp-nuget-signing-step.yml | 9 +++++++-- eng/pipelines/onebranch/steps/esrp-code-signing-step.yml | 9 ++++++--- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml index 809690a886..01d9b61da1 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml @@ -191,6 +191,7 @@ jobs: authSignCertName: ${{ parameters.authSignCertName }} esrpClientId: ${{ parameters.esrpClientId }} esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} + pattern: '${{ parameters.packageFullName }}.*nupkg' # Publish symbols to servers - ${{ if eq(parameters.publishSymbols, true) }}: diff --git a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml index a422a5351c..98f5c811aa 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml @@ -108,6 +108,7 @@ jobs: - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self parameters: artifactType: pkg + nupkgPattern: 'Microsoft.Data.SqlClient.$(mdsPackageVersion).*nupkg' # Copy signed DLLs and PDBs to APIScan folders. - task: CopyFiles@2 diff --git a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml b/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml index 34e903465f..e86964cf3d 100644 --- a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml +++ b/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml @@ -30,6 +30,11 @@ parameters: - name: esrpClientId type: string + # Glob pattern to match NuGet packages for scanning and signing. + - name: pattern + type: string + default: '*.*nupkg' + steps: # See: https://aka.ms/esrp.scantask - task: EsrpMalwareScanning@6 @@ -41,7 +46,7 @@ steps: ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' EsrpClientId: '${{ parameters.esrpClientId }}' FolderPath: '$(PACK_OUTPUT)' - Pattern: '*.*nupkg' + Pattern: '${{ parameters.pattern }}' UseMSIAuthentication: true VerboseLogin: 1 @@ -56,7 +61,7 @@ steps: AuthAKVName: '${{ parameters.authAkvName }}' AuthSignCertName: '${{ parameters.authSignCertName }}' FolderPath: '$(PACK_OUTPUT)' - Pattern: '*.*nupkg' + Pattern: '${{ parameters.pattern }}' signConfigType: 'inlineSignParams' UseMSIAuthentication: true inlineOperation: | diff --git a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml index 59322d67aa..83053a8d29 100644 --- a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml +++ b/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml @@ -17,6 +17,10 @@ parameters: type: string default: 'Microsoft.Data.SqlClient*.dll' + - name: nupkgPattern + type: string + default: '*.*nupkg' + - name: artifactDirectory type: string default: $(PACK_OUTPUT) @@ -125,7 +129,7 @@ steps: EsrpClientId: '${{parameters.EsrpClientId }}' UseMSIAuthentication: true FolderPath: '${{parameters.artifactDirectory }}' - Pattern: '*.*nupkg' + Pattern: '${{ parameters.nupkgPattern }}' CleanupTempStorage: 1 VerboseLogin: 1 @@ -133,7 +137,6 @@ steps: - task: EsrpCodeSigning@6 displayName: 'ESRP CodeSigning Nuget Package' inputs: - inputs: ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' @@ -142,7 +145,7 @@ steps: AuthAKVName: '${{parameters.AuthAKVName }}' AuthSignCertName: '${{parameters.AuthSignCertName }}' FolderPath: '${{parameters.artifactDirectory }}' - Pattern: '*.*nupkg' + Pattern: '${{ parameters.nupkgPattern }}' signConfigType: inlineSignParams inlineOperation: | [ From 40db478f1bced18c184ad1b23acef6490fd63d6d Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:44:30 -0300 Subject: [PATCH 05/22] Migrate to slnx solution file format (#4070) --- .devcontainer/devcontainer.json | 2 +- .github/workflows/codeql.yml | 2 +- src/Microsoft.Data.SqlClient.sln | 946 ------------------------------ src/Microsoft.Data.SqlClient.slnx | 335 +++++++++++ 4 files changed, 337 insertions(+), 948 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient.sln create mode 100644 src/Microsoft.Data.SqlClient.slnx diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c424e37834..db5f1c5da5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -16,7 +16,7 @@ "ms-mssql.mssql" ], "settings": { - "dotnet.defaultSolution": "src/Microsoft.Data.SqlClient.sln" + "dotnet.defaultSolution": "src/Microsoft.Data.SqlClient.slnx" } } }, diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 64584746df..464c0401f0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -94,7 +94,7 @@ jobs: - name: Run manual build steps if: matrix.build-mode == 'manual' shell: bash - run: dotnet build src/Microsoft.Data.SqlClient.sln + run: dotnet build src/Microsoft.Data.SqlClient.slnx - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln deleted file mode 100644 index b25ed21444..0000000000 --- a/src/Microsoft.Data.SqlClient.sln +++ /dev/null @@ -1,946 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31912.275 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\netfx\src\Microsoft.Data.SqlClient.csproj", "{407890AC-9876-4FEF-A6F1-F36A876BAADE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0CC4817A-12F3-4357-912C-09315FAAD008}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TDS.EndPoint", "Microsoft.Data.SqlClient\tests\tools\TDS\TDS.EndPoint\TDS.EndPoint.csproj", "{1FF891B4-D3DE-4CCE-887C-CB48F5351A45}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TDS.Servers", "Microsoft.Data.SqlClient\tests\tools\TDS\TDS.Servers\TDS.Servers.csproj", "{978063D3-FBB5-4E10-8C45-67C90BE1B931}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TDS", "Microsoft.Data.SqlClient\tests\tools\TDS\TDS\TDS.csproj", "{8DC9D1A0-351B-47BC-A90F-B9DA542550E9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.FunctionalTests", "Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.FunctionalTests.csproj", "{D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\netcore\src\Microsoft.Data.SqlClient.csproj", "{37431336-5307-4184-9356-C4B7E47DC714}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "netcore", "netcore", "{28E5EFE6-C9DD-4FF9-9FEC-532F72DFFA6E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "netfx", "netfx", "{3FDD425C-FE01-4B56-863E-1FCDD0677CF5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Address", "Microsoft.Data.SqlClient\tests\ManualTests\SQL\UdtTest\UDTs\Address\Address.csproj", "{D1392B54-998A-4F27-BC17-4CE149117BCC}" - ProjectSection(ProjectDependencies) = postProject - {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} - {407890AC-9876-4FEF-A6F1-F36A876BAADE} = {407890AC-9876-4FEF-A6F1-F36A876BAADE} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.ManualTests", "Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTests.csproj", "{45DB5F86-7AE3-45C6-870D-F9357B66BDB5}" - ProjectSection(ProjectDependencies) = postProject - {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Circle", "Microsoft.Data.SqlClient\tests\ManualTests\SQL\UdtTest\UDTs\Circle\Circle.csproj", "{6C88F00F-9597-43AD-9E5F-9B344DA3B16F}" - ProjectSection(ProjectDependencies) = postProject - {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shapes", "Microsoft.Data.SqlClient\tests\ManualTests\SQL\UdtTest\UDTs\Shapes\Shapes.csproj", "{B73A7063-37C3-415D-AD53-BB3DA20ABD6E}" - ProjectSection(ProjectDependencies) = postProject - {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utf8String", "Microsoft.Data.SqlClient\tests\ManualTests\SQL\UdtTest\UDTs\Utf8String\Utf8String.csproj", "{E0A6BB21-574B-43D9-890D-6E1144F2EE9E}" - ProjectSection(ProjectDependencies) = postProject - {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{A2E7E470-5EFF-4828-B55E-FCBA3650F51C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\netcore\ref\Microsoft.Data.SqlClient.csproj", "{1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{771F3F1E-7A68-4A9D-ADA8-A24F1D5BE71D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\netfx\ref\Microsoft.Data.SqlClient.csproj", "{412BCCC8-19F6-489A-B594-E9A506816155}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider", "Microsoft.Data.SqlClient\add-ons\AzureKeyVaultProvider\Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj", "{9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}" - ProjectSection(ProjectDependencies) = postProject - {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B} = {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B} - {412BCCC8-19F6-489A-B594-E9A506816155} = {412BCCC8-19F6-489A-B594-E9A506816155} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{ED952CF7-84DF-437A-B066-F516E9BE1C2C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "snippets", "snippets", "{71F356DC-DFA3-4163-8BFE-D268722CE189}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data", "Microsoft.Data", "{908C7DD3-C999-40A6-9433-9F5ACA7C36F5}" - ProjectSection(SolutionItems) = preProject - ..\doc\snippets\Microsoft.Data\OperationAbortedException.xml = ..\doc\snippets\Microsoft.Data\OperationAbortedException.xml - ..\doc\snippets\Microsoft.Data\SqlDbTypeExtensions.xml = ..\doc\snippets\Microsoft.Data\SqlDbTypeExtensions.xml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.Sql", "Microsoft.Data.Sql", "{0CE216CE-8072-4985-B248-61F0D0BE9C2E}" - ProjectSection(SolutionItems) = preProject - ..\doc\snippets\Microsoft.Data.Sql\SqlDataSourceEnumerator.xml = ..\doc\snippets\Microsoft.Data.Sql\SqlDataSourceEnumerator.xml - ..\doc\snippets\Microsoft.Data.Sql\SqlNotificationRequest.xml = ..\doc\snippets\Microsoft.Data.Sql\SqlNotificationRequest.xml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient", "{C05F4FFE-6A14-4409-AA0A-10630BE4F1EE}" - ProjectSection(SolutionItems) = preProject - ..\doc\snippets\Microsoft.Data.SqlClient\ActiveDirectoryAuthenticationProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\ActiveDirectoryAuthenticationProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\ApplicationIntent.xml = ..\doc\snippets\Microsoft.Data.SqlClient\ApplicationIntent.xml - ..\doc\snippets\Microsoft.Data.SqlClient\OnChangeEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\OnChangeEventHandler.xml - ..\doc\snippets\Microsoft.Data.SqlClient\PoolBlockingPeriod.xml = ..\doc\snippets\Microsoft.Data.SqlClient\PoolBlockingPeriod.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SortOrder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SortOrder.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationInitializer.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationInitializer.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationMethod.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationMethod.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationParameters.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationParameters.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationToken.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlAuthenticationToken.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBatch.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBatch.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBatchCommand.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBatchCommand.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBatchCommandCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBatchCommandCollection.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopy.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopy.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMapping.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMapping.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMappingCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnMappingCollection.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnOrderHint.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnOrderHint.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnOrderHintCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyColumnOrderHintCollection.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyOptions.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlBulkCopyOptions.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientDiagnostic.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientDiagnostic.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientFactory.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientFactory.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientLogger.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientLogger.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientMetaDataCollectionNames.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientMetaDataCollectionNames.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermission.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermission.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermissionAttribute.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlClientPermissionAttribute.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCertificateStoreProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCertificateStoreProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCngProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCngProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCspProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCspProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionEnclaveProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionEnclaveProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionKeyStoreProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionKeyStoreProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommand.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommand.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandBuilder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandBuilder.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandColumnEncryptionSetting.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCommandColumnEncryptionSetting.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConfigurableRetryFactory.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConfigurableRetryFactory.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnection.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionIPAddressPreference.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionIPAddressPreference.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionOverrides.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionOverrides.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataReader.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataReader.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlDependency.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDependency.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveAttestationParameters.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveAttestationParameters.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveSession.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlEnclaveSession.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlError.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlError.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlErrorCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlErrorCollection.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlException.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlException.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventArgs.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlInfoMessageEventHandler.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationEventArgs.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationInfo.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationInfo.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationSource.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationSource.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationType.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlNotificationType.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameter.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameterCollection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlParameterCollection.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryingEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryingEventArgs.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryIntervalBaseEnumerator.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryIntervalBaseEnumerator.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryLogicBase.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryLogicBase.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryLogicBaseProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryLogicBaseProvider.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryLogicOption.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRetryLogicOption.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventArgs.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowsCopiedEventHandler.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventArgs.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatedEventHandler.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventArgs.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlRowUpdatingEventHandler.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlTransaction.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SspiAuthenticationParameters.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SspiAuthenticationParameters.xml - ..\doc\snippets\Microsoft.Data.SqlClient\SspiContextProvider.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SspiContextProvider.xml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.DataClassification", "Microsoft.Data.SqlClient.DataClassification", "{5D1F0032-7B0D-4FB6-A969-FCFB25C9EA1D}" - ProjectSection(SolutionItems) = preProject - ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\ColumnSensitivity.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\ColumnSensitivity.xml - ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\InformationType.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\InformationType.xml - ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\Label.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\Label.xml - ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityClassification.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityClassification.xml - ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityProperty.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityProperty.xml - ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityRank.xml = ..\doc\snippets\Microsoft.Data.SqlClient.DataClassification\SensitivityRank.xml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.Server", "Microsoft.Data.SqlClient.Server", "{650EB7FA-EB0D-4F8E-AB2C-161C3AD8E363}" - ProjectSection(SolutionItems) = preProject - ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlDataRecord.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlDataRecord.xml - ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlMetaData.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\SqlMetaData.xml - ..\doc\snippets\Microsoft.Data.SqlClient.Server\TriggerAction.xml = ..\doc\snippets\Microsoft.Data.SqlClient.Server\TriggerAction.xml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlTypes", "Microsoft.Data.SqlTypes", "{5A7600BD-AED8-44AB-8F2A-7CB33A8D9C02}" - ProjectSection(SolutionItems) = preProject - ..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml - ..\doc\snippets\Microsoft.Data.SqlTypes\SqlJson.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlJson.xml - ..\doc\snippets\Microsoft.Data.SqlTypes\SqlVector.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlVector.xml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.TestUtilities", "Microsoft.Data.SqlClient\tests\tools\Microsoft.Data.SqlClient.TestUtilities\Microsoft.Data.SqlClient.TestUtilities.csproj", "{89D6D382-9B36-43C9-A912-03802FDA8E36}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.ExtUtilities", "Microsoft.Data.SqlClient\tests\tools\Microsoft.Data.SqlClient.ExtUtilities\Microsoft.Data.SqlClient.ExtUtilities.csproj", "{E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomRetryLogicProvider", "Microsoft.Data.SqlClient\tests\CustomConfigurableRetryLogic\CustomRetryLogicProvider.csproj", "{B499E477-C9B1-4087-A5CF-5C762D90E433}" - ProjectSection(ProjectDependencies) = postProject - {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.PerformanceTests", "Microsoft.Data.SqlClient\tests\PerformanceTests\Microsoft.Data.SqlClient.PerformanceTests.csproj", "{599A336B-2A5F-473D-8442-1223ED37C93E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4F3CD363-B1E6-4D6D-9466-97D78A56BE45}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlServer.Server", "Microsoft.SqlServer.Server\Microsoft.SqlServer.Server.csproj", "{A314812A-7820-4565-A2A8-ABBE391C11E4}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.SqlServer.Server", "Microsoft.SqlServer.Server", "{869A9BCC-D303-4365-9BF7-958CD6387916}" - ProjectSection(SolutionItems) = preProject - ..\doc\snippets\Microsoft.SqlServer.Server\DataAccessKind.xml = ..\doc\snippets\Microsoft.SqlServer.Server\DataAccessKind.xml - ..\doc\snippets\Microsoft.SqlServer.Server\Format.xml = ..\doc\snippets\Microsoft.SqlServer.Server\Format.xml - ..\doc\snippets\Microsoft.SqlServer.Server\IBinarySerialize.xml = ..\doc\snippets\Microsoft.SqlServer.Server\IBinarySerialize.xml - ..\doc\snippets\Microsoft.SqlServer.Server\InvalidUdtException.xml = ..\doc\snippets\Microsoft.SqlServer.Server\InvalidUdtException.xml - ..\doc\snippets\Microsoft.SqlServer.Server\SqlFacetAttribute.xml = ..\doc\snippets\Microsoft.SqlServer.Server\SqlFacetAttribute.xml - ..\doc\snippets\Microsoft.SqlServer.Server\SqlFunctionAttribute.xml = ..\doc\snippets\Microsoft.SqlServer.Server\SqlFunctionAttribute.xml - ..\doc\snippets\Microsoft.SqlServer.Server\SqlMethodAttribute.xml = ..\doc\snippets\Microsoft.SqlServer.Server\SqlMethodAttribute.xml - ..\doc\snippets\Microsoft.SqlServer.Server\SqlUserDefinedAggregateAttribute.xml = ..\doc\snippets\Microsoft.SqlServer.Server\SqlUserDefinedAggregateAttribute.xml - ..\doc\snippets\Microsoft.SqlServer.Server\SqlUserDefinedTypeAttribute.xml = ..\doc\snippets\Microsoft.SqlServer.Server\SqlUserDefinedTypeAttribute.xml - ..\doc\snippets\Microsoft.SqlServer.Server\SystemDataAccessKind.xml = ..\doc\snippets\Microsoft.SqlServer.Server\SystemDataAccessKind.xml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestClr", "TestClr", "{CDE508A5-F5D0-4A59-A4EF-978833830727}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "eng", "eng", "{4600328C-C134-499F-AAE2-964E8AD5472C}" - ProjectSection(SolutionItems) = preProject - ..\build.proj = ..\build.proj - ..\build2.proj = ..\build2.proj - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{4CAE9195-4F1A-4D48-854C-1C9FBC512C66}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\dotnet-sqlclient-ci-core.yml = ..\eng\pipelines\dotnet-sqlclient-ci-core.yml - ..\eng\pipelines\dotnet-sqlclient-ci-package-reference-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-ci-package-reference-pipeline.yml - ..\eng\pipelines\dotnet-sqlclient-ci-project-reference-pipeline.yml = ..\eng\pipelines\dotnet-sqlclient-ci-project-reference-pipeline.yml - ..\eng\pipelines\sqlclient-pr-package-ref-pipeline.yml = ..\eng\pipelines\sqlclient-pr-package-ref-pipeline.yml - ..\eng\pipelines\sqlclient-pr-project-ref-pipeline.yml = ..\eng\pipelines\sqlclient-pr-project-ref-pipeline.yml - ..\eng\pipelines\stress-tests-pipeline.yml = ..\eng\pipelines\stress-tests-pipeline.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{FD4D7A96-79B1-4F89-B64D-29FACCC9232F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{E76A4ED5-9137-4E4B-AE91-7AEDB2683823}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{3850810F-535E-443A-A9B4-7EE85B327E0A}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\common\templates\jobs\ci-build-nugets-job.yml = ..\eng\pipelines\common\templates\jobs\ci-build-nugets-job.yml - ..\eng\pipelines\common\templates\jobs\ci-code-coverage-job.yml = ..\eng\pipelines\common\templates\jobs\ci-code-coverage-job.yml - ..\eng\pipelines\common\templates\jobs\ci-run-tests-job.yml = ..\eng\pipelines\common\templates\jobs\ci-run-tests-job.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{01FA4774-E594-4A2D-B839-EA7A56D09A8B}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\common\templates\stages\ci-run-tests-stage.yml = ..\eng\pipelines\common\templates\stages\ci-run-tests-stage.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{EABE3A3E-D505-418A-B1B8-1B0AC4872F3D}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\common\templates\steps\build-and-run-tests-netcore-step.yml = ..\eng\pipelines\common\templates\steps\build-and-run-tests-netcore-step.yml - ..\eng\pipelines\common\templates\steps\build-and-run-tests-netfx-step.yml = ..\eng\pipelines\common\templates\steps\build-and-run-tests-netfx-step.yml - ..\eng\pipelines\common\templates\steps\ci-project-build-step.yml = ..\eng\pipelines\common\templates\steps\ci-project-build-step.yml - ..\eng\pipelines\common\templates\steps\configure-sql-server-linux-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-linux-step.yml - ..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-macos-step.yml - ..\eng\pipelines\common\templates\steps\configure-sql-server-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-step.yml - ..\eng\pipelines\common\templates\steps\configure-sql-server-win-step.yml = ..\eng\pipelines\common\templates\steps\configure-sql-server-win-step.yml - ..\eng\pipelines\common\templates\steps\generate-nuget-package-step.yml = ..\eng\pipelines\common\templates\steps\generate-nuget-package-step.yml - ..\eng\pipelines\common\templates\steps\override-sni-version.yml = ..\eng\pipelines\common\templates\steps\override-sni-version.yml - ..\eng\pipelines\common\templates\steps\pre-build-step.yml = ..\eng\pipelines\common\templates\steps\pre-build-step.yml - ..\eng\pipelines\common\templates\steps\prepare-test-db-step.yml = ..\eng\pipelines\common\templates\steps\prepare-test-db-step.yml - ..\eng\pipelines\common\templates\steps\publish-test-results-step.yml = ..\eng\pipelines\common\templates\steps\publish-test-results-step.yml - ..\eng\pipelines\common\templates\steps\run-all-tests-step.yml = ..\eng\pipelines\common\templates\steps\run-all-tests-step.yml - ..\eng\pipelines\common\templates\steps\update-config-file-step.yml = ..\eng\pipelines\common\templates\steps\update-config-file-step.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "libraries", "libraries", "{75BAE755-3A1F-41F2-9176-9F8FF9FEE2DD}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\libraries\ci-build-variables.yml = ..\eng\pipelines\libraries\ci-build-variables.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{09352F1D-878F-4F55-8AA2-6E47F1AD37D5}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\jobs\pack-abstractions-package-ci-job.yml = ..\eng\pipelines\jobs\pack-abstractions-package-ci-job.yml - ..\eng\pipelines\jobs\pack-azure-package-ci-job.yml = ..\eng\pipelines\jobs\pack-azure-package-ci-job.yml - ..\eng\pipelines\jobs\pack-logging-package-ci-job.yml = ..\eng\pipelines\jobs\pack-logging-package-ci-job.yml - ..\eng\pipelines\jobs\stress-tests-ci-job.yml = ..\eng\pipelines\jobs\stress-tests-ci-job.yml - ..\eng\pipelines\jobs\test-abstractions-package-ci-job.yml = ..\eng\pipelines\jobs\test-abstractions-package-ci-job.yml - ..\eng\pipelines\jobs\test-azure-package-ci-job.yml = ..\eng\pipelines\jobs\test-azure-package-ci-job.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{AD738BD4-6A02-4B88-8F93-FBBBA49A74C8}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\steps\install-dotnet.yml = ..\eng\pipelines\steps\install-dotnet.yml - ..\eng\pipelines\steps\install-dotnet-arm64.ps1 = ..\eng\pipelines\steps\install-dotnet-arm64.ps1 - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.UnitTests", "Microsoft.Data.SqlClient\tests\UnitTests\Microsoft.Data.SqlClient.UnitTests.csproj", "{4461063D-2F2B-274C-7E6F-F235119D258E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stress", "Stress", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IMonitorLoader", "Microsoft.Data.SqlClient\tests\StressTests\IMonitorLoader\IMonitorLoader.csproj", "{1A29B520-D16A-35F2-5CAC-64573C86E63D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlClient.Stress.Common", "Microsoft.Data.SqlClient\tests\StressTests\SqlClient.Stress.Common\SqlClient.Stress.Common.csproj", "{2AA12D54-540B-E515-CB82-80D691C9DCF1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlClient.Stress.Framework", "Microsoft.Data.SqlClient\tests\StressTests\SqlClient.Stress.Framework\SqlClient.Stress.Framework.csproj", "{92D9C6D6-6925-1AD1-69FA-485F83943BD2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlClient.Stress.Runner", "Microsoft.Data.SqlClient\tests\StressTests\SqlClient.Stress.Runner\SqlClient.Stress.Runner.csproj", "{4A9C11F4-9577-ABEC-C070-83A194746D9B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlClient.Stress.Tests", "Microsoft.Data.SqlClient\tests\StressTests\SqlClient.Stress.Tests\SqlClient.Stress.Tests.csproj", "{FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.Samples", "..\doc\samples\Microsoft.Data.SqlClient.Samples.csproj", "{C09B9D2F-E463-BEBD-34E4-E8F2C201A277}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.TestCommon", "Microsoft.Data.SqlClient\tests\Common\Microsoft.Data.SqlClient.TestCommon.csproj", "{3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.Extensions", "Microsoft.Data.SqlClient.Extensions", "{19F1F1E5-3013-7660-661A-2A15F7D606C1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Abstractions", "Abstractions", "{556B486E-F9B0-7EA9-6A25-DA560C312761}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{210228A5-979A-DE06-EE1F-B35C65E1583C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abstractions", "Microsoft.Data.SqlClient.Extensions\Abstractions\src\Abstractions.csproj", "{089582DC-FC8E-4DDE-99AC-E31BF95175B0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{59667E4C-0BD2-9F48-FB50-9E55DD8B1011}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abstractions.Test", "Microsoft.Data.SqlClient.Extensions\Abstractions\test\Abstractions.Test.csproj", "{21F71821-AC58-43A1-A0B1-A7DB5FA892D3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure", "Azure", "{A20114E1-82D8-903A-C389-726EB4FD943F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0D2F834B-6D91-18D0-3F09-672D448751BD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure", "Microsoft.Data.SqlClient.Extensions\Azure\src\Azure.csproj", "{DCD79241-612B-4081-A8CC-BD7A4ABC1662}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5AF52CDD-DF78-3712-7516-5B49F94F9491}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Test", "Microsoft.Data.SqlClient.Extensions\Azure\test\Azure.Test.csproj", "{4B953573-C3CD-4845-896B-EA0A0B7A7B27}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Logging", "Logging", "{B3A1F1C2-7D4E-4A5B-9C6D-8E2F0A1B3C4D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D4E5F6A7-1B2C-3D4E-5F6A-7B8C9D0E1F2A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "Microsoft.Data.SqlClient.Internal\Logging\src\Logging.csproj", "{A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.Internal", "Microsoft.Data.SqlClient.Internal", "{C1D2E3F4-A5B6-7C8D-9E0F-1A2B3C4D5E6F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient", "{7289C27E-D7DF-2C71-84B4-151F3A162493}" - ProjectSection(SolutionItems) = preProject - Microsoft.Data.SqlClient\MdsVersions.props = Microsoft.Data.SqlClient\MdsVersions.props - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7E0602AC-7F0A-362A-D734-0FDDFCC600B5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2B71F605-037E-5629-6E23-0FA3C297446D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{E9D12AEC-2F11-4871-89BB-343BF1CAEA4C}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\stages\build-abstractions-package-ci-stage.yml = ..\eng\pipelines\stages\build-abstractions-package-ci-stage.yml - ..\eng\pipelines\stages\build-azure-package-ci-stage.yml = ..\eng\pipelines\stages\build-azure-package-ci-stage.yml - ..\eng\pipelines\stages\build-logging-package-ci-stage.yml = ..\eng\pipelines\stages\build-logging-package-ci-stage.yml - ..\eng\pipelines\stages\build-sqlclient-package-ci-stage.yml = ..\eng\pipelines\stages\build-sqlclient-package-ci-stage.yml - ..\eng\pipelines\stages\generate-secrets-ci-stage.yml = ..\eng\pipelines\stages\generate-secrets-ci-stage.yml - ..\eng\pipelines\stages\stress-tests-ci-stage.yml = ..\eng\pipelines\stages\stress-tests-ci-stage.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{020A7E7B-04C9-4326-985F-045B42CC2200}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\ref\Microsoft.Data.SqlClient.csproj", "{D433ED2D-5E47-4A4B-B94A-EC71482715C7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\src\Microsoft.Data.SqlClient.csproj", "{AA77C107-9A78-4A99-98BB-21FF7A1E0B01}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureAuthentication", "..\doc\apps\AzureAuthentication\AzureAuthentication.csproj", "{C3FE67C1-D288-45ED-A35C-08107396F8BB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "notsupported", "notsupported", "{351BE847-A0BF-450C-A5BC-8337AFA49EAA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\notsupported\Microsoft.Data.SqlClient.csproj", "{1DB299CE-95EA-4566-84DD-171768758291}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.GenAPI", "..\tools\GenAPI\Microsoft.DotNet.GenAPI\Microsoft.DotNet.GenAPI.csproj", "{583E2B51-A90B-4F34-AD8B-4061504E855E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Cci.Extensions", "..\tools\GenAPI\Microsoft.Cci.Extensions\Microsoft.Cci.Extensions.csproj", "{2F900B8A-EA19-4274-9AE9-3E6A59856FCC}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "onebranch", "onebranch", "{5EC4C165-25BB-4346-9DCC-5EFEEAC00C02}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\onebranch\sqlclient-non-official.yml = ..\eng\pipelines\onebranch\sqlclient-non-official.yml - ..\eng\pipelines\onebranch\sqlclient-official.yml = ..\eng\pipelines\onebranch\sqlclient-official.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{B3061B34-2A1C-49B9-A3A9-D4D37B4704B2}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\onebranch\jobs\build-signed-csproj-package-job.yml = ..\eng\pipelines\onebranch\jobs\build-signed-csproj-package-job.yml - ..\eng\pipelines\onebranch\jobs\build-signed-sqlclient-package-job.yml = ..\eng\pipelines\onebranch\jobs\build-signed-sqlclient-package-job.yml - ..\eng\pipelines\onebranch\jobs\publish-nuget-package-job.yml = ..\eng\pipelines\onebranch\jobs\publish-nuget-package-job.yml - ..\eng\pipelines\onebranch\jobs\validate-signed-package-job.yml = ..\eng\pipelines\onebranch\jobs\validate-signed-package-job.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stages", "stages", "{3654906C-6852-48AF-BC7E-B3A62F3E9DB9}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\onebranch\stages\build-stages.yml = ..\eng\pipelines\onebranch\stages\build-stages.yml - ..\eng\pipelines\onebranch\stages\release-stages.yml = ..\eng\pipelines\onebranch\stages\release-stages.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "steps", "steps", "{45B9E5C3-DED5-4DB6-B1E6-4EAC4E68BA83}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\onebranch\steps\build-all-configurations-signed-dlls-step.yml = ..\eng\pipelines\onebranch\steps\build-all-configurations-signed-dlls-step.yml - ..\eng\pipelines\onebranch\steps\code-analyze-step.yml = ..\eng\pipelines\onebranch\steps\code-analyze-step.yml - ..\eng\pipelines\onebranch\steps\compound-build-csproj-step.yml = ..\eng\pipelines\onebranch\steps\compound-build-csproj-step.yml - ..\eng\pipelines\onebranch\steps\compound-esrp-dll-signing-step.yml = ..\eng\pipelines\onebranch\steps\compound-esrp-dll-signing-step.yml - ..\eng\pipelines\onebranch\steps\compound-esrp-nuget-signing-step.yml = ..\eng\pipelines\onebranch\steps\compound-esrp-nuget-signing-step.yml - ..\eng\pipelines\onebranch\steps\compound-nuget-pack-step.yml = ..\eng\pipelines\onebranch\steps\compound-nuget-pack-step.yml - ..\eng\pipelines\onebranch\steps\compound-pack-csproj-step.yml = ..\eng\pipelines\onebranch\steps\compound-pack-csproj-step.yml - ..\eng\pipelines\onebranch\steps\compound-publish-symbols-step.yml = ..\eng\pipelines\onebranch\steps\compound-publish-symbols-step.yml - ..\eng\pipelines\onebranch\steps\esrp-code-signing-step.yml = ..\eng\pipelines\onebranch\steps\esrp-code-signing-step.yml - ..\eng\pipelines\onebranch\steps\publish-symbols-step.yml = ..\eng\pipelines\onebranch\steps\publish-symbols-step.yml - ..\eng\pipelines\onebranch\steps\script-output-environment-variables-step.yml = ..\eng\pipelines\onebranch\steps\script-output-environment-variables-step.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "variables", "variables", "{E849E6FB-1AB0-4922-A060-0062EA669FD0}" - ProjectSection(SolutionItems) = preProject - ..\eng\pipelines\onebranch\variables\common-variables.yml = ..\eng\pipelines\onebranch\variables\common-variables.yml - ..\eng\pipelines\onebranch\variables\onebranch-variables.yml = ..\eng\pipelines\onebranch\variables\onebranch-variables.yml - ..\eng\pipelines\onebranch\variables\sqlclient-validation-variables.yml = ..\eng\pipelines\onebranch\variables\sqlclient-validation-variables.yml - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Debug|x64.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Debug|x64.Build.0 = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Debug|x86.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Debug|x86.Build.0 = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Release|Any CPU.Build.0 = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Release|x64.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Release|x64.Build.0 = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Release|x86.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.Release|x86.Build.0 = Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Debug|x64.ActiveCfg = Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Debug|x64.Build.0 = Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Debug|x86.ActiveCfg = Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Debug|x86.Build.0 = Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Release|Any CPU.Build.0 = Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Release|x64.ActiveCfg = Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Release|x64.Build.0 = Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Release|x86.ActiveCfg = Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.Release|x86.Build.0 = Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Debug|Any CPU.Build.0 = Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Debug|x64.ActiveCfg = Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Debug|x64.Build.0 = Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Debug|x86.ActiveCfg = Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Debug|x86.Build.0 = Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Release|Any CPU.ActiveCfg = Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Release|Any CPU.Build.0 = Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Release|x64.ActiveCfg = Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Release|x64.Build.0 = Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Release|x86.ActiveCfg = Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.Release|x86.Build.0 = Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Debug|x64.ActiveCfg = Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Debug|x64.Build.0 = Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Debug|x86.ActiveCfg = Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Debug|x86.Build.0 = Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Release|Any CPU.Build.0 = Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Release|x64.ActiveCfg = Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Release|x64.Build.0 = Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Release|x86.ActiveCfg = Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.Release|x86.Build.0 = Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|x64.ActiveCfg = Debug|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|x64.Build.0 = Debug|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|x86.ActiveCfg = Debug|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|x86.Build.0 = Debug|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Release|Any CPU.Build.0 = Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Release|x64.ActiveCfg = Release|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Release|x64.Build.0 = Release|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Release|x86.ActiveCfg = Release|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Release|x86.Build.0 = Release|x86 - {37431336-5307-4184-9356-C4B7E47DC714}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Debug|x64.ActiveCfg = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Debug|x64.Build.0 = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Debug|x86.ActiveCfg = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Debug|x86.Build.0 = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Release|Any CPU.Build.0 = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Release|x64.ActiveCfg = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Release|x64.Build.0 = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Release|x86.ActiveCfg = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.Release|x86.Build.0 = Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|x64.ActiveCfg = Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|x64.Build.0 = Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|x86.ActiveCfg = Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Debug|x86.Build.0 = Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|Any CPU.Build.0 = Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|x64.ActiveCfg = Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|x64.Build.0 = Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|x86.ActiveCfg = Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.Release|x86.Build.0 = Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|x64.ActiveCfg = Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|x64.Build.0 = Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|x86.ActiveCfg = Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|x86.Build.0 = Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|Any CPU.Build.0 = Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|x64.ActiveCfg = Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|x64.Build.0 = Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|x86.ActiveCfg = Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|x86.Build.0 = Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|x64.ActiveCfg = Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|x64.Build.0 = Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|x86.ActiveCfg = Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Debug|x86.Build.0 = Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|Any CPU.Build.0 = Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|x64.ActiveCfg = Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|x64.Build.0 = Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|x86.ActiveCfg = Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.Release|x86.Build.0 = Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|x64.ActiveCfg = Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|x64.Build.0 = Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|x86.ActiveCfg = Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Debug|x86.Build.0 = Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|Any CPU.Build.0 = Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|x64.ActiveCfg = Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|x64.Build.0 = Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|x86.ActiveCfg = Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.Release|x86.Build.0 = Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|x64.ActiveCfg = Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|x64.Build.0 = Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|x86.ActiveCfg = Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Debug|x86.Build.0 = Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|Any CPU.Build.0 = Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|x64.ActiveCfg = Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|x64.Build.0 = Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|x86.ActiveCfg = Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.Release|x86.Build.0 = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Debug|x64.ActiveCfg = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Debug|x64.Build.0 = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Debug|x86.ActiveCfg = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Debug|x86.Build.0 = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Release|Any CPU.Build.0 = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Release|x64.ActiveCfg = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Release|x64.Build.0 = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Release|x86.ActiveCfg = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.Release|x86.Build.0 = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Debug|Any CPU.Build.0 = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Debug|x64.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Debug|x64.Build.0 = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Debug|x86.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Debug|x86.Build.0 = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Release|Any CPU.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Release|Any CPU.Build.0 = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Release|x64.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Release|x64.Build.0 = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Release|x86.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.Release|x86.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Debug|x64.ActiveCfg = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Debug|x64.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Debug|x86.ActiveCfg = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Debug|x86.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.ActiveCfg = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x86.ActiveCfg = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x86.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Debug|x64.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Debug|x64.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Debug|x86.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Debug|x86.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Release|Any CPU.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Release|x64.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Release|x64.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Release|x86.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.Release|x86.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Debug|x64.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Debug|x64.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Debug|x86.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Debug|x86.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Release|Any CPU.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Release|x64.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Release|x64.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Release|x86.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.Release|x86.Build.0 = Release|Any CPU - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Debug|x64.ActiveCfg = Debug|x64 - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Debug|x64.Build.0 = Debug|x64 - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Debug|x86.ActiveCfg = Debug|x86 - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Debug|x86.Build.0 = Debug|x86 - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Release|Any CPU.Build.0 = Release|Any CPU - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Release|x64.ActiveCfg = Release|x64 - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Release|x64.Build.0 = Release|x64 - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Release|x86.ActiveCfg = Release|x86 - {B499E477-C9B1-4087-A5CF-5C762D90E433}.Release|x86.Build.0 = Release|x86 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {599A336B-2A5F-473D-8442-1223ED37C93E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {599A336B-2A5F-473D-8442-1223ED37C93E}.Debug|x64.ActiveCfg = Debug|x64 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Debug|x64.Build.0 = Debug|x64 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Debug|x86.ActiveCfg = Debug|x86 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Debug|x86.Build.0 = Debug|x86 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|Any CPU.Build.0 = Release|Any CPU - {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x64.ActiveCfg = Release|x64 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x64.Build.0 = Release|x64 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.ActiveCfg = Release|x86 - {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.Build.0 = Release|x86 - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x64.ActiveCfg = Debug|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x64.Build.0 = Debug|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x86.ActiveCfg = Debug|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x86.Build.0 = Debug|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|Any CPU.Build.0 = Release|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x64.ActiveCfg = Release|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x64.Build.0 = Release|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x86.ActiveCfg = Release|Any CPU - {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x86.Build.0 = Release|Any CPU - {4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|x64.ActiveCfg = Debug|x64 - {4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|x64.Build.0 = Debug|x64 - {4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|x86.ActiveCfg = Debug|x86 - {4461063D-2F2B-274C-7E6F-F235119D258E}.Debug|x86.Build.0 = Debug|x86 - {4461063D-2F2B-274C-7E6F-F235119D258E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4461063D-2F2B-274C-7E6F-F235119D258E}.Release|Any CPU.Build.0 = Release|Any CPU - {4461063D-2F2B-274C-7E6F-F235119D258E}.Release|x64.ActiveCfg = Release|x64 - {4461063D-2F2B-274C-7E6F-F235119D258E}.Release|x64.Build.0 = Release|x64 - {4461063D-2F2B-274C-7E6F-F235119D258E}.Release|x86.ActiveCfg = Release|x86 - {4461063D-2F2B-274C-7E6F-F235119D258E}.Release|x86.Build.0 = Release|x86 - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Debug|x64.ActiveCfg = Debug|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Debug|x64.Build.0 = Debug|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Debug|x86.ActiveCfg = Debug|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Debug|x86.Build.0 = Debug|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Release|Any CPU.Build.0 = Release|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Release|x64.ActiveCfg = Release|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Release|x64.Build.0 = Release|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Release|x86.ActiveCfg = Release|Any CPU - {1A29B520-D16A-35F2-5CAC-64573C86E63D}.Release|x86.Build.0 = Release|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Debug|x64.ActiveCfg = Debug|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Debug|x64.Build.0 = Debug|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Debug|x86.ActiveCfg = Debug|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Debug|x86.Build.0 = Debug|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Release|Any CPU.Build.0 = Release|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Release|x64.ActiveCfg = Release|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Release|x64.Build.0 = Release|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Release|x86.ActiveCfg = Release|Any CPU - {2AA12D54-540B-E515-CB82-80D691C9DCF1}.Release|x86.Build.0 = Release|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Debug|x64.ActiveCfg = Debug|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Debug|x64.Build.0 = Debug|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Debug|x86.ActiveCfg = Debug|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Debug|x86.Build.0 = Debug|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Release|Any CPU.Build.0 = Release|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Release|x64.ActiveCfg = Release|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Release|x64.Build.0 = Release|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Release|x86.ActiveCfg = Release|Any CPU - {92D9C6D6-6925-1AD1-69FA-485F83943BD2}.Release|x86.Build.0 = Release|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Debug|x64.ActiveCfg = Debug|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Debug|x64.Build.0 = Debug|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Debug|x86.ActiveCfg = Debug|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Debug|x86.Build.0 = Debug|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Release|Any CPU.Build.0 = Release|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Release|x64.ActiveCfg = Release|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Release|x64.Build.0 = Release|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Release|x86.ActiveCfg = Release|Any CPU - {4A9C11F4-9577-ABEC-C070-83A194746D9B}.Release|x86.Build.0 = Release|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Debug|x64.ActiveCfg = Debug|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Debug|x64.Build.0 = Debug|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Debug|x86.ActiveCfg = Debug|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Debug|x86.Build.0 = Debug|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Release|Any CPU.Build.0 = Release|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Release|x64.ActiveCfg = Release|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Release|x64.Build.0 = Release|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Release|x86.ActiveCfg = Release|Any CPU - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}.Release|x86.Build.0 = Release|Any CPU - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Debug|x64.ActiveCfg = Debug|x64 - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Debug|x64.Build.0 = Debug|x64 - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Debug|x86.ActiveCfg = Debug|x86 - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Debug|x86.Build.0 = Debug|x86 - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|Any CPU.Build.0 = Release|Any CPU - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|x64.ActiveCfg = Release|x64 - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|x64.Build.0 = Release|x64 - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|x86.ActiveCfg = Release|x86 - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|x86.Build.0 = Release|x86 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Debug|x64.ActiveCfg = Debug|x64 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Debug|x64.Build.0 = Debug|x64 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Debug|x86.ActiveCfg = Debug|x86 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Debug|x86.Build.0 = Debug|x86 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Release|Any CPU.Build.0 = Release|Any CPU - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Release|x64.ActiveCfg = Release|x64 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Release|x64.Build.0 = Release|x64 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Release|x86.ActiveCfg = Release|x86 - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B}.Release|x86.Build.0 = Release|x86 - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Debug|x64.ActiveCfg = Debug|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Debug|x64.Build.0 = Debug|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Debug|x86.ActiveCfg = Debug|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Debug|x86.Build.0 = Debug|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Release|Any CPU.Build.0 = Release|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Release|x64.ActiveCfg = Release|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Release|x64.Build.0 = Release|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Release|x86.ActiveCfg = Release|Any CPU - {089582DC-FC8E-4DDE-99AC-E31BF95175B0}.Release|x86.Build.0 = Release|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Debug|x64.ActiveCfg = Debug|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Debug|x64.Build.0 = Debug|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Debug|x86.ActiveCfg = Debug|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Debug|x86.Build.0 = Debug|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Release|Any CPU.Build.0 = Release|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Release|x64.ActiveCfg = Release|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Release|x64.Build.0 = Release|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Release|x86.ActiveCfg = Release|Any CPU - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3}.Release|x86.Build.0 = Release|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Debug|x64.ActiveCfg = Debug|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Debug|x64.Build.0 = Debug|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Debug|x86.ActiveCfg = Debug|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Debug|x86.Build.0 = Debug|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Release|Any CPU.Build.0 = Release|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Release|x64.ActiveCfg = Release|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Release|x64.Build.0 = Release|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Release|x86.ActiveCfg = Release|Any CPU - {DCD79241-612B-4081-A8CC-BD7A4ABC1662}.Release|x86.Build.0 = Release|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Debug|x64.ActiveCfg = Debug|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Debug|x64.Build.0 = Debug|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Debug|x86.ActiveCfg = Debug|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Debug|x86.Build.0 = Debug|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|Any CPU.Build.0 = Release|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|x64.ActiveCfg = Release|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|x64.Build.0 = Release|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|x86.ActiveCfg = Release|Any CPU - {4B953573-C3CD-4845-896B-EA0A0B7A7B27}.Release|x86.Build.0 = Release|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Debug|x64.ActiveCfg = Debug|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Debug|x64.Build.0 = Debug|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Debug|x86.ActiveCfg = Debug|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Debug|x86.Build.0 = Debug|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Release|Any CPU.Build.0 = Release|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Release|x64.ActiveCfg = Release|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Release|x64.Build.0 = Release|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Release|x86.ActiveCfg = Release|Any CPU - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D}.Release|x86.Build.0 = Release|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x64.ActiveCfg = Debug|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x64.Build.0 = Debug|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x86.ActiveCfg = Debug|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Debug|x86.Build.0 = Debug|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|Any CPU.Build.0 = Release|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x64.ActiveCfg = Release|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x64.Build.0 = Release|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x86.ActiveCfg = Release|Any CPU - {D433ED2D-5E47-4A4B-B94A-EC71482715C7}.Release|x86.Build.0 = Release|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x64.ActiveCfg = Debug|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x64.Build.0 = Debug|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x86.ActiveCfg = Debug|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Debug|x86.Build.0 = Debug|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|Any CPU.Build.0 = Release|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x64.ActiveCfg = Release|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x64.Build.0 = Release|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x86.ActiveCfg = Release|Any CPU - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01}.Release|x86.Build.0 = Release|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Debug|x64.ActiveCfg = Debug|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Debug|x64.Build.0 = Debug|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Debug|x86.ActiveCfg = Debug|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Debug|x86.Build.0 = Debug|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Release|Any CPU.Build.0 = Release|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Release|x64.ActiveCfg = Release|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Release|x64.Build.0 = Release|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Release|x86.ActiveCfg = Release|Any CPU - {C3FE67C1-D288-45ED-A35C-08107396F8BB}.Release|x86.Build.0 = Release|Any CPU - {1DB299CE-95EA-4566-84DD-171768758291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1DB299CE-95EA-4566-84DD-171768758291}.Debug|x64.ActiveCfg = Debug|Any CPU - {1DB299CE-95EA-4566-84DD-171768758291}.Debug|x86.ActiveCfg = Debug|Any CPU - {1DB299CE-95EA-4566-84DD-171768758291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1DB299CE-95EA-4566-84DD-171768758291}.Release|x64.ActiveCfg = Release|Any CPU - {1DB299CE-95EA-4566-84DD-171768758291}.Release|x86.ActiveCfg = Release|Any CPU - {583E2B51-A90B-4F34-AD8B-4061504E855E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {583E2B51-A90B-4F34-AD8B-4061504E855E}.Debug|x64.ActiveCfg = Debug|Any CPU - {583E2B51-A90B-4F34-AD8B-4061504E855E}.Debug|x86.ActiveCfg = Debug|Any CPU - {583E2B51-A90B-4F34-AD8B-4061504E855E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {583E2B51-A90B-4F34-AD8B-4061504E855E}.Release|x64.ActiveCfg = Release|Any CPU - {583E2B51-A90B-4F34-AD8B-4061504E855E}.Release|x86.ActiveCfg = Release|Any CPU - {2F900B8A-EA19-4274-9AE9-3E6A59856FCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F900B8A-EA19-4274-9AE9-3E6A59856FCC}.Debug|x64.ActiveCfg = Debug|Any CPU - {2F900B8A-EA19-4274-9AE9-3E6A59856FCC}.Debug|x86.ActiveCfg = Debug|Any CPU - {2F900B8A-EA19-4274-9AE9-3E6A59856FCC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F900B8A-EA19-4274-9AE9-3E6A59856FCC}.Release|x64.ActiveCfg = Release|Any CPU - {2F900B8A-EA19-4274-9AE9-3E6A59856FCC}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {407890AC-9876-4FEF-A6F1-F36A876BAADE} = {3FDD425C-FE01-4B56-863E-1FCDD0677CF5} - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {978063D3-FBB5-4E10-8C45-67C90BE1B931} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {37431336-5307-4184-9356-C4B7E47DC714} = {28E5EFE6-C9DD-4FF9-9FEC-532F72DFFA6E} - {28E5EFE6-C9DD-4FF9-9FEC-532F72DFFA6E} = {4F3CD363-B1E6-4D6D-9466-97D78A56BE45} - {3FDD425C-FE01-4B56-863E-1FCDD0677CF5} = {4F3CD363-B1E6-4D6D-9466-97D78A56BE45} - {D1392B54-998A-4F27-BC17-4CE149117BCC} = {CDE508A5-F5D0-4A59-A4EF-978833830727} - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F} = {CDE508A5-F5D0-4A59-A4EF-978833830727} - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E} = {CDE508A5-F5D0-4A59-A4EF-978833830727} - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E} = {CDE508A5-F5D0-4A59-A4EF-978833830727} - {A2E7E470-5EFF-4828-B55E-FCBA3650F51C} = {28E5EFE6-C9DD-4FF9-9FEC-532F72DFFA6E} - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B} = {A2E7E470-5EFF-4828-B55E-FCBA3650F51C} - {771F3F1E-7A68-4A9D-ADA8-A24F1D5BE71D} = {3FDD425C-FE01-4B56-863E-1FCDD0677CF5} - {412BCCC8-19F6-489A-B594-E9A506816155} = {771F3F1E-7A68-4A9D-ADA8-A24F1D5BE71D} - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7} = {4F3CD363-B1E6-4D6D-9466-97D78A56BE45} - {71F356DC-DFA3-4163-8BFE-D268722CE189} = {ED952CF7-84DF-437A-B066-F516E9BE1C2C} - {908C7DD3-C999-40A6-9433-9F5ACA7C36F5} = {71F356DC-DFA3-4163-8BFE-D268722CE189} - {0CE216CE-8072-4985-B248-61F0D0BE9C2E} = {71F356DC-DFA3-4163-8BFE-D268722CE189} - {C05F4FFE-6A14-4409-AA0A-10630BE4F1EE} = {71F356DC-DFA3-4163-8BFE-D268722CE189} - {5D1F0032-7B0D-4FB6-A969-FCFB25C9EA1D} = {71F356DC-DFA3-4163-8BFE-D268722CE189} - {650EB7FA-EB0D-4F8E-AB2C-161C3AD8E363} = {71F356DC-DFA3-4163-8BFE-D268722CE189} - {5A7600BD-AED8-44AB-8F2A-7CB33A8D9C02} = {71F356DC-DFA3-4163-8BFE-D268722CE189} - {89D6D382-9B36-43C9-A912-03802FDA8E36} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {B499E477-C9B1-4087-A5CF-5C762D90E433} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {599A336B-2A5F-473D-8442-1223ED37C93E} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {A314812A-7820-4565-A2A8-ABBE391C11E4} = {4F3CD363-B1E6-4D6D-9466-97D78A56BE45} - {869A9BCC-D303-4365-9BF7-958CD6387916} = {71F356DC-DFA3-4163-8BFE-D268722CE189} - {CDE508A5-F5D0-4A59-A4EF-978833830727} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} = {4600328C-C134-499F-AAE2-964E8AD5472C} - {FD4D7A96-79B1-4F89-B64D-29FACCC9232F} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} - {E76A4ED5-9137-4E4B-AE91-7AEDB2683823} = {FD4D7A96-79B1-4F89-B64D-29FACCC9232F} - {3850810F-535E-443A-A9B4-7EE85B327E0A} = {E76A4ED5-9137-4E4B-AE91-7AEDB2683823} - {01FA4774-E594-4A2D-B839-EA7A56D09A8B} = {E76A4ED5-9137-4E4B-AE91-7AEDB2683823} - {EABE3A3E-D505-418A-B1B8-1B0AC4872F3D} = {E76A4ED5-9137-4E4B-AE91-7AEDB2683823} - {75BAE755-3A1F-41F2-9176-9F8FF9FEE2DD} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} - {09352F1D-878F-4F55-8AA2-6E47F1AD37D5} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} - {AD738BD4-6A02-4B88-8F93-FBBBA49A74C8} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} - {4461063D-2F2B-274C-7E6F-F235119D258E} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {1A29B520-D16A-35F2-5CAC-64573C86E63D} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {2AA12D54-540B-E515-CB82-80D691C9DCF1} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {92D9C6D6-6925-1AD1-69FA-485F83943BD2} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {4A9C11F4-9577-ABEC-C070-83A194746D9B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {C09B9D2F-E463-BEBD-34E4-E8F2C201A277} = {ED952CF7-84DF-437A-B066-F516E9BE1C2C} - {3FF03FA9-E3C3-49E3-9DCB-C703A5B0278B} = {0CC4817A-12F3-4357-912C-09315FAAD008} - {556B486E-F9B0-7EA9-6A25-DA560C312761} = {19F1F1E5-3013-7660-661A-2A15F7D606C1} - {210228A5-979A-DE06-EE1F-B35C65E1583C} = {556B486E-F9B0-7EA9-6A25-DA560C312761} - {089582DC-FC8E-4DDE-99AC-E31BF95175B0} = {210228A5-979A-DE06-EE1F-B35C65E1583C} - {59667E4C-0BD2-9F48-FB50-9E55DD8B1011} = {556B486E-F9B0-7EA9-6A25-DA560C312761} - {21F71821-AC58-43A1-A0B1-A7DB5FA892D3} = {59667E4C-0BD2-9F48-FB50-9E55DD8B1011} - {A20114E1-82D8-903A-C389-726EB4FD943F} = {19F1F1E5-3013-7660-661A-2A15F7D606C1} - {0D2F834B-6D91-18D0-3F09-672D448751BD} = {A20114E1-82D8-903A-C389-726EB4FD943F} - {DCD79241-612B-4081-A8CC-BD7A4ABC1662} = {0D2F834B-6D91-18D0-3F09-672D448751BD} - {5AF52CDD-DF78-3712-7516-5B49F94F9491} = {A20114E1-82D8-903A-C389-726EB4FD943F} - {4B953573-C3CD-4845-896B-EA0A0B7A7B27} = {5AF52CDD-DF78-3712-7516-5B49F94F9491} - {B3A1F1C2-7D4E-4A5B-9C6D-8E2F0A1B3C4D} = {C1D2E3F4-A5B6-7C8D-9E0F-1A2B3C4D5E6F} - {D4E5F6A7-1B2C-3D4E-5F6A-7B8C9D0E1F2A} = {B3A1F1C2-7D4E-4A5B-9C6D-8E2F0A1B3C4D} - {A1B2C3D4-E5F6-7A8B-9C0D-1E2F3A4B5C6D} = {D4E5F6A7-1B2C-3D4E-5F6A-7B8C9D0E1F2A} - {7E0602AC-7F0A-362A-D734-0FDDFCC600B5} = {7289C27E-D7DF-2C71-84B4-151F3A162493} - {2B71F605-037E-5629-6E23-0FA3C297446D} = {7289C27E-D7DF-2C71-84B4-151F3A162493} - {E9D12AEC-2F11-4871-89BB-343BF1CAEA4C} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} - {020A7E7B-04C9-4326-985F-045B42CC2200} = {7289C27E-D7DF-2C71-84B4-151F3A162493} - {D433ED2D-5E47-4A4B-B94A-EC71482715C7} = {020A7E7B-04C9-4326-985F-045B42CC2200} - {AA77C107-9A78-4A99-98BB-21FF7A1E0B01} = {2B71F605-037E-5629-6E23-0FA3C297446D} - {351BE847-A0BF-450C-A5BC-8337AFA49EAA} = {7289C27E-D7DF-2C71-84B4-151F3A162493} - {1DB299CE-95EA-4566-84DD-171768758291} = {351BE847-A0BF-450C-A5BC-8337AFA49EAA} - {583E2B51-A90B-4F34-AD8B-4061504E855E} = {351BE847-A0BF-450C-A5BC-8337AFA49EAA} - {2F900B8A-EA19-4274-9AE9-3E6A59856FCC} = {351BE847-A0BF-450C-A5BC-8337AFA49EAA} - {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} = {4CAE9195-4F1A-4D48-854C-1C9FBC512C66} - {B3061B34-2A1C-49B9-A3A9-D4D37B4704B2} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} - {3654906C-6852-48AF-BC7E-B3A62F3E9DB9} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} - {45B9E5C3-DED5-4DB6-B1E6-4EAC4E68BA83} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} - {E849E6FB-1AB0-4922-A060-0062EA669FD0} = {5EC4C165-25BB-4346-9DCC-5EFEEAC00C02} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} - EndGlobalSection -EndGlobal diff --git a/src/Microsoft.Data.SqlClient.slnx b/src/Microsoft.Data.SqlClient.slnx new file mode 100644 index 0000000000..0452cba60c --- /dev/null +++ b/src/Microsoft.Data.SqlClient.slnx @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 60d4b92f2c1ae510f0ab6b206f1a3af5688b4a92 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:46:34 -0700 Subject: [PATCH 06/22] Add missing package reference to System.Data.Common (#4063) --- Directory.Packages.props | 1 + .../netfx/ref/Microsoft.Data.SqlClient.csproj | 1 + src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj | 1 + tools/specs/Microsoft.Data.SqlClient.nuspec | 1 + 4 files changed, 4 insertions(+) diff --git a/Directory.Packages.props b/Directory.Packages.props index d043543e78..b1a858bf72 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -94,6 +94,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 6765e009db..208181ffb6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -43,6 +43,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj index 49f5677535..0f1c3c2d40 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj @@ -127,6 +127,7 @@ + diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 2e80556c98..75aa9e786d 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -44,6 +44,7 @@ + From f0e6f6c9af361f876f68080c2d4562872cda01e7 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:26:35 -0700 Subject: [PATCH 07/22] Eng | Disable ESRP steps on non-official pipelines (#4077) * Disable ESRP steps on non-official pipelines * Update copilot instructions * Skip Signature verification for non-official builds * Address comments * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * touch-ups --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ado-pipelines.instructions.md | 292 +++----- .../onebranch-pipeline-design.instructions.md | 659 ++++-------------- .../jobs/build-signed-csproj-package-job.yml | 51 +- .../build-signed-sqlclient-package-job.yml | 245 +++---- .../jobs/validate-signed-package-job.yml | 483 ++++++------- .../onebranch/sqlclient-non-official.yml | 53 +- .../onebranch/sqlclient-official.yml | 53 +- .../onebranch/stages/build-stages.yml | 274 ++++---- .../compound-esrp-nuget-signing-step.yml | 36 +- .../steps/esrp-code-signing-step.yml | 232 +++--- 10 files changed, 973 insertions(+), 1405 deletions(-) diff --git a/.github/instructions/ado-pipelines.instructions.md b/.github/instructions/ado-pipelines.instructions.md index 82b8046dee..db053130e0 100644 --- a/.github/instructions/ado-pipelines.instructions.md +++ b/.github/instructions/ado-pipelines.instructions.md @@ -1,195 +1,121 @@ --- applyTo: "eng/pipelines/**/*.yml" --- -# Azure DevOps Pipelines Guide - -## Overview - -This repository uses Azure DevOps Pipelines for CI/CD. The pipeline configurations are located in `eng/pipelines/`. - -**ADO Organization**: sqlclientdrivers -**ADO Project**: ADO.NET - -## Pipeline Structure - -``` -eng/pipelines/ -├── abstractions/ # Abstractions package pipelines -├── azure/ # Azure package pipelines -├── common/ # Shared templates -│ └── templates/ -│ ├── jobs/ # Reusable job templates -│ ├── stages/ # Reusable stage templates -│ └── steps/ # Reusable step templates -├── jobs/ # Top-level job definitions -├── libraries/ # Shared variable definitions -├── stages/ # Stage definitions -├── steps/ # Step definitions -├── variables/ # Variable templates -├── akv-official-pipeline.yml # AKV provider official/signing build -├── dotnet-sqlclient-ci-core.yml # Core CI pipeline (reusable) -├── dotnet-sqlclient-ci-package-reference-pipeline.yml # CI with package references -├── dotnet-sqlclient-ci-project-reference-pipeline.yml # CI with project references -├── dotnet-sqlclient-signing-pipeline.yml # Package signing pipeline -├── sqlclient-pr-package-ref-pipeline.yml # PR validation (package ref) -├── sqlclient-pr-project-ref-pipeline.yml # PR validation (project ref) -└── stress-tests-pipeline.yml # Stress testing -``` - -## Main Pipelines - -### CI Core Pipeline (`dotnet-sqlclient-ci-core.yml`) -Reusable core CI pipeline consumed by both project-reference and package-reference CI pipelines. Configurable parameters: - -| Parameter | Description | Default | -|-----------|-------------|---------| -| `targetFrameworks` | Windows test frameworks | `[net462, net8.0, net9.0, net10.0]` | -| `targetFrameworksUnix` | Unix test frameworks | `[net8.0, net9.0, net10.0]` | -| `referenceType` | Project or Package reference | Required | -| `buildConfiguration` | Debug or Release | Required | -| `useManagedSNI` | Test with managed SNI | `[false, true]` | -| `testJobTimeout` | Test job timeout (minutes) | Required | -| `runAlwaysEncryptedTests` | Include AE tests | `true` | -| `enableStressTests` | Include stress test stage | `false` | - -### CI Reference Pipelines -- `dotnet-sqlclient-ci-project-reference-pipeline.yml` — Full CI using project references (builds from source) -- `dotnet-sqlclient-ci-package-reference-pipeline.yml` — Full CI using package references (tests against published NuGet packages) - -### PR Validation Pipelines -- `sqlclient-pr-project-ref-pipeline.yml` — PR validation with project references -- `sqlclient-pr-package-ref-pipeline.yml` — PR validation with package references - -These pipelines trigger on pull requests and run a subset of the full CI matrix to provide fast feedback. - -### Official/Signing Pipeline (`dotnet-sqlclient-signing-pipeline.yml`) -Signs and publishes NuGet packages. Used for official releases. Requires secure service connections and key vault access for code signing. - -### AKV Official Pipeline (`akv-official-pipeline.yml`) -Builds and signs the `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` add-on package separately from the main driver. Uses 1ES pipeline templates for compliance. - -### Stress Tests Pipeline (`stress-tests-pipeline.yml`) -Optional pipeline for long-running stress and endurance testing. Enabled via `enableStressTests` parameter in CI core. - -## Build Stages - -1. **build_abstractions_package_stage**: Build and pack abstractions -2. **build_sqlclient_package_stage**: Build main driver and AKV packages -3. **build_azure_package_stage**: Build Azure extensions package -4. **stress_tests_stage**: Optional stress testing -5. **run_tests_stage**: Execute all test suites +# Azure DevOps CI/CD Pipeline Guidelines + +## Purpose + +Rules and conventions for editing the Azure DevOps CI/CD pipelines that build, test, and validate Microsoft.Data.SqlClient. These pipelines live under `eng/pipelines/` (excluding `onebranch/`, which is covered by separate instructions). + +**ADO Organization**: sqlclientdrivers | **ADO Project**: ADO.NET + +## Pipeline Layout + +Two categories of pipelines exist in this repository: + +- **CI/PR pipelines** (`eng/pipelines/`) — Build, test, and validate on every push/PR +- **OneBranch pipelines** (`eng/pipelines/onebranch/`) — Official signing/release builds (separate instructions file) + +Top-level CI/PR pipeline files: +- `dotnet-sqlclient-ci-core.yml` — Reusable core template; all CI and PR pipelines extend this +- `dotnet-sqlclient-ci-package-reference-pipeline.yml` — CI with Package references (Release) +- `dotnet-sqlclient-ci-project-reference-pipeline.yml` — CI with Project references (Release) +- `sqlclient-pr-package-ref-pipeline.yml` — PR validation with Package references +- `sqlclient-pr-project-ref-pipeline.yml` — PR validation with Project references +- `stress-tests-pipeline.yml` — Stress tests triggered after successful CI-Package runs + +Reusable templates are organized under: +- `common/templates/jobs/` — Job templates (`ci-build-nugets-job`, `ci-code-coverage-job`, `ci-run-tests-job`) +- `common/templates/stages/` — Stage templates (`ci-run-tests-stage`) +- `common/templates/steps/` — Step templates (build, test, config, publish) +- `jobs/` — Package-specific CI jobs (pack/test Abstractions, Azure, Logging, stress) +- `stages/` — Package-specific CI stages (build Logging → Abstractions → SqlClient → Azure → verify → stress) +- `libraries/` — Shared variables (`ci-build-variables.yml`) +- `steps/` — SDK install steps + +## CI Core Template + +`dotnet-sqlclient-ci-core.yml` is the central orchestrator. All CI and PR pipelines extend it with different parameters. + +Key parameters: +- `referenceType` (required) — `Package` or `Project`; controls how sibling packages are referenced +- `buildConfiguration` (required) — `Debug` or `Release` +- `testJobTimeout` (required) — test job timeout in minutes +- `targetFrameworks` — Windows test TFMs; default `[net462, net8.0, net9.0, net10.0]` +- `targetFrameworksUnix` — Unix test TFMs; default `[net8.0, net9.0, net10.0]` +- `testSets` — test partitions; default `[1, 2, 3]` +- `useManagedSNI` — SNI variants to test; default `[false, true]` +- `runAlwaysEncryptedTests` — include AE test set; default `true` +- `enableStressTests` — enable stress test stage; default `false` +- `debug` — enable debug output; default `false` +- `dotnetVerbosity` — MSBuild verbosity; default `normal` + +## Build Stage Order + +Stages execute in dependency order (Package reference mode requires artifacts from prior stages): +1. `generate_secrets` — Generate test secrets +2. `build_logging_package_stage` — Build Logging package +3. `build_abstractions_package_stage` — Build Abstractions (depends on Logging) +4. `build_sqlclient_package_stage` — Build SqlClient + AKV Provider (depends on Abstractions + Logging) +5. `build_azure_package_stage` — Build Azure extensions (depends on Abstractions + Logging + SqlClient) +6. `verify_nuget_packages_stage` — Verify NuGet package metadata +7. `stress_tests_stage` — Optional stress tests +8. `ci_run_tests_stage` — Run MDS and AKV test suites + +When adding a new build stage, respect the dependency graph and pass artifact names/versions to downstream stages. + +## PR vs CI Pipeline Differences + +PR pipelines: +- Trigger on PRs to `dev/*`, `feat/*`, `main`; exclude `eng/pipelines/onebranch/*` paths +- Use reduced TFM matrix: `[net462, net8.0, net9.0]` (excludes net10.0) +- Timeout: 90 minutes +- Package-ref PR disables Always Encrypted tests in Debug config + +CI pipelines: +- Trigger on push to `main` (GitHub) and `internal/main` (ADO) with `batch: true` +- Scheduled weekday builds (see individual pipeline files for cron times) +- Full TFM matrix including net10.0 ## Test Configuration -### Test Sets -Tests are divided into sets for parallelization: -- `TestSet=1` — First partition of tests -- `TestSet=2` — Second partition -- `TestSet=3` — Third partition -- `TestSet=AE` — Always Encrypted tests - -### Test Filters -Tests use category-based filtering. The default filter excludes both `failing` and `flaky` tests: -``` -category!=failing&category!=flaky -``` - -Category values: -- `nonnetfxtests` — Excluded on .NET Framework -- `nonnetcoreapptests` — Excluded on .NET Core -- `nonwindowstests` — Excluded on Windows -- `nonlinuxtests` — Excluded on Linux -- `failing` — Known permanent failures (excluded from all runs) -- `flaky` — Intermittently failing tests (quarantined, run separately) - -### Flaky Test Quarantine in Pipelines -Quarantined tests (`[Trait("Category", "flaky")]`) run in **separate pipeline steps** after the main test steps. This ensures: -- Main test runs are **not blocked** by intermittent failures -- Flaky tests are still **monitored** for regression or resolution -- Code coverage is **not collected** for flaky test runs -- Results appear in pipeline output for visibility - -The quarantine steps are configured in: -- `eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml` -- `eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml` -- `eng/pipelines/common/templates/steps/run-all-tests-step.yml` - -### Test Timeout -All test runs use `--blame-hang-timeout 10m` (configured in `build.proj`). Tests exceeding 10 minutes are killed and reported as failures. - -### SNI Testing -The `useManagedSNI` parameter controls testing with: -- Native SNI (`false`) - Windows native library -- Managed SNI (`true`) - Cross-platform managed implementation +Test partitioning: +- Tests split into `TestSet=1`, `TestSet=2`, `TestSet=3` for parallelization +- `TestSet=AE` — Always Encrypted tests (controlled by `runAlwaysEncryptedTests`) -## Variables - -### Build Variables (`ci-build-variables.yml`) -Common build configuration: -- Package versions -- Build paths -- Signing configuration - -### Runtime Variables -Set via pipeline parameters or UI: -- `Configuration` - Debug/Release -- `Platform` - AnyCPU/x86/x64 -- `TF` - Target framework - -## Creating Pipeline Changes - -### Adding New Test Categories -1. Add category attribute to tests: `[Category("newcategory")]` -2. Update filter expressions in test job templates -3. Document category purpose in test documentation - -### Adding New Pipeline Parameters -1. Define parameter in appropriate `.yml` file -2. Add to parameter passing in calling templates -3. Document in this file - -### Modifying Build Steps -1. Changes should be made in template files for reusability -2. Test changes locally when possible -3. Submit as PR - validation will run +Test filters — default excludes `failing` and `flaky` categories: +- `failing` — known permanent failures, always excluded +- `flaky` — intermittent failures, quarantined in separate pipeline steps +- `nonnetfxtests` / `nonnetcoreapptests` — platform-specific exclusions +- `nonwindowstests` / `nonlinuxtests` — OS-specific exclusions -## Best Practices +Flaky test quarantine: +- Quarantined tests (`[Trait("Category", "flaky")]`) run in separate steps after main tests +- Main test runs are not blocked by flaky failures +- No code coverage collected for flaky runs +- Configured in `common/templates/steps/build-and-run-tests-netcore-step.yml`, `build-and-run-tests-netfx-step.yml`, and `run-all-tests-step.yml` -### Template Design -- Use templates for reusable definitions -- Pass parameters explicitly (avoid global variables) -- Use descriptive stage/job/step names +SNI testing — `useManagedSNI` controls testing with native SNI (`false`) or managed SNI (`true`) -### Variable Management -- Use template variables for shared values -- Use pipeline parameters for per-run configuration -- Avoid hardcoding versions (use Directory.Packages.props) +Test timeout — `--blame-hang-timeout 10m` (configured in `build.proj`); tests exceeding 10 minutes are killed -### Test Infrastructure -- Ensure tests are properly categorized -- Handle test configuration files properly -- Use test matrix for cross-platform coverage - -## Troubleshooting - -### Common Issues -1. **Test failures due to missing config**: Ensure `config.json` exists -2. **Platform-specific failures**: Check platform exclusion categories -3. **Timeout issues**: Increase `testJobTimeout` parameter - -### Debugging Pipelines -- Enable debug mode via `debug: true` parameter -- Use `dotnetVerbosity: diagnostic` for detailed output -- Check build logs in Azure DevOps - -## Security Considerations - -- Pipelines use service connections for artifact publishing -- Signing uses secure key vault integration -- Sensitive configuration should use pipeline secrets -- Never commit credentials in pipeline files - -## Related Documentation +## Variables -- [BUILDGUIDE.md](../../BUILDGUIDE.md) - Local build instructions -- [Azure DevOps Documentation](https://learn.microsoft.com/azure/devops/pipelines/) +- All CI build variables centralized in `libraries/ci-build-variables.yml` +- Package versions use `-ci` suffix (e.g., `7.0.0.$(Build.BuildNumber)-ci`) +- `assemblyBuildNumber` derived from first segment of `Build.BuildNumber` (16-bit safe) +- `localFeedPath` = `$(Build.SourcesDirectory)/packages` — local NuGet feed for inter-package deps +- `packagePath` = `$(Build.SourcesDirectory)/output` — NuGet pack output + +## Conventions When Editing Pipelines + +- Always use templates for reusable logic — do not inline complex steps +- Pass parameters explicitly; avoid relying on global variables +- Use descriptive stage/job/step display names +- When adding parameters, define them in the core template and thread through calling pipelines +- When adding test categories, update filter expressions in test step templates +- PR pipelines should run a minimal matrix for fast feedback +- Test changes via PR pipeline first — validation runs automatically +- Enable `debug: true` and `dotnetVerbosity: diagnostic` for troubleshooting +- Never commit credentials or secrets in pipeline files +- Signing and release are handled by OneBranch pipelines — not these CI/PR pipelines diff --git a/.github/instructions/onebranch-pipeline-design.instructions.md b/.github/instructions/onebranch-pipeline-design.instructions.md index 1c848e2493..f2319ee5b2 100644 --- a/.github/instructions/onebranch-pipeline-design.instructions.md +++ b/.github/instructions/onebranch-pipeline-design.instructions.md @@ -1,534 +1,139 @@ --- applyTo: "eng/pipelines/**/*.yml" --- -# Multi-Product Azure DevOps Pipeline in dotnet/sqlclient — Design Specification +# OneBranch Pipeline Guidelines -## 1. Overview +## Purpose -This document describes the design of the unified Azure DevOps YAML pipeline that builds, signs, packages, and optionally releases six NuGet packages with interdependencies. The pipeline uses **stages** and **jobs** to maximize parallelism while respecting dependency order. It comprises five stages: three build stages, a validation stage, and an on-demand release stage. +Rules and conventions for editing the OneBranch Azure DevOps YAML pipelines that build, sign, package, and release six NuGet packages with interdependencies. -Two pipeline variants exist from the same stage/job structure: +## Pipeline Variants + +- `sqlclient-official.yml` — Official pipeline; uses `OneBranch.Official.CrossPlat.yml`; CI trigger on `internal/main` + daily schedule at 04:30 UTC +- `sqlclient-non-official.yml` — Non-Official pipeline; uses `OneBranch.NonOfficial.CrossPlat.yml`; manual only (`pr: none`, `trigger: none`) +- Both live under `eng/pipelines/onebranch/` and extend OneBranch governed templates +- Never parameterize the OneBranch template name — hardcode it per pipeline for PRC compliance +- Official pipeline must never be run on PRs or dev branches. -| Pipeline | Template | Trigger | Purpose | -|----------|----------|---------|---------| -| `dotnet-sqlclient-official-pipeline.yml` | `OneBranch.Official.CrossPlat.yml` | CI + scheduled | Production-signed builds | -| `dotnet-sqlclient-non-official-pipeline.yml` | `OneBranch.NonOfficial.CrossPlat.yml` | Manual only | Validation / test builds (release in dry-run mode) | - -Both pipelines use the **OneBranch (1ES) governed template** infrastructure and share identical stage definitions, job templates, and variable chains. - ---- - -## 2. Products and Dependencies - -| # | Package | Dependencies | -|---|---------|-------------| -| 1 | `Microsoft.SqlServer.Server` | — | -| 2 | `Microsoft.Data.SqlClient.Internal.Logging` | — | -| 3 | `Microsoft.Data.SqlClient.Extensions.Abstractions` | `Internal.Logging` | -| 4 | `Microsoft.Data.SqlClient` | `Internal.Logging`, `Extensions.Abstractions` | -| 5 | `Microsoft.Data.SqlClient.Extensions.Azure` | `Extensions.Abstractions`, `Internal.Logging` | -| 6 | `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` | `SqlClient`, `Internal.Logging` | - ---- - -## 3. Pipeline Flow — Sequence Diagram - -```mermaid -sequenceDiagram - participant T as Trigger / User - participant P as Pipeline Orchestrator - participant B1a as Job: Build Internal.Logging - participant B1c as Job: Build SqlServer.Server - participant B1b as Job: Build Extensions.Abstractions - participant B2a as Job: Build SqlClient - participant B2b as Job: Build Extensions.Azure - participant V as Job: Validate MDS Package - participant B3 as Job: Build AKV Provider - participant R as Stage: Release - - Note over T,R: ══════ BUILD & SIGN PHASE ══════ - - T->>P: Pipeline triggered (CI / Scheduled / Manual) - - Note over P: Stage 1 — build_independent (parallel, no deps) - - par Stage 1 jobs (parallel) - P->>B1a: Build DLLs → ESRP sign DLLs → Pack → ESRP sign NuGet (Logging) - B1a-->>P: ✅ Signed .nupkg - and - P->>B1c: Build + ESRP sign + pack SqlServer.Server - B1c-->>P: ✅ Signed .nupkg - end - - Note over P: Stage 2 — build_abstractions (dependsOn: build_independent) - - P->>B1b: Build DLLs → ESRP sign DLLs → Pack → ESRP sign NuGet (Abstractions) - Note right of B1b: Downloads: Internal.Logging artifact - B1b-->>P: ✅ Signed .nupkg - - Note over P: Stage 3 — build_dependent (dependsOn: build_abstractions) - - par Stage 3 jobs (parallel) - P->>B2a: Build + ESRP sign + pack SqlClient - Note right of B2a: Downloads: Internal.Logging,
Extensions.Abstractions artifacts - B2a-->>P: ✅ Signed .nupkg + .snupkg - and - P->>B2b: Build DLLs → ESRP sign DLLs → Pack → ESRP sign NuGet (Azure) - Note right of B2b: Downloads: Extensions.Abstractions,
Internal.Logging artifacts - B2b-->>P: ✅ Signed .nupkg - end - - Note over P: Validation + Stage 4 (both dependsOn: build_dependent, run in parallel) - - par Validation and Stage 4 (parallel) - P->>V: Validate signed MDS package - V-->>P: ✅ Package validation passed - and - P->>B3: Build + ESRP sign + pack AKV Provider - Note right of B3: Downloads: SqlClient,
Internal.Logging artifacts - B3-->>P: ✅ Signed .nupkg - end - - Note over T,R: ══════ RELEASE PHASE (on-demand) ══════ - - alt At least one release parameter is true - P->>R: Stage: release (dependsOn: conditional on build stages) - Note right of R: ADO Environment Approval
(NuGet-Production environment) - R-->>P: ✅ Approved - Note right of R: Publish selected packages
via NuGetCommand@2 - R-->>P: ✅ Published to NuGet - else No release parameters set - Note over P: Release stage skipped - end - - Note over T,R: Pipeline complete 🎉 -``` - ---- - -## 4. Stage Design - -### 4.1 Build Phase - -The build phase runs automatically on every CI trigger, scheduled run, or manual queue. It is divided into four build stages plus a validation stage, based on the dependency graph. - -#### Stage 1 — `build_independent`: Independent Packages (no dependencies) - -| Job Template | Package | Build Target | Condition | -|--------------|---------|--------------|-----------| -| `build-signed-csproj-package-job.yml` | `Microsoft.Data.SqlClient.Internal.Logging` | `BuildLogging` / `PackLogging` | `buildAKVProvider OR buildSqlClient` | -| `build-signed-csproj-package-job.yml` | `Microsoft.SqlServer.Server` | `PackSqlServer` | `buildSqlServerServer` | - -- **`dependsOn`**: none -- **Parallelism**: Jobs run in parallel (depending on which are enabled) -- **Conditional builds**: Each job is wrapped with compile-time `${{ if }}` conditionals based on build parameters -- csproj-based jobs (`build-signed-csproj-package-job.yml`) perform: **Build DLLs → ESRP DLL signing → NuGet pack (NoBuild=true) → ESRP NuGet signing** → publish artifact - -#### Stage 2 — `build_abstractions`: Abstractions Package (depends on Stage 1) - -| Job Template | Package | Build Target | Artifact Dependencies | -|--------------|---------|--------------|----------------------| -| `build-signed-csproj-package-job.yml` | `Microsoft.Data.SqlClient.Extensions.Abstractions` | `BuildAbstractions` / `PackAbstractions` | `Internal.Logging` | - -- **Stage condition**: `buildSqlClient = true` (entire stage is excluded when false) -- **`dependsOn`**: `build_independent` -- Downloads `Microsoft.Data.SqlClient.Internal.Logging.nupkg` (from Stage 1) pipeline artifact - -#### Stage 3 — `build_dependent`: Core Packages (depend on Stage 2) - -| Job Template | Package | Build Target | Artifact Dependencies | -|--------------|---------|--------------|----------------------| -| `build-signed-package-job.yml` | `Microsoft.Data.SqlClient` | *(nuspec-based)* | `Internal.Logging`, `Extensions.Abstractions` | -| `build-signed-csproj-package-job.yml` | `Microsoft.Data.SqlClient.Extensions.Azure` | `BuildAzure` / `PackAzure` | `Extensions.Abstractions`, `Internal.Logging` | - -- **Stage condition**: `buildSqlClient = true` (entire stage is excluded when false) -- **`dependsOn`**: `build_abstractions` -- **Parallelism**: Both jobs run in parallel -- The MDS (SqlClient) job also publishes symbol packages (`.snupkg`) when `publishSymbols` is true -- All jobs configure APIScan with job-level `ob_sdl_apiscan_*` variables targeting package-specific folders - -#### Stage 4 — `build_addons`: Add-on Packages (depend on Stage 3) - -| Job Template | Package | Artifact Dependencies | -|--------------|---------|----------------------| -| `build-akv-official-job.yml` | `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` | `SqlClient`, `Internal.Logging` | - -- **Stage condition**: `buildAKVProvider AND buildSqlClient` (both must be true) -- **`dependsOn`**: `build_dependent` -- Downloads `Microsoft.Data.SqlClient.nupkg` (from Stage 3) and `Microsoft.Data.SqlClient.Internal.Logging.nupkg` (from Stage 1) pipeline artifacts -- Uses separate ESRP signing credentials (`Signing`-prefixed variables from `esrp-variables-v2` group) - -### 4.2 Validation Stage — `mds_package_validation` - -Validates the signed MDS (SqlClient) package after Stage 3 completes. - -- **Stage condition**: `buildSqlClient = true` -- **`dependsOn`**: `build_dependent` -- Runs in parallel with Stage 4 (`build_addons`) -- Uses `validate-signed-package-job.yml` template -- Downloads the `drop_build_dependent_build_signed_package` artifact and validates against `CurrentNetFxVersion` (default: `net462`) - -### 4.3 Release Phase — `release` - -The release stage is gated and only executes on demand when at least one release parameter is set to `true` at queue time. - -- **`dependsOn`**: Conditional based on which build stages are enabled: - - `build_independent` (when releasing SqlServer.Server or Logging) - - `build_abstractions` (when releasing Abstractions) - - `build_dependent`, `mds_package_validation` (when `buildSqlClient = true`) - - `build_addons` (when `buildAKVProvider AND buildSqlClient`) -- **Gate**: ADO Environment approvals (official pipeline only): - - Official: `NuGet-Production` environment with configured approvals - - Non-Official: `NuGet-DryRun` environment (no approvals, validation only) -- **Package selection**: Controlled by 6 runtime boolean parameters (see Section 5.2) -- **Stage condition**: The entire stage is skipped unless at least one release parameter is `true`: - ```yaml - - ${{ if or(parameters.releaseSqlServerServer, parameters.releaseLogging, ...) }}: - - stage: release - ``` -- **Publish jobs**: Each package has a conditional publish job that is included at compile time only when its parameter is `true`: - ```yaml - - ${{ if eq(parameters.releaseXxx, true) }}: - - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self - ``` -- **Environment variables**: Stage sets `ob_release_usedeploymentjob: true` for OneBranch integration: - - Official: `ob_release_environment: 'NuGet-Production'` - - Non-Official: `ob_release_environment: 'NuGet-DryRun'` - -#### Artifact → Publish Job Mapping - -| Package | Artifact Name | Publish Job | -|---------|---------------|-------------| -| `Microsoft.SqlServer.Server` | `drop_build_independent_build_package_SqlServer` | `publish_SqlServer_Server` | -| `Microsoft.Data.SqlClient.Internal.Logging` | `drop_build_independent_build_package_Logging` | `publish_Logging` | -| `Microsoft.Data.SqlClient.Extensions.Abstractions` | `drop_build_abstractions_build_package_Abstractions` | `publish_Abstractions` | -| `Microsoft.Data.SqlClient` | `drop_build_dependent_build_package_SqlClient` | `publish_SqlClient` | -| `Microsoft.Data.SqlClient.Extensions.Azure` | `drop_build_dependent_build_package_Azure` | `publish_Extensions_Azure` | -| `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` | `drop_build_addons_buildSignedAkvPackage` | `publish_AKVProvider` | - -Each publish job uses the reusable `publish-nuget-package-job.yml` template, which downloads the artifact and pushes `.nupkg`/`.snupkg` files via `NuGetCommand@2` with an external feed service connection. - -#### Dry-Run Mode - -Two ADO environments control release behavior: - -| Environment | Pipeline | Behavior | -|------------|----------|----------| -| `NuGet-DryRun` | Non-Official | Validation only — packages are never pushed | -| `NuGet-Production` | Official | Real releases with approval gate | - -**Non-official pipeline**: Always runs in dry-run mode. There is no `releaseDryRun` parameter — `dryRun: true` is hardcoded in every publish job. This prevents accidental publication from validation builds. - -**Official pipeline**: Exposes a `releaseDryRun` parameter (default: `true` for safety). When enabled, the template downloads artifacts and lists the `.nupkg`/`.snupkg` files that *would* be published but skips the actual `NuGetCommand@2` push. Set `releaseDryRun: false` to perform real pushes after final validation. - ---- - -## 5. Runtime Parameters - -### 5.1 Build Parameters - -The pipeline exposes the following parameters at queue time: - -```yaml -parameters: - - name: debug - displayName: 'Enable debug output' - type: boolean - default: false - - - name: publishSymbols - displayName: 'Publish symbols' - type: boolean - default: false - - - name: CurrentNetFxVersion - displayName: 'Lowest supported .NET Framework version (MDS validation)' - type: string - default: 'net462' - - - name: isPreview - displayName: 'Is this a preview build?' - type: boolean - default: false - - - name: testJobTimeout - displayName: 'Test job timeout (in minutes)' - type: number - default: 60 - - # Build parameters — control which packages to build - - name: buildSqlServerServer - displayName: 'Build Microsoft.SqlServer.Server' - type: boolean - default: true - - - name: buildSqlClient - displayName: 'Build Microsoft.Data.SqlClient and Extensions' - type: boolean - default: true - - - name: buildAKVProvider - displayName: 'Build Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider' - type: boolean - default: true -``` - -The `isPreview` parameter controls version resolution — when `true`, each package uses its preview version (e.g., `loggingPackagePreviewVersion`) instead of the GA version (e.g., `loggingPackageVersion`). All versions are defined in the centralized `libraries/common-variables.yml`. - -The build parameters enable selective package building: -- `buildSqlServerServer` — controls SqlServer.Server build job -- `buildSqlClient` — controls MDS, Extensions.Azure, Abstractions, Logging (when AKV is disabled), and validation stages -- `buildAKVProvider` — controls AKV Provider build (also requires `buildSqlClient=true`) and Logging (when SqlClient is disabled) - -When set to `false`, the respective jobs/stages are excluded at compile-time using `${{ if }}` conditionals. This allows faster pipeline runs when only certain packages need to be built. - -### 5.2 Release Parameters - -Six boolean parameters control selective package release. All default to `false` so the release stage is skipped on normal CI/scheduled builds: - -```yaml -parameters: - - name: releaseSqlServerServer - displayName: 'Release Microsoft.SqlServer.Server' - type: boolean - default: false - - - name: releaseLogging - displayName: 'Release Microsoft.Data.SqlClient.Internal.Logging' - type: boolean - default: false - - - name: releaseAbstractions - displayName: 'Release Microsoft.Data.SqlClient.Extensions.Abstractions' - type: boolean - default: false - - - name: releaseSqlClient - displayName: 'Release Microsoft.Data.SqlClient' - type: boolean - default: false - - - name: releaseAzure - displayName: 'Release Microsoft.Data.SqlClient.Extensions.Azure' - type: boolean - default: false - - - name: releaseAKVProvider - displayName: 'Release Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider' - type: boolean - default: false -``` - -#### Dry-Run Parameter (Official Pipeline Only) - -The **official pipeline** includes a `releaseDryRun` parameter that defaults to `true` for safety: - -```yaml - - name: releaseDryRun - displayName: 'Release Dry Run (do not push to NuGet)' - type: boolean - default: true # safety default — must explicitly disable for real releases -``` - -When `releaseDryRun: true`, publish jobs download artifacts and list packages but skip actual NuGet push. Set to `false` for production releases. - -> **Note**: The non-official pipeline does **not** expose this parameter — dry-run mode is hardcoded and cannot be disabled. - ---- - -## 6. Variable & Version Management - -### 6.1 Variable Chain - -Variables are defined in a layered template chain. All variable groups live inside the templates — none are declared inline at the pipeline level: - -``` -dotnet-sqlclient-official-pipeline.yml - └─ libraries/variables.yml - └─ libraries/build-variables.yml - ├─ group: 'Release Variables' - ├─ group: 'Symbols publishing' ← SymbolsPublishServer, SymbolsPublishTokenUri, etc. - └─ libraries/common-variables.yml - ├─ group: 'ESRP Federated Creds (AME)' ← ESRP signing credentials - ├─ SymbolServer / SymbolTokenUri aliases ← mapped from Symbols publishing group - └─ all package versions, paths, build variables -``` - -### 6.2 Package Version Variables - -All package versions are centralized in `libraries/common-variables.yml`: - -| Package | GA Version Var | Preview Version Var | Assembly Version Var | -|---------|---------------|--------------------|--------------------| -| Logging | `loggingPackageVersion` | `loggingPackagePreviewVersion` | `loggingAssemblyFileVersion` | -| Abstractions | `abstractionsPackageVersion` | `abstractionsPackagePreviewVersion` | `abstractionsAssemblyFileVersion` | -| SqlServer.Server | `sqlServerPackageVersion` | `sqlServerPackagePreviewVersion` | `sqlServerAssemblyFileVersion` | -| SqlClient (MDS) | `mdsPackageVersion` | `mdsPackagePreviewVersion` | `mdsAssemblyFileVersion` | -| Extensions.Azure | `azurePackageVersion` | `azurePackagePreviewVersion` | `azureAssemblyFileVersion` | -| AKV Provider | `akvPackageVersion` | `akvPackagePreviewVersion` | `akvAssemblyFileVersion` | - -The pipeline resolves `effective*Version` variables at compile time based on the `isPreview` parameter. - -### 6.3 Release & Symbol Variables - -| Variable | Defined In | Purpose | -|----------|-----------|---------| -| `NuGetServiceConnection` | `libraries/common-variables.yml` | External NuGet service connection name for `NuGetCommand@2` push | -| `SymbolServer` | `libraries/common-variables.yml` (alias) | Alias for `$(SymbolsPublishServer)` — used by MDS `publish-symbols-step.yml` | -| `SymbolTokenUri` | `libraries/common-variables.yml` (alias) | Alias for `$(SymbolsPublishTokenUri)` — used by MDS `publish-symbols-step.yml` | - -### 6.4 Variable Groups - -| Group | Included In | Purpose | -|-------|------------|---------| -| `Release Variables` | `build-variables.yml` | Release-specific configuration | -| `Symbols publishing` | `build-variables.yml` | Symbol publishing credentials (`SymbolsAzureSubscription`, `SymbolsPublishServer`, `SymbolsPublishTokenUri`, `SymbolsUploadAccount`, `SymbolsPublishProjectName`) | -| `ESRP Federated Creds (AME)` | `common-variables.yml` | Federated identity for ESRP signing (`ESRPConnectedServiceName`, `ESRPClientId`, `AppRegistrationClientId`, `AppRegistrationTenantId`, `AuthAKVName`, `AuthSignCertName`) | - ---- - -## 7. Code Signing (ESRP) - -All packages are signed using **ESRP (Enterprise Security Release Pipeline)** with federated identity authentication. - -### Signing Flow (per job) - -#### csproj-based Extension Packages (Logging, Abstractions, Azure) -1. **Build DLLs only** — `build.proj` target (e.g., `BuildLogging`) compiles assemblies without creating NuGet packages -2. **ESRP DLL signing** — Assemblies are signed with Authenticode certificates via ESRP -3. **NuGet pack** — `build.proj` pack target (e.g., `PackLogging`) creates `.nupkg` from signed DLLs using `NoBuild=true` -4. **ESRP NuGet signing** — The `.nupkg` files are signed with NuGet certificates via ESRP - -This workflow ensures the NuGet package contains **signed DLLs** rather than signing the NuGet package around unsigned assemblies. - -#### nuspec-based Packages (SqlServer.Server, SqlClient, AKV Provider) -1. **Build + pack** — MSBuild creates both assemblies and NuGet packages -2. **ESRP DLL signing** — Assemblies are signed with Authenticode certificates via ESRP -3. **ESRP NuGet signing** — The `.nupkg` files are signed with NuGet certificates via ESRP - -### Credential Model - -- Extension packages (Logging, Abstractions, Azure, SqlServer.Server, SqlClient) use the primary ESRP credentials from the `ESRP Federated Creds (AME)` variable group (loaded via `common-variables.yml`) -- AKV Provider uses separate `Signing`-prefixed credential parameters that are passed explicitly to the `build-akv-official-job.yml` template -- All credentials are sourced from Azure Key Vault and federated identity — no secrets stored in pipeline YAML - ---- - -## 8. SDL & Compliance (OneBranch) - -Both pipelines use **OneBranch governed templates** for 1ES compliance. The SDL configuration differs between Official and Non-Official: - -| SDL Tool | Official | Non-Official | Purpose | -|----------|----------|--------------|---------| -| **TSA** | ✅ `enabled: true` | ❌ `enabled: false` | Uploads SDL results to TSA for downstream analysis | -| **ApiScan** | ✅ `enabled: true`, `break: true` | ✅ `enabled: true`, `break: true` | Scans APIs for compliance issues | -| **CodeQL** | ✅ (non-preview) | ✅ (non-preview) | Static analysis for security vulnerabilities | -| **SBOM** | ✅ (non-preview) | ✅ (non-preview) | Software Bill of Materials generation | -| **Policheck** | ✅ `break: true` | ✅ `break: true` | Scans for policy-violating content | -| **BinSkim** | ✅ (async, non-preview) | ✅ (async, non-preview) | Binary security analysis | -| **CredScan** | ✅ (async, non-preview) | ✅ (async, non-preview) | Credential leak detection | -| **Roslyn** | ✅ (async, non-preview) | ✅ (async, non-preview) | Roslyn-based security analyzers | -| **Armory** | ✅ `break: true` | ✅ `break: true` | Additional security scanning | - -### APIScan Configuration - -APIScan is configured at **both pipeline level and job level**: - -**Pipeline-level** (`globalSdl:apiscan:`): Sets default configuration inherited by all jobs. This is configured for MDS (Microsoft.Data.SqlClient) as the primary product. - -**Job-level** (`ob_sdl_apiscan_*` variables): Each build job overrides the pipeline defaults with package-specific settings: - -| Variable | Purpose | -|----------|---------| -| `ob_sdl_apiscan_enabled` | Enable/disable APIScan for this job (`true`) | -| `ob_sdl_apiscan_softwareFolder` | Path to signed DLLs for scanning | -| `ob_sdl_apiscan_symbolsFolder` | Path to PDBs for scanning | -| `ob_sdl_apiscan_softwarename` | Package name (e.g., `Microsoft.Data.SqlClient.Internal.Logging`) | -| `ob_sdl_apiscan_versionNumber` | Assembly file version | - -Each job copies its signed DLLs and PDBs to a package-specific folder under `$(Build.SourcesDirectory)/apiScan//` after ESRP DLL signing, ensuring APIScan analyzes the correct signed binaries for each package. - -> **PRC Compliance**: The Official pipeline hardcodes `OneBranch.Official.CrossPlat.yml` (not parameterized) to satisfy Production Readiness Check static verification requirements. - ---- - -## 9. Artifact Strategy - -- Each build job publishes its output as a **pipeline artifact** managed by OneBranch's `ob_outputDirectory` convention. -- Artifact names follow the OneBranch auto-generated pattern: `drop__` (e.g., `drop_build_dependent_build_package_SqlClient`). -- Downstream stages use `DownloadPipelineArtifact@2` to pull required packages into a local directory. -- A local NuGet source is configured at build time pointing to the downloaded artifacts directory so `dotnet restore` resolves internal dependencies. - ---- - -## 10. Trigger Configuration - -### Official Pipeline (`dotnet-sqlclient-official-pipeline.yml`) - -```yaml -trigger: - branches: - include: - - internal/main - paths: - include: - - .azuredevops - - .config - - doc - - eng/pipelines - - src - - tools - - azurepipelines-coverage.yml - - build.proj - - NuGet.config - -schedules: - - cron: '30 4 * * Mon' # Weekly Sunday 9:30 PM (UTC-7) - branches: { include: [internal/main] } - always: true - - cron: '30 3 * * Mon-Fri' # Weekday 8:30 PM (UTC-7) - branches: { include: [internal/main] } -``` - -- **CI trigger**: Runs on pushes to `internal/main` when relevant paths change -- **Scheduled**: Weekly full build (Sundays) + weekday builds (Mon–Fri) -- **No PR trigger**: Official pipeline should not run on PRs (separate PR pipelines exist) - -### Non-Official Pipeline (`dotnet-sqlclient-non-official-pipeline.yml`) - -```yaml -trigger: none -pr: none -``` - -- **Manual only**: Queued on-demand for validation/test builds - ---- - -## 11. Infrastructure - -| Concern | Implementation | -|---------|---------------| -| **Pipeline template** | OneBranch governed templates (`OneBranch.Pipelines/GovernedTemplates`) | -| **Build agents** | OneBranch-managed Windows containers (`WindowsHostVersion: 1ESWindows2022`) | -| **.NET SDK** | Pinned via `global.json` (with `useGlobalJson: true` in install steps) | -| **Code signing** | ESRP v2 with federated identity (Azure Key Vault backed) | -| **Symbol publishing** | Optional, controlled by `publishSymbols` parameter; uses `Symbols publishing` variable group (aliases `SymbolServer`/`SymbolTokenUri` defined in `common-variables.yml`) | - ---- - -## 12. Key Design Decisions - -1. **Single pipeline, multiple stages** — avoids managing 6 separate pipelines while keeping clear separation of concerns. -2. **Official + Non-Official variants** — hardcoded OneBranch templates (no parameterized `oneBranchType`) for PRC compliance; Non-Official variant allows manual validation builds. -3. **Parallel jobs within stages** — minimizes total wall-clock time; only waits where dependencies demand it. -4. **Pipeline artifacts over Universal Packages** — faster, ephemeral, scoped to the run; appropriate for build-time dependency resolution. -5. **ESRP-based code signing** — all DLLs and NuGet packages are signed in-pipeline using ESRP with federated identity; no secrets in YAML. -6. **Centralized version management** — all 6 package versions (GA + preview) defined once in `libraries/common-variables.yml`; `isPreview` toggle selects the active set. -7. **Dependency-aware stage ordering** — ensures packages are always built after their dependencies, guaranteeing consistent, reproducible builds. -8. **Validation in parallel with Stage 3** — MDS package validation runs alongside AKV Provider build (both depend on Stage 2), reducing total pipeline duration. -9. **Selective on-demand release** — 6 boolean parameters control which packages are published; the release stage is entirely skipped when none are selected, keeping normal CI builds unaffected. -10. **ADO Environment approval gate** — two environments: `NuGet-Production` (official, with configured approvals) and `NuGet-DryRun` (non-official, validation only). Both use `ob_release_environment` for OneBranch integration. -11. **Compile-time conditional publish jobs** — `${{ if eq(parameters.releaseXxx, true) }}` template expansion ensures unselected publish jobs are excluded entirely from the pipeline run (not just skipped at runtime). -12. **Mandatory dry-run for non-official** — the non-official variant hardcodes `dryRun: true` (no parameter), preventing accidental publication. The official variant defaults `releaseDryRun: true` for safety but allows override for actual releases. -13. **Selective build parameters** — `buildSqlClient`, `buildSqlServerServer`, and `buildAKVProvider` allow building subsets of packages, with dependency-aware conditionals ensuring Logging builds when either SqlClient or AKV is needed. +## Package Dependency Order + +Respect this graph when modifying build stages: + +1. `Microsoft.SqlServer.Server` — no dependencies +2. `Microsoft.Data.SqlClient.Internal.Logging` — no dependencies +3. `Microsoft.Data.SqlClient.Extensions.Abstractions` — depends on Logging +4. `Microsoft.Data.SqlClient` — depends on Logging + Abstractions +5. `Microsoft.Data.SqlClient.Extensions.Azure` — depends on Abstractions + Logging +6. `Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider` — depends on SqlClient + Abstractions + Logging + +## Build Stages + +Defined in `stages/build-stages.yml`. Four build stages plus validation, ordered by dependency: + +- **`build_independent`** (Stage 1) — Logging and SqlServer.Server in parallel; no inter-package dependencies +- **`build_abstractions`** (Stage 2) — Abstractions; `dependsOn: build_independent`; downloads Logging artifact +- **`build_dependent`** (Stage 3) — SqlClient and Extensions.Azure in parallel; `dependsOn: build_abstractions`; downloads Abstractions + Logging artifacts +- **`build_addons`** (Stage 4) — AKV Provider; `dependsOn: build_dependent`; downloads SqlClient + Abstractions + Logging artifacts +- **`mds_package_validation`** — Validates signed SqlClient package; `dependsOn: build_dependent`; runs in parallel with Stage 4 + +Stage conditional rules: +- Wrap stages/jobs in `${{ if }}` compile-time conditionals based on build parameters +- `buildSqlClient` controls Stages 2, 3, validation, and Logging (when AKV is disabled) +- `buildAKVProvider AND buildSqlClient` controls Stage 4 +- `buildSqlServerServer` controls SqlServer.Server job in Stage 1 +- Logging builds when `buildAKVProvider OR buildSqlClient` is true + +## Job Templates + +- **`build-signed-csproj-package-job.yml`** — Generic job for csproj-based packages (Logging, SqlServer.Server, Abstractions, Azure, AKV Provider). Flow: Build DLLs → ESRP DLL signing → NuGet pack (`NoBuild=true`) → ESRP NuGet signing +- **`build-signed-sqlclient-package-job.yml`** — SqlClient-specific job (nuspec-based). Flow: Build all configurations → ESRP DLL signing (main + resource DLLs) → NuGet pack via nuspec → ESRP NuGet signing +- **`validate-signed-package-job.yml`** — Validates signed MDS package (signature, strong names, folder structure, target frameworks) +- **`publish-nuget-package-job.yml`** — Reusable release job using OneBranch `templateContext.type: releaseJob` with `inputs` for artifact download; pushes via `NuGetCommand@2` + +When adding a new csproj-based package: +- Use `build-signed-csproj-package-job.yml` with appropriate `packageName`, `packageFullName`, `versionProperties`, and `downloadArtifacts` +- Add build and pack targets to `build.proj` +- Add version variables to `variables/common-variables.yml` +- Add artifact name variable to `variables/onebranch-variables.yml` + +## Release Stage + +- Defined in `stages/release-stages.yml`; produces stage `release_production` (official) or `release_test` (non-official) via `stageNameSuffix` parameter +- Entire stage excluded at compile time when no release parameters are true +- `dependsOn` is conditional based on which release parameters are set +- `releaseToProduction` parameter controls NuGet target feed: + - `true` → service connection `ADO Nuget Org Connection` (NuGet Production) + - `false` → service connection `ADO Nuget Org Test Connection` (NuGet Test) +- Non-official pipeline always sets `releaseToProduction: false` +- Environment gating: + - Official: `ob_release_environment: Production`, `ob_deploymentjob_environment: NuGet-Production` + - Non-official: `ob_release_environment: Test`, `ob_deploymentjob_environment: NuGet-DryRun` +- Each publish job uses OneBranch deployment job syntax (`templateContext.type: releaseJob` with `inputs` for artifact download) + +## Parameters + +Build parameters (all boolean, default `true`): +- `debug` — enable debug output (default `false`) +- `isPreview` — use preview version numbers (default `false`) +- `publishSymbols` — publish symbols to servers (default `false`) +- `buildSqlServerServer` — build SqlServer.Server package +- `buildSqlClient` — build SqlClient, Extensions.Azure, Abstractions, and Logging +- `buildAKVProvider` — build AKV Provider (requires `buildSqlClient`) + +Release parameters (all boolean, default `false`): +- `releaseSqlServerServer`, `releaseLogging`, `releaseAbstractions`, `releaseSqlClient`, `releaseAzure`, `releaseAKVProvider` + +Official-only parameter: +- `releaseToProduction` — push to NuGet Production feed (default `false`) + +When `isPreview` is true, pipeline resolves `effective*Version` variables to preview versions; otherwise GA versions. All versions defined in `variables/common-variables.yml`. + +## Variables and Versions + +- Variable chain: pipeline YAML → `variables/onebranch-variables.yml` → `variables/common-variables.yml` +- All package versions (GA, preview, assembly file) centralized in `variables/common-variables.yml` +- `effective*Version` pipeline variables map to selected version set based on `isPreview` +- Artifact name variables defined in `variables/onebranch-variables.yml` following `drop__` pattern +- `assemblyBuildNumber` derived from first segment of `Build.BuildNumber` only (16-bit limit) +- When adding a new package, add GA version, preview version, and assembly file version entries + +Variable groups: +- `Release Variables` — release configuration (in `common-variables.yml`) +- `Symbols publishing` — symbol publishing credentials (in `common-variables.yml`) +- `ESRP Federated Creds (AME)` — ESRP signing credentials (in `common-variables.yml`) + +## Code Signing (ESRP) + +- Uses ESRP v6 tasks (`EsrpMalwareScanning@6`, `EsrpCodeSigning@6`) with MSI/federated identity authentication +- Signing only runs when `isOfficial: true` — non-official pipelines skip ESRP steps +- csproj-based packages: sign DLLs first → pack with `NoBuild=true` → sign NuGet package (ensures NuGet contains signed DLLs) +- SqlClient: sign DLLs (including resource DLLs) → nuspec pack → sign NuGet package +- DLL signing uses keyCode `CP-230012` (Authenticode); NuGet signing uses keyCode `CP-401405` +- All ESRP credentials come from variable groups — never hardcode secrets in YAML + +## SDL and Compliance + +- TSA: enabled only in official pipeline; disabled in non-official to avoid spurious alerts +- ApiScan: enabled in both; currently `break: false` pending package registration +- Each build job sets `ob_sdl_apiscan_*` variables pointing to `$(Build.SourcesDirectory)/apiScan//` +- CodeQL, SBOM, Policheck (`break: true`): enabled in both pipelines +- asyncSdl `enabled: false` in both; individual sub-tools (CredScan, BinSkim, Armory, Roslyn) configured underneath +- Policheck exclusions: `$(REPO_ROOT)\.config\PolicheckExclusions.xml` +- CredScan suppressions: `$(REPO_ROOT)/.config/CredScanSuppressions.json` + +## Artifact Conventions + +- `ob_outputDirectory` set to `$(PACK_OUTPUT)` (= `$(REPO_ROOT)/output`) — OneBranch auto-publishes this directory +- Artifact names follow `drop__` — defined in `variables/onebranch-variables.yml` +- Downstream jobs download artifacts via `DownloadPipelineArtifact@2` into `$(Build.SourcesDirectory)/packages` +- Downloaded packages serve as a local NuGet source for `dotnet restore` +- If stage or job names change, update artifact name variables in `onebranch-variables.yml` + +## Common Pitfalls + +- Do not use `PublishPipelineArtifacts` task — OneBranch auto-publishes from `ob_outputDirectory` +- Do not add `NuGetToolInstaller@1` in OneBranch containers — NuGet is pre-installed +- Variable templates are under `variables/` not `libraries/` +- Always test parameter changes in the non-official pipeline first +- When modifying stage names, update all `dependsOn` references and artifact name variables +- Release jobs must use `templateContext.type: releaseJob` with `inputs` for artifact download — deployment jobs do not auto-download artifacts diff --git a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml index 01d9b61da1..e257386918 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml @@ -70,6 +70,11 @@ parameters: - name: publishSymbols type: boolean + # True to enable ESRP malware scanning and code signing steps, which should not be + # run on non-official pipelines as they access production resources. + - name: isOfficial + type: boolean + # Values required by ESRP tasks. - name: esrpConnectedServiceName type: string @@ -144,18 +149,19 @@ jobs: buildConfiguration: ${{ parameters.buildConfiguration }} versionProperties: ${{ parameters.versionProperties }} - # ESRP sign the DLLs. - - template: /eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml@self - parameters: - appRegistrationClientId: ${{ parameters.appRegistrationClientId }} - appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} - authAkvName: ${{ parameters.authAkvName }} - authSignCertName: ${{ parameters.authSignCertName }} - esrpClientId: ${{ parameters.esrpClientId }} - esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - pattern: ${{ parameters.packageFullName }}.dll - - # Copy signed DLLs and PDBs to APIScan folders. + - ${{ if eq(parameters.isOfficial, true) }}: + # ESRP sign the DLLs. + - template: /eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml@self + parameters: + appRegistrationClientId: ${{ parameters.appRegistrationClientId }} + appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} + authAkvName: ${{ parameters.authAkvName }} + authSignCertName: ${{ parameters.authSignCertName }} + esrpClientId: ${{ parameters.esrpClientId }} + esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} + pattern: ${{ parameters.packageFullName }}.dll + + # Copy signed/unsigned DLLs and PDBs to APIScan folders. - task: CopyFiles@2 displayName: Copy DLLs for APIScan inputs: @@ -182,16 +188,17 @@ jobs: buildConfiguration: ${{ parameters.buildConfiguration }} versionProperties: ${{ parameters.versionProperties }} - # ESRP sign the NuGet package. - - template: /eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml@self - parameters: - appRegistrationClientId: ${{ parameters.appRegistrationClientId }} - appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} - authAkvName: ${{ parameters.authAkvName }} - authSignCertName: ${{ parameters.authSignCertName }} - esrpClientId: ${{ parameters.esrpClientId }} - esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - pattern: '${{ parameters.packageFullName }}.*nupkg' + - ${{ if eq(parameters.isOfficial, true) }}: + # ESRP sign the NuGet package. + - template: /eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml@self + parameters: + appRegistrationClientId: ${{ parameters.appRegistrationClientId }} + appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} + authAkvName: ${{ parameters.authAkvName }} + authSignCertName: ${{ parameters.authSignCertName }} + esrpClientId: ${{ parameters.esrpClientId }} + esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} + pattern: ${{ parameters.packageFullName }}.*nupkg # Publish symbols to servers - ${{ if eq(parameters.publishSymbols, true) }}: diff --git a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml index 98f5c811aa..f3033b07f0 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml @@ -16,123 +16,130 @@ parameters: - name: isPreview type: boolean + # True to enable ESRP malware scanning and code signing steps, which should not + # be run on non-official pipelines as they access production resources. + - name: isOfficial + type: boolean + jobs: -- job: build_package_SqlClient - displayName: 'Build Microsoft.Data.SqlClient' - pool: - type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs - - variables: - ob_outputDirectory: $(PACK_OUTPUT) - # APIScan configuration for this Extension package - ob_sdl_apiscan_enabled: true - ob_sdl_apiscan_softwareFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/dlls - ob_sdl_apiscan_symbolsFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/pdbs - ob_sdl_apiscan_softwarename: Microsoft.Data.SqlClient - ob_sdl_apiscan_versionNumber: $(assemblyBuildNumber) - - ${{ if parameters.isPreview }}: - abstractionsPackageVersion: $(abstractionsPackagePreviewVersion) - loggingPackageVersion: $(loggingPackagePreviewVersion) - mdsPackageVersion: $(mdsPackagePreviewVersion) - - steps: - - script: SET - displayName: 'Print Environment Variables' - - # Download the Abstractions and Logging packages from the previous stage into - # packages/ so that they're available via the local NuGet feed when restoring MDS. - # MDS depends on both Extensions.Abstractions and Internal.Logging. - - task: DownloadPipelineArtifact@2 - displayName: Download Abstractions Package - inputs: - artifactName: $(abstractionsArtifactsName) - targetPath: $(Build.SourcesDirectory)/packages - - - task: DownloadPipelineArtifact@2 - displayName: Download Logging Package - inputs: - artifactName: $(loggingArtifactsName) - targetPath: $(Build.SourcesDirectory)/packages - - # Install the .NET SDK. - - template: /eng/pipelines/steps/install-dotnet.yml@self - - # Build our tooling, which is required by the analysis step below, but - # shouldn't be analyzed itself. - - task: MSBuild@1 - displayName: 'Build Tooling' - inputs: - solution: '**/build.proj' - configuration: Release - msbuildArguments: -t:BuildTools - - # Perform analysis before building, since this step will clobber build output. - - template: /eng/pipelines/onebranch/steps/code-analyze-step.yml@self - - # Build MDS, producing signed DLLs. - - template: /eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml@self - parameters: - # These variables are sourced from common-variables.yml. - abstractionsAssemblyFileVersion: $(abstractionsAssemblyFileVersion) - abstractionsPackageVersion: $(abstractionsPackageVersion) - loggingAssemblyFileVersion: $(loggingAssemblyFileVersion) - loggingPackageVersion: $(loggingPackageVersion) - mdsAssemblyFileVersion: $(mdsAssemblyFileVersion) - mdsPackageVersion: $(mdsPackageVersion) - - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: dll - sourceRoot: $(BUILD_OUTPUT) - dllPattern: 'Microsoft.Data.SqlClient.dll' - - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: dll - sourceRoot: $(BUILD_OUTPUT) - dllPattern: 'Microsoft.Data.SqlClient.resources.dll' - - - template: /eng/pipelines/common/templates/steps/generate-nuget-package-step.yml@self - parameters: - buildConfiguration: Release - displayName: 'Create MDS NuGet Package' - generateSymbolsPackage: true - installNuget: false - nuspecPath: $(nuspecPath) - outputDirectory: $(PACK_OUTPUT) - packageVersion: $(mdsPackageVersion) - properties: 'AbstractionsPackageVersion=$(abstractionsPackageVersion);LoggingPackageVersion=$(loggingPackageVersion)' - referenceType: Package - - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: pkg - nupkgPattern: 'Microsoft.Data.SqlClient.$(mdsPackageVersion).*nupkg' - - # Copy signed DLLs and PDBs to APIScan folders. - - task: CopyFiles@2 - displayName: Copy DLLs for APIScan - inputs: - SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: '**/Microsoft.Data.SqlClient.dll' - TargetFolder: $(ob_sdl_apiscan_softwareFolder) - # We must preserve the folder structure since our C# projects may produce multiple - # identically named DLLs for different target frameworks (e.g. netstandard2.0, net5.0, - # etc.), and we need to keep those separate for APIScan to work correctly. - flattenFolders: false - - - task: CopyFiles@2 - displayName: Copy PDBs for APIScan - inputs: - SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: '**/Microsoft.Data.SqlClient.pdb' - TargetFolder: $(ob_sdl_apiscan_symbolsFolder) - flattenFolders: false - - # Publish symbols to servers - - ${{ if eq(parameters.publishSymbols, true) }}: - - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self - parameters: - packageFullName: Microsoft.Data.SqlClient - packageVersion: $(mdsPackageVersion) + - job: build_package_SqlClient + displayName: "Build Microsoft.Data.SqlClient" + pool: + type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs + + variables: + ob_outputDirectory: $(PACK_OUTPUT) + # APIScan configuration for this Extension package + ob_sdl_apiscan_enabled: true + ob_sdl_apiscan_softwareFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/dlls + ob_sdl_apiscan_symbolsFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/pdbs + ob_sdl_apiscan_softwarename: Microsoft.Data.SqlClient + ob_sdl_apiscan_versionNumber: $(assemblyBuildNumber) + + ${{ if parameters.isPreview }}: + abstractionsPackageVersion: $(abstractionsPackagePreviewVersion) + loggingPackageVersion: $(loggingPackagePreviewVersion) + mdsPackageVersion: $(mdsPackagePreviewVersion) + + steps: + - script: SET + displayName: "Print Environment Variables" + + # Download the Abstractions and Logging packages from the previous stage into + # packages/ so that they're available via the local NuGet feed when restoring MDS. + # MDS depends on both Extensions.Abstractions and Internal.Logging. + - task: DownloadPipelineArtifact@2 + displayName: Download Abstractions Package + inputs: + artifactName: $(abstractionsArtifactsName) + targetPath: $(Build.SourcesDirectory)/packages + + - task: DownloadPipelineArtifact@2 + displayName: Download Logging Package + inputs: + artifactName: $(loggingArtifactsName) + targetPath: $(Build.SourcesDirectory)/packages + + # Install the .NET SDK. + - template: /eng/pipelines/steps/install-dotnet.yml@self + + # Build our tooling, which is required by the analysis step below, but + # shouldn't be analyzed itself. + - task: MSBuild@1 + displayName: "Build Tooling" + inputs: + solution: "**/build.proj" + configuration: Release + msbuildArguments: -t:BuildTools + + # Perform analysis before building, since this step will clobber build output. + - template: /eng/pipelines/onebranch/steps/code-analyze-step.yml@self + + # Build MDS, producing signed DLLs. + - template: /eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml@self + parameters: + # These variables are sourced from common-variables.yml. + abstractionsAssemblyFileVersion: $(abstractionsAssemblyFileVersion) + abstractionsPackageVersion: $(abstractionsPackageVersion) + loggingAssemblyFileVersion: $(loggingAssemblyFileVersion) + loggingPackageVersion: $(loggingPackageVersion) + mdsAssemblyFileVersion: $(mdsAssemblyFileVersion) + mdsPackageVersion: $(mdsPackageVersion) + + - ${{ if eq(parameters.isOfficial, true) }}: + - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self + parameters: + artifactType: dll + sourceRoot: $(BUILD_OUTPUT) + dllPattern: "Microsoft.Data.SqlClient.dll" + + - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self + parameters: + artifactType: dll + sourceRoot: $(BUILD_OUTPUT) + dllPattern: "Microsoft.Data.SqlClient.resources.dll" + + - template: /eng/pipelines/common/templates/steps/generate-nuget-package-step.yml@self + parameters: + buildConfiguration: Release + displayName: "Create MDS NuGet Package" + generateSymbolsPackage: true + installNuget: false + nuspecPath: $(nuspecPath) + outputDirectory: $(PACK_OUTPUT) + packageVersion: $(mdsPackageVersion) + properties: "AbstractionsPackageVersion=$(abstractionsPackageVersion);LoggingPackageVersion=$(loggingPackageVersion)" + referenceType: Package + + - ${{ if eq(parameters.isOfficial, true) }}: + - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self + parameters: + artifactType: pkg + nupkgPattern: "Microsoft.Data.SqlClient.$(mdsPackageVersion).*nupkg" + + # Copy signed DLLs and PDBs to APIScan folders. + - task: CopyFiles@2 + displayName: Copy DLLs for APIScan + inputs: + SourceFolder: $(BUILD_OUTPUT)/Package/bin + Contents: "**/Microsoft.Data.SqlClient.dll" + TargetFolder: $(ob_sdl_apiscan_softwareFolder) + # We must preserve the folder structure since our C# projects may produce multiple + # identically named DLLs for different target frameworks (e.g. netstandard2.0, net5.0, + # etc.), and we need to keep those separate for APIScan to work correctly. + flattenFolders: false + + - task: CopyFiles@2 + displayName: Copy PDBs for APIScan + inputs: + SourceFolder: $(BUILD_OUTPUT)/Package/bin + Contents: "**/Microsoft.Data.SqlClient.pdb" + TargetFolder: $(ob_sdl_apiscan_symbolsFolder) + flattenFolders: false + + # Publish symbols to servers + - ${{ if eq(parameters.publishSymbols, true) }}: + - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self + parameters: + packageFullName: Microsoft.Data.SqlClient + packageVersion: $(mdsPackageVersion) diff --git a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml index 656ea8f181..3b463d0b81 100644 --- a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml +++ b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml @@ -4,7 +4,6 @@ # See the LICENSE file in the project root for more information. # ################################################################################# parameters: - # The name of the pipeline artifacts to download prior to building the tests. - name: artifactName type: string @@ -13,269 +12,277 @@ parameters: - name: isPreview type: boolean + # True if this build is an official build. This will be used to gate some checks + # that only apply to official builds, such as signature verification. + - name: isOfficial + type: boolean + jobs: -- job: validate_signed_package - displayName: 'Verify signed package' - - pool: - type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs - isCustom: true - name: ADO-1ES-Pool - vmImage: 'ADO-MMS22-SQL19' - - variables: # More settings at https://aka.ms/obpipelines/yaml/jobs - - template: /eng/pipelines/onebranch/variables/sqlclient-validation-variables.yml@self - - - name: pathToDownloadedNuget # path to the downloaded nuget files - value: $(Pipeline.Workspace)\${{parameters.artifactName }} - - - ${{ if parameters.isPreview }}: - - name: extractedNugetPath - value: $(extractedNugetRootPath).$(mdsPackagePreviewVersion) - - name: mdsPackageVersion - value: $(mdsPackagePreviewVersion) - - steps: - - script: SET - displayName: 'Print Environment Variables' - - - task: NuGetToolInstaller@1 - displayName: 'Use NuGet' - - - powershell: | - # Displays the paths of all the local cache directories - nuget locals all -List - - #Clears all files from all local cache directories - nuget locals all -Clear - displayName: 'Clear local cache' - - - download: current - artifact: ${{parameters.artifactName}} - patterns: '**/*.*nupkg' - displayName: 'Download NuGet Package' - - - powershell: | - # Install nuget package - Install-Package -Name "Microsoft.Data.SqlClient" -Destination "$(TempFolderName)" -Force -Source $(pathToDownloadedNuget) -SkipDependencies - - Write-Host "--------------------------------------------------" - Write-Host '$(TempFolderName)' - ls $(TempFolderName) - Write-Host "--------------------------------------------------" - displayName: 'Extract Nuget in temp folder' - - - powershell: | - Write-Host "--------------------------------------------------" - Write-Host "This will verify the artifact signature" -ForegroundColor Green - Write-Host "--------------------------------------------------" - - nuget verify -All $(pathToDownloadedNuget)\*.nupkg - nuget verify -All $(pathToDownloadedNuget)\*.snupkg - displayName: 'Verify nuget signature' - - - powershell: | - # Recursively find all .dll files in TempFolder (installed nuget folder) - # Microsoft.Data.SqlClient.dll and Microsoft.Data.SqlClient.resources.dll (in localized folders) should have strong name - $dllFiles = Get-ChildItem -Path $(TempFolderName) -Recurse -Filter *.dll - $badDlls = @() - foreach ($file in $dllFiles) - { - # Run sn.exe to verify the strong name on each dll - $result = & "C:\Program Files (x86)\Microsoft SDKs\Windows\*\bin\NETFX 4.8.1 Tools\sn.exe" -vf $file.FullName - Write-OutPut $result - - # if the dll is not valid, it would be delay signed or test-signed which is not meant for production - if($result[$result.Length-1] -notlike "* is valid") - { - $badDlls += $result[$result.Length-1] - } - } - if($badDlls.Count -gt 0) - { - Write-OutPut "Error: Invalid dlls are detected. Check the list below:" - foreach($dll in $badDlls) - { - Write-Output $dll - } - Exit -1 - } - Write-Host "Strong name has been verified for all dlls" - displayName: 'Verify assembly strong names' - - - powershell: | - # Checks the expected folder names such as lib, ref, runtimes - Get-ChildItem -Path $(extractedNugetPath) -Directory | select Name | foreach { - if('$(expectedFolderNames)'.contains($_.Name)){ - Write-Host expected folder name verfied: $_.Name - } - } - displayName: 'Check expected folder names' - - - powershell: | - # Checks the version of DotNetFramework and DotNet - $countErr = 0 - $countPass = 0 - $excludNamesFromRuntimeFolder = 'lib','win','unix' - - Get-ChildItem -Path $(extractedNugetPath) -Directory | foreach { - $parentname=$_.Name - Write-Host $_.FullName -ForegroundColor yellow - - if($_.Name -ne 'runtimes') { - Get-ChildItem -Path $_.FullName -Directory | select Name | foreach { - if('$(expectedDotnetVersions)'.Contains($_.Name)){ - Write-Host "`tExpected version verified in $parentname": $_.Name -ForegroundColor green - $countPass += 1 + - job: validate_nuget_package + displayName: "Validate NuGet package" + + pool: + type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs + isCustom: true + name: ADO-1ES-Pool + vmImage: "ADO-MMS22-SQL19" + + variables: # More settings at https://aka.ms/obpipelines/yaml/jobs + - template: /eng/pipelines/onebranch/variables/sqlclient-validation-variables.yml@self + + - name: pathToDownloadedNuget # path to the downloaded nuget files + value: $(Pipeline.Workspace)\${{parameters.artifactName }} + + - ${{ if parameters.isPreview }}: + - name: extractedNugetPath + value: $(extractedNugetRootPath).$(mdsPackagePreviewVersion) + - name: mdsPackageVersion + value: $(mdsPackagePreviewVersion) + + steps: + - script: SET + displayName: "Print Environment Variables" + + - task: NuGetToolInstaller@1 + displayName: "Use NuGet" + + - powershell: | + # Displays the paths of all the local cache directories + nuget locals all -List + + #Clears all files from all local cache directories + nuget locals all -Clear + displayName: "Clear local cache" + + - download: current + artifact: ${{parameters.artifactName}} + patterns: "**/*.*nupkg" + displayName: "Download NuGet Package" + + - powershell: | + # Install nuget package + Install-Package -Name "Microsoft.Data.SqlClient" -Destination "$(TempFolderName)" -Force -Source $(pathToDownloadedNuget) -SkipDependencies + + Write-Host "--------------------------------------------------" + Write-Host '$(TempFolderName)' + ls $(TempFolderName) + Write-Host "--------------------------------------------------" + displayName: "Extract Nuget in temp folder" + + - ${{ if eq(parameters.isOfficial, true) }}: + - powershell: | + Write-Host "--------------------------------------------------" + Write-Host "This will verify the artifact signature" -ForegroundColor Green + Write-Host "--------------------------------------------------" + + nuget verify -All $(pathToDownloadedNuget)\*.nupkg + nuget verify -All $(pathToDownloadedNuget)\*.snupkg + displayName: "Verify nuget signature" + + - powershell: | + # Recursively find all .dll files in TempFolder (installed nuget folder) + # Microsoft.Data.SqlClient.dll and Microsoft.Data.SqlClient.resources.dll (in localized folders) should have strong name + $dllFiles = Get-ChildItem -Path $(TempFolderName) -Recurse -Filter *.dll + $badDlls = @() + foreach ($file in $dllFiles) + { + # Run sn.exe to verify the strong name on each dll + $result = & "C:\Program Files (x86)\Microsoft SDKs\Windows\*\bin\NETFX 4.8.1 Tools\sn.exe" -vf $file.FullName + Write-OutPut $result + + # if the dll is not valid, it would be delay signed or test-signed which is not meant for production + if($result[$result.Length-1] -notlike "* is valid") + { + $badDlls += $result[$result.Length-1] } - else{ - Write-Host "`tUnexpected version detected in $parentname": $_.Name - $countErr += 1 + } + if($badDlls.Count -gt 0) + { + Write-OutPut "Error: Invalid dlls are detected. Check the list below:" + foreach($dll in $badDlls) + { + Write-Output $dll } + Exit -1 + } + Write-Host "Strong name has been verified for all dlls" + displayName: "Verify assembly strong names" + + - powershell: | + # Checks the expected folder names such as lib, ref, runtimes + Get-ChildItem -Path $(extractedNugetPath) -Directory | select Name | foreach { + if('$(expectedFolderNames)'.contains($_.Name)){ + Write-Host expected folder name verfied: $_.Name + } } - } + displayName: "Check expected folder names" + + - powershell: | + # Checks the version of DotNetFramework and DotNet + $countErr = 0 + $countPass = 0 + $excludNamesFromRuntimeFolder = 'lib','win','unix' + + Get-ChildItem -Path $(extractedNugetPath) -Directory | foreach { + $parentname=$_.Name + Write-Host $_.FullName -ForegroundColor yellow + + if($_.Name -ne 'runtimes') { + Get-ChildItem -Path $_.FullName -Directory | select Name | foreach { + if('$(expectedDotnetVersions)'.Contains($_.Name)){ + Write-Host "`tExpected version verified in $parentname": $_.Name -ForegroundColor green + $countPass += 1 + } + else{ + Write-Host "`tUnexpected version detected in $parentname": $_.Name + $countErr += 1 + } + } + } - elseif ($_.Name -eq 'runtimes'){ - Get-ChildItem -Depth 3 -Path $_.FullName -Exclude $excludNamesFromRuntimeFolder -Directory | foreach{ - if('$(expectedDotnetVersions)'.Contains($_.Name)){ - Write-Host "`tExpected version verfied in $parentname": $_.Name - $countPass += 1 + elseif ($_.Name -eq 'runtimes'){ + Get-ChildItem -Depth 3 -Path $_.FullName -Exclude $excludNamesFromRuntimeFolder -Directory | foreach{ + if('$(expectedDotnetVersions)'.Contains($_.Name)){ + Write-Host "`tExpected version verfied in $parentname": $_.Name + $countPass += 1 + } + else{ + Write-Host "`tUnexpected version detected": $_.Name -ForegroundColor Red + $countErr += 1 + } + } } else{ - Write-Host "`tUnexpected version detected": $_.Name -ForegroundColor Red - $countErr += 1 + Write-Host "`tUnknown folder " $_.Name -ForegroundColor Red + Exit -1 } } - } - else{ - Write-Host "`tUnknown folder " $_.Name -ForegroundColor Red - Exit -1 - } - } - - Write-Host "_______________" - Write-Host "Expected: $countPass" - Write-Host "Unexpected: $countErr" - Write-Host "_______________" - if ($countErr -ne 0) - { - Write-Host "Unexpected versions are detected!" -ForegroundColor Red - Exit -1 - } - displayName: 'Check Expected framework' - - - powershell: | - # list all the child items of created temp folder - - #Verify all DLLs unzipped match "expected" hierarchy - - foreach( $folderName in (Get-ChildItem -Path $(extractedNugetPath) -Directory).Name) - { - # List all Childerns of the Path - Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File - $subFiles = Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File - - foreach($file in $subFiles) - { - if($subFiles[0].Name -like "*.dll" ) + + Write-Host "_______________" + Write-Host "Expected: $countPass" + Write-Host "Unexpected: $countErr" + Write-Host "_______________" + if ($countErr -ne 0) { - Write-Host $subFiles[0].Name -ForegroundColor Green - Write-Host $subFiles[1].Name -ForegroundColor Green - if(($folderName -eq 'lib') -or ($folderName -eq 'ref')) + Write-Host "Unexpected versions are detected!" -ForegroundColor Red + Exit -1 + } + displayName: "Check Expected framework" + + - powershell: | + # list all the child items of created temp folder + + #Verify all DLLs unzipped match "expected" hierarchy + + foreach( $folderName in (Get-ChildItem -Path $(extractedNugetPath) -Directory).Name) + { + # List all Childerns of the Path + Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File + $subFiles = Get-ChildItem -Path $(extractedNugetPath)\$folderName -Recurse -File + + foreach($file in $subFiles) { - if($subFiles[2].Name -like "*.dll") + if($subFiles[0].Name -like "*.dll" ) { - Write-Host $subFiles[2].Name -ForegroundColor Green + Write-Host $subFiles[0].Name -ForegroundColor Green + Write-Host $subFiles[1].Name -ForegroundColor Green + if(($folderName -eq 'lib') -or ($folderName -eq 'ref')) + { + if($subFiles[2].Name -like "*.dll") + { + Write-Host $subFiles[2].Name -ForegroundColor Green + } + else + { + $subFiles[2].Name + Write-Host "Expected file pattern for localization did not match to *.dll" -ForegroundColor Red + Exit -1 + } + } } else { - $subFiles[2].Name - Write-Host "Expected file pattern for localization did not match to *.dll" -ForegroundColor Red + $subFiles[0].Name + $subFiles[1].Name + Write-Host "Expected file pattern did not match to *.dll" -ForegroundColor Red Exit -1 } } } - else - { - $subFiles[0].Name - $subFiles[1].Name - Write-Host "Expected file pattern did not match to *.dll" -ForegroundColor Red - Exit -1 - } - } - } - displayName: 'Verify all DLLs unzipped match "expected" hierarchy' - - powershell: | - # Verify all dlls status are Valid - - $dlls = Get-ChildItem -Path $(extractedNugetPath) -Recurse -Include *.dll - foreach ($status in $dlls | Get-AuthenticodeSignature) - { - if ($status.Status -eq "Valid") + displayName: 'Verify all DLLs unzipped match "expected" hierarchy' + + - ${{ if eq(parameters.isOfficial, true) }}: + - powershell: | + # Verify all dlls status are Valid + + $dlls = Get-ChildItem -Path $(extractedNugetPath) -Recurse -Include *.dll + foreach ($status in $dlls | Get-AuthenticodeSignature) + { + if ($status.Status -eq "Valid") + { + Write-Host $status.Status $status.Path + } + else + { + Write-Host "dll status of '$status.Path' is not valid!" -ForegroundColor Red + $status + Exit -1 + } + } + displayName: "Verify all dlls status are Valid" + + - powershell: | + # This will check each DLL's ProductVersion and FileVersion against + # expected values. + $failed = 0 + + foreach ( $pVersion in Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo ) { - Write-Host $status.Status $status.Path + if ($pVersion.ProductVersion -Like '$(mdsPackageVersion)*') + { + Write-Host -ForegroundColor Green "Correct ProductVersion detected for $($pVersion.FileName): $($pVersion.ProductVersion)" + } + else + { + Write-Host -ForegroundColor Red "Wrong ProductVersion detected for $($pVersion.FileName); expected: $(mdsPackageVersion); found: $($pVersion.ProductVersion)" + $failed = 1 + } + + if ($pVersion.FileVersion -eq '$(mdsAssemblyFileVersion)') + { + Write-Host -ForegroundColor Green "Correct FileVersion detected for $($pVersion.FileName): $($pVersion.FileVersion)" + } + else + { + Write-Host -ForegroundColor Red "Wrong FileVersion detected for $($pVersion.FileName); expected $(mdsAssemblyFileVersion); found: $($pVersion.FileVersion)" + $failed = 1 + } } - else + + if ($failed -ne 0) { - Write-Host "dll status of '$status.Path' is not valid!" -ForegroundColor Red - $status Exit -1 } - } - displayName: 'Verify all dlls status are Valid' - - powershell: | - # This will check each DLL's ProductVersion and FileVersion against - # expected values. - $failed = 0 + Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object VersionInfo | Format-List + displayName: 'Verify "File Version" matches expected values for DLLs' - foreach ( $pVersion in Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo ) - { - if ($pVersion.ProductVersion -Like '$(mdsPackageVersion)*') - { - Write-Host -ForegroundColor Green "Correct ProductVersion detected for $($pVersion.FileName): $($pVersion.ProductVersion)" - } - else - { - Write-Host -ForegroundColor Red "Wrong ProductVersion detected for $($pVersion.FileName); expected: $(mdsPackageVersion); found: $($pVersion.ProductVersion)" - $failed = 1 - } + - powershell: | + # Check assembly versions. + # + # GOTCHA: This expects the Versions.props file having XML elements in a + # certain order. If the order changes, this check will fail! + # + # TODO: This also isn't checking the versions of the actual assemblies in + # the package, so it isn't terribly useful. - if ($pVersion.FileVersion -eq '$(mdsAssemblyFileVersion)') - { - Write-Host -ForegroundColor Green "Correct FileVersion detected for $($pVersion.FileName): $($pVersion.FileVersion)" - } - else + [Xml] $versionprops = Get-Content -Path "tools/props/Versions.props" + $AssemblyFileVersion = $versionprops.Project.PropertyGroup[2].AssemblyFileVersion + $AssemblyVersion = $versionprops.Project.PropertyGroup[2].AssemblyVersion + + if($AssemblyFileVersion -eq $AssemblyVersion) { - Write-Host -ForegroundColor Red "Wrong FileVersion detected for $($pVersion.FileName); expected $(mdsAssemblyFileVersion); found: $($pVersion.FileVersion)" - $failed = 1 + Write-Host AssemblyFileVersion: $AssemblyFileVersion should not be equal to: $AssemblyVersion + Exit -1 } - } - - if ($failed -ne 0) - { - Exit -1 - } - - Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object VersionInfo | Format-List - displayName: 'Verify "File Version" matches expected values for DLLs' - - - powershell: | - # Check assembly versions. - # - # GOTCHA: This expects the Versions.props file having XML elements in a - # certain order. If the order changes, this check will fail! - # - # TODO: This also isn't checking the versions of the actual assemblies in - # the package, so it isn't terribly useful. - - [Xml] $versionprops = Get-Content -Path "tools/props/Versions.props" - $AssemblyFileVersion = $versionprops.Project.PropertyGroup[2].AssemblyFileVersion - $AssemblyVersion = $versionprops.Project.PropertyGroup[2].AssemblyVersion - - if($AssemblyFileVersion -eq $AssemblyVersion) - { - Write-Host AssemblyFileVersion: $AssemblyFileVersion should not be equal to: $AssemblyVersion - Exit -1 - } - displayName: 'Check "AssemblyFileVersion" is not same as "AssemblyVersion" in version.props' + displayName: 'Check "AssemblyFileVersion" is not same as "AssemblyVersion" in version.props' diff --git a/eng/pipelines/onebranch/sqlclient-non-official.yml b/eng/pipelines/onebranch/sqlclient-non-official.yml index 2274d8cccf..25dbe98097 100644 --- a/eng/pipelines/onebranch/sqlclient-non-official.yml +++ b/eng/pipelines/onebranch/sqlclient-non-official.yml @@ -94,31 +94,31 @@ variables: # Define the effective versions for all of the packages we build and release. - ${{ if parameters.isPreview }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackagePreviewVersion) - - name: effectiveLoggingVersion - value: $(loggingPackagePreviewVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackagePreviewVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackagePreviewVersion) - - name: effectiveAzureVersion - value: $(azurePackagePreviewVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackagePreviewVersion) + - name: effectiveSqlServerVersion + value: $(sqlServerPackagePreviewVersion) + - name: effectiveLoggingVersion + value: $(loggingPackagePreviewVersion) + - name: effectiveAbstractionsVersion + value: $(abstractionsPackagePreviewVersion) + - name: effectiveSqlClientVersion + value: $(mdsPackagePreviewVersion) + - name: effectiveAzureVersion + value: $(azurePackagePreviewVersion) + - name: effectiveAkvProviderVersion + value: $(akvPackagePreviewVersion) - ${{ else }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackageVersion) - - name: effectiveLoggingVersion - value: $(loggingPackageVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackageVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackageVersion) - - name: effectiveAzureVersion - value: $(azurePackageVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackageVersion) + - name: effectiveSqlServerVersion + value: $(sqlServerPackageVersion) + - name: effectiveLoggingVersion + value: $(loggingPackageVersion) + - name: effectiveAbstractionsVersion + value: $(abstractionsPackageVersion) + - name: effectiveSqlClientVersion + value: $(mdsPackageVersion) + - name: effectiveAzureVersion + value: $(azurePackageVersion) + - name: effectiveAkvProviderVersion + value: $(akvPackageVersion) resources: repositories: @@ -132,8 +132,8 @@ extends: template: /v2/OneBranch.NonOfficial.CrossPlat.yml@templates parameters: release: - # This indicates the pipeline category to deploy Box products. See: - # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts + # This indicates the pipeline category to deploy Box products. See: + # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts category: NonAzure featureFlags: EnableCDPxPAT: false @@ -183,6 +183,7 @@ extends: parameters: debug: ${{ parameters.debug }} isPreview: ${{ parameters.isPreview }} + isOfficial: false # This is a non-official pipeline. publishSymbols: ${{ parameters.publishSymbols }} buildSqlServerServer: ${{ parameters.buildSqlServerServer }} buildSqlClient: ${{ parameters.buildSqlClient }} diff --git a/eng/pipelines/onebranch/sqlclient-official.yml b/eng/pipelines/onebranch/sqlclient-official.yml index c9548b25d6..1754918116 100644 --- a/eng/pipelines/onebranch/sqlclient-official.yml +++ b/eng/pipelines/onebranch/sqlclient-official.yml @@ -115,31 +115,31 @@ variables: # Define the effective versions for all of the packages we build and release. - ${{ if parameters.isPreview }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackagePreviewVersion) - - name: effectiveLoggingVersion - value: $(loggingPackagePreviewVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackagePreviewVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackagePreviewVersion) - - name: effectiveAzureVersion - value: $(azurePackagePreviewVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackagePreviewVersion) + - name: effectiveSqlServerVersion + value: $(sqlServerPackagePreviewVersion) + - name: effectiveLoggingVersion + value: $(loggingPackagePreviewVersion) + - name: effectiveAbstractionsVersion + value: $(abstractionsPackagePreviewVersion) + - name: effectiveSqlClientVersion + value: $(mdsPackagePreviewVersion) + - name: effectiveAzureVersion + value: $(azurePackagePreviewVersion) + - name: effectiveAkvProviderVersion + value: $(akvPackagePreviewVersion) - ${{ else }}: - - name: effectiveSqlServerVersion - value: $(sqlServerPackageVersion) - - name: effectiveLoggingVersion - value: $(loggingPackageVersion) - - name: effectiveAbstractionsVersion - value: $(abstractionsPackageVersion) - - name: effectiveSqlClientVersion - value: $(mdsPackageVersion) - - name: effectiveAzureVersion - value: $(azurePackageVersion) - - name: effectiveAkvProviderVersion - value: $(akvPackageVersion) + - name: effectiveSqlServerVersion + value: $(sqlServerPackageVersion) + - name: effectiveLoggingVersion + value: $(loggingPackageVersion) + - name: effectiveAbstractionsVersion + value: $(abstractionsPackageVersion) + - name: effectiveSqlClientVersion + value: $(mdsPackageVersion) + - name: effectiveAzureVersion + value: $(azurePackageVersion) + - name: effectiveAkvProviderVersion + value: $(akvPackageVersion) resources: repositories: @@ -153,8 +153,8 @@ extends: template: /v2/OneBranch.Official.CrossPlat.yml@templates parameters: release: - # This indicates the pipeline category to deploy Box products. See: - # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts + # This indicates the pipeline category to deploy Box products. See: + # https://eng.ms/docs/products/onebranch/release/yamlreleasepipelines/deployboxproducts category: NonAzure featureFlags: EnableCDPxPAT: false @@ -208,6 +208,7 @@ extends: parameters: debug: ${{ parameters.debug }} isPreview: ${{ parameters.isPreview }} + isOfficial: true # This is an official pipeline. publishSymbols: ${{ parameters.publishSymbols }} buildSqlServerServer: ${{ parameters.buildSqlServerServer }} buildSqlClient: ${{ parameters.buildSqlClient }} diff --git a/eng/pipelines/onebranch/stages/build-stages.yml b/eng/pipelines/onebranch/stages/build-stages.yml index 0b6962f1af..fad5c9a699 100644 --- a/eng/pipelines/onebranch/stages/build-stages.yml +++ b/eng/pipelines/onebranch/stages/build-stages.yml @@ -35,6 +35,11 @@ parameters: - name: isPreview type: boolean + # True if this is an official build, which runs additional ESRP malware scanning + # and codesigning steps. + - name: isOfficial + type: boolean + # True to publish symbols to public and private servers. - name: publishSymbols type: boolean @@ -60,40 +65,42 @@ stages: jobs: - ${{ if or(eq(parameters.buildAKVProvider, true), eq(parameters.buildSqlClient, true)) }}: - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: Logging - packageFullName: Microsoft.Data.SqlClient.Internal.Logging - packageVersion: $(effectiveLoggingVersion) - versionProperties: >- - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - -p:LoggingAssemblyFileVersion=$(loggingAssemblyFileVersion) - assemblyFileVersion: $(loggingAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + packageName: Logging + packageFullName: Microsoft.Data.SqlClient.Internal.Logging + packageVersion: $(effectiveLoggingVersion) + versionProperties: >- + -p:LoggingPackageVersion=$(effectiveLoggingVersion) + -p:LoggingAssemblyFileVersion=$(loggingAssemblyFileVersion) + assemblyFileVersion: $(loggingAssemblyFileVersion) + publishSymbols: ${{ parameters.publishSymbols }} + isOfficial: ${{ parameters.isOfficial }} + esrpConnectedServiceName: $(ESRPConnectedServiceName) + esrpClientId: $(ESRPClientId) + appRegistrationClientId: $(AppRegistrationClientId) + appRegistrationTenantId: $(AppRegistrationTenantId) + authAkvName: $(AuthAKVName) + authSignCertName: $(AuthSignCertName) - ${{ if eq(parameters.buildSqlServerServer, true) }}: - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: SqlServer - packageFullName: Microsoft.SqlServer.Server - packageVersion: $(effectiveSqlServerVersion) - versionProperties: >- - -p:SqlServerAssemblyFileVersion=$(sqlServerAssemblyFileVersion) - -p:SqlServerPackageVersion=$(effectiveSqlServerVersion) - assemblyFileVersion: $(sqlServerAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + packageName: SqlServer + packageFullName: Microsoft.SqlServer.Server + packageVersion: $(effectiveSqlServerVersion) + versionProperties: >- + -p:SqlServerAssemblyFileVersion=$(sqlServerAssemblyFileVersion) + -p:SqlServerPackageVersion=$(effectiveSqlServerVersion) + assemblyFileVersion: $(sqlServerAssemblyFileVersion) + publishSymbols: ${{ parameters.publishSymbols }} + isOfficial: ${{ parameters.isOfficial }} + esrpConnectedServiceName: $(ESRPConnectedServiceName) + esrpClientId: $(ESRPClientId) + appRegistrationClientId: $(AppRegistrationClientId) + appRegistrationTenantId: $(AppRegistrationTenantId) + authAkvName: $(AuthAKVName) + authSignCertName: $(AuthSignCertName) # ==================================================================== # Stage 2: Abstractions package (depends on Logging from Stage 1) @@ -101,31 +108,32 @@ stages: # dependency on Internal.Logging. # ==================================================================== - ${{ if eq(parameters.buildSqlClient, true) }}: - - stage: build_abstractions - displayName: "Build Abstractions Package" - dependsOn: build_independent - - jobs: - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: Abstractions - packageFullName: Microsoft.Data.SqlClient.Extensions.Abstractions - packageVersion: $(effectiveAbstractionsVersion) - versionProperties: >- - -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) - -p:AbstractionsAssemblyFileVersion=$(abstractionsAssemblyFileVersion) - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - assemblyFileVersion: $(abstractionsAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) - downloadArtifacts: - - artifactName: $(loggingArtifactsName) - displayName: Logging Package + - stage: build_abstractions + displayName: "Build Abstractions Package" + dependsOn: build_independent + + jobs: + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + packageName: Abstractions + packageFullName: Microsoft.Data.SqlClient.Extensions.Abstractions + packageVersion: $(effectiveAbstractionsVersion) + versionProperties: >- + -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) + -p:AbstractionsAssemblyFileVersion=$(abstractionsAssemblyFileVersion) + -p:LoggingPackageVersion=$(effectiveLoggingVersion) + assemblyFileVersion: $(abstractionsAssemblyFileVersion) + publishSymbols: ${{ parameters.publishSymbols }} + isOfficial: ${{ parameters.isOfficial }} + esrpConnectedServiceName: $(ESRPConnectedServiceName) + esrpClientId: $(ESRPClientId) + appRegistrationClientId: $(AppRegistrationClientId) + appRegistrationTenantId: $(AppRegistrationTenantId) + authAkvName: $(AuthAKVName) + authSignCertName: $(AuthSignCertName) + downloadArtifacts: + - artifactName: $(loggingArtifactsName) + displayName: Logging Package # ==================================================================== # Stage 3: Core packages (depend on Abstractions) @@ -133,88 +141,92 @@ stages: # Stage name kept as 'build_dependent' for validate job compatibility. # ==================================================================== - ${{ if eq(parameters.buildSqlClient, true) }}: - - stage: build_dependent - displayName: "Build Core Packages" - dependsOn: build_abstractions - - jobs: - - template: /eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml@self - parameters: - publishSymbols: ${{ parameters.publishSymbols }} - isPreview: ${{ parameters.isPreview }} - # TODO: This job should use the effective versions for Abstractions, Logging, - # SqlServer, and SqlClient. - - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: Azure - packageFullName: Microsoft.Data.SqlClient.Extensions.Azure - packageVersion: $(effectiveAzureVersion) - versionProperties: >- - -p:AzurePackageVersion=$(effectiveAzureVersion) - -p:AzureAssemblyFileVersion=$(azureAssemblyFileVersion) - -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - assemblyFileVersion: $(azureAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) - downloadArtifacts: - - artifactName: $(abstractionsArtifactsName) - displayName: Abstractions Package - - artifactName: $(loggingArtifactsName) - displayName: Logging Package + - stage: build_dependent + displayName: "Build Core Packages" + dependsOn: build_abstractions + + jobs: + - template: /eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml@self + parameters: + publishSymbols: ${{ parameters.publishSymbols }} + isPreview: ${{ parameters.isPreview }} + isOfficial: ${{ parameters.isOfficial }} + # TODO: This job should use the effective versions for Abstractions, Logging, + # SqlServer, and SqlClient. + + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + packageName: Azure + packageFullName: Microsoft.Data.SqlClient.Extensions.Azure + packageVersion: $(effectiveAzureVersion) + versionProperties: >- + -p:AzurePackageVersion=$(effectiveAzureVersion) + -p:AzureAssemblyFileVersion=$(azureAssemblyFileVersion) + -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) + -p:LoggingPackageVersion=$(effectiveLoggingVersion) + assemblyFileVersion: $(azureAssemblyFileVersion) + publishSymbols: ${{ parameters.publishSymbols }} + isOfficial: ${{ parameters.isOfficial }} + esrpConnectedServiceName: $(ESRPConnectedServiceName) + esrpClientId: $(ESRPClientId) + appRegistrationClientId: $(AppRegistrationClientId) + appRegistrationTenantId: $(AppRegistrationTenantId) + authAkvName: $(AuthAKVName) + authSignCertName: $(AuthSignCertName) + downloadArtifacts: + - artifactName: $(abstractionsArtifactsName) + displayName: Abstractions Package + - artifactName: $(loggingArtifactsName) + displayName: Logging Package # ==================================================================== # Stage 4: Add-on packages (depend on core packages) # AKV Provider builds after MDS completes. # ==================================================================== - ${{ if and(eq(parameters.buildAKVProvider, true), eq(parameters.buildSqlClient, true)) }}: - - stage: build_addons - displayName: "Build Add-on Packages" - dependsOn: build_dependent - - jobs: - - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self - parameters: - packageName: AkvProvider - packageFullName: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider - packageVersion: $(effectiveAkvProviderVersion) - versionProperties: >- - -p:AkvPackageVersion=$(effectiveAkvProviderVersion) - -p:AkvAssemblyFileVersion=$(akvAssemblyFileVersion) - -p:MdsPackageVersion=$(effectiveSqlClientVersion) - -p:LoggingPackageVersion=$(effectiveLoggingVersion) - -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) - assemblyFileVersion: $(akvAssemblyFileVersion) - publishSymbols: ${{ parameters.publishSymbols }} - esrpConnectedServiceName: $(ESRPConnectedServiceName) - esrpClientId: $(ESRPClientId) - appRegistrationClientId: $(AppRegistrationClientId) - appRegistrationTenantId: $(AppRegistrationTenantId) - authAkvName: $(AuthAKVName) - authSignCertName: $(AuthSignCertName) - downloadArtifacts: - - artifactName: $(sqlClientArtifactsName) - displayName: SqlClient Package - - artifactName: $(abstractionsArtifactsName) - displayName: Abstractions Package - - artifactName: $(loggingArtifactsName) - displayName: Logging Package + - stage: build_addons + displayName: "Build Add-on Packages" + dependsOn: build_dependent + + jobs: + - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self + parameters: + packageName: AkvProvider + packageFullName: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider + packageVersion: $(effectiveAkvProviderVersion) + versionProperties: >- + -p:AkvPackageVersion=$(effectiveAkvProviderVersion) + -p:AkvAssemblyFileVersion=$(akvAssemblyFileVersion) + -p:MdsPackageVersion=$(effectiveSqlClientVersion) + -p:LoggingPackageVersion=$(effectiveLoggingVersion) + -p:AbstractionsPackageVersion=$(effectiveAbstractionsVersion) + assemblyFileVersion: $(akvAssemblyFileVersion) + publishSymbols: ${{ parameters.publishSymbols }} + isOfficial: ${{ parameters.isOfficial }} + esrpConnectedServiceName: $(ESRPConnectedServiceName) + esrpClientId: $(ESRPClientId) + appRegistrationClientId: $(AppRegistrationClientId) + appRegistrationTenantId: $(AppRegistrationTenantId) + authAkvName: $(AuthAKVName) + authSignCertName: $(AuthSignCertName) + downloadArtifacts: + - artifactName: $(sqlClientArtifactsName) + displayName: SqlClient Package + - artifactName: $(abstractionsArtifactsName) + displayName: Abstractions Package + - artifactName: $(loggingArtifactsName) + displayName: Logging Package # ==================================================================== # Validation # ==================================================================== - ${{ if eq(parameters.buildSqlClient, true) }}: - - stage: mds_package_validation - displayName: "MDS Package Validation" - dependsOn: build_dependent - jobs: - - template: /eng/pipelines/onebranch/jobs/validate-signed-package-job.yml@self - parameters: - artifactName: $(sqlClientArtifactsName) - isPreview: ${{ parameters.isPreview }} + - stage: mds_package_validation + displayName: "MDS Package Validation" + dependsOn: build_dependent + jobs: + - template: /eng/pipelines/onebranch/jobs/validate-signed-package-job.yml@self + parameters: + artifactName: $(sqlClientArtifactsName) + isPreview: ${{ parameters.isPreview }} + isOfficial: ${{ parameters.isOfficial }} diff --git a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml b/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml index e86964cf3d..824b3fab65 100644 --- a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml +++ b/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml @@ -33,36 +33,36 @@ parameters: # Glob pattern to match NuGet packages for scanning and signing. - name: pattern type: string - default: '*.*nupkg' + default: "*.*nupkg" steps: # See: https://aka.ms/esrp.scantask - task: EsrpMalwareScanning@6 - displayName: 'ESRP Nuget Malware Scanning' + displayName: "ESRP Nuget Malware Scanning" inputs: - AppRegistrationClientId: '${{ parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{ parameters.appRegistrationTenantId }}' + AppRegistrationClientId: "${{ parameters.appRegistrationClientId }}" + AppRegistrationTenantId: "${{ parameters.appRegistrationTenantId }}" CleanupTempStorage: 1 - ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' - EsrpClientId: '${{ parameters.esrpClientId }}' - FolderPath: '$(PACK_OUTPUT)' - Pattern: '${{ parameters.pattern }}' + ConnectedServiceName: "${{ parameters.esrpConnectedServiceName }}" + EsrpClientId: "${{ parameters.esrpClientId }}" + FolderPath: "$(PACK_OUTPUT)" + Pattern: "${{ parameters.pattern }}" UseMSIAuthentication: true VerboseLogin: 1 # See: https://aka.ms/esrp.signtask - task: EsrpCodeSigning@6 - displayName: 'ESRP Signing NuGet Package' + displayName: "ESRP Signing NuGet Package" inputs: - AppRegistrationClientId: '${{ parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{ parameters.appRegistrationTenantId }}' - ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' - EsrpClientId: '${{ parameters.esrpClientId }}' - AuthAKVName: '${{ parameters.authAkvName }}' - AuthSignCertName: '${{ parameters.authSignCertName }}' - FolderPath: '$(PACK_OUTPUT)' - Pattern: '${{ parameters.pattern }}' - signConfigType: 'inlineSignParams' + AppRegistrationClientId: "${{ parameters.appRegistrationClientId }}" + AppRegistrationTenantId: "${{ parameters.appRegistrationTenantId }}" + ConnectedServiceName: "${{ parameters.esrpConnectedServiceName }}" + EsrpClientId: "${{ parameters.esrpClientId }}" + AuthAKVName: "${{ parameters.authAkvName }}" + AuthSignCertName: "${{ parameters.authSignCertName }}" + FolderPath: "$(PACK_OUTPUT)" + Pattern: "${{ parameters.pattern }}" + signConfigType: "inlineSignParams" UseMSIAuthentication: true inlineOperation: | [ diff --git a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml index 83053a8d29..09f7715145 100644 --- a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml +++ b/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml @@ -3,11 +3,12 @@ # The .NET Foundation licenses this file to you under the MIT license. # # See the LICENSE file in the project root for more information. # ################################################################################# + parameters: - name: artifactType values: - - dll - - pkg + - dll + - pkg - name: sourceRoot type: string @@ -15,11 +16,11 @@ parameters: - name: dllPattern type: string - default: 'Microsoft.Data.SqlClient*.dll' + default: "Microsoft.Data.SqlClient*.dll" - name: nupkgPattern type: string - default: '*.*nupkg' + default: "*.*nupkg" - name: artifactDirectory type: string @@ -50,117 +51,118 @@ parameters: default: $(EsrpClientId) steps: -- ${{ if eq(parameters.artifactType, 'dll') }}: - # See: https://aka.ms/esrp.scantask - - task: EsrpMalwareScanning@6 - displayName: 'ESRP MalwareScanning' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - FolderPath: '${{parameters.sourceRoot }}' - Pattern: '${{ parameters.dllPattern }}' - CleanupTempStorage: 1 - VerboseLogin: 1 + # ESRP scan and sign the DLLs or NuGet packages, depending on the artifact type. + - ${{ if eq(parameters.artifactType, 'dll') }}: + # See: https://aka.ms/esrp.scantask + - task: EsrpMalwareScanning@6 + displayName: "ESRP MalwareScanning" + inputs: + ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" + AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" + AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" + EsrpClientId: "${{parameters.EsrpClientId }}" + UseMSIAuthentication: true + FolderPath: "${{parameters.sourceRoot }}" + Pattern: "${{ parameters.dllPattern }}" + CleanupTempStorage: 1 + VerboseLogin: 1 - # See: https://aka.ms/esrp.signtask - - task: EsrpCodeSigning@6 - displayName: 'ESRP CodeSigning' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - AuthAKVName: '${{parameters.AuthAKVName }}' - AuthSignCertName: '${{parameters.AuthSignCertName }}' - FolderPath: '${{parameters.sourceRoot }}' - Pattern: '${{ parameters.dllPattern }}' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "Microsoft Data SqlClient Data Provider for SQL Server" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "http://www.microsoft.com" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] + # See: https://aka.ms/esrp.signtask + - task: EsrpCodeSigning@6 + displayName: "ESRP CodeSigning" + inputs: + ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" + AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" + AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" + EsrpClientId: "${{parameters.EsrpClientId }}" + UseMSIAuthentication: true + AuthAKVName: "${{parameters.AuthAKVName }}" + AuthSignCertName: "${{parameters.AuthSignCertName }}" + FolderPath: "${{parameters.sourceRoot }}" + Pattern: "${{ parameters.dllPattern }}" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolSign", + "parameters": [ + { + "parameterName": "OpusName", + "parameterValue": "Microsoft Data SqlClient Data Provider for SQL Server" + }, + { + "parameterName": "OpusInfo", + "parameterValue": "http://www.microsoft.com" + }, + { + "parameterName": "FileDigest", + "parameterValue": "/fd \"SHA256\"" + }, + { + "parameterName": "PageHash", + "parameterValue": "/NPH" + }, + { + "parameterName": "TimeStamp", + "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + } + ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-230012", + "operationSetCode": "SigntoolVerify", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] -- ${{ if eq(parameters.artifactType, 'pkg') }}: - # See: https://aka.ms/esrp.scantask - - task: EsrpMalwareScanning@6 - displayName: 'ESRP MalwareScanning Nuget Package' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - FolderPath: '${{parameters.artifactDirectory }}' - Pattern: '${{ parameters.nupkgPattern }}' - CleanupTempStorage: 1 - VerboseLogin: 1 + - ${{ if eq(parameters.artifactType, 'pkg') }}: + # See: https://aka.ms/esrp.scantask + - task: EsrpMalwareScanning@6 + displayName: "ESRP MalwareScanning Nuget Package" + inputs: + ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" + AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" + AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" + EsrpClientId: "${{parameters.EsrpClientId }}" + UseMSIAuthentication: true + FolderPath: "${{parameters.artifactDirectory }}" + Pattern: "${{ parameters.nupkgPattern }}" + CleanupTempStorage: 1 + VerboseLogin: 1 - # See: https://aka.ms/esrp.signtask - - task: EsrpCodeSigning@6 - displayName: 'ESRP CodeSigning Nuget Package' - inputs: - ConnectedServiceName: '${{parameters.ESRPConnectedServiceName }}' - AppRegistrationClientId: '${{parameters.appRegistrationClientId }}' - AppRegistrationTenantId: '${{parameters.appRegistrationTenantId }}' - EsrpClientId: '${{parameters.EsrpClientId }}' - UseMSIAuthentication: true - AuthAKVName: '${{parameters.AuthAKVName }}' - AuthSignCertName: '${{parameters.AuthSignCertName }}' - FolderPath: '${{parameters.artifactDirectory }}' - Pattern: '${{ parameters.nupkgPattern }}' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] + # See: https://aka.ms/esrp.signtask + - task: EsrpCodeSigning@6 + displayName: "ESRP CodeSigning Nuget Package" + inputs: + ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" + AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" + AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" + EsrpClientId: "${{parameters.EsrpClientId }}" + UseMSIAuthentication: true + AuthAKVName: "${{parameters.AuthAKVName }}" + AuthSignCertName: "${{parameters.AuthSignCertName }}" + FolderPath: "${{parameters.artifactDirectory }}" + Pattern: "${{ parameters.nupkgPattern }}" + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401405", + "operationSetCode": "NuGetSign", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + }, + { + "keyCode": "CP-401405", + "operationSetCode": "NuGetVerify", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] From 59afd23a47b2a1da5598220df54b786a2bf4d019 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Wed, 25 Mar 2026 09:51:58 -0700 Subject: [PATCH 08/22] Test | Fix Transient Fault handling and other flaky unit tests (#4080) * Test | Fix Transient Fault handling flaky tests * Attempt to fix * Fix the MultiPartIdentifier tests getting skipped * Fix serialization issue of SqlTypeWorkaroundsTests * Fix one more cases of possible error scenarios --- .../Data/Common/MultipartIdentifierTests.cs | 8 ++++ .../Data/SqlTypes/SqlTypeWorkaroundsTests.cs | 6 +-- .../ConnectionFailoverTests.cs | 29 ++++++++---- .../ConnectionRoutingTests.cs | 5 +- .../ConnectionRoutingTestsAzure.cs | 5 +- .../SimulatedServerTests/ConnectionTests.cs | 47 ++++++++++--------- .../tools/TDS/TDS.Servers/GenericTdsServer.cs | 28 +++++++++-- .../TDS.Servers/TransientTdsErrorTdsServer.cs | 2 + 8 files changed, 85 insertions(+), 45 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/Common/MultipartIdentifierTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/Common/MultipartIdentifierTests.cs index 72f07573a1..a826f70447 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/Common/MultipartIdentifierTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/Common/MultipartIdentifierTests.cs @@ -26,6 +26,7 @@ public static TheoryData ValidSinglePartIdentifierVariations { ReadOnlySpan part1Words = ["word1", "word 1"]; TheoryData data = []; + HashSet seen = []; // Combination 1: embedded and non-embedded whitespace. // Combination 2: leading and/or trailing whitespace, and no whitespace @@ -36,6 +37,13 @@ public static TheoryData ValidSinglePartIdentifierVariations { foreach ((string p1Combination, string p1Expected) in GeneratePartCombinations(part1)) { + // Skip duplicates — different generation paths can produce + // identical (input, expected) pairs, which xUnit rejects. + if (!seen.Add(p1Combination)) + { + continue; + } + string onePartCombination = p1Combination; string[] onePartExpected = [p1Expected]; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlTypeWorkaroundsTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlTypeWorkaroundsTests.cs index cf97f21a39..0697ea6376 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlTypeWorkaroundsTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlTypeWorkaroundsTests.cs @@ -63,7 +63,7 @@ public void ByteArrayToSqlBinary_NullInput() }; [Theory] - [MemberData(nameof(SqlDecimalWriteTdsValue_NonNullInput_Data))] + [MemberData(nameof(SqlDecimalWriteTdsValue_NonNullInput_Data), DisableDiscoveryEnumeration = true)] public void SqlDecimalWriteTdsValue_NonNullInput(SqlDecimal input) { // Arrange @@ -155,7 +155,7 @@ public void ByteArrayToSqlGuid_ValidInput(byte[] input) }; [Theory] - [MemberData(nameof(LongToSqlMoney_Data))] + [MemberData(nameof(LongToSqlMoney_Data), DisableDiscoveryEnumeration = true)] public void LongToSqlMoney(long input, SqlMoney expected) { // Act @@ -176,7 +176,7 @@ public void LongToSqlMoney(long input, SqlMoney expected) }; [Theory] - [MemberData(nameof(SqlMoneyToLong_NonNullInput_Data))] + [MemberData(nameof(SqlMoneyToLong_NonNullInput_Data), DisableDiscoveryEnumeration = true)] public void SqlMoneyToLong_NonNullInput(SqlMoney input, long expected) { // Act diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs index d23fc9c532..7c8e67a09a 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionFailoverTests.cs @@ -11,7 +11,6 @@ namespace Microsoft.Data.SqlClient.UnitTests.SimulatedServerTests { - [Trait("Category", "flaky")] [Collection("SimulatedServerTests")] public class ConnectionFailoverTests { @@ -70,7 +69,7 @@ public void TransientFault_NoFailover_DoesNotClearPool(uint errorCode) Assert.Equal($"localhost,{initialServer.EndPoint.Port}", secondConnection.DataSource); // 1 for the initial connection, 2 for the second connection - Assert.Equal(3, initialServer.PreLoginCount); + Assert.Equal(3, initialServer.PreLoginCount - initialServer.AbandonedPreLoginCount); // A failover should not be triggered, so prelogin count to the failover server should be 0 Assert.Equal(0, failoverServer.PreLoginCount); } @@ -219,6 +218,7 @@ public void NetworkDelay_ShouldConnectToPrimary() InitialCatalog = "master", // Required for failover partner to work ConnectTimeout = 5, Encrypt = false, + Pooling = false, // Disable pooling to ensure a fresh connection attempt is made MultiSubnetFailover = false, #if NETFRAMEWORK TransparentNetworkIPResolution = false, @@ -268,6 +268,7 @@ public void NetworkError_WithUserProvidedPartner_RetryDisabled_ShouldConnectToFa ConnectRetryCount = 0, // Disable retry FailoverPartner = $"localhost,{failoverServer.EndPoint.Port}", // User provided failover partner Encrypt = false, + Pooling = false, // Disable pooling to ensure a fresh connection attempt is made on failover }; using SqlConnection connection = new(builder.ConnectionString); @@ -313,6 +314,9 @@ public void NetworkError_WithUserProvidedPartner_RetryEnabled_ShouldConnectToFai ConnectRetryInterval = 1, FailoverPartner = $"localhost,{failoverServer.EndPoint.Port}", // User provided failover partner Encrypt = false, +#if NETFRAMEWORK + TransparentNetworkIPResolution = false, +#endif }; using SqlConnection connection = new(builder.ConnectionString); // Act @@ -324,7 +328,8 @@ public void NetworkError_WithUserProvidedPartner_RetryEnabled_ShouldConnectToFai Assert.Equal(ConnectionState.Open, connection.State); Assert.Equal($"localhost,{failoverServer.EndPoint.Port}", connection.DataSource); Assert.Equal(1, server.PreLoginCount); - Assert.Equal(1, failoverServer.PreLoginCount); + Assert.Equal(0, server.Login7Count); + Assert.Equal(1, failoverServer.PreLoginCount - failoverServer.AbandonedPreLoginCount); } [Theory] @@ -357,7 +362,8 @@ public void TransientFault_ShouldConnectToPrimary(uint errorCode) InitialCatalog = "master", ConnectTimeout = 30, ConnectRetryInterval = 1, - Encrypt = false + Encrypt = false, + Pooling = false, // Disable pooling to ensure a fresh connection attempt is made }; using SqlConnection connection = new(builder.ConnectionString); @@ -369,7 +375,7 @@ public void TransientFault_ShouldConnectToPrimary(uint errorCode) Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource); // Failures should prompt the client to return to the original server, resulting in a login count of 2 - Assert.Equal(2, server.PreLoginCount); + Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount); } [Theory] @@ -454,7 +460,7 @@ public void TransientFault_WithUserProvidedPartner_ShouldConnectToPrimary(uint e FailoverPartner = $"localhost:{failoverServer.EndPoint.Port}", // User provided failover partner }; using SqlConnection connection = new(builder.ConnectionString); - + // Act connection.Open(); @@ -463,7 +469,7 @@ public void TransientFault_WithUserProvidedPartner_ShouldConnectToPrimary(uint e Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource); // Failures should prompt the client to return to the original server, resulting in a login count of 2 - Assert.Equal(2, server.PreLoginCount); + Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount); } [Theory] @@ -559,11 +565,14 @@ public void TransientFault_IgnoreServerProvidedFailoverPartner_ShouldConnectToUs // Close the connection to return it to the pool connection.Close(); - // Act // Dispose of the server to trigger a failover server.Dispose(); + // Clear the pool to ensure the next connection attempt doesn't reuse + // the pooled connection to the now-disposed primary server. + SqlConnection.ClearAllPools(); + // Opening a new connection will use the failover partner stored in the pool group. // This will fail if the server provided failover partner was stored to the pool group. using SqlConnection failoverConnection = new(builder.ConnectionString); @@ -573,9 +582,9 @@ public void TransientFault_IgnoreServerProvidedFailoverPartner_ShouldConnectToUs Assert.Equal(ConnectionState.Open, failoverConnection.State); Assert.Equal($"localhost,{failoverServer.EndPoint.Port}", failoverConnection.DataSource); // 1 for the initial connection - Assert.Equal(1, server.PreLoginCount); + Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount); // 1 for the failover connection - Assert.Equal(1, failoverServer.PreLoginCount); + Assert.Equal(1, failoverServer.PreLoginCount - failoverServer.AbandonedPreLoginCount); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTests.cs index 108118dda7..b763f2b55c 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTests.cs @@ -10,7 +10,6 @@ namespace Microsoft.Data.SqlClient.UnitTests.SimulatedServerTests { - [Trait("Category", "flaky")] [Collection("SimulatedServerTests")] public class ConnectionRoutingTests { @@ -58,8 +57,8 @@ public void TransientFaultAtRoutedLocation_ShouldReturnToGateway(uint errorCode) Assert.Equal($"localhost,{router.EndPoint.Port}", connection.DataSource); // Failures should prompt the client to return to the original server, resulting in a login count of 2 - Assert.Equal(2, router.PreLoginCount); - Assert.Equal(2, server.PreLoginCount); + Assert.Equal(2, router.PreLoginCount - router.AbandonedPreLoginCount); + Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount); } [Theory] diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTestsAzure.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTestsAzure.cs index dd945e37f3..84c3452826 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTestsAzure.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionRoutingTestsAzure.cs @@ -10,7 +10,6 @@ namespace Microsoft.Data.SqlClient.UnitTests.SimulatedServerTests { - [Trait("Category", "flaky")] [Collection("SimulatedServerTests")] public class ConnectionRoutingTestsAzure : IDisposable { @@ -76,8 +75,8 @@ public void TransientFaultAtRoutedLocation_ShouldReturnToGateway(uint errorCode) Assert.Equal($"localhost,{router.EndPoint.Port}", connection.DataSource); // Failures should prompt the client to return to the original server, resulting in a login count of 2 - Assert.Equal(2, router.PreLoginCount); - Assert.Equal(2, server.PreLoginCount); + Assert.Equal(2, router.PreLoginCount - router.AbandonedPreLoginCount); + Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount); } [Theory] diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs index a729e6afe2..21a6cb65de 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/SimulatedServerTests/ConnectionTests.cs @@ -29,7 +29,8 @@ public void ConnectionTest() { using TdsServer server = new(new TdsServerArguments() { }); server.Start(); - var connStr = new SqlConnectionStringBuilder() { + var connStr = new SqlConnectionStringBuilder() + { DataSource = $"localhost,{server.EndPoint.Port}", Encrypt = SqlConnectionEncryptOption.Optional, }.ConnectionString; @@ -43,7 +44,8 @@ public void IntegratedAuthConnectionTest() { using TdsServer server = new(new TdsServerArguments() { }); server.Start(); - var connStr = new SqlConnectionStringBuilder() { + var connStr = new SqlConnectionStringBuilder() + { DataSource = $"localhost,{server.EndPoint.Port}", Encrypt = SqlConnectionEncryptOption.Optional, }.ConnectionString; @@ -61,9 +63,10 @@ public void IntegratedAuthConnectionTest() [Fact] public async Task RequestEncryption_ServerDoesNotSupportEncryption_ShouldFail() { - using TdsServer server = new(new TdsServerArguments() {Encryption = TDSPreLoginTokenEncryptionType.None }); + using TdsServer server = new(new TdsServerArguments() { Encryption = TDSPreLoginTokenEncryptionType.None }); server.Start(); - var connStr = new SqlConnectionStringBuilder() { + var connStr = new SqlConnectionStringBuilder() + { DataSource = $"localhost,{server.EndPoint.Port}" }.ConnectionString; @@ -72,7 +75,6 @@ public async Task RequestEncryption_ServerDoesNotSupportEncryption_ShouldFail() Assert.Contains("The instance of SQL Server you attempted to connect to does not support encryption.", ex.Message, StringComparison.OrdinalIgnoreCase); } - [Trait("Category", "flaky")] [Theory] [InlineData(40613)] [InlineData(42108)] @@ -80,26 +82,28 @@ public async Task RequestEncryption_ServerDoesNotSupportEncryption_ShouldFail() public async Task TransientFault_RetryEnabled_ShouldSucceed_Async(uint errorCode) { using TransientTdsErrorTdsServer server = new( - new TransientTdsErrorTdsServerArguments() + new TransientTdsErrorTdsServerArguments() { - IsEnabledTransientError = true, - Number = errorCode, + IsEnabledTransientError = true, + Number = errorCode, }); server.Start(); SqlConnectionStringBuilder builder = new() { DataSource = "localhost," + server.EndPoint.Port, - Encrypt = SqlConnectionEncryptOption.Optional + Encrypt = SqlConnectionEncryptOption.Optional, +#if NETFRAMEWORK + TransparentNetworkIPResolution = false +#endif }; using SqlConnection connection = new(builder.ConnectionString); await connection.OpenAsync(); Assert.Equal(ConnectionState.Open, connection.State); Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource); - Assert.Equal(2, server.PreLoginCount); + Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount); } - [Trait("Category", "flaky")] [Theory] [InlineData(40613)] [InlineData(42108)] @@ -123,10 +127,9 @@ public void TransientFault_RetryEnabled_ShouldSucceed(uint errorCode) connection.Open(); Assert.Equal(ConnectionState.Open, connection.State); Assert.Equal($"localhost,{server.EndPoint.Port}", connection.DataSource); - Assert.Equal(2, server.PreLoginCount); + Assert.Equal(2, server.PreLoginCount - server.AbandonedPreLoginCount); } - [Trait("Category", "flaky")] [Theory] [InlineData(40613)] [InlineData(42108)] @@ -151,10 +154,9 @@ public async Task TransientFault_RetryDisabled_ShouldFail_Async(uint errorCode) SqlException e = await Assert.ThrowsAsync(async () => await connection.OpenAsync()); Assert.Equal((int)errorCode, e.Number); Assert.Equal(ConnectionState.Closed, connection.State); - Assert.Equal(1, server.PreLoginCount); + Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount); } - [Trait("Category", "flaky")] [Theory] [InlineData(40613)] [InlineData(42108)] @@ -179,10 +181,9 @@ public void TransientFault_RetryDisabled_ShouldFail(uint errorCode) SqlException e = Assert.Throws(() => connection.Open()); Assert.Equal((int)errorCode, e.Number); Assert.Equal(ConnectionState.Closed, connection.State); - Assert.Equal(1, server.PreLoginCount); + Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount); } - [Trait("Category", "flaky")] [Theory] [InlineData(false)] [InlineData(true)] @@ -200,6 +201,7 @@ public async Task NetworkError_RetryEnabled_ShouldSucceed_Async(bool multiSubnet DataSource = "localhost," + server.EndPoint.Port, Encrypt = SqlConnectionEncryptOption.Optional, ConnectTimeout = 5, + Pooling = false, // Disable pooling to ensure a fresh connection attempt is made MultiSubnetFailover = multiSubnetFailoverEnabled, #if NETFRAMEWORK TransparentNetworkIPResolution = multiSubnetFailoverEnabled @@ -216,11 +218,10 @@ public async Task NetworkError_RetryEnabled_ShouldSucceed_Async(bool multiSubnet } else { - Assert.Equal(1, server.PreLoginCount); + Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount); } } - [Trait("Category", "flaky")] [Theory] [InlineData(true)] [InlineData(false)] @@ -261,11 +262,10 @@ public async Task NetworkDelay_RetryDisabled_Async(bool multiSubnetFailoverEnabl } else { - Assert.Equal(1, server.PreLoginCount); + Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount); } } - [Trait("Category", "flaky")] [Theory] [InlineData(true)] [InlineData(false)] @@ -306,7 +306,7 @@ public void NetworkDelay_RetryDisabled(bool multiSubnetFailoverEnabled) } else { - Assert.Equal(1, server.PreLoginCount); + Assert.Equal(1, server.PreLoginCount - server.AbandonedPreLoginCount); } } @@ -467,7 +467,8 @@ public void ConnectionTimeoutTest(int timeout) //TODO: do we even need a server for this test? using TdsServer server = new(); server.Start(); - var connStr = new SqlConnectionStringBuilder() { + var connStr = new SqlConnectionStringBuilder() + { DataSource = $"localhost,{server.EndPoint.Port}", ConnectTimeout = timeout, Encrypt = SqlConnectionEncryptOption.Optional diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTdsServer.cs index d2c81e6ca1..1eb8bdf8c6 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTdsServer.cs @@ -95,6 +95,11 @@ public delegate void OnAuthenticationCompletedDelegate( /// private int _preLoginCount = 0; + /// + /// Counts Login7 requests to the server. + /// + protected int _login7Count = 0; + private TDSServerEndPoint _endpoint; /// @@ -137,6 +142,18 @@ public GenericTdsServer(T arguments, QueryEngine queryEngine) /// public int PreLoginCount => _preLoginCount; + /// + /// Counts Login7 requests to the server. + /// + public int Login7Count => _login7Count; + + /// + /// Counts pre-login requests that did not result in a Login7 request, + /// which indicates the client abandoned the connection (e.g. interval + /// timer timeout during TNIR or failover). + /// + public int AbandonedPreLoginCount => _preLoginCount - _login7Count; + public OnAuthenticationCompletedDelegate OnAuthenticationResponseCompleted { private get; set; } public OnLogin7ValidatedDelegate OnLogin7Validated { private get; set; } @@ -148,9 +165,12 @@ public void Start([CallerMemberName] string methodName = "") { throw new InvalidOperationException("Server is already started"); } - _endpoint = new TDSServerEndPoint(this) { ServerEndPoint = new IPEndPoint(IPAddress.Any, 0) }; - _endpoint.EndpointName = methodName; - _endpoint.EventLog = Arguments.Log; + _endpoint = new TDSServerEndPoint(this) + { + ServerEndPoint = new IPEndPoint(IPAddress.Any, 0), + EndpointName = methodName, + EventLog = Arguments.Log + }; _endpoint.Start(); } @@ -245,6 +265,8 @@ public virtual TDSMessageCollection OnPreLoginRequest(ITDSServerSession session, /// public virtual TDSMessageCollection OnLogin7Request(ITDSServerSession session, TDSMessage request) { + Interlocked.Increment(ref _login7Count); + // Inflate login7 request from the message TDSLogin7Token loginRequest = request[0] as TDSLogin7Token; diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientTdsErrorTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientTdsErrorTdsServer.cs index ecd89f5812..6ec7ac2528 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientTdsErrorTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TransientTdsErrorTdsServer.cs @@ -60,6 +60,8 @@ public override TDSMessageCollection OnLogin7Request(ITDSServerSession session, // Check if we're still going to raise transient error if (Arguments.IsEnabledTransientError && RequestCounter < Arguments.RepeatCount) { + // Increment Login7 count since we won't call base.OnLogin7Request + Interlocked.Increment(ref _login7Count); return GenerateErrorMessage(request); } From ab95f6f35d1aabfdcad4ea50a57b926932e77956 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 26 Mar 2026 07:08:57 +1100 Subject: [PATCH 09/22] remove duplicate dictionary lookups (#4000) --- ...taSourceEnumeratorManagedHelper.netcore.cs | 16 ++++----- .../Data/SqlClient/SQLFallbackDNSCache.cs | 9 ++--- .../Microsoft/Data/SqlClient/SqlDependency.cs | 33 +++++-------------- .../Data/SqlClient/SqlDependencyListener.cs | 22 ++++++------- .../Data/SqlClient/SqlDependencyUtils.cs | 6 +--- 5 files changed, 30 insertions(+), 56 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.netcore.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.netcore.cs index 2bc42aa38c..73a164a108 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.netcore.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.netcore.cs @@ -61,14 +61,14 @@ private static DataTable ParseServerEnumString(string serverInstances) if (InstanceDetails.Count > 0) { dataRow = dataTable.NewRow(); - dataRow[0] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.ServerNameCol) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.ServerNameCol] : string.Empty; - dataRow[1] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.InstanceNameCol) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.InstanceNameCol] : string.Empty; - dataRow[2] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.IsClusteredCol) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.IsClusteredCol] : string.Empty; - dataRow[3] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.VersionNameCol) == true ? - InstanceDetails[SqlDataSourceEnumeratorUtil.VersionNameCol] : string.Empty; + dataRow[0] = InstanceDetails.TryGetValue(SqlDataSourceEnumeratorUtil.ServerNameCol, out string serverName) ? + serverName : string.Empty; + dataRow[1] = InstanceDetails.TryGetValue(SqlDataSourceEnumeratorUtil.InstanceNameCol, out string instanceName) ? + instanceName : string.Empty; + dataRow[2] = InstanceDetails.TryGetValue(SqlDataSourceEnumeratorUtil.IsClusteredCol, out string isClustered) ? + isClustered : string.Empty; + dataRow[3] = InstanceDetails.TryGetValue(SqlDataSourceEnumeratorUtil.VersionNameCol, out string versionName) ? + versionName : string.Empty; dataTable.Rows.Add(dataRow); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs index 4dd3086b45..07ddd6fd50 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SQLFallbackDNSCache.cs @@ -28,13 +28,8 @@ internal bool AddDNSInfo(SQLDNSInfo item) { if (item != null) { - if (DNSInfoCache.ContainsKey(item.FQDN)) - { - - DeleteDNSInfo(item.FQDN); - } - - return DNSInfoCache.TryAdd(item.FQDN, item); + DNSInfoCache[item.FQDN] = item; + return true; } return false; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs index 83d9753799..10efdd9e05 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs @@ -817,29 +817,21 @@ private static bool AddToServerUserHash(string server, IdentityUserNamePair iden { Dictionary> identityDatabaseHash; - if (!s_serverUserHash.ContainsKey(server)) + if (!s_serverUserHash.TryGetValue(server, out identityDatabaseHash)) { SqlClientEventSource.Log.TryNotificationTraceEvent(" Hash did not contain server, adding."); identityDatabaseHash = new Dictionary>(); s_serverUserHash.Add(server, identityDatabaseHash); } - else - { - identityDatabaseHash = s_serverUserHash[server]; - } List databaseServiceList; - if (!identityDatabaseHash.ContainsKey(identityUser)) + if (!identityDatabaseHash.TryGetValue(identityUser, out databaseServiceList)) { SqlClientEventSource.Log.TryNotificationTraceEvent(" Hash contained server but not user, adding user."); databaseServiceList = new List(); identityDatabaseHash.Add(identityUser, databaseServiceList); } - else - { - databaseServiceList = identityDatabaseHash[identityUser]; - } if (!databaseServiceList.Contains(databaseService)) { @@ -870,15 +862,12 @@ private static void RemoveFromServerUserHash(string server, IdentityUserNamePair { Dictionary> identityDatabaseHash; - if (s_serverUserHash.ContainsKey(server)) + if (s_serverUserHash.TryGetValue(server, out identityDatabaseHash)) { - identityDatabaseHash = s_serverUserHash[server]; - List databaseServiceList; - if (identityDatabaseHash.ContainsKey(identityUser)) + if (identityDatabaseHash.TryGetValue(identityUser, out databaseServiceList)) { - databaseServiceList = identityDatabaseHash[identityUser]; int index = databaseServiceList.IndexOf(databaseService); if (index >= 0) @@ -934,7 +923,9 @@ internal static string GetDefaultComposedOptions(string server, string failoverS lock (s_serverUserHash) { - if (!s_serverUserHash.ContainsKey(server)) + Dictionary> identityDatabaseHash; + + if (!s_serverUserHash.TryGetValue(server, out identityDatabaseHash)) { if (0 == s_serverUserHash.Count) { @@ -942,7 +933,7 @@ internal static string GetDefaultComposedOptions(string server, string failoverS SqlClientEventSource.Log.TryNotificationTraceEvent(" ERROR - no start calls have been made, about to throw."); throw SQL.SqlDepDefaultOptionsButNoStart(); } - else if (!string.IsNullOrEmpty(failoverServer) && s_serverUserHash.ContainsKey(failoverServer)) + else if (!string.IsNullOrEmpty(failoverServer) && s_serverUserHash.TryGetValue(failoverServer, out identityDatabaseHash)) { SqlClientEventSource.Log.TryNotificationTraceEvent(" using failover server instead\n"); server = failoverServer; @@ -954,11 +945,9 @@ internal static string GetDefaultComposedOptions(string server, string failoverS } } - Dictionary> identityDatabaseHash = s_serverUserHash[server]; - List databaseList = null; - if (!identityDatabaseHash.ContainsKey(identityUser)) + if (!identityDatabaseHash.TryGetValue(identityUser, out databaseList)) { if (identityDatabaseHash.Count > 1) { @@ -977,10 +966,6 @@ internal static string GetDefaultComposedOptions(string server, string failoverS } } } - else - { - databaseList = identityDatabaseHash[identityUser]; - } DatabaseServicePair pair = new(database, null); DatabaseServicePair resultingPair = null; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index 306b36b747..e97d8589a3 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -252,11 +252,10 @@ internal bool AppDomainUnload(string appDomainKey) // For each decrement, subtract from count, and delete if we reach 0. lock (_appDomainKeyHash) { - if (_appDomainKeyHash.ContainsKey(appDomainKey)) + if (_appDomainKeyHash.TryGetValue(appDomainKey, out int value)) { // Do nothing if AppDomain did not call Start! SqlClientEventSource.Log.TryNotificationTraceEvent(" _appDomainKeyHash contained AppDomainKey: '{0}'.", appDomainKey); - int value = _appDomainKeyHash[appDomainKey]; SqlClientEventSource.Log.TryNotificationTraceEvent("SqlConnectionContainer.AppDomainUnload|DEP> _appDomainKeyHash for AppDomainKey: '{0}' count: '{1}'.", appDomainKey, value); Debug.Assert(value > 0, "Why is value 0 or less?"); @@ -272,9 +271,9 @@ internal bool AppDomainUnload(string appDomainKey) Debug.Assert(0 == value, "We did not reach 0 at end of loop in AppDomainUnload!"); Debug.Assert(!_appDomainKeyHash.ContainsKey(appDomainKey), "Key not removed after AppDomainUnload!"); - if (_appDomainKeyHash.ContainsKey(appDomainKey)) + if (_appDomainKeyHash.TryGetValue(appDomainKey, out int remainingCount)) { - SqlClientEventSource.Log.TryNotificationTraceEvent("SqlConnectionContainer.AppDomainUnload|DEP|ERR> ERROR - after the Stop() loop, _appDomainKeyHash for AppDomainKey: '{0}' entry not removed from hash. Count: {1}'", appDomainKey, _appDomainKeyHash[appDomainKey]); + SqlClientEventSource.Log.TryNotificationTraceEvent("SqlConnectionContainer.AppDomainUnload|DEP|ERR> ERROR - after the Stop() loop, _appDomainKeyHash for AppDomainKey: '{0}' entry not removed from hash. Count: {1}'", appDomainKey, remainingCount); } } else @@ -510,10 +509,11 @@ internal void IncrementStartCount(string appDomainKey, out bool appDomainStart) // For each increment, add to count, and create entry if not present. lock (_appDomainKeyHash) { - if (_appDomainKeyHash.ContainsKey(appDomainKey)) + if (_appDomainKeyHash.TryGetValue(appDomainKey, out int count)) { - _appDomainKeyHash[appDomainKey] = _appDomainKeyHash[appDomainKey] + 1; - SqlClientEventSource.Log.TryNotificationTraceEvent("SqlConnectionContainer.IncrementStartCount|DEP> _appDomainKeyHash contained AppDomainKey: '{0}', incremented count: '{1}'.", appDomainKey, _appDomainKeyHash[appDomainKey]); + count++; + _appDomainKeyHash[appDomainKey] = count; + SqlClientEventSource.Log.TryNotificationTraceEvent("SqlConnectionContainer.IncrementStartCount|DEP> _appDomainKeyHash contained AppDomainKey: '{0}', incremented count: '{1}'.", appDomainKey, count); } else { @@ -1618,7 +1618,7 @@ private bool Start( { if (!_sqlDependencyPerAppDomainDispatchers.ContainsKey(appDomainKey)) { - _sqlDependencyPerAppDomainDispatchers[appDomainKey] = dispatcher; + _sqlDependencyPerAppDomainDispatchers.Add(appDomainKey, dispatcher); } } @@ -1637,7 +1637,7 @@ private bool Start( SqlConnectionContainer container = null; lock (_connectionContainers) { - if (!_connectionContainers.ContainsKey(hashHelper)) + if (!_connectionContainers.TryGetValue(hashHelper, out container)) { SqlClientEventSource.Log.TryNotificationTraceEvent(" {0}, hashtable miss, creating new container.", ObjectID); container = new SqlConnectionContainer(hashHelper, appDomainKey, useDefaults); @@ -1647,7 +1647,6 @@ private bool Start( } else { - container = _connectionContainers[hashHelper]; SqlClientEventSource.Log.TryNotificationTraceEvent(" {0}, hashtable hit, container: {1}", ObjectID, container.ObjectID); if (container.InErrorState) { @@ -1713,9 +1712,8 @@ internal bool Stop( lock (_connectionContainers) { - if (_connectionContainers.ContainsKey(hashHelper)) + if (_connectionContainers.TryGetValue(hashHelper, out SqlConnectionContainer container)) { - SqlConnectionContainer container = _connectionContainers[hashHelper]; SqlClientEventSource.Log.TryNotificationTraceEvent(" {0}, hashtable hit, container: {1}", ObjectID, container.ObjectID); server = container.Server; // Return server, database, and queue info for use by calling SqlDependency. database = container.Database; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs index 59b36d23cc..b990270924 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs @@ -365,11 +365,7 @@ internal SqlDependency LookupDependencyEntry(string id) lock (_instanceLock) { - if (_dependencyIdToDependencyHash.ContainsKey(id)) - { - entry = _dependencyIdToDependencyHash[id]; - } - else + if (!_dependencyIdToDependencyHash.TryGetValue(id, out entry)) { SqlClientEventSource.Log.TryNotificationTraceEvent(" ERROR - dependency ID mismatch - not throwing."); } From aea5ade9e0d379b202e7fe3117709fcc50f75f5b Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:25:03 -0300 Subject: [PATCH 10/22] Added CI test stages for SQL Server 16 and 17. (#4087) --- eng/pipelines/dotnet-sqlclient-ci-core.yml | 62 +++++++++++++++++++ .../sqlclient-pr-package-ref-pipeline.yml | 2 + .../sqlclient-pr-project-ref-pipeline.yml | 2 + 3 files changed, 66 insertions(+) diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml index f9ead686ff..e4d6d70a7f 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-core.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml @@ -93,6 +93,12 @@ parameters: type: boolean default: true + # If true, run manual tests against legacy SQL Server versions (2016, 2017). + # Enabled for CI pipelines; disabled for PR pipelines to keep validation fast. + - name: runLegacySqlTests + type: boolean + default: true + - name: dotnetVerbosity type: string default: normal @@ -289,6 +295,62 @@ stages: # Configuration of test jobs. Each entry in this object will become a test job, and the # properties of each entry will be supplied as parameters to the test job template. testConfigurations: + # SQL Server 2016 and 2017 on Windows Server 2022 (x64 only). + # x86 testing is intentionally skipped for these legacy SQL versions + # because x86 support is already validated via SQL 2019 and 2022 images. + ${{ if eq(parameters.runLegacySqlTests, true) }}: + # Windows Server 22 with local SQL Server 2016, x64 build platform. + windows_sql_16_x64: + pool: ${{parameters.defaultPoolName }} + images: + Win22_Sql16: ADO-MMS22-SQL16 + TargetFrameworks: ${{parameters.targetFrameworks }} + netcoreVersionTestUtils: ${{parameters.netcoreVersionTestUtils }} + buildPlatforms: ${{parameters.buildPlatforms }} + testSets: ${{parameters.testSets }} + useManagedSNI: ${{parameters.useManagedSNI }} + configSqlFor: local + operatingSystem: Windows + configProperties: + TCPConnectionString: $(SQL_TCP_CONN_STRING) + NPConnectionString: $(SQL_NP_CONN_STRING) + AzureKeyVaultUrl: $(AzureKeyVaultUrl) + AzureKeyVaultTenantId: $(AzureKeyVaultTenantId) + SupportsIntegratedSecurity: true + UserManagedIdentityClientId: $(UserManagedIdentityClientId) + FileStreamDirectory: $(FileStreamDirectory) + LocalDbAppName: $(LocalDbAppName) + LocalDbSharedInstanceName: $(LocalDbSharedInstanceName) + AliasName: $(SQLAliasName) + SQLRootPath: $(SQL16RootPath) + enableLocalDB: true + + # Windows Server 22 with local SQL Server 2017, x64 build platform. + windows_sql_17_x64: + pool: ${{parameters.defaultPoolName }} + images: + Win22_Sql17: ADO-MMS22-SQL17 + TargetFrameworks: ${{parameters.targetFrameworks }} + netcoreVersionTestUtils: ${{parameters.netcoreVersionTestUtils }} + buildPlatforms: ${{parameters.buildPlatforms }} + testSets: ${{parameters.testSets }} + useManagedSNI: ${{parameters.useManagedSNI }} + configSqlFor: local + operatingSystem: Windows + configProperties: + TCPConnectionString: $(SQL_TCP_CONN_STRING) + NPConnectionString: $(SQL_NP_CONN_STRING) + AzureKeyVaultUrl: $(AzureKeyVaultUrl) + AzureKeyVaultTenantId: $(AzureKeyVaultTenantId) + SupportsIntegratedSecurity: true + UserManagedIdentityClientId: $(UserManagedIdentityClientId) + FileStreamDirectory: $(FileStreamDirectory) + LocalDbAppName: $(LocalDbAppName) + LocalDbSharedInstanceName: $(LocalDbSharedInstanceName) + AliasName: $(SQLAliasName) + SQLRootPath: $(SQL17RootPath) + enableLocalDB: true + # Windows Server 22 with local SQL Server 2019, x64 build platform. windows_sql_19_x64: pool: ${{parameters.defaultPoolName }} diff --git a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml index a575d2dd6a..440d7afa27 100644 --- a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml @@ -139,6 +139,8 @@ extends: testJobTimeout: ${{ parameters.testJobTimeout }} testSets: ${{ parameters.testSets }} useManagedSNI: ${{ parameters.useManagedSNI }} + # Legacy SQL Server tests (2016/2017) run in CI only, not on PRs. + runLegacySqlTests: false # Don't run the AE tests in Debug mode; they rarely succeed. ${{ if eq(parameters.buildConfiguration, 'Debug') }}: runAlwaysEncryptedTests: false diff --git a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml index dd8f34e58b..d3ae773181 100644 --- a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml @@ -139,6 +139,8 @@ extends: testJobTimeout: ${{ parameters.testJobTimeout }} testSets: ${{ parameters.testSets }} useManagedSNI: ${{ parameters.useManagedSNI }} + # Legacy SQL Server tests (2016/2017) run in CI only, not on PRs. + runLegacySqlTests: false # Don't run the AE tests in Debug mode; they rarely succeed. ${{ if eq(parameters.buildConfiguration, 'Debug') }}: runAlwaysEncryptedTests: false From 39ae09b4abecc328423f157f43fccccd1b1535da Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:48:30 -0700 Subject: [PATCH 11/22] Test | Fix Diagnostic Tests and remove Remote Executor (#4078) --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../tests/Directory.Packages.props | 1 - ...icrosoft.Data.SqlClient.ManualTests.csproj | 6 +- .../TracingTests/DiagnosticTest.cs | 1053 ++++------------- 4 files changed, 268 insertions(+), 796 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs index e22988b35d..b96e6f654d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1937,8 +1937,10 @@ private Task InternalOpenAsync(SqlConnectionOverrides overrides, CancellationTok s_diagnosticListener.IsEnabled(SqlClientConnectionOpenError.Name)) { result.Task.ContinueWith( - continuationAction: s_openAsyncComplete, + continuationAction: static (task, state) => s_openAsyncComplete(task, state), state: operationId, // connection is passed in TaskCompletionSource async state + cancellationToken: CancellationToken.None, // we want the continuation task to run even if the original operation was cancelled + continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default ); } diff --git a/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props b/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props index 78ea36f1fa..72bddcc898 100644 --- a/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props +++ b/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props @@ -12,7 +12,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj index 336838b068..25a1d53d0d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj @@ -342,7 +342,8 @@ + Condition="'$(ReferenceType)' != 'Package'" + Private="true" /> @@ -360,7 +361,6 @@ - @@ -395,7 +395,6 @@ - @@ -422,4 +421,5 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs index f550e7db70..db49fc7c32 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs @@ -2,31 +2,30 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections; +using System.Collections.Generic; +using System.Data; using System.Diagnostics; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Xml; using Microsoft.SqlServer.TDS; using Microsoft.SqlServer.TDS.Done; using Microsoft.SqlServer.TDS.EndPoint; using Microsoft.SqlServer.TDS.Error; -using Microsoft.SqlServer.TDS.Servers; using Microsoft.SqlServer.TDS.SQLBatch; +using Microsoft.SqlServer.TDS.Servers; using Xunit; -using System.Runtime.CompilerServices; -using System; -using System.Data; -using Microsoft.DotNet.RemoteExecutor; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - // TODO(ADO-39873): Re-enable these tests after addressing their flakiness. - [Trait("Category", "flaky")] + // Serialized execution: DiagnosticListener is global state, so these tests + // must not run in parallel with each other. + [Collection("DiagnosticTests")] public class DiagnosticTest { - private const string BadConnectionString = "data source = bad; initial catalog = bad; integrated security = true; connection timeout = 1;"; - private const string WriteCommandBefore = "Microsoft.Data.SqlClient.WriteCommandBefore"; private const string WriteCommandAfter = "Microsoft.Data.SqlClient.WriteCommandAfter"; private const string WriteCommandError = "Microsoft.Data.SqlClient.WriteCommandError"; @@ -35,969 +34,441 @@ public class DiagnosticTest private const string WriteConnectionOpenError = "Microsoft.Data.SqlClient.WriteConnectionOpenError"; private const string WriteConnectionCloseBefore = "Microsoft.Data.SqlClient.WriteConnectionCloseBefore"; private const string WriteConnectionCloseAfter = "Microsoft.Data.SqlClient.WriteConnectionCloseAfter"; - private const string WriteConnectionCloseError = "Microsoft.Data.SqlClient.WriteConnectionCloseError"; + private const string BadConnectionString = "data source = bad; initial catalog = bad; integrated security = true; connection timeout = 1;"; + + #region Sync tests [Fact] public void ExecuteScalarTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - cmd.ExecuteScalar(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + cmd.ExecuteScalar(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteScalarErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - Assert.Throws(() => cmd.ExecuteScalar()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteScalar()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteNonQueryTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - cmd.ExecuteNonQuery(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + cmd.ExecuteNonQuery(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteNonQueryErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - // Limiting the command timeout to 3 seconds. This should be lower than the Process timeout. - cmd.CommandTimeout = 3; - conn.Open(); - - Assert.Throws(() => cmd.ExecuteNonQuery()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); + cmd.CommandTimeout = 3; + conn.Open(); + Assert.Throws(() => cmd.ExecuteNonQuery()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteReaderTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - SqlDataReader reader = cmd.ExecuteReader(); - while (reader.Read()) - { - // Read until end. - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + using SqlDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) { } + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteReaderErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - // @TODO: TestTdsServer should not throw on ExecuteReader, it should throw on reader.Read - Assert.Throws(() => cmd.ExecuteReader()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteReaderWithCommandBehaviorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + using SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default); + while (reader.Read()) { } + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - // Synapse: Parse error at line: 1, column: 27: Incorrect syntax near 'for'. - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + [Fact] public void ExecuteXmlReaderTest() { - RemoteExecutor.Invoke(() => + // The TDS test server does not return XML-formatted results, so + // ExecuteXmlReader will throw. We verify the error diagnostic path. + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(_ => - { - // @TODO: Test TDS server doesn't support ExecuteXmlReader, so connect to real server as workaround - using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;"; - - conn.Open(); - XmlReader reader = cmd.ExecuteXmlReader(); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteXmlReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [Fact] public void ExecuteXmlReaderErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;"; - - conn.Open(); - // @TODO: TestTdsServer should not throw on ExecuteXmlReader, should throw on reader.Read - Assert.Throws(() => cmd.ExecuteXmlReader()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteXmlReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteScalarAsyncTest() + public void ConnectionOpenTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { -#if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) -#else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) -#endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - await cmd.ExecuteScalarAsync(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + conn.Open(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteScalarAsyncErrorTest() + public void ConnectionOpenErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(_ => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { -#if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) -#else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) -#endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - await Assert.ThrowsAsync(() => cmd.ExecuteScalarAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(BadConnectionString); + Assert.Throws(() => conn.Open()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); } + #endregion + + #region Async tests + [Fact] - public void ExecuteNonQueryAsyncTest() + public async Task ExecuteScalarAsyncTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - await cmd.ExecuteNonQueryAsync(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await cmd.ExecuteScalarAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteNonQueryAsyncErrorTest() + public async Task ExecuteScalarAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT 1 / 0;", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - await Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteScalarAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteReaderAsyncTest() + public async Task ExecuteNonQueryAsyncTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - SqlDataReader reader = await cmd.ExecuteReaderAsync(); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await cmd.ExecuteNonQueryAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteReaderAsyncErrorTest() + public async Task ExecuteNonQueryAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT 1 / 0;", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - // @TODO: TestTdsServer should not throw on ExecuteReader, should throw on reader.Read - await Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - // Synapse: Parse error at line: 1, column: 27: Incorrect syntax near 'for'. - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public void ExecuteXmlReaderAsyncTest() + [Fact] + public async Task ExecuteReaderAsyncTest() { - // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async _ => - { #if NET - await using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #else - using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;"; - - conn.Open(); - XmlReader reader = await cmd.ExecuteXmlReaderAsync(); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + using SqlDataReader reader = await cmd.ExecuteReaderAsync(); + while (reader.Read()) { } + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteXmlReaderAsyncErrorTest() + public async Task ExecuteReaderAsyncErrorTest() { - // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - - CollectStatisticsDiagnosticsAsync(async _ => - { #if NET - await using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT 1 / 0;", conn); #else - using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "select *, baddata = 1 / 0 from sys.objects for xml auto, xmldata;"; - - // @TODO: Since this test uses a real database connection, the exception is - // thrown during reader.Read. (ie, TestTdsServer does not obey proper - // exception behavior) - // NB: As a result of the exception being thrown during reader.Read, - // cmd.ExecuteXmlReaderAsync returns successfully. This means that we receive - // a WriteCommandAfter event rather than WriteCommandError. - await conn.OpenAsync(); - XmlReader reader = await cmd.ExecuteXmlReaderAsync(); - await Assert.ThrowsAsync(() => reader.ReadAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenTest() + public async Task ExecuteXmlReaderAsyncTest() { - RemoteExecutor.Invoke(() => + // The TDS test server does not return XML-formatted results, so + // ExecuteXmlReaderAsync will throw. We verify the error diagnostic path. + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection sqlConnection = new SqlConnection(connectionString)) - { - sqlConnection.Open(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); +#if NET + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;", conn); +#else + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;", conn); +#endif + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenErrorTest() + public async Task ExecuteXmlReaderAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnostics(_ => - { - using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) - { - Assert.Throws(() => sqlConnection.Open()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); +#if NET + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;", conn); +#else + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;", conn); +#endif + conn.Open(); + Assert.Throws(() => cmd.ExecuteXmlReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenAsyncTest() + public async Task ConnectionOpenAsyncTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection sqlConnection = new SqlConnection(connectionString)) + await using SqlConnection conn = new(connectionString); #else - using (SqlConnection sqlConnection = new SqlConnection(connectionString)) + using SqlConnection conn = new(connectionString); #endif - { - await sqlConnection.OpenAsync(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + await conn.OpenAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenAsyncErrorTest() + public async Task ConnectionOpenAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async _ => { - CollectStatisticsDiagnosticsAsync(async _ => - { #if NET - await using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) + await using SqlConnection conn = new(BadConnectionString); #else - using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) + using SqlConnection conn = new(BadConnectionString); #endif - { - await Assert.ThrowsAsync(() => sqlConnection.OpenAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenError]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + await Assert.ThrowsAsync(() => conn.OpenAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); } - private static void CollectStatisticsDiagnostics(Action sqlOperation, string[] expectedDiagnostics, [CallerMemberName] string methodName = "") - { - bool statsLogged = false; - bool operationHasError = false; - Guid beginOperationId = Guid.Empty; - - FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp => - { - IDictionary statistics; - - if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandBefore")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlCommand.Connection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - beginOperationId = retrievedOperationId; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandAfter")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - if (!operationHasError) - { - Assert.NotNull(statistics); - } - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlCommand.Connection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - // if we get to this point, then statistics exist and this must be the "end" - // event, so we need to make sure the operation IDs match - Assert.Equal(retrievedOperationId, beginOperationId); - beginOperationId = Guid.Empty; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandError")) - { - operationHasError = true; - Assert.NotNull(kvp.Value); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlCommand.Connection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenBefore")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenAfter")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - Assert.NotNull(statistics); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseBefore")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseAfter")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); + #endregion - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); + #region Helpers - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - }); - - diagnosticListenerObserver.Enable(); - using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) + private static void CollectStatisticsDiagnostics( + Action sqlOperation, + string[] expectedDiagnostics, + [CallerMemberName] string methodName = "") + { + bool statsLogged = false; + FakeDiagnosticListenerObserver observer = new(kvp => { + ValidateDiagnosticPayload(kvp); + statsLogged = true; + }); - Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName)); - - using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) + observer.Enable(); + using (DiagnosticListener.AllListeners.Subscribe(observer)) + using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) + { + server.Start(methodName); + string connectionString = new SqlConnectionStringBuilder { - server.Start(methodName); - Console.WriteLine(string.Format("Test: {0} Started Server", methodName)); - - var connectionString = new SqlConnectionStringBuilder - { - DataSource = $"localhost,{server.EndPoint.Port}", - Encrypt = SqlConnectionEncryptOption.Optional - }.ConnectionString; - - sqlOperation(connectionString); + DataSource = $"localhost,{server.EndPoint.Port}", + Encrypt = SqlConnectionEncryptOption.Optional + }.ConnectionString; - Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName)); + sqlOperation(connectionString); - Assert.True(statsLogged); + Assert.True(statsLogged, "Expected at least one diagnostic event"); + observer.Disable(); - diagnosticListenerObserver.Disable(); - foreach (string expected in expectedDiagnostics) - { - Assert.True(diagnosticListenerObserver.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); - } - - Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName)); + foreach (string expected in expectedDiagnostics) + { + Assert.True(observer.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); } - Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName)); } - Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName)); } - private static async Task CollectStatisticsDiagnosticsAsync(Func sqlOperation, string[] expectedDiagnostics, [CallerMemberName] string methodName = "") + private static async Task CollectStatisticsDiagnosticsAsync( + Func sqlOperation, + string[] expectedDiagnostics, + [CallerMemberName] string methodName = "") { bool statsLogged = false; - bool operationHasError = false; - Guid beginOperationId = Guid.Empty; - - FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp => + FakeDiagnosticListenerObserver observer = new(kvp => { - IDictionary statistics; - - if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandBefore")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - beginOperationId = retrievedOperationId; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandAfter")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - if (!operationHasError) - { - Assert.NotNull(statistics); - } - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - // if we get to this point, then statistics exist and this must be the "end" - // event, so we need to make sure the operation IDs match - Assert.Equal(retrievedOperationId, beginOperationId); - beginOperationId = Guid.Empty; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandError")) - { - operationHasError = true; - Assert.NotNull(kvp.Value); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenBefore")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenAfter")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - Assert.NotNull(statistics); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlConnection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); + ValidateDiagnosticPayload(kvp); + statsLogged = true; + }); - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseBefore")) + observer.Enable(); + using (DiagnosticListener.AllListeners.Subscribe(observer)) + using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) + { + server.Start(methodName); + string connectionString = new SqlConnectionStringBuilder { - Assert.NotNull(kvp.Value); + DataSource = $"localhost,{server.EndPoint.Port}", + Encrypt = SqlConnectionEncryptOption.Optional + }.ConnectionString; - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); + await sqlOperation(connectionString); - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); + Assert.True(statsLogged, "Expected at least one diagnostic event"); + observer.Disable(); - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseAfter")) + foreach (string expected in expectedDiagnostics) { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; + Assert.True(observer.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); + } + } - statsLogged = true; - } - }); + private static void ValidateDiagnosticPayload(KeyValuePair kvp) + { + Assert.NotNull(kvp.Value); - diagnosticListenerObserver.Enable(); - using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) + if (kvp.Key.Contains("Command")) { - Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName)); - using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) - { - server.Start(methodName); - Console.WriteLine(string.Format("Test: {0} Started Server", methodName)); - - var connectionString = new SqlConnectionStringBuilder - { - DataSource = $"localhost,{server.EndPoint.Port}", - Encrypt = SqlConnectionEncryptOption.Optional - }.ConnectionString; - await sqlOperation(connectionString); - - Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName)); - - Assert.True(statsLogged); - - diagnosticListenerObserver.Disable(); - foreach (string expected in expectedDiagnostics) - { - Assert.True(diagnosticListenerObserver.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); - } + SqlCommand cmd = GetPropertyValueFromType(kvp.Value, "Command"); + Assert.NotNull(cmd); + } + else if (kvp.Key.Contains("Connection")) + { + SqlConnection conn = GetPropertyValueFromType(kvp.Value, "Connection"); + Assert.NotNull(conn); + } - Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName)); - } - Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName)); + if (kvp.Key.Contains("Error")) + { + Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); + Assert.NotNull(ex); } - Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName)); + + string operation = GetPropertyValueFromType(kvp.Value, "Operation"); + Assert.False(string.IsNullOrWhiteSpace(operation)); } private static T GetPropertyValueFromType(object obj, string propName) { Type type = obj.GetType(); PropertyInfo pi = type.GetRuntimeProperty(propName); - - var propertyValue = pi.GetValue(obj); - return (T)propertyValue; + return (T)pi.GetValue(obj); } + + #endregion } + /// + /// Query engine that handles error queries (division by zero). + /// public class DiagnosticsQueryEngine : QueryEngine { - public DiagnosticsQueryEngine() : base(new TdsServerArguments()) - { - } + public DiagnosticsQueryEngine() : base(new TdsServerArguments()) { } protected override TDSMessageCollection CreateQueryResponse(ITDSServerSession session, TDSSQLBatchToken batchRequest) { string lowerBatchText = batchRequest.Text.ToLowerInvariant(); - - if (lowerBatchText.Contains("1 / 0")) // SELECT 1/0 + if (lowerBatchText.Contains("1 / 0")) { - TDSErrorToken errorToken = new TDSErrorToken(8134, 1, 16, "Divide by zero error encountered."); - TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Count, TDSDoneTokenCommandType.Select, 1); - TDSMessage responseMessage = new TDSMessage(TDSMessageType.Response, errorToken, doneToken); + TDSErrorToken errorToken = new(8134, 1, 16, "Divide by zero error encountered."); + TDSDoneToken doneToken = new(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Count, TDSDoneTokenCommandType.Select, 1); + TDSMessage responseMessage = new(TDSMessageType.Response, errorToken, doneToken); return new TDSMessageCollection(responseMessage); } - else - { - return base.CreateQueryResponse(session, batchRequest); - } + return base.CreateQueryResponse(session, batchRequest); } } } From 49db91a7d028e73258f2d9b5eac0297ac936e65e Mon Sep 17 00:00:00 2001 From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com> Date: Thu, 26 Mar 2026 14:11:12 -0700 Subject: [PATCH 12/22] Add type forwards to public Abstractions types (#4067) --- .../ref/Microsoft.Data.SqlClient.csproj | 3 ++ .../src/Microsoft.Data.SqlClient.csproj | 3 ++ .../netfx/ref/Microsoft.Data.SqlClient.csproj | 3 ++ .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 ++ .../src/TypeForwards.Abstractions.cs | 9 +++++ .../tests/UnitTests/TypeForwardTests.cs | 34 +++++++++++++++++++ 6 files changed, 55 insertions(+) create mode 100644 src/Microsoft.Data.SqlClient/src/TypeForwards.Abstractions.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/TypeForwardTests.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj index f3d5bdc844..3215e42068 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -31,6 +31,9 @@ + + TypeForwards.Abstractions.cs +
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 6c54000f9c..7145dbac28 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -1023,6 +1023,9 @@ TypeForwards.netcore.cs + + TypeForwards.Abstractions.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 208181ffb6..93fc5a5375 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -26,6 +26,9 @@ + + TypeForwards.Abstractions.cs + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 05a4284565..8408dabc0d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -1006,6 +1006,9 @@ System\Runtime\CompilerServices\IsExternalInit.netfx.cs + + TypeForwards.Abstractions.cs + diff --git a/src/Microsoft.Data.SqlClient/src/TypeForwards.Abstractions.cs b/src/Microsoft.Data.SqlClient/src/TypeForwards.Abstractions.cs new file mode 100644 index 0000000000..f8272c5d2b --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/TypeForwards.Abstractions.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.Data.SqlClient.SqlAuthenticationMethod))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.Data.SqlClient.SqlAuthenticationParameters))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.Data.SqlClient.SqlAuthenticationProvider))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.Data.SqlClient.SqlAuthenticationProviderException))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.Data.SqlClient.SqlAuthenticationToken))] diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/TypeForwardTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/TypeForwardTests.cs new file mode 100644 index 0000000000..8cb9811933 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/TypeForwardTests.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests +{ + public class TypeForwardTests + { + private static readonly Assembly s_sqlClientAssembly = typeof(SqlConnection).Assembly; + + [Theory] + [InlineData("Microsoft.Data.SqlClient.SqlAuthenticationMethod", true)] + [InlineData("Microsoft.Data.SqlClient.SqlAuthenticationParameters", false)] + [InlineData("Microsoft.Data.SqlClient.SqlAuthenticationProvider", false)] + [InlineData("Microsoft.Data.SqlClient.SqlAuthenticationProviderException", false)] + [InlineData("Microsoft.Data.SqlClient.SqlAuthenticationToken", false)] + public void AbstractionsType_CanBeLoadedFromSqlClientAssembly(string typeName, bool isEnum) + { + // Types moved to the Abstractions assembly must remain loadable via the + // Microsoft.Data.SqlClient assembly for backward compatibility. + Type? type = s_sqlClientAssembly.GetType(typeName, throwOnError: true); + + Assert.NotNull(type); + Assert.Equal(isEnum, type.IsEnum); + + // Assert that the assembly containing the type is the Abstractions assembly. + Assert.Equal("Microsoft.Data.SqlClient.Extensions.Abstractions", type.Assembly.GetName().Name); + } + } +} From ccdd846a99f4ec33e39d0de089712ea570de7ed2 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:24:12 -0300 Subject: [PATCH 13/22] Create stress test pipeline (#3867) * Task 40503: Create stress test pipeline - Added initial placeholder pipeline entry point file. - Wired up the new top-level stress test pipeline to the existing stages/jobs. - Using human friendly triggering pipeline names. - Removed PR pipeline as upstream trigger. - When triggered manually, we must choose one of the upstream pipelines to use for artifact download. - Temporarily added PR-SqlClient-Package to help with PR-based testing. - Sorted template parameters alphabetically. - Added debugging for artifact details. * Changed the pipeline to expect project-references to SqlClient projects. * Moved stress test pipeline files into a stress/ directory. * Fixed expansion errors. * Addressed Copilot feedback. * Addressed my own comments. --- eng/pipelines/dotnet-sqlclient-ci-core.yml | 18 -- ...qlclient-ci-package-reference-pipeline.yml | 7 - ...qlclient-ci-project-reference-pipeline.yml | 7 - .../jobs/pack-abstractions-package-ci-job.yml | 4 +- .../jobs/pack-azure-package-ci-job.yml | 4 +- eng/pipelines/jobs/stress-tests-ci-job.yml | 194 ----------------- .../jobs/test-abstractions-package-ci-job.yml | 6 +- .../jobs/test-azure-package-ci-job.yml | 6 +- .../sqlclient-pr-package-ref-pipeline.yml | 8 +- .../sqlclient-pr-project-ref-pipeline.yml | 8 +- .../stages/stress-tests-ci-stage.yml | 205 ------------------ eng/pipelines/stress-tests-pipeline.yml | 81 ------- eng/pipelines/stress/stress-tests-job.yml | 180 +++++++++++++++ .../stress/stress-tests-pipeline.yml | 101 +++++++++ eng/pipelines/stress/stress-tests-stage.yml | 130 +++++++++++ .../tests/StressTests/Readme.md | 94 +++----- .../tests/StressTests/StressTests.slnx | 7 - 17 files changed, 449 insertions(+), 611 deletions(-) delete mode 100644 eng/pipelines/jobs/stress-tests-ci-job.yml delete mode 100644 eng/pipelines/stages/stress-tests-ci-stage.yml delete mode 100644 eng/pipelines/stress-tests-pipeline.yml create mode 100644 eng/pipelines/stress/stress-tests-job.yml create mode 100644 eng/pipelines/stress/stress-tests-pipeline.yml create mode 100644 eng/pipelines/stress/stress-tests-stage.yml delete mode 100644 src/Microsoft.Data.SqlClient/tests/StressTests/StressTests.slnx diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml index e4d6d70a7f..d39c590efc 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-core.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml @@ -78,11 +78,6 @@ parameters: type: string default: $(ci_var_defaultPoolName) - # True to add a Stress Test stage to the pipeline. - - name: enableStressTests - type: boolean - default: false - # The timeout, in minutes, for each test job. - name: testJobTimeout type: number @@ -218,19 +213,6 @@ stages: loggingArtifactsName: $(loggingArtifactsName) mdsArtifactsName: $(mdsArtifactsName) - # Run the stress tests, if desired. - - ${{ if eq(parameters.enableStressTests, true) }}: - - template: /eng/pipelines/stages/stress-tests-ci-stage.yml@self - parameters: - buildConfiguration: ${{ parameters.buildConfiguration }} - additionalDependsOn: - - build_sqlclient_package_stage - - build_azure_package_stage - mdsArtifactsName: $(mdsArtifactsName) - mdsPackageVersion: $(mdsPackageVersion) - azurePackageVersion: $(azurePackageVersion) - dotnetVerbosity: ${{ parameters.dotnetVerbosity }} - # Run the MDS and AKV tests. - template: /eng/pipelines/common/templates/stages/ci-run-tests-stage.yml@self parameters: diff --git a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml index 262c2b909a..d38fb14d57 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml @@ -103,12 +103,6 @@ parameters: type: boolean default: false - # True to run the stress tests stage. - - name: enableStressTests - displayName: Enable Stress Tests - type: boolean - default: false - # The target frameworks to build and run tests for on Windows. # # These are _not_ the target frameworks to build the driver packages for. @@ -173,7 +167,6 @@ extends: referenceType: Package debug: ${{ parameters.debug }} dotnetVerbosity: ${{ parameters.dotnetVerbosity }} - enableStressTests: ${{ parameters.enableStressTests }} targetFrameworks: ${{ parameters.targetFrameworks }} targetFrameworksUnix: ${{ parameters.targetFrameworksUnix }} testJobTimeout: ${{ parameters.testJobTimeout }} diff --git a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml index a5a78f0f8b..7fef476e59 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml @@ -103,12 +103,6 @@ parameters: type: boolean default: false - # True to run the stress tests stage. - - name: enableStressTests - displayName: Enable Stress Tests - type: boolean - default: false - # The target frameworks to build and run tests for on Windows. # # These are _not_ the target frameworks to build the driver packages for. @@ -173,7 +167,6 @@ extends: referenceType: Project debug: ${{ parameters.debug }} dotnetVerbosity: ${{ parameters.dotnetVerbosity }} - enableStressTests: ${{ parameters.enableStressTests }} targetFrameworks: ${{ parameters.targetFrameworks }} targetFrameworksUnix: ${{ parameters.targetFrameworksUnix }} testJobTimeout: ${{ parameters.testJobTimeout }} diff --git a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml index 9db1c05696..aed7672618 100644 --- a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml +++ b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml @@ -78,9 +78,7 @@ jobs: # Explicitly unset the $PLATFORM environment variable that is set by the # 'ADO Build properties' Library in the ADO SqlClientDrivers public project. # This is defined with a non-standard Platform of 'AnyCPU', and will fail - # the builds if left defined. The stress tests solution does not require - # any specific Platform, and so its solution file doesn't support any - # non-standard platforms. + # the builds if left defined. # # Note that Azure Pipelines will inject this variable as PLATFORM into the # environment of all tasks in this job. diff --git a/eng/pipelines/jobs/pack-azure-package-ci-job.yml b/eng/pipelines/jobs/pack-azure-package-ci-job.yml index 95853d43e0..3b03dc0adc 100644 --- a/eng/pipelines/jobs/pack-azure-package-ci-job.yml +++ b/eng/pipelines/jobs/pack-azure-package-ci-job.yml @@ -107,9 +107,7 @@ jobs: # Explicitly unset the $PLATFORM environment variable that is set by the # 'ADO Build properties' Library in the ADO SqlClientDrivers public # project. This is defined with a non-standard Platform of 'AnyCPU', and - # will fail the builds if left defined. The stress tests solution does - # not require any specific Platform, and so its solution file doesn't - # support any non-standard platforms. + # will fail the builds if left defined. # # Note that Azure Pipelines will inject this variable as PLATFORM into the # environment of all tasks in this job. diff --git a/eng/pipelines/jobs/stress-tests-ci-job.yml b/eng/pipelines/jobs/stress-tests-ci-job.yml deleted file mode 100644 index 7477295f54..0000000000 --- a/eng/pipelines/jobs/stress-tests-ci-job.yml +++ /dev/null @@ -1,194 +0,0 @@ -################################################################################ -# Licensed to the .NET Foundation under one or more agreements. The .NET -# Foundation licenses this file to you under the MIT license. See the LICENSE -# file in the project root for more information. -################################################################################ - -# This job builds and runs stress tests against an MDS NuGet package available -# as a pipeline artifact. -# -# The stress tests are located here: -# -# src/Microsoft.Data.SqlClient/tests/StressTests -# -# This template defines a job named 'run_stress_tests_job_' that can be -# depended on by downstream jobs. - -parameters: - # The suffix to append to the job name. - - name: jobNameSuffix - type: string - default: '' - - # The prefix to prepend to the job's display name: - # - # [] Run Stress Tests - # - - name: displayNamePrefix - type: string - default: '' - - # The name of the Azure Pipelines pool to use. - - name: poolName - type: string - default: '' - - # The pool VM image to use. - - name: vmImage - type: string - default: '' - - # The pipeline step to run to configure SQL Server. - - name: sqlSetupStep - type: step - - # The name of the MDS pipeline artifacts to download. - - name: mdsArtifactsName - type: string - default: '' - - # The solution file to restore/build. - - name: solution - type: string - default: '' - - # The test project to run. - - name: testProject - type: string - default: '' - - # dotnet CLI arguments for the restore step. - - name: restoreArguments - type: string - default: '' - - # dotnet CLI arguments for the build and run steps. - - name: buildArguments - type: string - default: '' - - # The list of .NET runtimes to test against. - - name: netTestRuntimes - type: object - default: [] - - # The list of .NET Framework runtimes to test against. - - name: netFrameworkTestRuntimes - type: object - default: [] - - # The stress test config file contents to write to the config file. - - name: configContent - type: string - default: '' - -jobs: -- job: run_stress_tests_job_${{ parameters.jobNameSuffix }} - displayName: '[${{ parameters.displayNamePrefix }}] Run Stress Tests' - pool: - name: ${{ parameters.poolName }} - ${{ if eq(parameters.poolName, 'Azure Pipelines') }}: - vmImage: ${{ parameters.vmImage }} - ${{ else }}: - demands: - - imageOverride -equals ${{ parameters.vmImage }} - - variables: - # Stress test command-line arguments. - - name: testArguments - value: -a SqlClient.Stress.Tests -console - - # Explicitly unset the $PLATFORM environment variable that is set by the - # 'ADO Build properties' Library in the ADO SqlClientDrivers public project. - # This is defined with a non-standard Platform of 'AnyCPU', and will fail - # the builds if left defined. The stress tests solution does not require - # any specific Platform, and so its solution file doesn't support any - # non-standard platforms. - # - # Note that Azure Pipelines will inject this variable as PLATFORM into the - # environment of all tasks in this job. - # - # See: - # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch - # - - name: Platform - value: '' - - # Do the same for $CONFIGURATION since we explicitly set it using our - # 'buildConfiguration' parameter, and we don't want the environment to - # override us. - - name: Configuration - value: '' - - steps: - - # Install the .NET SDK and Runtimes. - - template: /eng/pipelines/steps/install-dotnet.yml@self - parameters: - runtimes: [8.x, 9.x] - - # Download the pipeline artifact that contains the MDS package to test. - - task: DownloadPipelineArtifact@2 - displayName: Download MDS Artifacts - inputs: - artifactName: ${{ parameters.mdsArtifactsName }} - targetPath: $(Build.SourcesDirectory)/packages - - # Setup the local SQL Server. - - ${{ parameters.sqlSetupStep }} - - # We use the 'custom' command because the DotNetCoreCLI@2 task doesn't support - # all of our argument combinations for the different build steps. - - # Restore the solution. - - task: DotNetCoreCLI@2 - displayName: Restore Solution - inputs: - command: custom - custom: restore - projects: ${{ parameters.solution }} - arguments: ${{ parameters.restoreArguments }} - - # Build the solution. - - task: DotNetCoreCLI@2 - displayName: Build Solution - inputs: - command: custom - custom: build - projects: ${{ parameters.solution }} - arguments: ${{ parameters.buildArguments }} --no-restore - - # Write the config file. - - task: PowerShell@2 - displayName: Write Config File - inputs: - pwsh: true - targetType: inline - script: | - # Capture the multi-line JSON content into a variable. - $content = @" - ${{ parameters.configContent }} - "@ - - # Write the JSON content to the config file. - $content | Out-File -FilePath "config.json" - - # Run the stress tests for each .NET runtime. - - ${{ each runtime in parameters.netTestRuntimes }}: - - task: DotNetCoreCLI@2 - displayName: Test [${{runtime}}] - inputs: - command: custom - custom: run - projects: ${{ parameters.testProject }} - arguments: ${{ parameters.buildArguments }} --no-build -f ${{runtime}} -e STRESS_CONFIG_FILE=config.json -- $(testArguments) - - # Run the stress tests for each .NET Framework runtime. - - ${{ each runtime in parameters.netFrameworkTestRuntimes }}: - - task: DotNetCoreCLI@2 - displayName: Test [${{runtime}}] - inputs: - command: custom - custom: run - projects: ${{ parameters.testProject }} - arguments: ${{ parameters.buildArguments }} --no-build -f ${{runtime}} -e STRESS_CONFIG_FILE=config.json -- $(testArguments) diff --git a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml index 9d205b5108..a88d4dc347 100644 --- a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml +++ b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml @@ -27,7 +27,7 @@ parameters: # The prefix to prepend to the job's display name: # - # [] Run Stress Tests + # [] Test Abstractions Package # - name: displayNamePrefix type: string @@ -97,9 +97,7 @@ jobs: # Explicitly unset the $PLATFORM environment variable that is set by the # 'ADO Build properties' Library in the ADO SqlClientDrivers public project. # This is defined with a non-standard Platform of 'AnyCPU', and will fail - # the builds if left defined. The stress tests solution does not require - # any specific Platform, and so its solution file doesn't support any - # non-standard platforms. + # the builds if left defined. # # Note that Azure Pipelines will inject this variable as PLATFORM into the # environment of all tasks in this job. diff --git a/eng/pipelines/jobs/test-azure-package-ci-job.yml b/eng/pipelines/jobs/test-azure-package-ci-job.yml index 53ca26366f..5e5dffe381 100644 --- a/eng/pipelines/jobs/test-azure-package-ci-job.yml +++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml @@ -46,7 +46,7 @@ parameters: # The prefix to prepend to the job's display name: # - # [] Run Stress Tests + # [] Test Azure Package # - name: displayNamePrefix type: string @@ -158,9 +158,7 @@ jobs: # Explicitly unset the $PLATFORM environment variable that is set by the # 'ADO Build properties' Library in the ADO SqlClientDrivers public # project. This is defined with a non-standard Platform of 'AnyCPU', and - # will fail the builds if left defined. The stress tests solution does - # not require any specific Platform, and so its solution file doesn't - # support any non-standard platforms. + # will fail the builds if left defined. # # Note that Azure Pipelines will inject this variable as PLATFORM into the # environment of all tasks in this job. diff --git a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml index 440d7afa27..c454da710a 100644 --- a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml @@ -51,6 +51,7 @@ pr: - NuGet.config exclude: - eng/pipelines/onebranch/* + - eng/pipelines/stress/* # Do not trigger commit or schedule runs for this pipeline. trigger: none @@ -78,12 +79,6 @@ parameters: type: boolean default: false - # True to run the stress tests stage. - - name: enableStressTests - displayName: Enable Stress Tests - type: boolean - default: false - # The target frameworks to build and run tests for on Windows. # # These are _not_ the target frameworks to build the driver packages for. @@ -133,7 +128,6 @@ extends: buildPlatforms: ${{ parameters.buildPlatforms }} referenceType: Package debug: ${{ parameters.debug }} - enableStressTests: ${{ parameters.enableStressTests }} targetFrameworks: ${{ parameters.targetFrameworks }} targetFrameworksUnix: ${{ parameters.targetFrameworksUnix }} testJobTimeout: ${{ parameters.testJobTimeout }} diff --git a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml index d3ae773181..539d03fe82 100644 --- a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml +++ b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml @@ -51,6 +51,7 @@ pr: - NuGet.config exclude: - eng/pipelines/onebranch/* + - eng/pipelines/stress/* # Do not trigger commit or schedule runs for this pipeline. trigger: none @@ -78,12 +79,6 @@ parameters: type: boolean default: false - # True to run the stress tests stage. - - name: enableStressTests - displayName: Enable Stress Tests - type: boolean - default: false - # The target frameworks to build and run tests for on Windows. # # These are _not_ the target frameworks to build the driver packages for. @@ -133,7 +128,6 @@ extends: buildPlatforms: ${{ parameters.buildPlatforms }} referenceType: Project debug: ${{ parameters.debug }} - enableStressTests: ${{ parameters.enableStressTests }} targetFrameworks: ${{ parameters.targetFrameworks }} targetFrameworksUnix: ${{ parameters.targetFrameworksUnix }} testJobTimeout: ${{ parameters.testJobTimeout }} diff --git a/eng/pipelines/stages/stress-tests-ci-stage.yml b/eng/pipelines/stages/stress-tests-ci-stage.yml deleted file mode 100644 index 978105d580..0000000000 --- a/eng/pipelines/stages/stress-tests-ci-stage.yml +++ /dev/null @@ -1,205 +0,0 @@ -################################################################################ -# Licensed to the .NET Foundation under one or more agreements. The .NET -# Foundation licenses this file to you under the MIT license. See the LICENSE -# file in the project root for more information. -################################################################################ - -# This stage builds and runs stress tests against an MDS NuGet package available -# as a pipeline artifact. -# -# The stress tests are located here: -# -# src/Microsoft.Data.SqlClient/tests/StressTests -# -# All tests use a localhost SQL Server configured for SQL auth via the 'sa' user -# and the provided password. -# -# This stage depends on the secrets_stage. -# -# This template defines a stage named 'run_stress_tests_stage' that can be -# depended on by downstream stages. - -parameters: - - # The names of any additional stages this stage depends on, for example the stages that publish - # the MDS package artifacts we will test. - - name: additionalDependsOn - type: object - default: [] - - # The Azure package version to stress test. This version must be available in - # one of the configured NuGet sources. - - name: azurePackageVersion - displayName: Azure Package Version - type: string - - # The type of build to produce (Debug or Release) - - name: buildConfiguration - type: string - default: Debug - values: - - Debug - - Release - - # The verbosity level for the dotnet CLI commands. - - name: dotnetVerbosity - type: string - default: normal - values: - - quiet - - minimal - - normal - - detailed - - diagnostic - - # The name of the MDS pipeline artifacts to download. - - name: mdsArtifactsName - type: string - default: MDS.Artifacts - - # The MDS package version found in the pipeline artifacts we will download. - - name: mdsPackageVersion - type: string - - # The list of .NET Framework runtimes to test against. - - name: netFrameworkTestRuntimes - type: object - default: [net462] - - # The list of .NET runtimes to test against. - - name: netTestRuntimes - type: object - default: [net8.0, net9.0, net10.0] - -stages: - - stage: run_stress_tests_stage - displayName: Run Stress Tests - dependsOn: - - secrets_stage - - ${{ each dep in parameters.additionalDependsOn }}: - - ${{ dep }} - - variables: - # The directory where dotnet artifacts will be staged. Not to be - # confused with pipeline artifact. - - name: dotnetArtifactsDir - value: $(Build.StagingDirectory)/dotnetArtifacts - - # The solution file to use for all dotnet CLI commands. - - name: solution - value: src/Microsoft.Data.SqlClient/tests/StressTests/StressTests.slnx - - # The stress test project to run. - - name: testProject - value: src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Runner/SqlClient.Stress.Runner.csproj - - # dotnet CLI arguments common to all commands. - - name: commonArguments - value: >- - --verbosity ${{parameters.dotnetVerbosity}} - --artifacts-path $(dotnetArtifactsDir) - -p:MdsPackageVersion=${{parameters.mdsPackageVersion}} - -p:AzurePackageVersion=${{parameters.azurePackageVersion}} - - # dotnet CLI arguments for build/run commands. - - name: buildArguments - value: >- - $(commonArguments) - --configuration ${{parameters.buildConfiguration}} - - # Bring the SA password from the secrets_stage into scope here. - - name: saPassword - value: $[stageDependencies.secrets_stage.secrets_job.outputs['SaPassword.Value']] - - # The contents of the config file to use for all tests. We will write - # this to a JSON file for each test job, and then point to it via the - # STRESS_CONFIG_FILE environment variable. - - name: ConfigContent - value: | - [ - { - "name": "Azure SQL", - "type": "SqlServer", - "isDefault": true, - "dataSource": "localhost", - "user": "sa", - "password": "$(saPassword)", - "supportsWindowsAuthentication": false, - "isLocal": false, - "disableMultiSubnetFailover": true, - "disableNamedPipes": true, - "encrypt": false - } - ] - - jobs: - - # -------------------------------------------------------------------------- - # Build and test on Linux. - - - template: /eng/pipelines/jobs/stress-tests-ci-job.yml@self - parameters: - jobNameSuffix: linux - displayNamePrefix: Linux - poolName: $(ci_var_defaultPoolName) - vmImage: ADO-UB22-SQL22 - sqlSetupStep: - template: /eng/pipelines/common/templates/steps/configure-sql-server-linux-step.yml@self - parameters: - saPassword: $(saPassword) - mdsArtifactsName: ${{ parameters.mdsArtifactsName }} - solution: $(solution) - testProject: $(testProject) - restoreArguments: $(commonArguments) - buildArguments: $(buildArguments) - netTestRuntimes: ${{ parameters.netTestRuntimes }} - configContent: $(ConfigContent) - - # -------------------------------------------------------------------------- - # Build and test on Windows - - - template: /eng/pipelines/jobs/stress-tests-ci-job.yml@self - parameters: - jobNameSuffix: windows - displayNamePrefix: Win - poolName: $(ci_var_defaultPoolName) - # The Windows images include a suitable .NET Framework runtime, so we - # don't have to install one explicitly. - vmImage: ADO-MMS22-SQL22 - sqlSetupStep: - template: /eng/pipelines/common/templates/steps/configure-sql-server-win-step.yml@self - parameters: - saPassword: $(saPassword) - mdsArtifactsName: ${{ parameters.mdsArtifactsName }} - solution: $(solution) - testProject: $(testProject) - restoreArguments: $(commonArguments) - buildArguments: $(buildArguments) - netTestRuntimes: ${{ parameters.netTestRuntimes }} - # Note that we include the .NET Framework runtimes for test runs on - # Windows. - netFrameworkTestRuntimes: ${{ parameters.netFrameworkTestRuntimes }} - configContent: $(ConfigContent) - - # -------------------------------------------------------------------------- - # Build and test on macOS. - - - template: /eng/pipelines/jobs/stress-tests-ci-job.yml@self - parameters: - jobNameSuffix: macos - displayNamePrefix: macOS - # We don't have any 1ES Hosted Pool images for macOS, so we use a - # generic one from Azure Pipelines. - poolName: Azure Pipelines - vmImage: macos-latest - sqlSetupStep: - template: /eng/pipelines/common/templates/steps/configure-sql-server-macos-step.yml@self - parameters: - saPassword: $(saPassword) - mdsArtifactsName: ${{ parameters.mdsArtifactsName }} - solution: $(solution) - testProject: $(testProject) - restoreArguments: $(commonArguments) - buildArguments: $(buildArguments) - netTestRuntimes: ${{ parameters.netTestRuntimes }} - configContent: $(ConfigContent) diff --git a/eng/pipelines/stress-tests-pipeline.yml b/eng/pipelines/stress-tests-pipeline.yml deleted file mode 100644 index e08f255e5a..0000000000 --- a/eng/pipelines/stress-tests-pipeline.yml +++ /dev/null @@ -1,81 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -# This pipeline runs the stress test suite using the most recent artifacts from -# the CI-SqlClient-Package pipeline. -# -# It runs after every successful run of the following pipelines: -# -# Public project: -# - PR-SqlClient-Package -# - CI-SqlClient-Package -# -# ADO.net project: -# - MDS Main CI-Package -# -# This pipeline definition is mapped to the Stress-SqlClient pipelines: -# -# Public project: -# -# TODO -# -# ADO.net project: -# -# TODO - -# Set the pipeline run name to the day-of-year and the daily run counter. -name: $(DayOfYear)$(Rev:rr) - -# Do not trigger this pipeline for PRs, commits, or schedules. -pr: none -trigger: none - -# Trigger this pipeline after successful runs of each of the following pipelines. -resources: - pipelines: - # The PR-SqlClient-Package pipeline in the Public project. - - pipeline: upstreamPipeline1 - project: Public - source: /ADO/PR-SqlClient-Package - trigger: true - # The CI-SqlClient-Package pipeline in the Public project. - - pipeline: upstreamPipeline2 - project: Public - source: /ADO/CI-SqlClient-Package - trigger: true - # The MDS Main CI-Package pipeline in the ADO.net project. - - pipeline: upstreamPipeline3 - project: ADO.net - source: /Internal CI/MDS Main CI-Package - trigger: true - -# Pipeline parameters, visible in the Azure DevOps UI. -parameters: - - # The build configuration to use; defaults to Release. - - name: buildConfiguration - displayName: Build Configuration - type: string - default: Release - values: - - Debug - - Release - - # True to emit debug information and steps. - - name: debug - displayName: Enable debug output - type: boolean - default: false - -stages: -- stage: Placeholder - displayName: Placeholder Stage - jobs: - - job: PlaceholderJob - displayName: Placeholder Job - steps: - - script: echo "This is a placeholder stage." - displayName: Placeholder Step diff --git a/eng/pipelines/stress/stress-tests-job.yml b/eng/pipelines/stress/stress-tests-job.yml new file mode 100644 index 0000000000..40cecf6e93 --- /dev/null +++ b/eng/pipelines/stress/stress-tests-job.yml @@ -0,0 +1,180 @@ +#################################################################################################### +# Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this +# file to you under the MIT license. See the LICENSE file in the project root for more information. +#################################################################################################### + +# This job builds and runs stress tests by compiling the SqlClient projects transitively. +# +# The stress tests are located here: +# +# src/Microsoft.Data.SqlClient/tests/StressTests +# +# This template defines a job named 'stress_tests_job_' that can be depended on by +# downstream jobs. + +parameters: + + # The type of build to produce (Debug or Release) + - name: buildConfiguration + type: string + values: + - Debug + - Release + + # True to enable debugging steps. + - name: debug + type: boolean + default: false + + # The prefix to prepend to the job's display name: + # + # [] Run Stress Tests + # + - name: displayNamePrefix + type: string + + # The verbosity level for the dotnet CLI commands. + - name: dotnetVerbosity + type: string + default: normal + values: + - quiet + - minimal + - normal + - detailed + - diagnostic + + # The suffix to append to the job name. + - name: jobNameSuffix + type: string + + # The list of .NET Framework runtimes to test against. + - name: netFrameworkTestRuntimes + type: object + default: [] + + # The list of .NET runtimes to test against. + - name: netTestRuntimes + type: object + default: [] + + # The name of the Azure Pipelines pool to use. + - name: poolName + type: string + + # The local SQL Server instance's 'sa' password, for use in the config file. + - name: saPassword + type: string + + # The step to run to configure SQL Server. This should configure a local SQL Server instance with + # an 'sa' login using the same password provided by the saPassword parameter. + - name: sqlSetupStep + type: step + + # The pool VM image to use. + - name: vmImage + type: string + +jobs: + - job: stress_tests_job_${{ parameters.jobNameSuffix }} + displayName: '[${{ parameters.displayNamePrefix }}] Run Stress Tests' + pool: + name: ${{ parameters.poolName }} + ${{ if eq(parameters.poolName, 'Azure Pipelines') }}: + vmImage: ${{ parameters.vmImage }} + ${{ else }}: + demands: + - imageOverride -equals ${{ parameters.vmImage }} + + variables: + + # The directory where dotnet artifacts will be staged. Not to be + # confused with pipeline artifacts. + - name: dotnetArtifactsDir + value: $(Build.StagingDirectory)/dotnetArtifacts + + # The top-level project file to build and run. + - name: project + value: $(Build.SourcesDirectory)/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Runner/SqlClient.Stress.Runner.csproj + + # dotnet CLI arguments. + - name: dotnetArguments + value: >- + --verbosity ${{ parameters.dotnetVerbosity }} + --artifacts-path $(dotnetArtifactsDir) + --configuration ${{ parameters.buildConfiguration }} + + # The contents of the config file to use for all tests. We will write this to a JSON file and + # then point to it via the STRESS_CONFIG_FILE environment variable. + - name: configContent + value: | + [ + { + "name": "Azure SQL", + "type": "SqlServer", + "isDefault": true, + "dataSource": "localhost", + "user": "sa", + "password": "${{ parameters.saPassword }}", + "supportsWindowsAuthentication": false, + "isLocal": false, + "disableMultiSubnetFailover": true, + "disableNamedPipes": true, + "encrypt": false + } + ] + + # Stress test command-line arguments. + - name: testArguments + value: -a SqlClient.Stress.Tests -console + + steps: + + # Install the .NET SDK and Runtimes. + - template: /eng/pipelines/steps/install-dotnet.yml@self + parameters: + runtimes: [8.x, 9.x] + + # Setup the local SQL Server. + - ${{ parameters.sqlSetupStep }} + + # Write the config file. + - task: PowerShell@2 + displayName: Write Config File + inputs: + pwsh: true + targetType: inline + script: | + # Capture the multi-line JSON content into a variable. + $content = @" + $(configContent) + "@ + + # Write the JSON content to the config file. + $content | Out-File -FilePath "config.json" + + # Build the project. + - task: DotNetCoreCLI@2 + displayName: Build Project + inputs: + command: build + projects: $(project) + arguments: $(dotnetArguments) + + # Run the stress tests for each .NET runtime. + - ${{ each runtime in parameters.netTestRuntimes }}: + - task: DotNetCoreCLI@2 + displayName: Test [${{ runtime }}] + inputs: + command: run + projects: $(project) + arguments: $(dotnetArguments) --no-build -f ${{ runtime }} -e STRESS_CONFIG_FILE=config.json -- $(testArguments) + + # Run the stress tests for each .NET Framework runtime. + - ${{ each runtime in parameters.netFrameworkTestRuntimes }}: + - task: DotNetCoreCLI@2 + displayName: Test [${{ runtime }}] + inputs: + command: run + projects: $(project) + arguments: $(dotnetArguments) --no-build -f ${{ runtime }} -e STRESS_CONFIG_FILE=config.json -- $(testArguments) diff --git a/eng/pipelines/stress/stress-tests-pipeline.yml b/eng/pipelines/stress/stress-tests-pipeline.yml new file mode 100644 index 0000000000..cec68912fb --- /dev/null +++ b/eng/pipelines/stress/stress-tests-pipeline.yml @@ -0,0 +1,101 @@ +#################################################################################################### +# Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this +# file to you under the MIT license. See the LICENSE file in the project root for more information. +#################################################################################################### + +# This pipeline runs the stress test suite against the SqlClient projects, building them +# transitively as necessary, triggered by successful runs of the following pipelines: +# +# Public project: +# Triggering pipelines: PR-SqlClient-Project, CI-SqlClient (branch main only) +# Pipeline name: Stress-SqlClient +# Pipeline URL: TODO: Create the Azure DevOps pipeline. +# +# ADO.Net project: +# Triggering pipeline: MDS Main CI (branch internal/main only) +# Pipeline name: Stress-SqlClient +# Pipeline URL: TODO: Create the Azure DevOps pipeline. + +# Set the pipeline run name to the day-of-year and the daily run counter. +name: $(DayOfYear)$(Rev:rr) + +# Do not trigger this pipeline for PRs, commits, or schedules. +pr: none +trigger: none + +# Trigger this pipeline after successful runs of the desired pipelines. +# +# The pipeline identifiers are displayed in the Azure DevOps UI, so it is helpful if they indicate +# the project, folder, and pipeline name, hence the verbose values below. +# +resources: + pipelines: + + # The PR-SqlClient-Project pipeline in the Public project. + - pipeline: Public-ADO-PR-SqlClient-Project + project: Public + source: /ADO/PR-SqlClient-Project + trigger: true + + # The CI-SqlClient pipeline in the Public project. + - pipeline: Public-ADO-CI-SqlClient + project: Public + source: /ADO/CI-SqlClient + trigger: + branches: + include: + - main + + # The MDS Main CI pipeline in the ADO.Net project. + - pipeline: ADO-Net-Internal-CI-MDS-Main-CI + project: ADO.Net + source: /Internal CI/MDS Main CI + trigger: + branches: + include: + - internal/main + +# Pipeline parameters, visible in the Azure DevOps UI. +parameters: + + # The build configuration to use; defaults to Release. + - name: buildConfiguration + displayName: Build Configuration + type: string + default: Release + values: + - Debug + - Release + + # True to emit debug information and steps. + - name: debug + displayName: Enable debug output + type: boolean + default: false + + # Dotnet CLI verbosity level. + - name: dotnetVerbosity + displayName: dotnet CLI Verbosity + type: string + default: normal + values: + - quiet + - minimal + - normal + - detailed + - diagnostic + +# The stages to run. +stages: + + # Generate secrets. We use these for the local SQL Server 'sa' logins. + - template: /eng/pipelines/stages/generate-secrets-ci-stage.yml@self + parameters: + debug: ${{ parameters.debug }} + + # Run the stress tests. + - template: /eng/pipelines/stress/stress-tests-stage.yml@self + parameters: + buildConfiguration: ${{ parameters.buildConfiguration }} + debug: ${{ parameters.debug }} + dotnetVerbosity: ${{ parameters.dotnetVerbosity }} diff --git a/eng/pipelines/stress/stress-tests-stage.yml b/eng/pipelines/stress/stress-tests-stage.yml new file mode 100644 index 0000000000..0f0000f99a --- /dev/null +++ b/eng/pipelines/stress/stress-tests-stage.yml @@ -0,0 +1,130 @@ +#################################################################################################### +# Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this +# file to you under the MIT license. See the LICENSE file in the project root for more information. +#################################################################################################### + +# This stage builds and runs stress tests against the SqlClient projects, building them transitively +# as necessary. +# +# The stress tests are located here: +# +# src/Microsoft.Data.SqlClient/tests/StressTests +# +# All tests use a localhost SQL Server configured for SQL auth via the 'sa' user and the generated +# password. +# +# This stage depends on the secrets_stage. +# +# This template defines a stage named 'stress_tests_stage' that can be depended on by downstream +# stages. + +parameters: + + # The type of build to produce (Debug or Release) + - name: buildConfiguration + type: string + values: + - Debug + - Release + + # True to enable debugging steps. + - name: debug + type: boolean + default: false + + # The verbosity level for the dotnet CLI commands. + - name: dotnetVerbosity + type: string + default: normal + values: + - quiet + - minimal + - normal + - detailed + - diagnostic + + # The list of .NET Framework runtimes to test against. + - name: netFrameworkTestRuntimes + type: object + default: [net462] + + # The list of .NET runtimes to test against. + - name: netTestRuntimes + type: object + default: [net8.0, net9.0, net10.0] + +stages: + - stage: stress_tests_stage + displayName: Run Stress Tests + dependsOn: + - secrets_stage + + variables: + # Bring the SA password from the secrets_stage into scope here. + - name: saPassword + value: $[stageDependencies.secrets_stage.secrets_job.outputs['SaPassword.Value']] + + jobs: + + # ---------------------------------------------------------------------------------------------- + # Build and test on Linux. + + - template: /eng/pipelines/stress/stress-tests-job.yml@self + parameters: + buildConfiguration: ${{ parameters.buildConfiguration }} + debug: ${{ parameters.debug }} + displayNamePrefix: Linux + dotnetVerbosity: ${{ parameters.dotnetVerbosity }} + jobNameSuffix: linux + netTestRuntimes: ${{ parameters.netTestRuntimes }} + poolName: ADO-CI-1ES-Pool + saPassword: $(saPassword) + sqlSetupStep: + template: /eng/pipelines/common/templates/steps/configure-sql-server-linux-step.yml@self + parameters: + saPassword: $(saPassword) + vmImage: ADO-UB22-SQL22 + + # ---------------------------------------------------------------------------------------------- + # Build and test on Windows + + - template: /eng/pipelines/stress/stress-tests-job.yml@self + parameters: + buildConfiguration: ${{ parameters.buildConfiguration }} + debug: ${{ parameters.debug }} + displayNamePrefix: Win + dotnetVerbosity: ${{ parameters.dotnetVerbosity }} + jobNameSuffix: windows + # Note that we include the .NET Framework runtimes for test runs on Windows. + netFrameworkTestRuntimes: ${{ parameters.netFrameworkTestRuntimes }} + netTestRuntimes: ${{ parameters.netTestRuntimes }} + poolName: ADO-CI-1ES-Pool + saPassword: $(saPassword) + sqlSetupStep: + template: /eng/pipelines/common/templates/steps/configure-sql-server-win-step.yml@self + parameters: + saPassword: $(saPassword) + # The Windows images include a suitable .NET Framework runtime, so we don't have to install + # one explicitly. + vmImage: ADO-MMS22-SQL22 + + # ---------------------------------------------------------------------------------------------- + # Build and test on macOS. + + - template: /eng/pipelines/stress/stress-tests-job.yml@self + parameters: + buildConfiguration: ${{ parameters.buildConfiguration }} + debug: ${{ parameters.debug }} + displayNamePrefix: macOS + dotnetVerbosity: ${{ parameters.dotnetVerbosity }} + jobNameSuffix: macos + netTestRuntimes: ${{ parameters.netTestRuntimes }} + # We don't have any 1ES Hosted Pool images for macOS, so we use a generic one from Azure + # Pipelines. + poolName: Azure Pipelines + saPassword: $(saPassword) + sqlSetupStep: + template: /eng/pipelines/common/templates/steps/configure-sql-server-macos-step.yml@self + parameters: + saPassword: $(saPassword) + vmImage: macos-latest diff --git a/src/Microsoft.Data.SqlClient/tests/StressTests/Readme.md b/src/Microsoft.Data.SqlClient/tests/StressTests/Readme.md index f0723c3189..546428dbe9 100644 --- a/src/Microsoft.Data.SqlClient/tests/StressTests/Readme.md +++ b/src/Microsoft.Data.SqlClient/tests/StressTests/Readme.md @@ -1,26 +1,21 @@ # Microsoft.Data.SqlClient Stress Test -This Stress testing application for `Microsoft.Data.SqlClient` is under progress. +This Stress testing application for the `Microsoft.Data.SqlClient` suite is a work in progress. -This project intends to help finding a certain level of effectiveness under -unfavorable conditions, and verifying the mode of failures. +This project intends to help finding a certain level of effectiveness under unfavorable conditions, +and verifying the mode of failures. -This is a console application targeting all frameworks supported by MDS, -currently: +This is a console application targeting all frameworks supported by MDS, currently: -- .NET 8.0 +- .NET 10.0 - .NET 9.0 -- .NET Framework 4.6.2 -- .NET Framework 4.7 -- .NET Framework 4.7.1 -- .NET Framework 4.7.2 -- .NET Framework 4.8 -- .NET Framework 4.8.1 +- .NET 8.0 +- .NET Framework 4.6.2 (Windows only; higher .NET Framework versions are supported at runtime via + compatibility) ## Purpose of application for developers -Define fuzz tests for all new features/APIs in the driver and to be run before -every GA release. +Define fuzz tests for all new features/APIs in the driver and to be run before every GA release. ## Pre-Requisites @@ -40,14 +35,13 @@ Required in the config file: |`disableNamedPipes`|`true`, `false`|`true` means the connections will create just using tcp protocol.| |`encrypt`|`true`, `false`|Assigns the encrypt property of the connection strings.| -Note: The database user must have permission to create and drop databases. -Each execution of the stress tests will create a database with a name like: +Note: The database user must have permission to create and drop databases. Each execution of the +stress tests will create a database with a name like: - `StressTests-` -The database will be dropped as a best effort once testing is complete. This -allows for multiple test runs to execute in parallel against the same database -server without colliding. +The database will be dropped as a best effort once testing is complete. This allows for multiple +test runs to execute in parallel against the same database server without colliding. ## Adding new Tests @@ -55,83 +49,55 @@ server without colliding. ## Building the application -To build the application using the `StressTests.slnx` solution: - -```bash -dotnet build [-c|--configuration ] -``` - -```bash -# Builds the application for the Client Os in `Debug` Configuration for `AnyCpu` -# platform. -# -# All supported target frameworks are built by default. - -$ dotnet build -``` - -```bash -# Build the application for .Net framework 4.8.1 with `Debug` configuration. - -$ dotnet build -f net481 -``` +Build the application using the top-level project `SqlClient.Stress.Runner`: ```bash -# Build the application for .Net 9.0 with `Release` configuration. - -$ dotnet build -f net9.0 -c Release -``` - -```bash -# Cleans all build directories - -$ dotnet clean +$ cd .../src/Microsoft.Data.SqlClient/tests/StressTests +dotnet build SqlClient.Stress.Runner [-c ] ``` ## Running tests -After building the application, find the built folder with target framework and -run the `stresstest.exe` file with required arguments. +After building the application, find the built folder with target framework and run the +`stresstest.exe` file with required arguments. -Find the result in a log file inside the `logs` folder besides the command -prompt. +Find the result in a log file inside the `logs` folder besides the command prompt. -You may specify the config file by supplying an environment variable that -points to the file: +You may specify the config file by supplying an environment variable that points to the file: - `STRESS_CONFIG_FILE=/path/to/my/config.jsonc` ## Command prompt -You must run the stress tests from the root of the Stress Tests project -directory (i.e. the same directory this readme file is in). +You must run the stress tests from the root of the Stress Tests project directory (i.e. the same +directory this readme file is in). ```bash # Linux -$ cd /home/paul/dev/SqlClient/src/Microsoft.Data.SqlClient/tests/StressTests +$ cd .../src/Microsoft.Data.SqlClient/tests/StressTests # Via dotnet run CLI: -$ dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner/SqlClient.Stress.Runner.csproj -- -a SqlClient.Stress.Tests +$ dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner -- -a SqlClient.Stress.Tests # Via dotnet CLI: $ dotnet SqlClient.Stress.Runner/bin/Debug/net9.0/stresstest.dll -a SqlClient.Stress.Tests # With a specific config file and all output to console: -$ dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner/SqlClient.Stress.Runner.csproj -e STRESS_CONFIG_FILE=/path/to/config.jsonc -- -a SqlClient.Stress.Tests -console +$ dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner -e STRESS_CONFIG_FILE=/path/to/config.jsonc -- -a SqlClient.Stress.Tests -console ``` ```powershell # Windows -> cd \dev\SqlClient\src\Microsoft.Data.SqlClient\tests\StressTests +> cd ...\src\Microsoft.Data.SqlClient\tests\StressTests # Via dotnet run CLI: -> dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner\SqlClient.Stress.Runner.csproj -- -a SqlClient.Stress.Tests +> dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner -- -a SqlClient.Stress.Tests # Via executable: > .\SqlClient.Stress.Runner\bin\Debug\net481\stresstest.exe -a SqlClient.Stress.Tests # With a specific config file and all output to console: -> dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner\SqlClient.Stress.Runner.csproj -e STRESS_CONFIG_FILE=c:\path\to\config.jsonc -- -a SqlClient.Stress.Tests -console +> dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner -e STRESS_CONFIG_FILE=c:\path\to\config.jsonc -- -a SqlClient.Stress.Tests -console ``` ## Supported arguments @@ -164,14 +130,14 @@ $ dotnet run --no-build -f net9.0 --project SqlClient.Stress.Runner/SqlClient.St # Run the application for a built target framework and all discovered tests # without debugger attached and shows the test methods' names. -> .\stresstest.exe -a SqlClient.Stress.Tests -all -printMethodName +> .\stresstest.exe -a SqlClient.Stress.Tests -all -printMethodName ``` ```powershell # Run the application for a built target framework and all discovered tests and # will wait for debugger to be attached. -> .\stresstest.exe -a SqlClient.Stress.Tests -all -debug +> .\stresstest.exe -a SqlClient.Stress.Tests -all -debug ``` ```powershell diff --git a/src/Microsoft.Data.SqlClient/tests/StressTests/StressTests.slnx b/src/Microsoft.Data.SqlClient/tests/StressTests/StressTests.slnx deleted file mode 100644 index a0dfd5e503..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/StressTests/StressTests.slnx +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - From 8c887846aaa2846d7a3ab3718b61a24b60e1b394 Mon Sep 17 00:00:00 2001 From: Apoorv Deshmukh Date: Sat, 28 Mar 2026 01:57:23 +0530 Subject: [PATCH 14/22] Fix API docs (#4084) --- .../Microsoft.Data.SqlClient/SqlCommand.xml | 414 +++++++++--------- .../SqlDataAdapter.xml | 224 +++++----- .../SqlDataReader.xml | 38 +- 3 files changed, 338 insertions(+), 338 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index a3d87c843d..7509f756b3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -357,35 +357,35 @@ Next, compile and execute the following: using System; using System.Data; using Microsoft.Data.SqlClient; - + class Class1 { static void Main() { - // This is a simple example that demonstrates the usage of the + // This is a simple example that demonstrates the usage of the // BeginExecuteNonQuery functionality. - // The WAITFOR statement simply adds enough time to prove the + // The WAITFOR statement simply adds enough time to prove the // asynchronous nature of the command. - + string commandText = "UPDATE Production.Product SET ReorderPoint = ReorderPoint + 1 " + "WHERE ReorderPoint Is Not Null;" + "WAITFOR DELAY '0:0:3';" + "UPDATE Production.Product SET ReorderPoint = ReorderPoint - 1 " + "WHERE ReorderPoint Is Not Null"; - + RunCommandAsynchronously(commandText, GetConnectionString()); - + Console.WriteLine("Press ENTER to continue."); Console.ReadLine(); } - + private static void RunCommandAsynchronously(string commandText, string connectionString) { // Given command text and connection string, asynchronously execute // the specified command against the connection. For this example, - // the code displays an indicator as it is working, verifying the - // asynchronous behavior. + // the code displays an indicator as it is working, verifying the + // asynchronous behavior. using (SqlConnection connection = new SqlConnection(connectionString)) { try @@ -393,17 +393,17 @@ Next, compile and execute the following: int count = 0; SqlCommand command = new SqlCommand(commandText, connection); connection.Open(); - + IAsyncResult result = command.BeginExecuteNonQuery(); while (!result.IsCompleted) { Console.WriteLine("Waiting ({0})", count++); // Wait for 1/10 second, so the counter - // does not consume all available resources + // does not consume all available resources // on the main thread. System.Threading.Thread.Sleep(100); } - + Console.WriteLine("Command complete. Affected {0} rows.", command.EndExecuteNonQuery(result)); } @@ -423,11 +423,11 @@ Next, compile and execute the following: } } } - + private static string GetConnectionString() { - // To avoid storing the connection string in your code, - // you can retrieve it from a configuration file. + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=SSPI;" + "Initial Catalog=AdventureWorks"; } @@ -527,7 +527,7 @@ Next, compile and execute the following: using System.Text; using System.Windows.Forms; using Microsoft.Data.SqlClient; - + namespace Microsoft.AdoDotNet.CodeSamples { public partial class Form1 : Form @@ -536,9 +536,9 @@ Next, compile and execute the following: { InitializeComponent(); } - - // Hook up the form's Load event handler (you can double-click on - // the form's design surface in Visual Studio), and then add + + // Hook up the form's Load event handler (you can double-click on + // the form's design surface in Visual Studio), and then add // this code to the form's class: private void Form1_Load(object sender, EventArgs e) { @@ -546,42 +546,42 @@ Next, compile and execute the following: this.FormClosing += new System.Windows.Forms. FormClosingEventHandler(this.Form1_FormClosing); } - + // You need this delegate in order to display text from a thread // other than the form's thread. See the HandleCallback // procedure for more information. - // This same delegate matches both the DisplayStatus + // This same delegate matches both the DisplayStatus // and DisplayResults methods. private delegate void DisplayInfoDelegate(string Text); - + // This flag ensures that the user does not attempt - // to restart the command or close the form while the + // to restart the command or close the form while the // asynchronous command is executing. private bool isExecuting; - - // This example maintains the connection object + + // This example maintains the connection object // externally, so that it is available for closing. private SqlConnection connection; - + private static string GetConnectionString() { - // To avoid storing the connection string in your code, + // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=true;" + "Initial Catalog=AdventureWorks"; } - + private void DisplayStatus(string Text) { this.label1.Text = Text; } - + private void DisplayResults(string Text) { this.label1.Text = Text; DisplayStatus("Ready"); } - + private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e) { if (isExecuting) @@ -591,7 +591,7 @@ Next, compile and execute the following: e.Cancel = true; } } - + private void button1_Click(object sender, System.EventArgs e) { if (isExecuting) @@ -608,7 +608,7 @@ Next, compile and execute the following: DisplayResults(""); DisplayStatus("Connecting..."); connection = new SqlConnection(GetConnectionString()); - // To emulate a long-running query, wait for + // To emulate a long-running query, wait for // a few seconds before working with the data. // This command does not do much, but that's the point-- // it does not change your data, in the long run. @@ -618,19 +618,19 @@ Next, compile and execute the following: "WHERE ReorderPoint Is Not Null;" + "UPDATE Production.Product SET ReorderPoint = ReorderPoint - 1 " + "WHERE ReorderPoint Is Not Null"; - + command = new SqlCommand(commandText, connection); connection.Open(); - + DisplayStatus("Executing..."); isExecuting = true; - // Although it is not required that you pass the - // SqlCommand object as the second parameter in the + // Although it is not required that you pass the + // SqlCommand object as the second parameter in the // BeginExecuteNonQuery call, doing so makes it easier // to call EndExecuteNonQuery in the callback procedure. AsyncCallback callback = new AsyncCallback(HandleCallback); command.BeginExecuteNonQuery(callback, command); - + } catch (Exception ex) { @@ -643,7 +643,7 @@ Next, compile and execute the following: } } } - + private void HandleCallback(IAsyncResult result) { try @@ -658,38 +658,38 @@ Next, compile and execute the following: { rowText = " row affected."; } - + rowText = rowCount + rowText; - + // You may not interact with the form and its contents // from a different thread, and this callback procedure // is all but guaranteed to be running from a different thread - // than the form. Therefore you cannot simply call code that + // than the form. Therefore you cannot simply call code that // displays the results, like this: // DisplayResults(rowText) - + // Instead, you must call the procedure from the form's thread. // One simple way to accomplish this is to call the Invoke // method of the form, which calls the delegate you supply - // from the form's thread. + // from the form's thread. DisplayInfoDelegate del = new DisplayInfoDelegate(DisplayResults); this.Invoke(del, rowText); - + } catch (Exception ex) { - // Because you are now running code in a separate thread, + // Because you are now running code in a separate thread, // if you do not handle the exception here, none of your other - // code catches the exception. Because none of + // code catches the exception. Because none of // your code is on the call stack in this thread, there is nothing - // higher up the stack to catch the exception if you do not - // handle it here. You can either log the exception or - // invoke a delegate (as in the non-error case in this + // higher up the stack to catch the exception if you do not + // handle it here. You can either log the exception or + // invoke a delegate (as in the non-error case in this // example) to display the error on the form. In no case // can you simply display the error without executing a delegate - // as in the try block here. - - // You can create the delegate instance as you + // as in the try block here. + + // You can create the delegate instance as you // invoke it, like this: this.Invoke(new DisplayInfoDelegate(DisplayStatus), String.Format("Ready(last error: {0}", ex.Message)); @@ -851,7 +851,7 @@ Next, compile and execute the following: // Display the data within the reader. while (reader.Read()) { - // Display all the columns. + // Display all the columns. for (int i = 0; i < reader.FieldCount; i++) { Console.Write("{0}\t", reader.GetValue(i)); @@ -862,8 +862,8 @@ Next, compile and execute the following: private static string GetConnectionString() { - // To avoid storing the connection string in your code, - // you can retrieve it from a configuration file. + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=true;" + "Initial Catalog=AdventureWorks"; @@ -953,54 +953,54 @@ Next, compile and execute the following: using System; using System.Data; using Microsoft.Data.SqlClient; - + class Class1 { static void Main() { // This example is not terribly useful, but it proves a point. - // The WAITFOR statement simply adds enough time to prove the + // The WAITFOR statement simply adds enough time to prove the // asynchronous nature of the command. string commandText = "WAITFOR DELAY '00:00:03';" + "SELECT ProductID, Name FROM Production.Product WHERE ListPrice < 100"; - + RunCommandAsynchronously(commandText, GetConnectionString()); - + Console.WriteLine("Press ENTER to continue."); Console.ReadLine(); } - + private static void RunCommandAsynchronously(string commandText, string connectionString) { // Given command text and connection string, asynchronously execute // the specified command against the connection. For this example, - // the code displays an indicator as it is working, verifying the - // asynchronous behavior. + // the code displays an indicator as it is working, verifying the + // asynchronous behavior. try { // The code does not need to handle closing the connection explicitly-- // the use of the CommandBehavior.CloseConnection option takes care - // of that for you. + // of that for you. SqlConnection connection = new SqlConnection(connectionString); SqlCommand command = new SqlCommand(commandText, connection); - + connection.Open(); IAsyncResult result = command.BeginExecuteReader(CommandBehavior.CloseConnection); - + // Although it is not necessary, the following code - // displays a counter in the console window, indicating that - // the main thread is not blocked while awaiting the command + // displays a counter in the console window, indicating that + // the main thread is not blocked while awaiting the command // results. int count = 0; while (!result.IsCompleted) { Console.WriteLine("Waiting ({0})", count++); // Wait for 1/10 second, so the counter - // does not consume all available resources + // does not consume all available resources // on the main thread. System.Threading.Thread.Sleep(100); } - + using (SqlDataReader reader = command.EndExecuteReader(result)) { DisplayResults(reader); @@ -1021,26 +1021,26 @@ Next, compile and execute the following: Console.WriteLine("Error: {0}", ex.Message); } } - + private static void DisplayResults(SqlDataReader reader) { // Display the data within the reader. while (reader.Read()) { - // Display all the columns. + // Display all the columns. for (int i = 0; i < reader.FieldCount; i++) { Console.Write("{0}\t", reader.GetValue(i)); } - + Console.WriteLine(); } } - + private static string GetConnectionString() { - // To avoid storing the connection string in your code, - // you can retrieve it from a configuration file. + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=true;" + "Initial Catalog=AdventureWorks"; } @@ -1138,54 +1138,54 @@ Next, compile and execute the following: using System; using System.Data; using Microsoft.Data.SqlClient; - + class Class1 { static void Main() { // This example is not terribly useful, but it proves a point. - // The WAITFOR statement simply adds enough time to prove the + // The WAITFOR statement simply adds enough time to prove the // asynchronous nature of the command. string commandText = "WAITFOR DELAY '00:00:03';" + "SELECT ProductID, Name FROM Production.Product WHERE ListPrice < 100"; - + RunCommandAsynchronously(commandText, GetConnectionString()); - + Console.WriteLine("Press ENTER to continue."); Console.ReadLine(); } - + private static void RunCommandAsynchronously(string commandText, string connectionString) { // Given command text and connection string, asynchronously execute // the specified command against the connection. For this example, - // the code displays an indicator as it is working, verifying the - // asynchronous behavior. + // the code displays an indicator as it is working, verifying the + // asynchronous behavior. try { // The code does not need to handle closing the connection explicitly-- // the use of the CommandBehavior.CloseConnection option takes care - // of that for you. + // of that for you. SqlConnection connection = new SqlConnection(connectionString); SqlCommand command = new SqlCommand(commandText, connection); - + connection.Open(); IAsyncResult result = command.BeginExecuteReader(CommandBehavior.CloseConnection); - + // Although it is not necessary, the following code - // displays a counter in the console window, indicating that - // the main thread is not blocked while awaiting the command + // displays a counter in the console window, indicating that + // the main thread is not blocked while awaiting the command // results. int count = 0; while (!result.IsCompleted) { Console.WriteLine("Waiting ({0})", count++); // Wait for 1/10 second, so the counter - // does not consume all available resources + // does not consume all available resources // on the main thread. System.Threading.Thread.Sleep(100); } - + using (SqlDataReader reader = command.EndExecuteReader(result)) { DisplayResults(reader); @@ -1206,26 +1206,26 @@ Next, compile and execute the following: Console.WriteLine("Error: {0}", ex.Message); } } - + private static void DisplayResults(SqlDataReader reader) { // Display the data within the reader. while (reader.Read()) { - // Display all the columns. + // Display all the columns. for (int i = 0; i < reader.FieldCount; i++) { Console.Write("{0}\t", reader.GetValue(i)); } - + Console.WriteLine(); } } - + private static string GetConnectionString() { - // To avoid storing the connection string in your code, - // you can retrieve it from a configuration file. + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=true;" + "Initial Catalog=AdventureWorks"; } @@ -1335,7 +1335,7 @@ Next, compile and execute the following: using System.Text; using System.Windows.Forms; using Microsoft.Data.SqlClient; - + namespace Microsoft.AdoDotNet.CodeSamples { public partial class Form1 : Form @@ -1344,28 +1344,28 @@ Next, compile and execute the following: { InitializeComponent(); } - - // Hook up the form's Load event handler (you can double-click on - // the form's design surface in Visual Studio), and then add + + // Hook up the form's Load event handler (you can double-click on + // the form's design surface in Visual Studio), and then add // this code to the form's class: // You need this delegate in order to fill the grid from // a thread other than the form's thread. See the HandleCallback // procedure for more information. private delegate void FillGridDelegate(SqlDataReader reader); - + // You need this delegate to update the status bar. private delegate void DisplayStatusDelegate(string Text); - + // This flag ensures that the user does not attempt - // to restart the command or close the form while the + // to restart the command or close the form while the // asynchronous command is executing. private bool isExecuting; - + private void DisplayStatus(string Text) { this.label1.Text = Text; } - + private void FillGrid(SqlDataReader reader) { try @@ -1385,7 +1385,7 @@ Next, compile and execute the following: finally { // Closing the reader also closes the connection, - // because this reader was created using the + // because this reader was created using the // CommandBehavior.CloseConnection value. if (reader != null) { @@ -1393,7 +1393,7 @@ Next, compile and execute the following: } } } - + private void HandleCallback(IAsyncResult result) { try @@ -1403,37 +1403,37 @@ Next, compile and execute the following: // of the IAsyncResult parameter. SqlCommand command = (SqlCommand)result.AsyncState; SqlDataReader reader = command.EndExecuteReader(result); - + // You may not interact with the form and its contents // from a different thread, and this callback procedure // is all but guaranteed to be running from a different thread - // than the form. Therefore you cannot simply call code that + // than the form. Therefore you cannot simply call code that // fills the grid, like this: // FillGrid(reader); // Instead, you must call the procedure from the form's thread. // One simple way to accomplish this is to call the Invoke // method of the form, which calls the delegate you supply - // from the form's thread. + // from the form's thread. FillGridDelegate del = new FillGridDelegate(FillGrid); this.Invoke(del, reader); - - // Do not close the reader here, because it is being used in + + // Do not close the reader here, because it is being used in // a separate thread. Instead, have the procedure you have // called close the reader once it is done with it. } catch (Exception ex) { - // Because you are now running code in a separate thread, + // Because you are now running code in a separate thread, // if you do not handle the exception here, none of your other - // code catches the exception. Because there is none of + // code catches the exception. Because there is none of // your code on the call stack in this thread, there is nothing - // higher up the stack to catch the exception if you do not - // handle it here. You can either log the exception or - // invoke a delegate (as in the non-error case in this + // higher up the stack to catch the exception if you do not + // handle it here. You can either log the exception or + // invoke a delegate (as in the non-error case in this // example) to display the error on the form. In no case // can you simply display the error without executing a delegate - // as in the try block here. - // You can create the delegate instance as you + // as in the try block here. + // You can create the delegate instance as you // invoke it, like this: this.Invoke(new DisplayStatusDelegate(DisplayStatus), "Error: " + ex.Message); } @@ -1442,15 +1442,15 @@ Next, compile and execute the following: isExecuting = false; } } - + private string GetConnectionString() { - // To avoid storing the connection string in your code, - // you can retrieve it from a configuration file. + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=true;" + "Initial Catalog=AdventureWorks"; } - + private void button1_Click(object sender, System.EventArgs e) { if (isExecuting) @@ -1467,17 +1467,17 @@ Next, compile and execute the following: { DisplayStatus("Connecting..."); connection = new SqlConnection(GetConnectionString()); - // To emulate a long-running query, wait for + // To emulate a long-running query, wait for // a few seconds before retrieving the real data. command = new SqlCommand("WAITFOR DELAY '0:0:5';" + "SELECT ProductID, Name, ListPrice, Weight FROM Production.Product", connection); connection.Open(); - + DisplayStatus("Executing..."); isExecuting = true; - // Although it is not required that you pass the - // SqlCommand object as the second parameter in the + // Although it is not required that you pass the + // SqlCommand object as the second parameter in the // BeginExecuteReader call, doing so makes it easier // to call EndExecuteReader in the callback procedure. AsyncCallback callback = new AsyncCallback(HandleCallback); @@ -1494,13 +1494,13 @@ Next, compile and execute the following: } } } - + private void Form1_Load(object sender, System.EventArgs e) { this.button1.Click += new System.EventHandler(this.button1_Click); this.FormClosing += new FormClosingEventHandler(Form1_FormClosing); } - + void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (isExecuting) @@ -1605,58 +1605,58 @@ Next, compile and execute the following: using System.Data; using Microsoft.Data.SqlClient; using System.Xml; - + class Class1 { static void Main() { // This example is not terribly effective, but it proves a point. - // The WAITFOR statement simply adds enough time to prove the + // The WAITFOR statement simply adds enough time to prove the // asynchronous nature of the command. string commandText = "WAITFOR DELAY '00:00:03';" + "SELECT Name, ListPrice FROM Production.Product " + "WHERE ListPrice < 100 " + "FOR XML AUTO, XMLDATA"; - + RunCommandAsynchronously(commandText, GetConnectionString()); - + Console.WriteLine("Press ENTER to continue."); Console.ReadLine(); } - + private static void RunCommandAsynchronously(string commandText, string connectionString) { // Given command text and connection string, asynchronously execute // the specified command against the connection. For this example, - // the code displays an indicator as it is working, verifying the - // asynchronous behavior. + // the code displays an indicator as it is working, verifying the + // asynchronous behavior. using (SqlConnection connection = new SqlConnection(connectionString)) { SqlCommand command = new SqlCommand(commandText, connection); - + connection.Open(); IAsyncResult result = command.BeginExecuteXmlReader(); - + // Although it is not necessary, the following procedure - // displays a counter in the console window, indicating that - // the main thread is not blocked while awaiting the command + // displays a counter in the console window, indicating that + // the main thread is not blocked while awaiting the command // results. int count = 0; while (!result.IsCompleted) { Console.WriteLine("Waiting ({0})", count++); // Wait for 1/10 second, so the counter - // does not consume all available resources + // does not consume all available resources // on the main thread. System.Threading.Thread.Sleep(100); } - + XmlReader reader = command.EndExecuteXmlReader(result); DisplayProductInfo(reader); } } - + private static void DisplayProductInfo(XmlReader reader) { // Display the data within the reader. @@ -1670,11 +1670,11 @@ Next, compile and execute the following: } } } - + private static string GetConnectionString() { - // To avoid storing the connection string in your code, - // you can retrieve it from a configuration file. + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=true;" + "Initial Catalog=AdventureWorks"; } @@ -1788,49 +1788,49 @@ Next, compile and execute the following: using System.Windows.Forms; using System.Xml; using Microsoft.Data.SqlClient; - + namespace Microsoft.AdoDotNet.CodeSamples { public partial class Form1 : Form { - // Hook up the form's Load event handler and then add + // Hook up the form's Load event handler and then add // this code to the form's class: // You need these delegates in order to display text from a thread // other than the form's thread. See the HandleCallback // procedure for more information. private delegate void DisplayInfoDelegate(string Text); private delegate void DisplayReaderDelegate(XmlReader reader); - + private bool isExecuting; - - // This example maintains the connection object + + // This example maintains the connection object // externally, so that it is available for closing. private SqlConnection connection; - + public Form1() { InitializeComponent(); } - + private string GetConnectionString() { - // To avoid storing the connection string in your code, - // you can retrieve it from a configuration file. + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. return "Data Source=(local);Integrated Security=true;" + "Initial Catalog=AdventureWorks"; } - + private void DisplayStatus(string Text) { this.label1.Text = Text; } - + private void ClearProductInfo() { // Clear the list box. this.listBox1.Items.Clear(); } - + private void DisplayProductInfo(XmlReader reader) { // Display the data within the reader. @@ -1845,7 +1845,7 @@ Next, compile and execute the following: } DisplayStatus("Ready"); } - + private void Form1_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e) { @@ -1856,7 +1856,7 @@ Next, compile and execute the following: e.Cancel = true; } } - + private void button1_Click(object sender, System.EventArgs e) { if (isExecuting) @@ -1873,27 +1873,27 @@ Next, compile and execute the following: ClearProductInfo(); DisplayStatus("Connecting..."); connection = new SqlConnection(GetConnectionString()); - - // To emulate a long-running query, wait for + + // To emulate a long-running query, wait for // a few seconds before working with the data. string commandText = "WAITFOR DELAY '00:00:03';" + "SELECT Name, ListPrice FROM Production.Product " + "WHERE ListPrice < 100 " + "FOR XML AUTO, XMLDATA"; - + command = new SqlCommand(commandText, connection); connection.Open(); - + DisplayStatus("Executing..."); isExecuting = true; - // Although it is not required that you pass the - // SqlCommand object as the second parameter in the + // Although it is not required that you pass the + // SqlCommand object as the second parameter in the // BeginExecuteXmlReader call, doing so makes it easier // to call EndExecuteXmlReader in the callback procedure. AsyncCallback callback = new AsyncCallback(HandleCallback); command.BeginExecuteXmlReader(callback, command); - + } catch (Exception ex) { @@ -1906,7 +1906,7 @@ Next, compile and execute the following: } } } - + private void HandleCallback(IAsyncResult result) { try @@ -1916,34 +1916,34 @@ Next, compile and execute the following: // of the IAsyncResult parameter. SqlCommand command = (SqlCommand)result.AsyncState; XmlReader reader = command.EndExecuteXmlReader(result); - + // You may not interact with the form and its contents // from a different thread, and this callback procedure // is all but guaranteed to be running from a different thread - // than the form. - + // than the form. + // Instead, you must call the procedure from the form's thread. // One simple way to accomplish this is to call the Invoke // method of the form, which calls the delegate you supply - // from the form's thread. + // from the form's thread. DisplayReaderDelegate del = new DisplayReaderDelegate(DisplayProductInfo); this.Invoke(del, reader); - + } catch (Exception ex) { - // Because you are now running code in a separate thread, + // Because you are now running code in a separate thread, // if you do not handle the exception here, none of your other - // code catches the exception. Because none of + // code catches the exception. Because none of // your code is on the call stack in this thread, there is nothing - // higher up the stack to catch the exception if you do not - // handle it here. You can either log the exception or - // invoke a delegate (as in the non-error case in this + // higher up the stack to catch the exception if you do not + // handle it here. You can either log the exception or + // invoke a delegate (as in the non-error case in this // example) to display the error on the form. In no case // can you simply display the error without executing a delegate - // as in the try block here. - - // You can create the delegate instance as you + // as in the try block here. + + // You can create the delegate instance as you // invoke it, like this: this.Invoke(new DisplayInfoDelegate(DisplayStatus), String.Format("Ready(last error: {0}", ex.Message)); @@ -1957,7 +1957,7 @@ Next, compile and execute the following: } } } - + private void Form1_Load(object sender, System.EventArgs e) { this.button1.Click += new System.EventHandler(this.button1_Click); @@ -2031,22 +2031,22 @@ Next, compile and execute the following: using System.Data; using System.Threading; using Microsoft.Data.SqlClient; - + class Program { private static SqlCommand m_rCommand; - + public static SqlCommand Command { get { return m_rCommand; } set { m_rCommand = value; } } - + public static void Thread_Cancel() { Command.Cancel(); } - + static void Main() { string connectionString = GetConnectionString(); @@ -2055,7 +2055,7 @@ Next, compile and execute the following: using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); - + Command = connection.CreateCommand(); Command.CommandText = "DROP TABLE TestCancel"; try @@ -2063,19 +2063,19 @@ Next, compile and execute the following: Command.ExecuteNonQuery(); } catch { } - + Command.CommandText = "CREATE TABLE TestCancel(co1 int, co2 char(10))"; Command.ExecuteNonQuery(); Command.CommandText = "INSERT INTO TestCancel VALUES (1, '1')"; Command.ExecuteNonQuery(); - + Command.CommandText = "SELECT * FROM TestCancel"; SqlDataReader reader = Command.ExecuteReader(); - + Thread rThread2 = new Thread(new ThreadStart(Thread_Cancel)); rThread2.Start(); rThread2.Join(); - + reader.Read(); System.Console.WriteLine(reader.FieldCount); reader.Close(); @@ -2088,7 +2088,7 @@ Next, compile and execute the following: } static private string GetConnectionString() { - // To avoid storing the connection string in your code, + // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. return "Data Source=(local);Initial Catalog=AdventureWorks;" + "Integrated Security=SSPI"; @@ -2528,7 +2528,7 @@ If the option is enabled and a parameter with Direction Output or InputOutput is Although the returns no rows, any output parameters or return values mapped to parameters are populated with data. - For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. For all other types of statements, the return value is -1. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. When SET NOCOUNT ON is set on the connection (before or as part of executing the command, or as part of a trigger initiated by the execution of the command) the rows affected by individual statements stop contributing to the count of rows affected that is returned by this method. If no statements are detected that contribute to the count, the return value is -1. If a rollback occurs, the return value is also -1. + For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. For all other types of statements, the return value is -1. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. When SET NOCOUNT ON is set on the connection (before or as part of executing the command, or as part of a trigger initiated by the execution of the command) the rows affected by individual statements stop contributing to the count of rows affected that is returned by this method. If no statements are detected that contribute to the count, the return value is -1. If a rollback occurs, the return value is also -1. @@ -2540,7 +2540,7 @@ If the option is enabled and a parameter with Direction Output or InputOutput is using System; using System.Data; using Microsoft.Data.SqlClient; - + namespace SqlCommandCS { class Program @@ -2762,7 +2762,7 @@ The following example creates a , and using System; using System.Data; using Microsoft.Data.SqlClient; - + class Program { static void Main() @@ -2772,7 +2772,7 @@ The following example creates a , and string qs = "SELECT OrderID, CustomerID FROM dbo.Orders;"; CreateCommand(qs, str); } - + private static void CreateCommand(string queryString, string connectionString) { using (SqlConnection connection = new SqlConnection(connectionString)) @@ -3076,7 +3076,7 @@ For more information about asynchronous programming in the .NET Framework Data P using System; using System.Data; using Microsoft.Data.SqlClient; - + public class Sample { public void CreateSqlCommand(string queryString, SqlConnection connection) @@ -3218,7 +3218,7 @@ For more information about asynchronous programming in the .NET Framework Data P using System; using System.Data; using Microsoft.Data.SqlClient; - + private static void CreateXMLReader(string queryString, string connectionString) { using (SqlConnection connection = new SqlConnection(connectionString)) @@ -3474,9 +3474,9 @@ The blocking connection simulates a situation like a command still running in th ### How to use with legacy asynchronous commands Besides assigning the provider to the command and executing the command, it's possible to run it directly using the following methods: -- +- - -- +- [!code-csharp[SqlConfigurableRetryLogic_SqlCommand#4](~/../sqlclient/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs#4)] @@ -3535,7 +3535,7 @@ The following example demonstrates how to create a . -For vector data types, the property is ignored. The size of the vector is inferred from the of type . +For vector data types, the property is ignored. The size of the vector is inferred from the of type . Prior to Visual Studio 2010, threw an exception. Beginning in Visual Studio 2010, this method does not throw an exception. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml index 7a2f32a0bf..468c5046a6 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml @@ -72,7 +72,7 @@ The following example uses the , , , , , , , , , , , , , , , , , , , The values in the are moved to the parameter values. - The event is raised. + The event is raised. The command executes. If the command is set to FirstReturnedRecord, the first returned result is placed in the . If there are output parameters, they are placed in the . - The event is raised. + The event is raised. is called. @@ -853,55 +853,55 @@ The following example uses the , , , The values in the are moved to the parameter values. - The event is raised. + The event is raised. The command executes. If the command is set to FirstReturnedRecord, the first returned result is placed in the . If there are output parameters, they are placed in the . - The event is raised. + The event is raised. is called. @@ -966,55 +966,55 @@ The following example uses the , , , method retur - Gets the value of the specified column as a . + Gets the value of the specified column as a . - A object representing the column at the given ordinal. + A object representing the column at the given ordinal. The index passed was outside the range of 0 to - 1 @@ -979,7 +979,7 @@ The method retur An attempt was made to read or access columns in a closed . - The retrieved data is not compatible with the type. + The retrieved data is not compatible with the type. No conversions are performed; therefore, the data retrieved must already be a vector value, or an exception is generated. @@ -1193,7 +1193,7 @@ The method retur There is no data ready to be read (for example, the first hasn't been called, or returned false). Tried to read a previously-read column in sequential mode. There was an asynchronous operation in progress. This applies to all Get* methods when running in sequential mode, as they could be called while reading a stream. - + Trying to read a column that does not exist. @@ -1282,21 +1282,21 @@ The method retur // enough for all the columns. Object[] values = new Object[reader.FieldCount]; int fieldCount = reader.GetValues(values); - + Console.WriteLine("reader.GetValues retrieved {0} columns.", fieldCount); for (int i = 0; i < fieldCount; i++) { Console.WriteLine(values[i]); } - + Console.WriteLine(); - - // Now repeat, using an array that may contain a different + + // Now repeat, using an array that may contain a different // number of columns than the original data. This should work correctly, - // whether the size of the array is larger or smaller than + // whether the size of the array is larger or smaller than // the number of columns. - + // Attempt to retrieve three columns of data. values = new Object[3]; fieldCount = reader.GetValues(values); @@ -1334,7 +1334,7 @@ The method retur There is no data ready to be read (for example, the first hasn't been called, or returned false). Trying to read a previously read column in sequential mode. There was an asynchronous operation in progress. This applies to all Get* methods when running in sequential mode, as they could be called while reading a stream. - + Trying to read a column that does not exist. @@ -1394,7 +1394,7 @@ The method retur using System; using System.Data; using Microsoft.Data.SqlClient; - + class Program { static void Main(string[] args) @@ -1448,7 +1448,7 @@ The method retur There is no data ready to be read (for example, the first hasn't been called, or returned false). Trying to read a previously read column in sequential mode. There was an asynchronous operation in progress. This applies to all Get* methods when running in sequential mode, as they could be called while reading a stream. - + Trying to read a column that does not exist. From 14bbf42e44ae35eaa95decf08c9b8863af2f617e Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:29:06 +0000 Subject: [PATCH 15/22] Merge SqlType resource methodology, removed unused strings (#3733) --- .../src/Microsoft.Data.SqlClient.csproj | 3 - .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 - .../src/Microsoft/Data/Common/AdapterUtil.cs | 3 + .../src/Microsoft/Data/SqlClient/SqlBuffer.cs | 4 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 11 ++- .../Microsoft/Data/SqlTypes/SQLResource.cs | 77 ------------------- .../src/Microsoft/Data/SqlTypes/SqlVector.cs | 4 +- .../Microsoft/Data/SqlTypes/SqlVectorTest.cs | 4 +- 8 files changed, 19 insertions(+), 90 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SQLResource.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 7145dbac28..c2fdb27f3f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -1002,9 +1002,6 @@ Microsoft\Data\SqlTypes\SqlJson.cs - - Microsoft\Data\SQLTypes\SQLResource.cs - Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 8408dabc0d..eb14ccc985 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -979,9 +979,6 @@ Microsoft\Data\SqlTypes\SqlVector.cs - - Microsoft\Data\SqlTypes\SQLResource.cs - Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index e2a84bad04..7868c8c876 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -378,6 +378,9 @@ internal static AuthenticationException SSLCertificateAuthenticationException(st internal static ArgumentOutOfRangeException NotSupportedEnumerationValue(Type type, string value, string method) => ArgumentOutOfRange(StringsHelper.GetString(Strings.ADP_NotSupportedEnumerationValue, type.Name, value, method), type.Name); + internal static ArgumentOutOfRangeException InvalidArraySize(string parameterName) => + ArgumentOutOfRange(StringsHelper.GetString(Strings.SqlMisc_InvalidArraySizeMessage), parameterName); + internal static void CheckArgumentNull(object value, string parameterName) { if (value is null) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs index 385a49c802..0ec34003f7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs @@ -271,11 +271,11 @@ internal decimal Decimal } catch (OverflowException) { - throw new OverflowException(SQLResource.ConversionOverflowMessage); + throw SQL.ConversionOverflow(); } } } - throw new OverflowException(SQLResource.ConversionOverflowMessage); + throw SQL.ConversionOverflow(); } return new decimal(_value._numericInfo._data1, _value._numericInfo._data2, _value._numericInfo._data3, !_value._numericInfo._positive, _value._numericInfo._scale); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs index 8f0d7fcba0..ab2261f0e9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -830,9 +830,14 @@ internal static Exception UDTUnexpectedResult(string exceptionText) return ADP.TypeLoad(StringsHelper.GetString(Strings.SQLUDT_Unexpected, exceptionText)); } + internal static Exception ConversionOverflow() + { + return new OverflowException(StringsHelper.GetString(Strings.SqlMisc_ConversionOverflowMessage)); + } + internal static Exception DateTimeOverflow() { - return new OverflowException(SqlTypes.SQLResource.DateTimeOverflowMessage); + return new OverflowException(StringsHelper.GetString(Strings.SqlMisc_DateTimeOverflowMessage)); } // @@ -2285,6 +2290,10 @@ internal static string ExRoutingDestination() { return StringsHelper.GetString(Strings.SQL_ExRoutingDestination); } + internal static string NullString() + { + return StringsHelper.GetString(Strings.SqlMisc_NullString); + } } /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SQLResource.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SQLResource.cs deleted file mode 100644 index 327d182d03..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SQLResource.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.SqlTypes -{ - - using System; - using Microsoft.Data; - - internal sealed class SQLResource - { - - private SQLResource() { /* prevent utility class from being instantiated*/ } - - internal static readonly String NullString = StringsHelper.GetString(Strings.SqlMisc_NullString); - - internal static readonly String MessageString = StringsHelper.GetString(Strings.SqlMisc_MessageString); - - internal static readonly String ArithOverflowMessage = StringsHelper.GetString(Strings.SqlMisc_ArithOverflowMessage); - - internal static readonly String DivideByZeroMessage = StringsHelper.GetString(Strings.SqlMisc_DivideByZeroMessage); - - internal static readonly String NullValueMessage = StringsHelper.GetString(Strings.SqlMisc_NullValueMessage); - - internal static readonly String TruncationMessage = StringsHelper.GetString(Strings.SqlMisc_TruncationMessage); - - internal static readonly String DateTimeOverflowMessage = StringsHelper.GetString(Strings.SqlMisc_DateTimeOverflowMessage); - - internal static readonly String ConcatDiffCollationMessage = StringsHelper.GetString(Strings.SqlMisc_ConcatDiffCollationMessage); - - internal static readonly String CompareDiffCollationMessage = StringsHelper.GetString(Strings.SqlMisc_CompareDiffCollationMessage); - - internal static readonly String InvalidFlagMessage = StringsHelper.GetString(Strings.SqlMisc_InvalidFlagMessage); - - internal static readonly String NumeToDecOverflowMessage = StringsHelper.GetString(Strings.SqlMisc_NumeToDecOverflowMessage); - - internal static readonly String ConversionOverflowMessage = StringsHelper.GetString(Strings.SqlMisc_ConversionOverflowMessage); - - internal static readonly String InvalidDateTimeMessage = StringsHelper.GetString(Strings.SqlMisc_InvalidDateTimeMessage); - - internal static readonly String TimeZoneSpecifiedMessage = StringsHelper.GetString(Strings.SqlMisc_TimeZoneSpecifiedMessage); - - internal static readonly String InvalidArraySizeMessage = StringsHelper.GetString(Strings.SqlMisc_InvalidArraySizeMessage); - - internal static readonly String InvalidPrecScaleMessage = StringsHelper.GetString(Strings.SqlMisc_InvalidPrecScaleMessage); - - internal static readonly String FormatMessage = StringsHelper.GetString(Strings.SqlMisc_FormatMessage); - - internal static readonly String NotFilledMessage = StringsHelper.GetString(Strings.SqlMisc_NotFilledMessage); - - internal static readonly String AlreadyFilledMessage = StringsHelper.GetString(Strings.SqlMisc_AlreadyFilledMessage); - - internal static readonly String ClosedXmlReaderMessage = StringsHelper.GetString(Strings.SqlMisc_ClosedXmlReaderMessage); - - internal static String InvalidOpStreamClosed(String method) - { - return StringsHelper.GetString(Strings.SqlMisc_InvalidOpStreamClosed, method); - } - - internal static String InvalidOpStreamNonWritable(String method) - { - return StringsHelper.GetString(Strings.SqlMisc_InvalidOpStreamNonWritable, method); - } - - internal static String InvalidOpStreamNonReadable(String method) - { - return StringsHelper.GetString(Strings.SqlMisc_InvalidOpStreamNonReadable, method); - } - - internal static String InvalidOpStreamNonSeekable(String method) - { - return StringsHelper.GetString(Strings.SqlMisc_InvalidOpStreamNonSeekable, method); - } - } // SqlResource - -} // namespace System diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs index 14e790e977..2e83b8daff 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlVector.cs @@ -41,7 +41,7 @@ private SqlVector(int length) { if (length < 0) { - throw ADP.ArgumentOutOfRange(nameof(length), SQLResource.InvalidArraySizeMessage); + throw ADP.InvalidArraySize(nameof(length)); } (_elementType, _elementSize) = GetTypeFieldsOrThrow(); @@ -92,7 +92,7 @@ internal string GetString() { if (IsNull) { - return SQLResource.NullString; + return SQLMessage.NullString(); } return JsonSerializer.Serialize(Memory); } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs index adca0e2b99..1b34c66298 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlTypes/SqlVectorTest.cs @@ -38,7 +38,7 @@ public void Construct_Length() // to the same memory. We want to check memory content equality, so we // compare their arrays instead. Assert.Equal(new ReadOnlyMemory().ToArray(), vec.Memory.ToArray()); - Assert.Equal(SQLResource.NullString, vec.GetString()); + Assert.Equal(SQLMessage.NullString(), vec.GetString()); var ivec = vec as ISqlVector; Assert.Equal(0x00, ivec.ElementType); @@ -58,7 +58,7 @@ public void Construct_WithLengthZero() // to the same memory. We want to check memory content equality, so we // compare their arrays instead. Assert.Equal(new ReadOnlyMemory().ToArray(), vec.Memory.ToArray()); - Assert.Equal(SQLResource.NullString, vec.GetString()); + Assert.Equal(SQLMessage.NullString(), vec.GetString()); var ivec = vec as ISqlVector; Assert.Equal(0x00, ivec.ElementType); From 938d245026622ad80cabbf3ebde17565fe45d157 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Mon, 30 Mar 2026 08:12:11 -0300 Subject: [PATCH 16/22] Changed SBOM package version to use build number (#4095) --- eng/pipelines/onebranch/sqlclient-non-official.yml | 2 +- eng/pipelines/onebranch/sqlclient-official.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/onebranch/sqlclient-non-official.yml b/eng/pipelines/onebranch/sqlclient-non-official.yml index 25dbe98097..d4b62ac5e8 100644 --- a/eng/pipelines/onebranch/sqlclient-non-official.yml +++ b/eng/pipelines/onebranch/sqlclient-non-official.yml @@ -154,7 +154,7 @@ extends: sbom: enabled: true packageName: Microsoft.Data.SqlClient - packageVersion: $(mdsPackageVersion) + packageVersion: $(Build.BuildNumber) policheck: enabled: true break: true diff --git a/eng/pipelines/onebranch/sqlclient-official.yml b/eng/pipelines/onebranch/sqlclient-official.yml index 1754918116..93287c3c51 100644 --- a/eng/pipelines/onebranch/sqlclient-official.yml +++ b/eng/pipelines/onebranch/sqlclient-official.yml @@ -179,7 +179,7 @@ extends: sbom: enabled: true packageName: Microsoft.Data.SqlClient - packageVersion: $(mdsPackageVersion) + packageVersion: $(Build.BuildNumber) policheck: enabled: true break: true From 2c37eb979b7be6616700832637788d941aa3a4cb Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Tue, 31 Mar 2026 15:39:36 -0500 Subject: [PATCH 17/22] Merge | Official Builds From Common Project (#4068) * Drop compound-* from onebranch step files for my own sanity * Consolidate compound-publish-symbols and publish-symbols-step into publish-symbols-step. * Rename code-analyze-step to roslyn-analyzers-csproj-step (for later changes) * Add mds-specific job/steps * Swap in build-signed-mds-package-job.yml for build-signed-sqlclient-package-job.yml * Remove dead job/steps * Fix folder path for DLL signing steps. * Addressing comments, round 1 * Addressing comments, round 2 Not sure how the PackBuild target got lost, but I brought it back * Add the new parameters to the csproj build job * Use variables artifact names in release stage * Two more quick fixes as per comments * ESRP searches recursively? * Fine, I give up, I will put the package output in PACK_OUTPUT. * Reinstate job-level apiscan parameter assignment * Wire isOfficial to MDS build job after rebase * Rework versioning logic and move package version calculation (outside of pipelines) entirely to MdsVersions.props * Add build number to PackMds target call in official pipeline * Remove assembly "file" versions as it is not necessary. * CI pipelines to automatic versioning system. * Rework the package version for CI to match the package version for OneBranch. This band-aid avoids completely messing with the CI properties. * Assembly version = major.0.0.0 File version = major.minor.patch.build Package version = major.minor.patch[-suffixbuildnumber] * Comment that Malcolm is holding the build up for. * Can I please be done with this PR now? --- build2.proj | 78 +++++- .../templates/steps/ci-project-build-step.yml | 6 +- .../libraries/ci-build-variables.yml | 10 +- .../jobs/build-signed-csproj-package-job.yml | 27 +- .../jobs/build-signed-mds-package-job.yml | 182 ++++++++++++ .../build-signed-sqlclient-package-job.yml | 145 ---------- .../jobs/validate-signed-package-job.yml | 29 +- .../onebranch/stages/build-stages.yml | 27 +- .../onebranch/stages/release-stages.yml | 12 +- ...ld-all-configurations-signed-dlls-step.yml | 58 ---- ...-csproj-step.yml => build-csproj-step.yml} | 2 +- .../onebranch/steps/build-mds-step.yml | 41 +++ .../steps/compound-nuget-pack-step.yml | 86 ------ .../steps/compound-publish-symbols-step.yml | 162 ----------- .../steps/copy-apiscan-files-mds-step.yml | 39 +++ .../steps/esrp-code-signing-step.yml | 168 ----------- ...ing-step.yml => esrp-dll-signing-step.yml} | 30 +- ...g-step.yml => esrp-nuget-signing-step.yml} | 44 +-- ...k-csproj-step.yml => pack-csproj-step.yml} | 0 .../onebranch/steps/pack-mds-step.yml | 42 +++ .../onebranch/steps/publish-symbols-step.yml | 264 ++++++++++-------- ...p.yml => roslyn-analyzers-csproj-step.yml} | 0 .../steps/roslyn-analyzers-mds-step.yml | 40 +++ .../onebranch/variables/common-variables.yml | 3 +- .../variables/onebranch-variables.yml | 2 +- .../MdsVersions.props | 102 +++++-- .../Microsoft.Data.SqlClient.csproj | 2 +- .../ref/Microsoft.Data.SqlClient.csproj | 2 +- .../src/Microsoft.Data.SqlClient.csproj | 2 +- tools/targets/GenerateThisAssemblyCs.targets | 4 +- 30 files changed, 751 insertions(+), 858 deletions(-) create mode 100644 eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml delete mode 100644 eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml delete mode 100644 eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml rename eng/pipelines/onebranch/steps/{compound-build-csproj-step.yml => build-csproj-step.yml} (95%) create mode 100644 eng/pipelines/onebranch/steps/build-mds-step.yml delete mode 100644 eng/pipelines/onebranch/steps/compound-nuget-pack-step.yml delete mode 100644 eng/pipelines/onebranch/steps/compound-publish-symbols-step.yml create mode 100644 eng/pipelines/onebranch/steps/copy-apiscan-files-mds-step.yml delete mode 100644 eng/pipelines/onebranch/steps/esrp-code-signing-step.yml rename eng/pipelines/onebranch/steps/{compound-esrp-dll-signing-step.yml => esrp-dll-signing-step.yml} (78%) rename eng/pipelines/onebranch/steps/{compound-esrp-nuget-signing-step.yml => esrp-nuget-signing-step.yml} (59%) rename eng/pipelines/onebranch/steps/{compound-pack-csproj-step.yml => pack-csproj-step.yml} (100%) create mode 100644 eng/pipelines/onebranch/steps/pack-mds-step.yml rename eng/pipelines/onebranch/steps/{code-analyze-step.yml => roslyn-analyzers-csproj-step.yml} (100%) create mode 100644 eng/pipelines/onebranch/steps/roslyn-analyzers-mds-step.yml diff --git a/build2.proj b/build2.proj index b9b08f9a4a..e2f760af75 100644 --- a/build2.proj +++ b/build2.proj @@ -25,6 +25,25 @@ -p:BuildNumber=$(BuildNumber) + + + + -p:BuildSuffix=$(BuildSuffix) + + nuget + + + true + BuildMds + $(BuildNumberArgument) - $(PacakgeVersionMdsArgument) + $(BuildSuffixArgument) + $(PackageVersionMdsArgument) $(ReferenceTypeArgument) @@ -316,7 +357,8 @@ $(BuildNumberArgument) - $(PacakgeVersionMdsArgument) + $(BuildSuffixArgument) + $(PackageVersionMdsArgument) $(ReferenceTypeArgument) @@ -341,6 +383,7 @@ $(BuildNumberArgument) + $(BuildSuffixArgument) $(PackageVersionMdsArgument) @@ -367,6 +410,7 @@ $(BuildNumberArgument) + $(BuildSuffixArgument) $(PackageVersionMdsArgument) @@ -388,13 +432,11 @@ automatically build before packaging. Instead, we rely on target dependency to handle building the same binaries that will be packaged. --> - + - @@ -408,13 +450,37 @@ + + + + "$(DotnetPath)dotnet" msbuild "$(MdsProjectPath)" + -nologo + -verbosity:quiet + -getProperty:MdsPackageVersion + + + $(BuildNumberArgument) + $(BuildSuffixArgument) + $(PackageVersionMdsArgument) + + + $([System.Text.RegularExpressions.Regex]::Replace($(GetMdsPackageVersionCommand), "\s+", " ")) + + + + + + + + <_EvaluatedMdsPackageVersion>$([System.Text.RegularExpressions.Regex]::Replace($(_EvaluatedMdsPackageVersion), "\s", "")) $([System.Text.RegularExpressions.Regex]::Replace($(CommitId), "\s", "")) "$(NugetPath)" pack "$(MdsNuspecPath)" -Symbols -SymbolPackageFormat snupkg - -Version "$(PackageVersionMds)" + -Version "$(_EvaluatedMdsPackageVersion)" -OutputDirectory "$(MdsArtifactRoot)/$(ReferenceType)-$(Configuration)" -properties "COMMITID=$(CommitId);Configuration=$(Configuration);ReferenceType=$(ReferenceType);AbstractionsPackageVersion=$(PackageVersionAbstractions);LoggingPackageVersion=$(PackageVersionLogging)" diff --git a/eng/pipelines/common/templates/steps/ci-project-build-step.yml b/eng/pipelines/common/templates/steps/ci-project-build-step.yml index d43222bfd1..d2604e0b3d 100644 --- a/eng/pipelines/common/templates/steps/ci-project-build-step.yml +++ b/eng/pipelines/common/templates/steps/ci-project-build-step.yml @@ -60,7 +60,9 @@ parameters: type: string default: $(loggingPackageVersion) - # Necessary to build AKV Provider when referenceType is Package. Ignored when referenceType is Project. + # Package version to use for MDS package. Because we explicitly provide this version, the + # assembly version will be generated based on this package version and the build number. + # See MdsVersions.props for details on how this works. - name: mdsPackageVersion type: string default: $(mdsPackageVersion) @@ -84,7 +86,7 @@ steps: msbuildArguments: -t:BuildMds -p:ReferenceType=${{ parameters.referenceType }} - -p:BuildNumber=${{ parameters.buildNumber }} + -p:BuildNumber=${{ parameters.assemblyBuildNumber }} -p:PackageVersionAbstractions=${{ parameters.abstractionsPackageVersion }} -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} -p:PackageVersionMds=${{ parameters.mdsPackageVersion }} diff --git a/eng/pipelines/libraries/ci-build-variables.yml b/eng/pipelines/libraries/ci-build-variables.yml index 2779277678..027501df0f 100644 --- a/eng/pipelines/libraries/ci-build-variables.yml +++ b/eng/pipelines/libraries/ci-build-variables.yml @@ -54,13 +54,13 @@ variables: - name: loggingPackageVersion value: 1.0.0.$(Build.BuildNumber)-ci - # MDS library assembly file version - - name: mdsAssemblyFileVersion - value: 7.0.0.$(assemblyBuildNumber) - # MDS library NuGet package version + # NOTE: This differs from the other structures! MdsVersions.props will deconstruct a provided + # package version and build an assembly version from it. If the build number is included + # before the "-" the build number will be appended again, generating an invalid file version. + # @TODO: This is a band-aid to ensure that CI builds until we can centralize versioning. - name: mdsPackageVersion - value: 7.0.0.$(Build.BuildNumber)-ci + value: 7.0.0-ci$(AssemblyBuildNumber) # Local NuGet feed directory where downloaded pipeline artifacts are placed. # NuGet.config references this as a local package source for restore. diff --git a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml index e257386918..653d9419bc 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml @@ -134,7 +134,7 @@ jobs: - template: /eng/pipelines/steps/install-dotnet.yml@self # Perform Roslyn analysis before building, since this step will clobber build output. - - template: /eng/pipelines/onebranch/steps/code-analyze-step.yml@self + - template: /eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml@self parameters: msBuildArguments: >- -t:$(buildTarget) @@ -143,7 +143,7 @@ jobs: ${{ parameters.versionProperties }} # Build the package, producing DLLs only (no NuGet package yet). - - template: /eng/pipelines/onebranch/steps/compound-build-csproj-step.yml@self + - template: /eng/pipelines/onebranch/steps/build-csproj-step.yml@self parameters: buildTarget: $(buildTarget) buildConfiguration: ${{ parameters.buildConfiguration }} @@ -151,7 +151,7 @@ jobs: - ${{ if eq(parameters.isOfficial, true) }}: # ESRP sign the DLLs. - - template: /eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml@self + - template: /eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml@self parameters: appRegistrationClientId: ${{ parameters.appRegistrationClientId }} appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} @@ -182,7 +182,7 @@ jobs: flattenFolders: false # Pack the signed DLLs into NuGet package (NoBuild=true). - - template: /eng/pipelines/onebranch/steps/compound-pack-csproj-step.yml@self + - template: /eng/pipelines/onebranch/steps/pack-csproj-step.yml@self parameters: packTarget: $(packTarget) buildConfiguration: ${{ parameters.buildConfiguration }} @@ -190,7 +190,7 @@ jobs: - ${{ if eq(parameters.isOfficial, true) }}: # ESRP sign the NuGet package. - - template: /eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml@self + - template: /eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml@self parameters: appRegistrationClientId: ${{ parameters.appRegistrationClientId }} appRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} @@ -198,11 +198,22 @@ jobs: authSignCertName: ${{ parameters.authSignCertName }} esrpClientId: ${{ parameters.esrpClientId }} esrpConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - pattern: ${{ parameters.packageFullName }}.*nupkg + searchPath: $(PACK_OUTPUT) + searchPattern: '${{ parameters.packageFullName }}.*nupkg' # Publish symbols to servers + # @TODO: Get these parameters from variables/libraries - ${{ if eq(parameters.publishSymbols, true) }}: - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self parameters: - packageFullName: ${{ parameters.packageFullName }} - packageVersion: ${{ parameters.packageVersion }} + artifactName: '${{ parameters.packageFullName }}_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.packageVersion }}_$(System.TimelineId)' + azureSubscription: 'Symbols publishing Workload Identity federation service-ADO.Net' + packageName: '${{ parameters.packageFullName }}' + publishProjectName: 'Microsoft.Data.SqlClient.SNI' # This is used for all SqlClient packages. Don't know why, but it is. + publishServer: '$(SymbolServer)' + publishToInternal: 'true' + publishToPublic: 'true' + publishTokenUri: '$(SymbolTokenUri)' + searchPattern: '**/${{ parameters.packageFullName }}*.pdb' + uploadAccount: 'SqlClientDrivers' + version: '${{ parameters.packageVersion }}' diff --git a/eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml new file mode 100644 index 0000000000..4464a3a9f1 --- /dev/null +++ b/eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml @@ -0,0 +1,182 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# Job that performs a build of Microsoft.Data.SqlClient using the build2.proj file. + +parameters: + - name: apiScanDllPath + type: string + + - name: apiScanPdbPath + type: string + + # Whether this build is for an "official" OneBranch pipeline. This is used to enable ESRP signing + # on the artifacts of this job. + - name: isOfficial + type: boolean + + - name: publishSymbols + type: boolean + + - name: signingAppRegistrationClientId + type: string + + - name: signingAppRegistrationTenantId + type: string + + - name: signingAuthAkvName + type: string + + - name: signingAuthSignCertName + type: string + + - name: signingEsrpClientId + type: string + + - name: signingEsrpConnectedServiceName + type: string + + - name: symbolsAzureSubscription + type: string + + - name: symbolsPublishProjectName + type: string + + - name: symbolsPublishServer + type: string + + - name: symbolsPublishTokenUri + type: string + + - name: symbolsUploadAccount + type: string + + # Package Parameters + - name: abstractionsArtifactName + type: string + + - name: abstractionsPackageVersion + type: string + + - name: loggingArtifactName + type: string + + - name: loggingPackageVersion + type: string + + - name: mdsAssemblyFileVersion + type: string + + - name: mdsPackageVersion + type: string + +jobs: + - job: build_package_Mds + displayName: 'Build Microsoft.Data.SqlClient' + pool: + type: windows + + variables: + ob_outputDirectory: '$(PACK_OUTPUT)' + ob_sdl_apiscan_softwareFolder: ${{ parameters.apiScanDllPath }} + ob_sdl_apiscan_symbolsFolder: ${{ parameters.apiScanPdbPath }} + ob_sdl_apiscan_softwarename: 'Microsoft.Data.SqlClient' + ob_sdl_apiscan_versionNumber: ${{ parameters.mdsAssemblyFileVersion }} + + steps: + # Dump environment and parameters + - template: /eng/pipelines/onebranch/steps/script-output-environment-variables-step.yml@self + + - powershell: | + $jsonParams = '${{ convertToJson(parameters) }}' -replace '\\', '\\' + $jsonParams | ConvertFrom-Json | Format-List + displayName: 'Output Job Parameters' + + # Download Abstractions and Logging packages from previous stages into /packages/ so they + # are available via the local NuGet feed when restoring. + # @TODO: With the new build2.proj pack targets, this technically is not necessary. + - task: DownloadPipelineArtifact@2 + displayName: Download Microsoft.Data.SqlClient.Extensions.Abstractions Artifact + inputs: + artifactName: '${{ parameters.abstractionsArtifactName }}' + targetPath: '$(REPO_ROOT)/packages' + + - task: DownloadPipelineArtifact@2 + displayName: Download Microsoft.Data.SqlClient.Extensions.Logging Artifact + inputs: + artifactName: '${{ parameters.loggingArtifactName }}' + targetPath: '$(REPO_ROOT)/packages' + + # Install the .NET SDK + - template: /eng/pipelines/steps/install-dotnet.yml@self + + # Perform analysis before building, since this step will clobber build output + - template: /eng/pipelines/onebranch/steps/roslyn-analyzers-mds-step.yml@self + parameters: + abstractionsPackageVersion: '${{ parameters.abstractionsPackageVersion }}' + loggingPackageVersion: '${{ parameters.loggingPackageVersion }}' + mdsPackageVersion: '${{ parameters.mdsPackageVersion }}' + + # Perform the actual build + - template: /eng/pipelines/onebranch/steps/build-mds-step.yml@self + parameters: + abstractionsPackageVersion: '${{ parameters.abstractionsPackageVersion }}' + loggingPackageVersion: '${{ parameters.loggingPackageVersion }}' + mdsPackageVersion: '${{ parameters.mdsPackageVersion }}' + + # Copy the built DLLs and PDBs to the APIScan output folder for APIScanning post-build + - template: /eng/pipelines/onebranch/steps/copy-apiscan-files-mds-step.yml@self + parameters: + dllPath: '${{ parameters.apiScanDllPath }}' + pdbPath: '${{ parameters.apiScanPdbPath }}' + referenceType: 'Package' + + # Sign the DLLs + - ${{ if parameters.isOfficial }}: + - template: /eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml@self + parameters: + appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + authAkvName: '${{ parameters.signingAuthAkvName }}' + authSignCertName: '${{ parameters.signingAuthSignCertName }}' + esrpClientId: '${{ parameters.signingEsrpClientId }}' + esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + pattern: 'Microsoft.Data.SqlClient*.dll' + + # Package the build output into a NuGet package + - template: /eng/pipelines/onebranch/steps/pack-mds-step.yml + parameters: + abstractionsPackageVersion: '${{ parameters.abstractionsPackageVersion }}' + loggingPackageVersion: '${{ parameters.loggingPackageVersion }}' + mdsPackageVersion: '${{ parameters.mdsPackageVersion }}' + + # Sign the NuGet packages + - ${{ if parameters.isOfficial }}: + - template: /eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml@self + parameters: + appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}' + appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}' + authAkvName: '${{ parameters.signingAuthAkvName }}' + authSignCertName: '${{ parameters.signingAuthSignCertName }}' + esrpClientId: '${{ parameters.signingEsrpClientId }}' + esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}' + searchPath: '$(PACK_OUTPUT)' + searchPattern: 'Microsoft.Data.SqlClient.*nupkg' + + - ${{ if parameters.publishSymbols }}: + - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self + parameters: + artifactName: 'Microsoft.Data.SqlClient_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.mdsPackageVersion }}_$(System.TimelineId)' + azureSubscription: '${{ parameters.symbolsAzureSubscription }}' + packageName: 'Microsoft.Data.SqlClient' + publishProjectName: '${{ parameters.symbolsPublishProjectName }}' + publishServer: '${{ parameters.symbolsPublishServer }}' + publishToInternal: true + publishToPublic: true + publishTokenUri: '${{ parameters.symbolsPublishTokenUri }}' + searchPattern: '**/Microsoft.Data.SqlClient*.pdb' # @TODO: This seems very heavy + uploadAccount: '${{ parameters.symbolsUploadAccount }}' + version: '${{ parameters.mdsPackageVersion }}' diff --git a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml deleted file mode 100644 index f3033b07f0..0000000000 --- a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml +++ /dev/null @@ -1,145 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -# This file is only included in MDS OneBranch Official pipelines. - -parameters: - # True to publish symbols to public and private feeds after the build completes. - - name: publishSymbols - type: boolean - - # True if this is a preview build, which uses the preview version numbers from - # common-variables.yml. - - name: isPreview - type: boolean - - # True to enable ESRP malware scanning and code signing steps, which should not - # be run on non-official pipelines as they access production resources. - - name: isOfficial - type: boolean - -jobs: - - job: build_package_SqlClient - displayName: "Build Microsoft.Data.SqlClient" - pool: - type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs - - variables: - ob_outputDirectory: $(PACK_OUTPUT) - # APIScan configuration for this Extension package - ob_sdl_apiscan_enabled: true - ob_sdl_apiscan_softwareFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/dlls - ob_sdl_apiscan_symbolsFolder: $(Build.SourcesDirectory)/apiScan/SqlClient/pdbs - ob_sdl_apiscan_softwarename: Microsoft.Data.SqlClient - ob_sdl_apiscan_versionNumber: $(assemblyBuildNumber) - - ${{ if parameters.isPreview }}: - abstractionsPackageVersion: $(abstractionsPackagePreviewVersion) - loggingPackageVersion: $(loggingPackagePreviewVersion) - mdsPackageVersion: $(mdsPackagePreviewVersion) - - steps: - - script: SET - displayName: "Print Environment Variables" - - # Download the Abstractions and Logging packages from the previous stage into - # packages/ so that they're available via the local NuGet feed when restoring MDS. - # MDS depends on both Extensions.Abstractions and Internal.Logging. - - task: DownloadPipelineArtifact@2 - displayName: Download Abstractions Package - inputs: - artifactName: $(abstractionsArtifactsName) - targetPath: $(Build.SourcesDirectory)/packages - - - task: DownloadPipelineArtifact@2 - displayName: Download Logging Package - inputs: - artifactName: $(loggingArtifactsName) - targetPath: $(Build.SourcesDirectory)/packages - - # Install the .NET SDK. - - template: /eng/pipelines/steps/install-dotnet.yml@self - - # Build our tooling, which is required by the analysis step below, but - # shouldn't be analyzed itself. - - task: MSBuild@1 - displayName: "Build Tooling" - inputs: - solution: "**/build.proj" - configuration: Release - msbuildArguments: -t:BuildTools - - # Perform analysis before building, since this step will clobber build output. - - template: /eng/pipelines/onebranch/steps/code-analyze-step.yml@self - - # Build MDS, producing signed DLLs. - - template: /eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml@self - parameters: - # These variables are sourced from common-variables.yml. - abstractionsAssemblyFileVersion: $(abstractionsAssemblyFileVersion) - abstractionsPackageVersion: $(abstractionsPackageVersion) - loggingAssemblyFileVersion: $(loggingAssemblyFileVersion) - loggingPackageVersion: $(loggingPackageVersion) - mdsAssemblyFileVersion: $(mdsAssemblyFileVersion) - mdsPackageVersion: $(mdsPackageVersion) - - - ${{ if eq(parameters.isOfficial, true) }}: - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: dll - sourceRoot: $(BUILD_OUTPUT) - dllPattern: "Microsoft.Data.SqlClient.dll" - - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: dll - sourceRoot: $(BUILD_OUTPUT) - dllPattern: "Microsoft.Data.SqlClient.resources.dll" - - - template: /eng/pipelines/common/templates/steps/generate-nuget-package-step.yml@self - parameters: - buildConfiguration: Release - displayName: "Create MDS NuGet Package" - generateSymbolsPackage: true - installNuget: false - nuspecPath: $(nuspecPath) - outputDirectory: $(PACK_OUTPUT) - packageVersion: $(mdsPackageVersion) - properties: "AbstractionsPackageVersion=$(abstractionsPackageVersion);LoggingPackageVersion=$(loggingPackageVersion)" - referenceType: Package - - - ${{ if eq(parameters.isOfficial, true) }}: - - template: /eng/pipelines/onebranch/steps/esrp-code-signing-step.yml@self - parameters: - artifactType: pkg - nupkgPattern: "Microsoft.Data.SqlClient.$(mdsPackageVersion).*nupkg" - - # Copy signed DLLs and PDBs to APIScan folders. - - task: CopyFiles@2 - displayName: Copy DLLs for APIScan - inputs: - SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: "**/Microsoft.Data.SqlClient.dll" - TargetFolder: $(ob_sdl_apiscan_softwareFolder) - # We must preserve the folder structure since our C# projects may produce multiple - # identically named DLLs for different target frameworks (e.g. netstandard2.0, net5.0, - # etc.), and we need to keep those separate for APIScan to work correctly. - flattenFolders: false - - - task: CopyFiles@2 - displayName: Copy PDBs for APIScan - inputs: - SourceFolder: $(BUILD_OUTPUT)/Package/bin - Contents: "**/Microsoft.Data.SqlClient.pdb" - TargetFolder: $(ob_sdl_apiscan_symbolsFolder) - flattenFolders: false - - # Publish symbols to servers - - ${{ if eq(parameters.publishSymbols, true) }}: - - template: /eng/pipelines/onebranch/steps/publish-symbols-step.yml@self - parameters: - packageFullName: Microsoft.Data.SqlClient - packageVersion: $(mdsPackageVersion) diff --git a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml index 3b463d0b81..2a4bf08e97 100644 --- a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml +++ b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml @@ -12,7 +12,7 @@ parameters: - name: isPreview type: boolean - # True if this build is an official build. This will be used to gate some checks + # True if this build is an official build. This will be used to gate some checks # that only apply to official builds, such as signature verification. - name: isOfficial type: boolean @@ -210,7 +210,7 @@ jobs: } } displayName: 'Verify all DLLs unzipped match "expected" hierarchy' - + - ${{ if eq(parameters.isOfficial, true) }}: - powershell: | # Verify all dlls status are Valid @@ -235,6 +235,7 @@ jobs: # This will check each DLL's ProductVersion and FileVersion against # expected values. $failed = 0 + $expectedFileVersion = "$(mdsPackageVersion)".Split('-')[0] + ".$(assemblyBuildNumber)" foreach ( $pVersion in Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo ) { @@ -248,13 +249,13 @@ jobs: $failed = 1 } - if ($pVersion.FileVersion -eq '$(mdsAssemblyFileVersion)') + if ($pVersion.FileVersion -eq $expectedFileVersion) { Write-Host -ForegroundColor Green "Correct FileVersion detected for $($pVersion.FileName): $($pVersion.FileVersion)" } else { - Write-Host -ForegroundColor Red "Wrong FileVersion detected for $($pVersion.FileName); expected $(mdsAssemblyFileVersion); found: $($pVersion.FileVersion)" + Write-Host -ForegroundColor Red "Wrong FileVersion detected for $($pVersion.FileName); expected $expectedFileVersion; found: $($pVersion.FileVersion)" $failed = 1 } } @@ -266,23 +267,3 @@ jobs: Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object VersionInfo | Format-List displayName: 'Verify "File Version" matches expected values for DLLs' - - - powershell: | - # Check assembly versions. - # - # GOTCHA: This expects the Versions.props file having XML elements in a - # certain order. If the order changes, this check will fail! - # - # TODO: This also isn't checking the versions of the actual assemblies in - # the package, so it isn't terribly useful. - - [Xml] $versionprops = Get-Content -Path "tools/props/Versions.props" - $AssemblyFileVersion = $versionprops.Project.PropertyGroup[2].AssemblyFileVersion - $AssemblyVersion = $versionprops.Project.PropertyGroup[2].AssemblyVersion - - if($AssemblyFileVersion -eq $AssemblyVersion) - { - Write-Host AssemblyFileVersion: $AssemblyFileVersion should not be equal to: $AssemblyVersion - Exit -1 - } - displayName: 'Check "AssemblyFileVersion" is not same as "AssemblyVersion" in version.props' diff --git a/eng/pipelines/onebranch/stages/build-stages.yml b/eng/pipelines/onebranch/stages/build-stages.yml index fad5c9a699..738e91d26f 100644 --- a/eng/pipelines/onebranch/stages/build-stages.yml +++ b/eng/pipelines/onebranch/stages/build-stages.yml @@ -146,13 +146,30 @@ stages: dependsOn: build_abstractions jobs: - - template: /eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml@self + - template: /eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml@self parameters: - publishSymbols: ${{ parameters.publishSymbols }} - isPreview: ${{ parameters.isPreview }} + apiScanDllPath: '$(REPO_ROOT)/apiScan/Microsoft.Data.SqlClient/dlls' + apiScanPdbPath: '$(REPO_ROOT)/apiScan/Microsoft.Data.SqlClient/pdbs' isOfficial: ${{ parameters.isOfficial }} - # TODO: This job should use the effective versions for Abstractions, Logging, - # SqlServer, and SqlClient. + publishSymbols: ${{ parameters.publishSymbols }} + signingAppRegistrationClientId: '$(AppRegistrationClientId)' + signingAppRegistrationTenantId: '$(AppRegistrationTenantId)' + signingAuthAkvName: '$(AuthAKVName)' + signingAuthSignCertName: '$(AuthSignCertName)' + signingEsrpClientId: '$(ESRPClientId)' + signingEsrpConnectedServiceName: '$(ESRPConnectedServiceName)' + symbolsAzureSubscription: 'Symbols publishing Workload Identity federation service-ADO.Net' + symbolsPublishProjectName: 'Microsoft.Data.SqlClient.SNI' + symbolsPublishServer: '$(SymbolServer)' + symbolsPublishTokenUri: '$(SymbolTokenUri)' + symbolsUploadAccount: 'SqlClientDrivers' + + abstractionsArtifactName: '$(abstractionsArtifactsName)' + abstractionsPackageVersion: '$(effectiveAbstractionsVersion)' + loggingArtifactName: '$(loggingArtifactsName)' + loggingPackageVersion: '$(effectiveLoggingVersion)' + mdsAssemblyFileVersion: '$(mdsAssemblyFileVersion)' + mdsPackageVersion: '$(effectiveSqlClientVersion)' - template: /eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml@self parameters: diff --git a/eng/pipelines/onebranch/stages/release-stages.yml b/eng/pipelines/onebranch/stages/release-stages.yml index 51eea6e5f0..a9810f1251 100644 --- a/eng/pipelines/onebranch/stages/release-stages.yml +++ b/eng/pipelines/onebranch/stages/release-stages.yml @@ -153,7 +153,7 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.SqlServer.Server - artifactName: drop_build_independent_build_package_SqlServer + artifactName: $(sqlServerArtifactsName) packagePath: Microsoft.SqlServer.Server.$(effectiveSqlServerVersion).nupkg nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} @@ -163,7 +163,7 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.Internal.Logging - artifactName: drop_build_independent_build_package_Logging + artifactName: $(loggingArtifactsName) packagePath: Microsoft.Data.SqlClient.Internal.Logging.$(effectiveLoggingVersion).nupkg nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} @@ -173,7 +173,7 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.Extensions.Abstractions - artifactName: drop_build_abstractions_build_package_Abstractions + artifactName: $(abstractionsArtifactsName) packagePath: Microsoft.Data.SqlClient.Extensions.Abstractions.$(effectiveAbstractionsVersion).nupkg nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} @@ -183,7 +183,7 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient - artifactName: drop_build_dependent_build_package_SqlClient + artifactName: $(sqlClientArtifactsName) packagePath: Microsoft.Data.SqlClient.$(effectiveSqlClientVersion).nupkg nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} @@ -193,7 +193,7 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.Extensions.Azure - artifactName: drop_build_dependent_build_package_Azure + artifactName: $(azureArtifactsName) packagePath: Microsoft.Data.SqlClient.Extensions.Azure.$(effectiveAzureVersion).nupkg nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} @@ -203,7 +203,7 @@ stages: - template: /eng/pipelines/onebranch/jobs/publish-nuget-package-job.yml@self parameters: packageName: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider - artifactName: drop_build_addons_build_package_AkvProvider + artifactName: $(akvArtifactsName) packagePath: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.$(effectiveAkvProviderVersion).nupkg nugetServiceConnection: ${{ variables.nugetServiceConnection }} isProduction: ${{ parameters.isOfficial }} diff --git a/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml b/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml deleted file mode 100644 index b1429e1fd3..0000000000 --- a/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml +++ /dev/null @@ -1,58 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# -parameters: - - # The assembly file version to apply to the Abstractions package. - - name: abstractionsAssemblyFileVersion - type: string - - # The version to apply to the Abstractions package. - - name: abstractionsPackageVersion - type: string - - # The assembly file version to apply to the Logging package. - - name: loggingAssemblyFileVersion - type: string - - # The version to apply to the Logging package. - - name: loggingPackageVersion - type: string - - # The assembly file version to apply to the Mds package. - - name: mdsAssemblyFileVersion - type: string - - # The version to apply to the Mds package. - - name: mdsPackageVersion - type: string - -steps: - # Download our signing key. - - task: DownloadSecureFile@1 - displayName: 'Download Key Pair' - inputs: - secureFile: netfxKeypair.snk - name: keyFile - - # Install the .NET SDK. - - template: /eng/pipelines/steps/install-dotnet.yml@self - - - task: MSBuild@1 - displayName: 'BuildAllConfigurations using build.proj' - inputs: - solution: '**/build.proj' - configuration: Release - msbuildArguments: >- - -t:BuildAllConfigurations - -p:ReferenceType=Package - -p:GenerateNuget=false - -p:SigningKeyPath=$(keyFile.secureFilePath) - -p:AssemblyFileVersion=${{ parameters.mdsAssemblyFileVersion }} - -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} - -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} - -p:AbstractionsAssemblyFileVersion=${{ parameters.abstractionsAssemblyFileVersion }} - -p:LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - -p:LoggingAssemblyFileVersion=${{ parameters.loggingAssemblyFileVersion }} diff --git a/eng/pipelines/onebranch/steps/compound-build-csproj-step.yml b/eng/pipelines/onebranch/steps/build-csproj-step.yml similarity index 95% rename from eng/pipelines/onebranch/steps/compound-build-csproj-step.yml rename to eng/pipelines/onebranch/steps/build-csproj-step.yml index 1f1fee00c4..d1cea7c190 100644 --- a/eng/pipelines/onebranch/steps/compound-build-csproj-step.yml +++ b/eng/pipelines/onebranch/steps/build-csproj-step.yml @@ -7,7 +7,7 @@ # Generic build step for csproj-based packages. Each project uses a build.proj target that runs # Build only and produces assemblies within $(BUILD_OUTPUT). Downstream ESRP DLL signing must # locate the assemblies within $(BUILD_OUTPUT) for all target frameworks that the csproj targets. -# NuGet packaging is done separately via compound-pack-csproj-step.yml after DLL signing. +# NuGet packaging is done separately via pack-csproj-step.yml after DLL signing. parameters: # The MSBuild build target in build.proj (e.g. BuildLogging, BuildAbstractions, diff --git a/eng/pipelines/onebranch/steps/build-mds-step.yml b/eng/pipelines/onebranch/steps/build-mds-step.yml new file mode 100644 index 0000000000..4d5058f261 --- /dev/null +++ b/eng/pipelines/onebranch/steps/build-mds-step.yml @@ -0,0 +1,41 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# This collection of steps builds Microsoft.Data.SqlClient via build2.proj + +parameters: + - name: abstractionsPackageVersion + type: string + + - name: loggingPackageVersion + type: string + + - name: mdsPackageVersion + type: string + +steps: + - task: DownloadSecureFile@1 + displayName: 'Download Signing Key' + inputs: + secureFile: 'netfxKeypair.snk' + name: keyFile + + - task: MSBuild@1 + displayName: 'Build2.proj - BuildMds' + inputs: + solution: '$(REPO_ROOT)/build2.proj' + configuration: 'Release' + msbuildArguments: >- + -t:BuildMds + -p:BuildNumber="$(Build.BuildNumber)" + -p:PackageVersionAbstractions="${{ parameters.abstractionsPackageVersion }}" + -p:PackageVersionLogging="${{ parameters.loggingPackageVersion }}" + -p:PackageVersionMds="${{ parameters.mdsPackageVersion }}" + -p:ReferenceType=Package + -p:SigningKeyPath="$(keyFile.secureFilePath)" + + - script: tree /a /f $(BUILD_OUTPUT) + displayName: Output Build Output Tree diff --git a/eng/pipelines/onebranch/steps/compound-nuget-pack-step.yml b/eng/pipelines/onebranch/steps/compound-nuget-pack-step.yml deleted file mode 100644 index ef1f3b946a..0000000000 --- a/eng/pipelines/onebranch/steps/compound-nuget-pack-step.yml +++ /dev/null @@ -1,86 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -parameters: - # The C# build configuration to use (e.g. Debug or Release). - - name: buildConfiguration - type: string - values: - - Debug - - Release - - - name: generateSymbolsPackage - type: boolean - - - name: packageVersion - type: string - - - name: nuspecPath - type: string - - - name: outputDirectory - type: string - - # The C# project reference type to use when building and packing the packages. - - name: referenceType - type: string - values: - # Reference sibling packages as NuGet packages. - - Package - # Reference sibling packages as C# projects. - - Project - - # Semi-colon separated properties to pass to nuget via the -properties argument. - - name: properties - type: string - default: '' - -steps: - # This tool is failing on OneBranch pipelines, possibly due to new - # network isolation rules: - # - # ERR:Client network socket disconnected before secure TLS connection was established - # - # Our AKV Official build uses this 1ES image: - # - # Image: 1ES-OB-2022-D8-Netlock-V2_westus2_1_image - # - # An ICM for this issue exists: - # - # https://portal.microsofticm.com/imp/v5/incidents/details/690355343/summary - # - # Recommendation is to remove this step since NuGet is already present on - # the 1ES images. - # - # - task: NuGetToolInstaller@1 - # displayName: 'Install Latest Nuget' - # inputs: - # checkLatest: true - - - ${{ if parameters.generateSymbolsPackage }}: - - task: NuGetCommand@2 - displayName: 'Generate NuGet Package and Symbols Package' - inputs: - command: custom - arguments: >- - pack - ${{ parameters.nuspecPath }} - -Symbols - -SymbolPackageFormat snupkg - -Version ${{ parameters.packageVersion }} - -OutputDirectory ${{ parameters.outputDirectory }} - -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }};${{ parameters.properties }}" - - ${{ else }}: - - task: NuGetCommand@2 - displayName: 'Generate NuGet Package' - inputs: - command: custom - arguments: >- - pack - ${{ parameters.nuspecPath }} - -Version ${{ parameters.packageVersion }} - -OutputDirectory ${{ parameters.outputDirectory }} - -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }};${{ parameters.properties }}" diff --git a/eng/pipelines/onebranch/steps/compound-publish-symbols-step.yml b/eng/pipelines/onebranch/steps/compound-publish-symbols-step.yml deleted file mode 100644 index 1b2e3cb3b9..0000000000 --- a/eng/pipelines/onebranch/steps/compound-publish-symbols-step.yml +++ /dev/null @@ -1,162 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -# For more details, see https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL - -parameters: - # Name of the symbols artifact that will be published - - name: artifactName - type: string - - # Azure subscription where the publishing task will execute - - name: azureSubscription - type: string - - # Package name, typically the name of the nuget package being built - - name: packageName - type: string - - # Project that symbols will belong to (decided during symbols onboarding) - - name: publishProjectName - type: string - - # Where symbols publishing service is hosted, will be prepended to trafficmanager.net - - name: publishServer - type: string - - # Whether to publish the uploaded symbols to the internal symbols servers - - name: publishToInternal - type: boolean - - # Whether to publish the uploaded symbols to the public symbols servers - - name: publishToPublic - type: boolean - - # URI to use for requesting a bearer-token for publishing the symbols - - name: publishTokenUri - type: string - - # The C# project reference type to use when building and packing the packages. - - name: referenceType - type: string - values: - # Reference sibling packages as NuGet packages. - - Package - # Reference sibling packages as C# projects. - - Project - - # Pattern to use to search for pdb symbols files to upload/publish - - name: searchPattern - type: string - - # Account/org where the symbols will be uploaded - - name: uploadAccount - type: string - - # Version of the symbols to publish, typically the same as the NuGet package version - - name: version - type: string - -steps: - # Set variable for downstream tasks (allegedly). - # - # Note: Because variables cannot be set in top-level of template, this has to be done during - # runtime. - # - - script: 'echo ##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{ parameters.uploadAccount }}' - displayName: 'Set ArtifactServices.Symbol.AccountName to ${{ parameters.uploadAccount }}' - - - task: PublishSymbols@2 - displayName: 'Upload symbols to ${{ parameters.uploadAccount }} org' - inputs: - IndexSources: false - Pat: '$(System.AccessToken)' - SearchPattern: '${{ parameters.searchPattern }}' - SymbolExpirationInDays: 1825 # 5 years - SymbolServerType: 'TeamServices' - SymbolsArtifactName: '${{ parameters.artifactName }}' - SymbolsFolder: '$(BUILD_OUTPUT)/${{ parameters.referenceType }}/bin' - SymbolsMaximumWaitTime: 60 - SymbolsProduct: '${{ parameters.packageName }}' - SymbolsVersion: '${{ parameters.version }}' - - - task: AzureCLI@2 - displayName: 'Publish Symbols' - inputs: - azureSubscription: '${{ parameters.azureSubscription }}' - scriptLocation: inlineScript - scriptType: ps - inlineScript: | - # Propagate parameters to PS variables ################################################ - $artifactName = "${{ parameters.artifactName }}" - echo "artifactName= $artifactName" - - $publishProjectName = "${{ parameters.publishProjectName }}" - echo "publishProjectName= $publishProjectName" - - $publishToInternal = "${{ parameters.publishToInternal }}".ToLower() - echo "publishToInternal= $publishToInternal" - - $publishToPublic = "${{ parameters.publishToPublic }}".ToLower() - echo "publishToPublic= $publishToPublic" - - $publishServer = "${{ parameters.publishServer }}" - echo "publishServer= $publishServer" - - $publishTokenUri = "${{ parameters.publishTokenUri }}" - echo "publishTokenUri= $publishTokenUri" - - # Publish symbols ##################################################################### - # 1) Get the access token for the symbol publishing service - echo "> 1.Acquiring symbol publishing token..." - $symbolPublishingToken = az account get-access-token --resource $publishTokenUri --query accessToken -o tsv - echo "> 1.Symbol publishing token acquired." - - # 2) Register the request name - echo "> 2.Registering request name..." - $requestNameRegistrationBody = "{'requestName': '$artifactName'}" - Invoke-RestMethod ` - -Method POST ` - -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests" ` - -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` - -ContentType "application/json" ` - -Body $requestNameRegistrationBody - echo "> 2.Request name registered successfully." - - # 3) Publish the symbols - echo "> 3.Submitting request to publish symbols..." - $publishSymbolsBody = "{'publishToInternalServer': $publishToInternal, 'publishToPublicServer': $publishToPublic}" - Invoke-RestMethod ` - -Method POST ` - -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests/$artifactName" ` - -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` - -ContentType "application/json" ` - -Body $publishSymbolsBody - echo "> 3.Request to publish symbols submitted successfully." - - # The following REST calls are used to check publishing status. - echo "> 4.Checking the status of the request ..." - Invoke-RestMethod ` - -Method GET ` - -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests/$artifactName" ` - -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` - -ContentType "application/json" - - echo "Use below tables to interpret the values of xxxServerStatus and xxxServerResult fields from the response." - - echo "PublishingStatus" - echo "-----------------" - echo "0 NotRequested; The request has not been requested to publish." - echo "1 Submitted; The request is submitted to be published" - echo "2 Processing; The request is still being processed" - echo "3 Completed; The request has been completed processing. It can be failed or successful. Check PublishingResult to get more details" - - echo "PublishingResult" - echo "-----------------" - echo "0 Pending; The request has not completed or has not been requested." - echo "1 Succeeded; The request has published successfully" - echo "2 Failed; The request has failed to publish" - echo "3 Cancelled; The request was cancelled" diff --git a/eng/pipelines/onebranch/steps/copy-apiscan-files-mds-step.yml b/eng/pipelines/onebranch/steps/copy-apiscan-files-mds-step.yml new file mode 100644 index 0000000000..653eb40f5d --- /dev/null +++ b/eng/pipelines/onebranch/steps/copy-apiscan-files-mds-step.yml @@ -0,0 +1,39 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# @TODO: This can be made more generic + +parameters: + # Path where dll files should be copied to for APIScan + - name: dllPath + type: string + + # Path where pdb files should be copied to for APIScan + - name: pdbPath + type: string + + - name: referenceType + type: string + values: + - Package + - Project + +steps: + - task: CopyFiles@2 + displayName: 'Copy DLLs for APIScan' + inputs: + contents: '**/Microsoft.Data.SqlClient.dll' + flattenFolders: false # Disabled to keep different frameworks and OS builds separate in the target + sourceFolder: '$(BUILD_OUTPUT)/Microsoft.Data.SqlClient/${{ parameters.referenceType }}-Release/' + targetFolder: '${{ parameters.dllPath }}/' + + - task: CopyFiles@2 + displayName: 'Copy PDBs for APIScan' + inputs: + contents: '**/Microsoft.Data.SqlClient.pdb' + flattenFolders: false # Disabled to keep different frameworks and OS builds separate in the target + sourceFolder: '$(BUILD_OUTPUT)/Microsoft.Data.SqlClient/${{ parameters.referenceType }}-Release/' + targetFolder: '${{ parameters.pdbPath }}/' diff --git a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml deleted file mode 100644 index 09f7715145..0000000000 --- a/eng/pipelines/onebranch/steps/esrp-code-signing-step.yml +++ /dev/null @@ -1,168 +0,0 @@ -################################################################################# -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -################################################################################# - -parameters: - - name: artifactType - values: - - dll - - pkg - - - name: sourceRoot - type: string - default: $(REPO_ROOT) - - - name: dllPattern - type: string - default: "Microsoft.Data.SqlClient*.dll" - - - name: nupkgPattern - type: string - default: "*.*nupkg" - - - name: artifactDirectory - type: string - default: $(PACK_OUTPUT) - - - name: ESRPConnectedServiceName - type: string - default: $(ESRPConnectedServiceName) - - - name: appRegistrationClientId - type: string - default: $(appRegistrationClientId) - - - name: appRegistrationTenantId - type: string - default: $(appRegistrationTenantId) - - - name: AuthAKVName - type: string - default: $(AuthAKVName) - - - name: AuthSignCertName - type: string - default: $(AuthSignCertName) - - - name: EsrpClientId - type: string - default: $(EsrpClientId) - -steps: - # ESRP scan and sign the DLLs or NuGet packages, depending on the artifact type. - - ${{ if eq(parameters.artifactType, 'dll') }}: - # See: https://aka.ms/esrp.scantask - - task: EsrpMalwareScanning@6 - displayName: "ESRP MalwareScanning" - inputs: - ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" - AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" - AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" - EsrpClientId: "${{parameters.EsrpClientId }}" - UseMSIAuthentication: true - FolderPath: "${{parameters.sourceRoot }}" - Pattern: "${{ parameters.dllPattern }}" - CleanupTempStorage: 1 - VerboseLogin: 1 - - # See: https://aka.ms/esrp.signtask - - task: EsrpCodeSigning@6 - displayName: "ESRP CodeSigning" - inputs: - ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" - AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" - AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" - EsrpClientId: "${{parameters.EsrpClientId }}" - UseMSIAuthentication: true - AuthAKVName: "${{parameters.AuthAKVName }}" - AuthSignCertName: "${{parameters.AuthSignCertName }}" - FolderPath: "${{parameters.sourceRoot }}" - Pattern: "${{ parameters.dllPattern }}" - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "Microsoft Data SqlClient Data Provider for SQL Server" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "http://www.microsoft.com" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - - - ${{ if eq(parameters.artifactType, 'pkg') }}: - # See: https://aka.ms/esrp.scantask - - task: EsrpMalwareScanning@6 - displayName: "ESRP MalwareScanning Nuget Package" - inputs: - ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" - AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" - AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" - EsrpClientId: "${{parameters.EsrpClientId }}" - UseMSIAuthentication: true - FolderPath: "${{parameters.artifactDirectory }}" - Pattern: "${{ parameters.nupkgPattern }}" - CleanupTempStorage: 1 - VerboseLogin: 1 - - # See: https://aka.ms/esrp.signtask - - task: EsrpCodeSigning@6 - displayName: "ESRP CodeSigning Nuget Package" - inputs: - ConnectedServiceName: "${{parameters.ESRPConnectedServiceName }}" - AppRegistrationClientId: "${{parameters.appRegistrationClientId }}" - AppRegistrationTenantId: "${{parameters.appRegistrationTenantId }}" - EsrpClientId: "${{parameters.EsrpClientId }}" - UseMSIAuthentication: true - AuthAKVName: "${{parameters.AuthAKVName }}" - AuthSignCertName: "${{parameters.AuthSignCertName }}" - FolderPath: "${{parameters.artifactDirectory }}" - Pattern: "${{ parameters.nupkgPattern }}" - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] diff --git a/eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml similarity index 78% rename from eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml rename to eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml index 44649f94aa..073911e5fe 100644 --- a/eng/pipelines/onebranch/steps/compound-esrp-dll-signing-step.yml +++ b/eng/pipelines/onebranch/steps/esrp-dll-signing-step.yml @@ -30,7 +30,7 @@ parameters: - name: esrpClientId type: string - # Globbing pattern for the files to sign. All files in $(BUILD_OUTPUT)/Package/bin + # Globbing pattern for the files to sign. All files in $(BUILD_OUTPUT) # that match this pattern will be scanned and signed. This should end with ".dll". - name: pattern type: string @@ -40,13 +40,13 @@ steps: - task: EsrpMalwareScanning@6 displayName: ESRP DLL Malware Scanning inputs: - AppRegistrationClientId: ${{ parameters.appRegistrationClientId }} - AppRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} + AppRegistrationClientId: '${{ parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{ parameters.appRegistrationTenantId }}' CleanupTempStorage: 1 - ConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - EsrpClientId: ${{ parameters.esrpClientId }} - FolderPath: $(BUILD_OUTPUT)/Package/bin - Pattern: ${{ parameters.pattern }} + ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' + EsrpClientId: '${{ parameters.esrpClientId }}' + FolderPath: '$(BUILD_OUTPUT)' + Pattern: '${{ parameters.pattern }}' UseMSIAuthentication: true VerboseLogin: 1 @@ -54,14 +54,14 @@ steps: - task: EsrpCodeSigning@6 displayName: ESRP DLL Signing inputs: - AppRegistrationClientId: ${{ parameters.appRegistrationClientId }} - AppRegistrationTenantId: ${{ parameters.appRegistrationTenantId }} - AuthAKVName: ${{ parameters.authAkvName }} - AuthSignCertName: ${{ parameters.authSignCertName }} - ConnectedServiceName: ${{ parameters.esrpConnectedServiceName }} - EsrpClientId: ${{ parameters.esrpClientId }} - FolderPath: $(BUILD_OUTPUT)/Package/bin - Pattern: ${{ parameters.pattern }} + AppRegistrationClientId: '${{ parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{ parameters.appRegistrationTenantId }}' + AuthAKVName: '${{ parameters.authAkvName }}' + AuthSignCertName: '${{ parameters.authSignCertName }}' + ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' + EsrpClientId: '${{ parameters.esrpClientId }}' + FolderPath: '$(BUILD_OUTPUT)' + Pattern: '${{ parameters.pattern }}' signConfigType: inlineSignParams UseMSIAuthentication: true inlineOperation: | diff --git a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml b/eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml similarity index 59% rename from eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml rename to eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml index 824b3fab65..bddbac517c 100644 --- a/eng/pipelines/onebranch/steps/compound-esrp-nuget-signing-step.yml +++ b/eng/pipelines/onebranch/steps/esrp-nuget-signing-step.yml @@ -30,39 +30,43 @@ parameters: - name: esrpClientId type: string - # Glob pattern to match NuGet packages for scanning and signing. - - name: pattern + # Folder path to search for NuGet packages to sign + - name: searchPath type: string - default: "*.*nupkg" + + # Globbing pattern to use to search for NuGet packages. If not provided, defaults to '*.*nupkg' + - name: searchPattern + type: string + default: '*.*nupkg' steps: # See: https://aka.ms/esrp.scantask - task: EsrpMalwareScanning@6 - displayName: "ESRP Nuget Malware Scanning" + displayName: 'ESRP Nuget Malware Scanning' inputs: - AppRegistrationClientId: "${{ parameters.appRegistrationClientId }}" - AppRegistrationTenantId: "${{ parameters.appRegistrationTenantId }}" + AppRegistrationClientId: '${{ parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{ parameters.appRegistrationTenantId }}' CleanupTempStorage: 1 - ConnectedServiceName: "${{ parameters.esrpConnectedServiceName }}" - EsrpClientId: "${{ parameters.esrpClientId }}" - FolderPath: "$(PACK_OUTPUT)" - Pattern: "${{ parameters.pattern }}" + ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' + EsrpClientId: '${{ parameters.esrpClientId }}' + FolderPath: '${{ parameters.searchPath }}' + Pattern: '${{ parameters.searchPattern }}' UseMSIAuthentication: true VerboseLogin: 1 # See: https://aka.ms/esrp.signtask - task: EsrpCodeSigning@6 - displayName: "ESRP Signing NuGet Package" + displayName: 'ESRP Signing NuGet Package' inputs: - AppRegistrationClientId: "${{ parameters.appRegistrationClientId }}" - AppRegistrationTenantId: "${{ parameters.appRegistrationTenantId }}" - ConnectedServiceName: "${{ parameters.esrpConnectedServiceName }}" - EsrpClientId: "${{ parameters.esrpClientId }}" - AuthAKVName: "${{ parameters.authAkvName }}" - AuthSignCertName: "${{ parameters.authSignCertName }}" - FolderPath: "$(PACK_OUTPUT)" - Pattern: "${{ parameters.pattern }}" - signConfigType: "inlineSignParams" + AppRegistrationClientId: '${{ parameters.appRegistrationClientId }}' + AppRegistrationTenantId: '${{ parameters.appRegistrationTenantId }}' + ConnectedServiceName: '${{ parameters.esrpConnectedServiceName }}' + EsrpClientId: '${{ parameters.esrpClientId }}' + AuthAKVName: '${{ parameters.authAkvName }}' + AuthSignCertName: '${{ parameters.authSignCertName }}' + FolderPath: '${{ parameters.searchPath }}' + Pattern: '${{ parameters.searchPattern }}' + signConfigType: 'inlineSignParams' UseMSIAuthentication: true inlineOperation: | [ diff --git a/eng/pipelines/onebranch/steps/compound-pack-csproj-step.yml b/eng/pipelines/onebranch/steps/pack-csproj-step.yml similarity index 100% rename from eng/pipelines/onebranch/steps/compound-pack-csproj-step.yml rename to eng/pipelines/onebranch/steps/pack-csproj-step.yml diff --git a/eng/pipelines/onebranch/steps/pack-mds-step.yml b/eng/pipelines/onebranch/steps/pack-mds-step.yml new file mode 100644 index 0000000000..f1277280ae --- /dev/null +++ b/eng/pipelines/onebranch/steps/pack-mds-step.yml @@ -0,0 +1,42 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +parameters: + # Package version parameters ---- + - name: abstractionsPackageVersion + type: string + + - name: loggingPackageVersion + type: string + + - name: mdsPackageVersion + type: string + +steps: + - task: MSBuild@1 + displayName: 'Build2.proj - PackMds' + inputs: + solution: '$(REPO_ROOT)/build2.proj' + configuration: 'Release' + msbuildArguments: >- + -t:PackMds + -p:BuildNumber="$(Build.BuildNumber)" + -p:PackBuild=false + -p:PackageVersionAbstractions="${{ parameters.abstractionsPackageVersion }}" + -p:PackageVersionLogging="${{ parameters.loggingPackageVersion }}" + -p:PackageVersionMds="${{ parameters.mdsPackageVersion }}" + -p:ReferenceType=Package + + - script: tree /a /f $(BUILD_OUTPUT) + displayName: Output Build Output Tree + + - task: CopyFiles@2 + displayName: 'Copy NuGet Packages to PACK_OUTPUT' + inputs: + contents: '**/Microsoft.Data.SqlClient*.*nupkg' + flattenFolders: true + sourceFolder: '$(BUILD_OUTPUT)/Microsoft.Data.SqlClient/' + targetFolder: '$(PACK_OUTPUT)' diff --git a/eng/pipelines/onebranch/steps/publish-symbols-step.yml b/eng/pipelines/onebranch/steps/publish-symbols-step.yml index a23dbf4556..48ee30b9c2 100644 --- a/eng/pipelines/onebranch/steps/publish-symbols-step.yml +++ b/eng/pipelines/onebranch/steps/publish-symbols-step.yml @@ -1,119 +1,153 @@ -#################################################################################### -# Licensed to the .NET Foundation under one or more agreements. # -# The .NET Foundation licenses this file to you under the MIT license. # -# See the LICENSE file in the project root for more information. # -# # -# doc: https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL # -#################################################################################### +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# For more details, see https://www.osgwiki.com/wiki/Symbols_Publishing_Pipeline_to_SymWeb_and_MSDL + parameters: + # Name of the symbols artifact that will be published + - name: artifactName + type: string + + # Azure subscription where the publishing task will execute + - name: azureSubscription + type: string + + # Package name, typically the name of the nuget package being built + - name: packageName + type: string + + # Project that symbols will belong to (decided during symbols onboarding) + - name: publishProjectName + type: string + + # Where symbols publishing service is hosted, will be prepended to trafficmanager.net + - name: publishServer + type: string + + # Whether to publish the uploaded symbols to the internal symbols servers + - name: publishToInternal + type: boolean + + # Whether to publish the uploaded symbols to the public symbols servers + - name: publishToPublic + type: boolean + + # URI to use for requesting a bearer-token for publishing the symbols + - name: publishTokenUri + type: string + + # Pattern to use to search for pdb symbols files to upload/publish + - name: searchPattern + type: string + + # Account/org where the symbols will be uploaded + - name: uploadAccount + type: string - # The full name of the package whose symbols are being published. - - name: packageFullName - type: string - - # The version of the package whose symbols are being published. - - name: packageVersion - type: string - - # Our symbols account name. - - name: symbolsAccount - type: string - default: SqlClientDrivers - - # The symbols server to publish to. - - name: symbolServer - type: string - default: $(SymbolServer) - - # The token URI for the symbol publishing service. - - name: symbolTokenUri - type: string - default: $(SymbolTokenUri) - - # A pair of flags indicating whether to publish to the internal and public symbol servers. Both - # default to true. - - name: publishToServers - type: object - default: - internal: true - public: true + # Version of the symbols to publish, typically the same as the NuGet package version + - name: version + type: string steps: -- pwsh: 'Write-Host "##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{parameters.symbolsAccount}}"' - displayName: 'Set ArtifactServices.Symbol.AccountName to ${{parameters.symbolsAccount}}' - -- pwsh: 'Write-Host "##vso[task.setvariable variable=symbolsArtifactName;]${{ parameters.packageFullName }}_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.packageVersion }}_$(System.TimelineId)"' - displayName: 'Set symbolsArtifactName variable' - -- task: PublishSymbols@2 - displayName: 'Upload symbols to ${{parameters.symbolsAccount }} org' - inputs: - SymbolsFolder: '$(Build.SourcesDirectory)\artifacts\Package\bin' - SearchPattern: '**/*.pdb' - IndexSources: false - SymbolServerType: TeamServices - SymbolsMaximumWaitTime: 60 - SymbolExpirationInDays: 1825 # 5 years - SymbolsProduct: ${{ parameters.packageFullName }} - SymbolsVersion: ${{ parameters.packageVersion }} - SymbolsArtifactName: $(symbolsArtifactName) - Pat: $(System.AccessToken) - -- task: AzureCLI@2 - displayName: 'Publish symbols' - inputs: - azureSubscription: 'Symbols publishing Workload Identity federation service-ADO.Net' - scriptType: ps - scriptLocation: inlineScript - inlineScript: | - $publishToInternalServer = "${{parameters.publishToServers.internal }}".ToLower() - $publishToPublicServer = "${{parameters.publishToServers.public }}".ToLower() - - echo "Publishing request name: $(symbolsArtifactName)" - echo "Publish to internal server: $publishToInternalServer" - echo "Publish to public server: $publishToPublicServer" - - $symbolServer = "${{parameters.symbolServer }}" - $tokenUri = "${{parameters.symbolTokenUri }}" - # Registered project name in the symbol publishing pipeline: https://portal.microsofticm.com/imp/v3/incidents/incident/520844254/summary - $projectName = "Microsoft.Data.SqlClient.SNI" - - # Get the access token for the symbol publishing service - $symbolPublishingToken = az account get-access-token --resource $tokenUri --query accessToken -o tsv - - echo "> 1.Symbol publishing token acquired." - - echo "Registering the request name ..." - $requestName = "$(symbolsArtifactName)" - $requestNameRegistrationBody = "{'requestName': '$requestName'}" - Invoke-RestMethod -Method POST -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" -Body $requestNameRegistrationBody - - echo "> 2.Registration of request name succeeded." - - echo "Publishing the symbols ..." - $publishSymbolsBody = "{'publishToInternalServer': $publishToInternalServer, 'publishToPublicServer': $publishToPublicServer}" - echo "Publishing symbols request body: $publishSymbolsBody" - Invoke-RestMethod -Method POST -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests/$requestName" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" -Body $publishSymbolsBody - - echo "> 3.Request to publish symbols succeeded." - - # The following REST calls are used to check publishing status. - echo "> 4.Checking the status of the request ..." - - Invoke-RestMethod -Method GET -Uri "https://$symbolServer.trafficmanager.net/projects/$projectName/requests/$requestName" -Headers @{ Authorization = "Bearer $symbolPublishingToken" } -ContentType "application/json" - - echo "Use below tables to interpret the values of xxxServerStatus and xxxServerResult fields from the response." - - echo "PublishingStatus" - echo "-----------------" - echo "0 NotRequested; The request has not been requested to publish." - echo "1 Submitted; The request is submitted to be published" - echo "2 Processing; The request is still being processed" - echo "3 Completed; The request has been completed processing. It can be failed or successful. Check PublishingResult to get more details" - - echo "PublishingResult" - echo "-----------------" - echo "0 Pending; The request has not completed or has not been requested." - echo "1 Succeeded; The request has published successfully" - echo "2 Failed; The request has failed to publish" - echo "3 Cancelled; The request was cancelled" + # Set variable for downstream tasks (allegedly). + # + # Note: Because variables cannot be set in top-level of template, this has to be done during + # runtime. + # + - script: 'echo ##vso[task.setvariable variable=ArtifactServices.Symbol.AccountName;]${{ parameters.uploadAccount }}' + displayName: 'Set ArtifactServices.Symbol.AccountName to ${{ parameters.uploadAccount }}' + + - task: PublishSymbols@2 + displayName: 'Upload symbols to ${{ parameters.uploadAccount }} org' + inputs: + IndexSources: false + Pat: '$(System.AccessToken)' + SearchPattern: '${{ parameters.searchPattern }}' + SymbolExpirationInDays: 1825 # 5 years + SymbolServerType: 'TeamServices' + SymbolsArtifactName: '${{ parameters.artifactName }}' + SymbolsFolder: '$(BUILD_OUTPUT)' + SymbolsMaximumWaitTime: 60 + SymbolsProduct: '${{ parameters.packageName }}' + SymbolsVersion: '${{ parameters.version }}' + + - task: AzureCLI@2 + displayName: 'Publish Symbols' + inputs: + azureSubscription: '${{ parameters.azureSubscription }}' + scriptLocation: inlineScript + scriptType: ps + inlineScript: | + # Propagate parameters to PS variables ################################################ + $artifactName = "${{ parameters.artifactName }}" + echo "artifactName= $artifactName" + + $publishProjectName = "${{ parameters.publishProjectName }}" + echo "publishProjectName= $publishProjectName" + + $publishToInternal = "${{ parameters.publishToInternal }}".ToLower() + echo "publishToInternal= $publishToInternal" + + $publishToPublic = "${{ parameters.publishToPublic }}".ToLower() + echo "publishToPublic= $publishToPublic" + + $publishServer = "${{ parameters.publishServer }}" + echo "publishServer= $publishServer" + + $publishTokenUri = "${{ parameters.publishTokenUri }}" + echo "publishTokenUri= $publishTokenUri" + + # Publish symbols ##################################################################### + # 1) Get the access token for the symbol publishing service + echo "> 1.Acquiring symbol publishing token..." + $symbolPublishingToken = az account get-access-token --resource $publishTokenUri --query accessToken -o tsv + echo "> 1.Symbol publishing token acquired." + + # 2) Register the request name + echo "> 2.Registering request name..." + $requestNameRegistrationBody = "{'requestName': '$artifactName'}" + Invoke-RestMethod ` + -Method POST ` + -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests" ` + -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` + -ContentType "application/json" ` + -Body $requestNameRegistrationBody + echo "> 2.Request name registered successfully." + + # 3) Publish the symbols + echo "> 3.Submitting request to publish symbols..." + $publishSymbolsBody = "{'publishToInternalServer': $publishToInternal, 'publishToPublicServer': $publishToPublic}" + Invoke-RestMethod ` + -Method POST ` + -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests/$artifactName" ` + -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` + -ContentType "application/json" ` + -Body $publishSymbolsBody + echo "> 3.Request to publish symbols submitted successfully." + + # The following REST calls are used to check publishing status. + echo "> 4.Checking the status of the request ..." + Invoke-RestMethod ` + -Method GET ` + -Uri "https://$publishServer.trafficmanager.net/projects/$publishProjectName/requests/$artifactName" ` + -Headers @{ Authorization = "Bearer $symbolPublishingToken" } ` + -ContentType "application/json" + + echo "Use below tables to interpret the values of xxxServerStatus and xxxServerResult fields from the response." + + echo "PublishingStatus" + echo "-----------------" + echo "0 NotRequested; The request has not been requested to publish." + echo "1 Submitted; The request is submitted to be published" + echo "2 Processing; The request is still being processed" + echo "3 Completed; The request has been completed processing. It can be failed or successful. Check PublishingResult to get more details" + + echo "PublishingResult" + echo "-----------------" + echo "0 Pending; The request has not completed or has not been requested." + echo "1 Succeeded; The request has published successfully" + echo "2 Failed; The request has failed to publish" + echo "3 Cancelled; The request was cancelled" diff --git a/eng/pipelines/onebranch/steps/code-analyze-step.yml b/eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml similarity index 100% rename from eng/pipelines/onebranch/steps/code-analyze-step.yml rename to eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml diff --git a/eng/pipelines/onebranch/steps/roslyn-analyzers-mds-step.yml b/eng/pipelines/onebranch/steps/roslyn-analyzers-mds-step.yml new file mode 100644 index 0000000000..5fe8009078 --- /dev/null +++ b/eng/pipelines/onebranch/steps/roslyn-analyzers-mds-step.yml @@ -0,0 +1,40 @@ +################################################################################# +# Licensed to the .NET Foundation under one or more agreements. # +# The .NET Foundation licenses this file to you under the MIT license. # +# See the LICENSE file in the project root for more information. # +################################################################################# + +# This template defines a step to run Roslyn Analyzers on the MDS build. It uses the +# RoslynAnalyzers@3 task from the Secure Development Team's SDL extension: +# +# https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-mohanb/security-integration/guardian-wiki/sdl-azdo-extension/roslyn-analyzers-build-task +# +# GOTCHA: This step will clobber any existing build output. It should be run _before_ any build +# steps that perform versioning or signing. + +parameters: + - name: abstractionsPackageVersion + type: string + + - name: loggingPackageVersion + type: string + + - name: mdsPackageVersion + type: string + +steps: + - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3 + displayName: 'Roslyn Analyzers: Build2.proj BuildMds' + inputs: + msBuildArchitecture: x64 + msBuildCommandLine: >- + msbuild + $(REPO_ROOT)/build2.proj + -t:BuildMds + -p:Configuration=Release + -p:PackageVersionAbstractions="${{ parameters.abstractionsPackageVersion }}" + -p:PackageVersionLogging="${{ parameters.loggingPackageVersion }}" + -p:PackageVersionMds="${{ parameters.mdsPackageVersion }}" + -p:ReferenceType=Package + msBuildVersion: 17.0 + setupCommandLinePicker: vs2022 diff --git a/eng/pipelines/onebranch/variables/common-variables.yml b/eng/pipelines/onebranch/variables/common-variables.yml index feddd2c6ce..dbbddefc51 100644 --- a/eng/pipelines/onebranch/variables/common-variables.yml +++ b/eng/pipelines/onebranch/variables/common-variables.yml @@ -28,8 +28,7 @@ variables: - name: CommitHead value: '' # the value will be extracted from the repo's head - # Aliases required by compound step templates (compound-esrp-dll-signing-step, - # compound-esrp-nuget-signing-step, compound-nuget-pack-step, etc.). + # "Well-Known" Variables that are ok to use directly, anywhere in the pipeline. # The root of our repo. - name: REPO_ROOT diff --git a/eng/pipelines/onebranch/variables/onebranch-variables.yml b/eng/pipelines/onebranch/variables/onebranch-variables.yml index 9c60ff4cf0..004786e25f 100644 --- a/eng/pipelines/onebranch/variables/onebranch-variables.yml +++ b/eng/pipelines/onebranch/variables/onebranch-variables.yml @@ -52,4 +52,4 @@ variables: # The SqlClient package artifacts. - name: sqlClientArtifactsName - value: drop_build_dependent_build_package_SqlClient + value: drop_build_dependent_build_package_Mds diff --git a/src/Microsoft.Data.SqlClient/MdsVersions.props b/src/Microsoft.Data.SqlClient/MdsVersions.props index e50e813f66..a9f8d1cc83 100644 --- a/src/Microsoft.Data.SqlClient/MdsVersions.props +++ b/src/Microsoft.Data.SqlClient/MdsVersions.props @@ -1,35 +1,89 @@ - + + + + + 7.0.0 - 0 - - $(MdsVersionDefault).$(BuildNumber.Split('.')[0]) - $(MdsPackageVersion.Split('-')[0]) + - + + + + + $(MdsPackageVersion.Split('-')[0]).$(AssemblyBuildNumber) + + - NOTE: NuGet versioning can handle arbitrary version strings, so we do not need to trim the - build number like we do for the assembly version. + + - $(MdsVersionDefault).$(BuildNumber)-dev + If a build suffix is provided, this is appended to the package version. This is meant to be + used by automated pre-release systems to indicate the source of the build. + + If a build suffix is not provided, no pre-release tag will be added to package version. If + the default version already has a pre-release tag added to it (eg, "7.0.0-preview1") this + will be retained for the package version, but will be stripped off for the file version + (letters are not allowed in file/assembly versions). This is meant to be used by official + build pipelines to generate production-ready builds. + --> + + $(MdsVersionDefault)-$(BuildSuffix)$(AssemblyBuildNumber) + $(MdsVersionDefault) + + $(MdsVersionDefault.Split('-')[0]).$(AssemblyBuildNumber) + + + + + + + dev + + $(MdsVersionDefault)-$(BuildSuffix) + $(MdsVersionDefault.Split('-')[0]).$(AssemblyBuildNumber) + + + + + + + $(MdsFileVersion.Split('.')[0]).0.0.0 + + diff --git a/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj index 6f5fc44f59..4099207511 100644 --- a/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/notsupported/Microsoft.Data.SqlClient.csproj @@ -71,7 +71,7 @@ $(MdsAssemblyVersion) - $(MdsAssemblyVersion) + $(MdsFileVersion) $(MdsPackageVersion) diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj index dbdf7e9338..dfad9e601c 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj @@ -9,7 +9,7 @@ $(MdsAssemblyVersion) - $(MdsAssemblyVersion) + $(MdsFileVersion) $(MdsPackageVersion) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj index 0f1c3c2d40..2f7c726500 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj @@ -13,7 +13,7 @@ $(MdsAssemblyVersion) - $(MdsAssemblyVersion) + $(MdsFileVersion) $(MdsPackageVersion) diff --git a/tools/targets/GenerateThisAssemblyCs.targets b/tools/targets/GenerateThisAssemblyCs.targets index 6dfd670c5c..8632d80dc4 100644 --- a/tools/targets/GenerateThisAssemblyCs.targets +++ b/tools/targets/GenerateThisAssemblyCs.targets @@ -8,8 +8,8 @@ - - $(AssemblyVersion) + + $(FileVersion) System From 75b33ad53c18d53fe8bcfbb8287f5b3ce78ee11b Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Tue, 31 Mar 2026 23:26:42 -0700 Subject: [PATCH 18/22] Trim docs using pwsh via dotnet tool to support cross-platform local development. (#4121) --- dotnet-tools.json | 7 +++++++ .../ref/Microsoft.Data.SqlClient.csproj | 9 ++++++--- tools/targets/TrimDocsForIntelliSense.targets | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/dotnet-tools.json b/dotnet-tools.json index 1f59e06063..11e3494512 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -15,6 +15,13 @@ "apicompat" ], "rollForward": false + }, + "powershell": { + "version": "7.6.0", + "commands": [ + "pwsh" + ], + "rollForward": false } } } \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj index dfad9e601c..252e8ba0b0 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj @@ -36,6 +36,10 @@ $(ArtifactPath)$(AssemblyName).ref/$(ReferenceType)-$(Configuration)/ + + + + - powershell.exe - pwsh - $(PowerShellCommand) + dotnet tool run pwsh -NonInteractive -ExecutionPolicy Unrestricted -Command "$(RepoRoot)tools\intellisense\TrimDocs.ps1 -inputFile '$(DocumentationFile)' -outputFile '$(DocumentationFile)'" diff --git a/tools/targets/TrimDocsForIntelliSense.targets b/tools/targets/TrimDocsForIntelliSense.targets index 05feb26739..fd0ca40ea8 100644 --- a/tools/targets/TrimDocsForIntelliSense.targets +++ b/tools/targets/TrimDocsForIntelliSense.targets @@ -3,7 +3,7 @@ The .NET Foundation licenses this file to you under the MIT license. --> - - + + From a11341f5bcd78e28044a683c8f61877a7f132a57 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:25:09 -0300 Subject: [PATCH 19/22] Address flaky DEBUG assertions (#4085) --- .../ManualTests/DataCommon/DataTestUtility.cs | 89 ++++--- .../SQL/AdapterTest/AdapterTest.cs | 6 +- .../SQL/DataStreamTest/DataStreamTest.cs | 222 ++++++++++-------- .../SQL/LocalDBTest/LocalDBTest.cs | 2 +- .../ManualTests/SQL/MARSTest/MARSTest.cs | 4 +- .../ParallelTransactionsTest.cs | 6 +- .../SQL/ParameterTest/ParametersTest.cs | 22 +- .../CopyAllFromReaderCancelAsync.cs | 2 +- .../CopyAllFromReaderConnectionCloseAsync.cs | 2 +- ...llFromReaderConnectionCloseOnEventAsync.cs | 3 +- .../SQL/SqlBulkCopyTest/CopyWithEvent1.cs | 2 +- .../SqlBulkCopyTest/MissingTargetColumn.cs | 2 +- .../SqlBulkCopyTest/MissingTargetColumns.cs | 2 +- .../OrderHintDuplicateColumn.cs | 2 +- .../OrderHintMissingTargetColumn.cs | 4 +- .../SQL/SqlBulkCopyTest/Transaction.cs | 2 +- .../SQL/SqlBulkCopyTest/Transaction1.cs | 2 +- .../SQL/SqlBulkCopyTest/Transaction3.cs | 4 +- .../SQL/SqlBulkCopyTest/Transaction4.cs | 2 +- .../SqlBulkCopyTest/TransactionTestAsync.cs | 2 +- .../SQL/SqlCommand/SqlCommandCancelTest.cs | 6 +- .../SqlNotificationTest.cs | 6 +- .../SQL/TransactionTest/TransactionTest.cs | 18 +- .../tests/ManualTests/SQL/UdtTest/UdtTest2.cs | 15 +- 24 files changed, 245 insertions(+), 182 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 3b4245d84f..5bceb81b58 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -302,9 +302,9 @@ private static Task AcquireTokenAsync(string authorityURL, string userID SecureString securePassword = new SecureString(); securePassword.MakeReadOnly(); -#pragma warning disable CS0618 // Type or member is obsolete + #pragma warning disable CS0618 // Type or member is obsolete result = app.AcquireTokenByUsernamePassword(scopes, userID, password).ExecuteAsync().Result; -#pragma warning restore CS0618 // Type or member is obsolete + #pragma warning restore CS0618 // Type or member is obsolete return result.AccessToken; }); @@ -389,7 +389,7 @@ public static string GetSqlServerProperty(SqlConnection connection, ServerProper } } - #nullable disable + #nullable restore private static bool GetSQLServerStatusOnTDS8(string connectionString) { @@ -951,35 +951,48 @@ public static void AssertEqualsWithDescription(object expectedValue, object actu } } - public static TException AssertThrowsWrapper(Action actionThatFails, string exceptionMessage = null, bool innerExceptionMustBeNull = false, Func customExceptionVerifier = null) where TException : Exception + #nullable enable + + /// + /// Asserts that throws an exception of type + /// and optionally verifies that its message contains + /// . + /// + public static TException AssertThrows( + Action actionThatFails, + string? exceptionMessage = null) + where TException : Exception { TException ex = Assert.Throws(actionThatFails); + if (exceptionMessage != null) { Assert.True(ex.Message.Contains(exceptionMessage), string.Format("FAILED: Exception did not contain expected message.\nExpected: {0}\nActual: {1}", exceptionMessage, ex.Message)); } - if (innerExceptionMustBeNull) - { - Assert.True(ex.InnerException == null, "FAILED: Expected InnerException to be null."); - } - - if (customExceptionVerifier != null) - { - Assert.True(customExceptionVerifier(ex), "FAILED: Custom exception verifier returned false for this exception."); - } - return ex; } - public static TException AssertThrowsWrapper(Action actionThatFails, string exceptionMessage = null, string innerExceptionMessage = null, bool innerExceptionMustBeNull = false, Func customExceptionVerifier = null) where TException : Exception + /// + /// Asserts that throws + /// whose is of type . + /// Optionally verifies message text on both the outer and inner exceptions. + /// + public static TException AssertThrowsInner( + Action actionThatFails, + string? exceptionMessage = null, + string? innerExceptionMessage = null) + where TException : Exception + where TInnerException : Exception { - TException ex = AssertThrowsWrapper(actionThatFails, exceptionMessage, innerExceptionMustBeNull, customExceptionVerifier); + TException ex = AssertThrows(actionThatFails, exceptionMessage); + + Assert.NotNull(ex.InnerException); + Assert.IsAssignableFrom(ex.InnerException); if (innerExceptionMessage != null) { - Assert.True(ex.InnerException != null, "FAILED: Cannot check innerExceptionMessage because InnerException is null."); Assert.True(ex.InnerException.Message.Contains(innerExceptionMessage), string.Format("FAILED: Inner Exception did not contain expected message.\nExpected: {0}\nActual: {1}", innerExceptionMessage, ex.InnerException.Message)); } @@ -987,26 +1000,40 @@ public static TException AssertThrowsWrapper(Action return ex; } - public static TException AssertThrowsWrapper(Action actionThatFails, string exceptionMessage = null, string innerExceptionMessage = null, string innerInnerExceptionMessage = null, bool innerInnerInnerExceptionMustBeNull = false) where TException : Exception where TInnerException : Exception where TInnerInnerException : Exception + /// + /// Asserts that throws + /// whose is either + /// or . Use this when a race condition + /// (e.g. disposal during an async read) may cause the inner exception type to vary + /// between runs. The is only verified when the + /// inner exception is . + /// + public static TException AssertThrowsInnerWithAlternate( + Action actionThatFails, + string? exceptionMessage = null, + string? innerExceptionMessage = null) + where TException : Exception + where TInnerException : Exception + where TAlternateInnerException : Exception { - TException ex = AssertThrowsWrapper(actionThatFails, exceptionMessage, innerExceptionMessage); - if (innerInnerInnerExceptionMustBeNull) - { - Assert.True(ex.InnerException != null, "FAILED: Cannot check innerInnerInnerExceptionMustBeNull since InnerException is null"); - Assert.True(ex.InnerException.InnerException == null, "FAILED: Expected InnerInnerException to be null."); - } + TException ex = AssertThrows(actionThatFails, exceptionMessage); - if (innerInnerExceptionMessage != null) + Assert.NotNull(ex.InnerException); + Assert.True( + ex.InnerException is TInnerException or TAlternateInnerException, + $"Expected {typeof(TInnerException).Name} or {typeof(TAlternateInnerException).Name}, got: {ex.InnerException?.GetType()}"); + + if (innerExceptionMessage != null && ex.InnerException is TInnerException) { - Assert.True(ex.InnerException != null, "FAILED: Cannot check innerInnerExceptionMessage since InnerException is null"); - Assert.True(ex.InnerException.InnerException != null, "FAILED: Cannot check innerInnerExceptionMessage since InnerInnerException is null"); - Assert.True(ex.InnerException.InnerException.Message.Contains(innerInnerExceptionMessage), - string.Format("FAILED: Inner Exception did not contain expected message.\nExpected: {0}\nActual: {1}", innerInnerExceptionMessage, ex.InnerException.InnerException.Message)); + Assert.True(ex.InnerException.Message.Contains(innerExceptionMessage), + string.Format("FAILED: Inner Exception did not contain expected message.\nExpected: {0}\nActual: {1}", innerExceptionMessage, ex.InnerException.Message)); } return ex; } + #nullable restore + public static TException ExpectFailure(Action actionThatFails, string[] exceptionMessages, bool innerExceptionMustBeNull = false, Func customExceptionVerifier = null) where TException : Exception { try @@ -1320,7 +1347,7 @@ public static string GetMachineFQDN(string hostname) } return fqdn.ToString(); } - } - #nullable disable + #nullable restore + } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index ed9d9ab541..9bf5351a83 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -1318,13 +1318,13 @@ public void TestDeriveParameters() using (SqlCommand cmd = new SqlCommand(procName, connection)) { string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DeriveParametersNotSupported, "SqlCommand", cmd.CommandType); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => SqlCommandBuilder.DeriveParameters(cmd), errorMessage); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_OpenConnectionRequired, "DeriveParameters", ""); cmd.CommandType = CommandType.StoredProcedure; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => SqlCommandBuilder.DeriveParameters(cmd), errorMessage); @@ -1335,7 +1335,7 @@ public void TestDeriveParameters() cmd.CommandText = "Test_EmployeeSalesBy"; errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NoStoredProcedureExists, cmd.CommandText); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => SqlCommandBuilder.DeriveParameters(cmd), errorMessage); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index 1c6d851ecc..2de13c98ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -276,7 +276,7 @@ static async Task LocalCopyTo(Stream source, Stream destination, int bufferSize, { await destination.WriteAsync(new ReadOnlyMemory(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false); } -#endif +#endif } finally { @@ -320,7 +320,7 @@ private static byte[] CreateBinaryTable(SqlConnection connection, string tableNa { cmd.CommandText = $@" IF OBJECT_ID('dbo.{tableName}', 'U') IS NOT NULL -DROP TABLE {tableName}; +DROP TABLE {tableName}; CREATE TABLE {tableName} (id INT, foo VARBINARY(MAX)) "; cmd.ExecuteNonQuery(); @@ -377,7 +377,7 @@ private static void MultipleResults(string connectionString) { Assert.True(numBatches < expectedResults.Length, "ERROR: Received more batches than were expected."); object[] values = new object[r1.FieldCount]; - // Current "column" in expected row is (valuesChecked MOD FieldCount), since + // Current "column" in expected row is (valuesChecked MOD FieldCount), since // expected rows for current batch are appended together for easy formatting int valuesChecked = 0; while (r1.Read()) @@ -410,7 +410,7 @@ private static void InvalidRead(string connectionString) using (SqlDataReader reader = cmd.ExecuteReader()) { string errorMessage = SystemDataResourceManager.Instance.SQL_InvalidRead; - DataTestUtility.AssertThrowsWrapper(() => reader.GetInt32(0), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetInt32(0), errorMessage); } } } @@ -497,7 +497,7 @@ private static void TypeRead(string connectionString) s = rdr.GetString(10); //ShipCity; // should get an exception here string errorMessage = SystemDataResourceManager.Instance.SqlMisc_NullValueMessage; - DataTestUtility.AssertThrowsWrapper(() => rdr.GetString(11), errorMessage); + DataTestUtility.AssertThrows(() => rdr.GetString(11), errorMessage); s = rdr.GetString(12); //ShipPostalCode; s = rdr.GetString(13); //ShipCountry; @@ -533,7 +533,7 @@ private static void GetValueOfTRead(string connectionString) rdr.IsDBNull(10); rdr.GetFieldValue(10); //ShipCity; // should get an exception here - DataTestUtility.AssertThrowsWrapper(() => rdr.GetFieldValue(11), errorMessage); + DataTestUtility.AssertThrows(() => rdr.GetFieldValue(11), errorMessage); rdr.IsDBNull(11); rdr.GetFieldValue(11); rdr.IsDBNull(11); @@ -542,7 +542,7 @@ private static void GetValueOfTRead(string connectionString) rdr.IsDBNull(12); rdr.GetFieldValue(13);//ShipCountry; rdr.GetFieldValue(14); - DataTestUtility.AssertThrowsWrapper(() => rdr.GetFieldValue(15), errorMessage); + DataTestUtility.AssertThrows(() => rdr.GetFieldValue(15), errorMessage); rdr.Read(); // read data out of buffer @@ -559,7 +559,7 @@ private static void GetValueOfTRead(string connectionString) Assert.False(rdr.IsDBNullAsync(10).Result, "FAILED: IsDBNull was true for a non-null value"); rdr.GetFieldValueAsync(10).Wait(); //ShipCity; // should get an exception here - DataTestUtility.AssertThrowsWrapper(() => rdr.GetFieldValueAsync(11).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInner(() => rdr.GetFieldValueAsync(11).Wait(), innerExceptionMessage: errorMessage); Assert.True(rdr.IsDBNullAsync(11).Result, "FAILED: IsDBNull was false for a null value"); rdr.IsDBNullAsync(11).Wait(); @@ -787,7 +787,7 @@ private static void OrphanReader(string connectionString) conn.Close(); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "CheckDataIsReady"); - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); Assert.True(reader.IsClosed, "FAILED: Stream was not closed by connection close (Scenario: Read)"); conn.Open(); } @@ -798,7 +798,7 @@ private static void OrphanReader(string connectionString) value = reader[0]; conn.Close(); - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); Assert.True(reader.IsClosed, "FAILED: Stream was not closed by connection close (Scenario: Read Partial Data)"); conn.Open(); } @@ -817,7 +817,7 @@ private static void OrphanReader(string connectionString) } while (reader.NextResult()); conn.Close(); - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); Assert.True(reader.IsClosed, "FAILED: Stream was not closed by connection close (Scenario: Read All Data)"); } } @@ -866,7 +866,7 @@ private static void ExecuteXmlReaderTest(string connectionString) // make sure we get an exception if we try to get another reader errorMessage = SystemDataResourceManager.Instance.ADP_OpenReaderExists("Connection"); - DataTestUtility.AssertThrowsWrapper(() => xr = cmd.ExecuteXmlReader(), errorMessage); + DataTestUtility.AssertThrows(() => xr = cmd.ExecuteXmlReader(), errorMessage); } // use a big result to fill up the pipe and do a partial read @@ -943,12 +943,12 @@ private static void ExecuteXmlReaderTest(string connectionString) // multiple columns cmd.CommandText = "select * from customers"; errorMessage = SystemDataResourceManager.Instance.SQL_NonXmlResult; - DataTestUtility.AssertThrowsWrapper(() => xr = cmd.ExecuteXmlReader(), errorMessage); + DataTestUtility.AssertThrows(() => xr = cmd.ExecuteXmlReader(), errorMessage); // non-ntext column cmd.CommandText = "select employeeID from employees"; errorMessage = SystemDataResourceManager.Instance.SQL_NonXmlResult; - DataTestUtility.AssertThrowsWrapper(() => xr = cmd.ExecuteXmlReader(), errorMessage); + DataTestUtility.AssertThrows(() => xr = cmd.ExecuteXmlReader(), errorMessage); } } } @@ -1114,31 +1114,31 @@ private static void SequentialAccess(string connectionString) i = reader.GetOrdinal("notes"); reader.GetChars(i, 14, chars, 0, 14); string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, i, i + 1); - DataTestUtility.AssertThrowsWrapper(() => reader.GetString(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetString(i), errorMessage); // Tests GetValue before GetBytes\Chars reader.Read(); i = reader.GetOrdinal("photo"); reader.GetSqlBinary(i); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, i, i + 1); - DataTestUtility.AssertThrowsWrapper(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); i = reader.GetOrdinal("notes"); reader.GetString(i); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, i, i + 1); - DataTestUtility.AssertThrowsWrapper(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); // Tests GetBytes\GetChars re-reading same characters reader.Read(); i = reader.GetOrdinal("photo"); reader.GetBytes(i, 0, data, 0, 13); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSeqByteAccess, 0, 13, "GetBytes"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); i = reader.GetOrdinal("notes"); reader.GetChars(i, 0, chars, 0, 14); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSeqByteAccess, 0, 14, "GetChars"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); } using (reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) @@ -1150,12 +1150,12 @@ private static void SequentialAccess(string connectionString) int columnToTry = 0; string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, columnToTry, sqldata.Length); - DataTestUtility.AssertThrowsWrapper(() => reader.GetInt32(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrows(() => reader.GetInt32(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); reader.Read(); columnToTry = 17; @@ -1163,12 +1163,12 @@ private static void SequentialAccess(string connectionString) s = reader.GetString(columnToTry); DataTestUtility.AssertEqualsWithDescription("http://accweb/emmployees/fuller.bmp", s, "FAILED: Did not receive expected string."); - DataTestUtility.AssertThrowsWrapper(() => reader.GetInt32(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrows(() => reader.GetInt32(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); reader.Read(); // skip all columns up to photo, and read from it partially @@ -1189,14 +1189,14 @@ private static void SequentialAccess(string connectionString) // now try to read one more byte string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "GetBytes"); - DataTestUtility.AssertThrowsWrapper(() => cb = reader.GetBytes(i, 51, data, 0, 1), errorMessage); + DataTestUtility.AssertThrows(() => cb = reader.GetBytes(i, 51, data, 0, 1), errorMessage); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "CheckDataIsReady"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetValue(i), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(i), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetValue(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(i), errorMessage); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "GetFieldValueAsync"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInnerWithAlternate(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInnerWithAlternate(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); } } } @@ -1236,10 +1236,10 @@ private static void NumericRead(string connectionString) // Em object value; string errorMessage = SystemDataResourceManager.Instance.SqlMisc_ConversionOverflowMessage; - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); - DataTestUtility.AssertThrowsWrapper(() => value = reader[1], errorMessage); - DataTestUtility.AssertThrowsWrapper(() => value = reader.GetDecimal(0), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => value = reader.GetDecimal(1), errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[1], errorMessage); + DataTestUtility.AssertThrows(() => value = reader.GetDecimal(0), errorMessage); + DataTestUtility.AssertThrows(() => value = reader.GetDecimal(1), errorMessage); } } finally @@ -1297,7 +1297,7 @@ private static void HasRowsTest(string connectionString) bool result; string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "HasRows"); - DataTestUtility.AssertThrowsWrapper(() => result = reader.HasRows, errorMessage); + DataTestUtility.AssertThrows(() => result = reader.HasRows, errorMessage); } } } @@ -1382,7 +1382,7 @@ private static void SeqAccessFailureWrapper(Action action, CommandBe { if (behavior == CommandBehavior.SequentialAccess) { - DataTestUtility.AssertThrowsWrapper(action); + DataTestUtility.AssertThrows(action); } else { @@ -1390,6 +1390,24 @@ private static void SeqAccessFailureWrapper(Action action, CommandBe } } + /// + /// Waits for a task that may or may not throw due to a race condition, and if they do + /// throw, they may throw one of a few acceptable exceptions. + /// + /// See: https://github.com/dotnet/SqlClient/issues/4088 + /// + private static void WaitIgnoringFlakyException(Task task) + { + try { task.Wait(); } + catch (AggregateException) + { + // A faulted Task stores its exception permanently. Calling .Wait() again is + // guaranteed to re-throw the same AggregateException, so we can safely pass it to + // the assert helper for inner-exception validation. + DataTestUtility.AssertThrowsInnerWithAlternate(() => task.Wait()); + } + } + private static void GetStream(string connectionString) { using (SqlConnection connection = new SqlConnection(connectionString)) @@ -1410,7 +1428,7 @@ private static void GetStream(string connectionString) reader.GetStream(1); // Bad values - DataTestUtility.AssertThrowsWrapper(() => reader.GetStream(2)); + DataTestUtility.AssertThrows(() => reader.GetStream(2)); // Null stream Stream stream = reader.GetStream(3); Assert.False(stream.Read(buffer, 0, buffer.Length) > 0, "FAILED: Read more than 0 bytes from a null stream"); @@ -1446,12 +1464,12 @@ private static void GetStream(string connectionString) { t = reader.ReadAsync(); Assert.False(t.Wait(1), "FAILED: Read completed immediately"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetStream(8)); + DataTestUtility.AssertThrows(() => reader.GetStream(8)); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); - // GetStream after Read - DataTestUtility.AssertThrowsWrapper(() => reader.GetStream(0)); + // GetStream after Read + DataTestUtility.AssertThrows(() => reader.GetStream(0)); #endif } @@ -1486,8 +1504,7 @@ private static void GetStream(string connectionString) Assert.True(t.IsCompleted, "FAILED: Failed to get stream within 1 second"); t = reader.ReadAsync(); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); } #endif } @@ -1523,7 +1540,7 @@ private static void GetTextReader(string connectionString) reader.GetTextReader(1); // Bad values - DataTestUtility.AssertThrowsWrapper(() => reader.GetTextReader(2)); + DataTestUtility.AssertThrows(() => reader.GetTextReader(2)); // Null stream TextReader textReader = reader.GetTextReader(3); Assert.False(textReader.Read(buffer, 0, buffer.Length) > 0, "FAILED: Read more than 0 chars from a null TextReader"); @@ -1559,13 +1576,12 @@ private static void GetTextReader(string connectionString) { t = reader.ReadAsync(); Assert.False(t.IsCompleted, "FAILED: Read completed immediately"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetTextReader(8)); + DataTestUtility.AssertThrows(() => reader.GetTextReader(8)); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); - // GetTextReader after Read - DataTestUtility.AssertThrowsWrapper(() => reader.GetTextReader(0)); + // GetTextReader after Read + DataTestUtility.AssertThrows(() => reader.GetTextReader(0)); #endif } @@ -1601,8 +1617,7 @@ private static void GetTextReader(string connectionString) Assert.True(t.IsCompleted, "FAILED: Failed to get TextReader within 1 second"); t = reader.ReadAsync(); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); } #endif } @@ -1631,7 +1646,7 @@ private static void GetXmlReader(string connectionString) reader.GetXmlReader(1); // Bad values - DataTestUtility.AssertThrowsWrapper(() => reader.GetXmlReader(2)); + DataTestUtility.AssertThrows(() => reader.GetXmlReader(2)); // Null stream XmlReader xmlReader = reader.GetXmlReader(3); Assert.False(xmlReader.Read(), "FAILED: Successfully read on a null XmlReader"); @@ -1651,13 +1666,12 @@ private static void GetXmlReader(string connectionString) { t = reader.ReadAsync(); Assert.False(t.IsCompleted, "FAILED: Read completed immediately"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetXmlReader(6)); + DataTestUtility.AssertThrows(() => reader.GetXmlReader(6)); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); - // GetXmlReader after Read - DataTestUtility.AssertThrowsWrapper(() => reader.GetXmlReader(0)); + // GetXmlReader after Read + DataTestUtility.AssertThrows(() => reader.GetXmlReader(0)); #endif } } @@ -1691,16 +1705,16 @@ private static void ReadStream(string connectionString) // Testing stream properties stream.Flush(); - DataTestUtility.AssertThrowsWrapper(() => stream.SetLength(1)); + DataTestUtility.AssertThrows(() => stream.SetLength(1)); Action performOnStream = ((s) => { int i = s.WriteTimeout; }); - DataTestUtility.AssertThrowsWrapper(() => performOnStream(stream)); + DataTestUtility.AssertThrows(() => performOnStream(stream)); if (behavior == CommandBehavior.SequentialAccess) { - DataTestUtility.AssertThrowsWrapper(() => stream.Seek(0, SeekOrigin.Begin)); + DataTestUtility.AssertThrows(() => stream.Seek(0, SeekOrigin.Begin)); performOnStream = ((s) => { long i = s.Position; }); - DataTestUtility.AssertThrowsWrapper(() => performOnStream(stream)); + DataTestUtility.AssertThrows(() => performOnStream(stream)); performOnStream = ((s) => { long i = s.Length; }); - DataTestUtility.AssertThrowsWrapper(() => performOnStream(stream)); + DataTestUtility.AssertThrows(() => performOnStream(stream)); } else { @@ -1711,7 +1725,7 @@ private static void ReadStream(string connectionString) } // Once Stream is closed - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(buffer, 0, buffer.Length); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(buffer, 0, buffer.Length); }); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1723,9 +1737,9 @@ private static void ReadStream(string connectionString) _ = stream.Read(buffer, 0, buffer.Length); // Argument exceptions - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(null, 0, 1); }); - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(buffer, -1, 2); }); - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(buffer, 2, -1); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(null, 0, 1); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(buffer, -1, 2); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(buffer, 2, -1); }); // Prior to net6 comment:ArgumentException is thrown in net5 and earlier. ArgumentOutOfRangeException in net6 and later ArgumentException ex = Assert.ThrowsAny(() => { _ = stream.Read(buffer, buffer.Length, buffer.Length); }); @@ -1777,10 +1791,10 @@ private static void ReadStream(string connectionString) { // Read during async t = stream.ReadAsync(largeBuffer, 0, largeBuffer.Length); - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(largeBuffer, 0, largeBuffer.Length); }); - DataTestUtility.AssertThrowsWrapper(() => reader.Read()); + DataTestUtility.AssertThrows(() => { _ = stream.Read(largeBuffer, 0, largeBuffer.Length); }); + DataTestUtility.AssertThrows(() => reader.Read()); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInnerWithAlternate(() => t.Wait()); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) { @@ -1796,7 +1810,7 @@ private static void ReadStream(string connectionString) // Guarantee that timeout occurs: Thread.Sleep(stream.ReadTimeout * 4); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInnerWithAlternate(() => t.Wait()); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1811,7 +1825,10 @@ private static void ReadStream(string connectionString) t = stream.ReadAsync(largeBuffer, 0, largeBuffer.Length, tokenSource.Token); tokenSource.Cancel(); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + // Normally the cancellation wins (TaskCanceledException), but if the + // PendAsyncReadsScope disposal completes the read first, the inner + // exception may be InvalidOperationException instead (GH-4088). + DataTestUtility.AssertThrowsInnerWithAlternate(() => t.Wait()); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1824,7 +1841,18 @@ private static void ReadStream(string connectionString) // Error during read t = stream.ReadAsync(largeBuffer, 0, largeBuffer.Length); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + // PendAsyncReadsScope(errorCode: 11) injects a network error, normally producing + // AggregateException -> IOException -> SqlException. In rare race conditions + // the inner exception may be ObjectDisposedException instead (GH-4088). + AggregateException aex = Assert.Throws(() => t.Wait()); + if (aex.InnerException is IOException ioEx) + { + Assert.IsAssignableFrom(ioEx.InnerException); + } + else + { + Assert.IsAssignableFrom(aex.InnerException); + } } #endif } @@ -1876,7 +1904,7 @@ private static void ReadTextReader(string connectionString) } // Once Reader is closed - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, 0, buffer.Length)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, 0, buffer.Length)); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1890,11 +1918,11 @@ private static void ReadTextReader(string connectionString) textReader.Peek(); // Argument exceptions - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(null, 0, 1)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, -1, 2)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, 2, -1)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, buffer.Length, buffer.Length)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, int.MaxValue, int.MaxValue)); + DataTestUtility.AssertThrows(() => textReader.Read(null, 0, 1)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, -1, 2)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, 2, -1)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, buffer.Length, buffer.Length)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, int.MaxValue, int.MaxValue)); } // Once Reader is closed @@ -1936,11 +1964,10 @@ private static void ReadTextReader(string connectionString) { // Read during async t = textReader.ReadAsync(largeBuffer, 0, largeBuffer.Length); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(largeBuffer, 0, largeBuffer.Length)); - DataTestUtility.AssertThrowsWrapper(() => reader.Read()); + DataTestUtility.AssertThrows(() => textReader.Read(largeBuffer, 0, largeBuffer.Length)); + DataTestUtility.AssertThrows(() => reader.Read()); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1953,7 +1980,18 @@ private static void ReadTextReader(string connectionString) // Error during read t = textReader.ReadAsync(largeBuffer, 0, largeBuffer.Length); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + // PendAsyncReadsScope(errorCode: 11) injects a network error, normally producing + // AggregateException -> IOException -> SqlException. In rare race conditions + // the inner exception may be ObjectDisposedException instead (GH-4088). + AggregateException aex = Assert.Throws(() => t.Wait()); + if (aex.InnerException is IOException ioEx) + { + Assert.IsAssignableFrom(ioEx.InnerException); + } + else + { + Assert.IsAssignableFrom(aex.InnerException); + } } #endif } @@ -2118,7 +2156,7 @@ private void TestXEventsStreaming(string connectionString) byte[] bytes = new byte[cb]; long read = reader.GetBytes(1, 0, bytes, 0, cb); - // Don't send data on the first read because there is already data in the buffer. + // Don't send data on the first read because there is already data in the buffer. // Don't send data on the last iteration. We will not be reading that data. if (i == 0 || i == streamXeventCount - 1) { @@ -2164,7 +2202,7 @@ private static void TimeoutDuringReadAsyncWithClosedReaderTest(string connection // Wait for the task to see the timeout string errorMessage = SystemDataResourceManager.Instance.SQL_Timeout_Execution; - DataTestUtility.AssertThrowsWrapper(() => task.Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInnerWithAlternate(() => task.Wait(), innerExceptionMessage: errorMessage); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs index ad84ac7881..c3aed471c0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -54,7 +54,7 @@ public static void LocalDBMarsTest() public static void InvalidLocalDBTest() { using var connection = new SqlConnection(s_badConnectionString); - DataTestUtility.AssertThrowsWrapper(() => connection.Open()); + DataTestUtility.AssertThrows(() => connection.Open()); } #endregion diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs index 69265c4e75..1213c5bfa4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs @@ -591,7 +591,7 @@ public static void MARSMultiDataReaderErrTest() { using (SqlDataReader reader1 = command.ExecuteReader()) { - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { SqlDataReader reader2 = command.ExecuteReader(); }, openReaderExistsMessage); @@ -611,7 +611,7 @@ public static void MARSMultiDataReaderErrTest() { using (SqlDataReader reader1 = command1.ExecuteReader()) { - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { SqlDataReader reader2 = command2.ExecuteReader(); }, openReaderExistsMessage); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs index efc99ea757..73fbd8bdc6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs @@ -20,7 +20,7 @@ public static void BasicParallelTest_shouldThrowsUnsupported() try { tempTableName = CreateTempTable(connectionString); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( actionThatFails: () => { BasicParallelTest(connectionString, tempTableName); }, exceptionMessage: expectedErrorMessage); } @@ -77,7 +77,7 @@ public static void MultipleExecutesInSameTransactionTest_shouldThrowsUnsupported try { tempTableName = CreateTempTable(connectionString); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( actionThatFails: () => { MultipleExecutesInSameTransactionTest(connectionString, tempTableName); }, exceptionMessage: expectedErrorMessage); } @@ -157,5 +157,3 @@ private static void DropTempTable(string connectionString, string tempTableName) } } } - - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index a4cd63b0c1..d53209e912 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -42,14 +42,14 @@ public static void CodeCoverageSqlClient() { string failValue; - DataTestUtility.AssertThrowsWrapper(() => failValue = opc[0].ParameterName, "Invalid index 0 for this SqlParameterCollection with Count=0."); + DataTestUtility.AssertThrows(() => failValue = opc[0].ParameterName, "Invalid index 0 for this SqlParameterCollection with Count=0."); - DataTestUtility.AssertThrowsWrapper(() => failValue = opc["@p1"].ParameterName, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); + DataTestUtility.AssertThrows(() => failValue = opc["@p1"].ParameterName, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); - DataTestUtility.AssertThrowsWrapper(() => opc["@p1"] = null, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); + DataTestUtility.AssertThrows(() => opc["@p1"] = null, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); } - DataTestUtility.AssertThrowsWrapper(() => opc.Add(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); + DataTestUtility.AssertThrows(() => opc.Add(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); opc.Add((object)new SqlParameter()); IEnumerator enm = opc.GetEnumerator(); @@ -70,11 +70,11 @@ public static void CodeCoverageSqlClient() SqlParameter p = opc[0]; - DataTestUtility.AssertThrowsWrapper(() => opc.Add((object)p), "The SqlParameter is already contained by another SqlParameterCollection."); + DataTestUtility.AssertThrows(() => opc.Add((object)p), "The SqlParameter is already contained by another SqlParameterCollection."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Add(p), "The SqlParameter is already contained by another SqlParameterCollection."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Add(p), "The SqlParameter is already contained by another SqlParameterCollection."); - DataTestUtility.AssertThrowsWrapper(() => opc.Remove(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); + DataTestUtility.AssertThrows(() => opc.Remove(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); string pname = p.ParameterName; p.ParameterName = pname; @@ -106,13 +106,13 @@ public static void CodeCoverageSqlClient() new SqlCommand().Parameters.CopyTo(new object[0], 0); Assert.False(new SqlCommand().Parameters.GetEnumerator().MoveNext(), "FAILED: Expected MoveNext to be false"); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Add(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Add(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Insert(0, 0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Insert(0, 0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Remove(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(new SqlParameter()), "Attempted to remove an SqlParameter that is not contained by this SqlParameterCollection."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Remove(new SqlParameter()), "Attempted to remove an SqlParameter that is not contained by this SqlParameterCollection."); } // TODO Synapse: Parse error at line: 1, column: 12: Incorrect syntax near 'IF'. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs index 3ae79dbe31..3fb3d051a1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs @@ -19,7 +19,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) cts = new CancellationTokenSource(); cts.Cancel(); Task t = TestAsync(srcConstr, dstConstr, dstTable, cts.Token); - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInner(() => t.Wait()); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs index fd6062659a..ede16437d3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs @@ -14,7 +14,7 @@ public class CopyAllFromReaderConnectionClosedAsync public static void Test(string srcConstr, string dstConstr, string dstTable) { Task t = TestAsync(srcConstr, dstConstr, dstTable); - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInner(() => t.Wait()); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs index 1afd2d93d0..3d832925a6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs @@ -4,6 +4,7 @@ using System; using System.Data.Common; +using System.IO; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { @@ -49,7 +50,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) // Check that the copying fails string message = string.Format(SystemDataResourceManager.Instance.ADP_OpenConnectionRequired, "WriteToServer", SystemDataResourceManager.Instance.ADP_ConnectionStateMsg_Closed); - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServerAsync(reader).Wait(5000), innerExceptionMessage: message); + DataTestUtility.AssertThrowsInnerWithAlternate(() => bulkcopy.WriteToServerAsync(reader).Wait(5000), innerExceptionMessage: message); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs index e89f2f83a3..8bc72435d0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs @@ -66,7 +66,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) ColumnMappings.Add("ShipName", "shipname"); bulkcopy.NotifyAfter = 3; - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader)); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader)); bulkcopy.SqlRowsCopied -= new SqlRowsCopiedEventHandler(OnRowCopied); bulkcopy.Close(); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs index d8fe8f66da..e4df366137 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs @@ -38,7 +38,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) string errorMsg = SystemDataResourceManager.Instance.SQL_BulkLoadNonMatchingColumnName; errorMsg = string.Format(errorMsg, "col2"); - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs index 2696770f69..5fb47f44c1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs @@ -38,7 +38,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) string errorMsg = SystemDataResourceManager.Instance.SQL_BulkLoadNonMatchingColumnName; errorMsg = string.Format(errorMsg, "col3,col4"); - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs index 9e535ccae1..ebed5e9918 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs @@ -45,7 +45,7 @@ public static void Test(string connStr, string dstTable) string expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintDuplicateColumn, destColumn); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => bulkcopy.ColumnOrderHints.Add(destColumn, SortOrder.Ascending), exceptionMessage: expectedErrorMsg); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs index 46a7f39eee..cc486e9e4e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs @@ -44,7 +44,7 @@ public static void Test(string connStr, string dstTable) string expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintInvalidColumn, nonexistentColumn); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => bulkcopy.WriteToServer(reader), exceptionMessage: expectedErrorMsg); @@ -56,7 +56,7 @@ public static void Test(string connStr, string dstTable) expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintInvalidColumn, sourceColumn); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => bulkcopy.WriteToServer(reader), exceptionMessage: expectedErrorMsg); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs index e7632044b0..0738f32010 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs @@ -34,7 +34,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) SqlTransaction myTrans = dstConn.BeginTransaction(); try { - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader)); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader)); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs index 1f6ba5e823..930e1ac6f5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs @@ -37,7 +37,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) try { - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader)); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader)); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs index 8e53989ed5..4b65f2a5d2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs @@ -37,13 +37,13 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) bulkcopy.DestinationTableName = dstTable; string exceptionMsg = SystemDataResourceManager.Instance.ADP_TransactionConnectionMismatch; - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader), exceptionMessage: exceptionMsg); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader), exceptionMessage: exceptionMsg); SqlCommand myCmd = dstConn.CreateCommand(); myCmd.CommandText = "select * from " + dstTable; myCmd.Transaction = myTrans; - DataTestUtility.AssertThrowsWrapper(() => myCmd.ExecuteReader(), exceptionMessage: exceptionMsg); + DataTestUtility.AssertThrows(() => myCmd.ExecuteReader(), exceptionMessage: exceptionMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs index c8028ca5c5..40b2d2c251 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs @@ -31,7 +31,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) // Start a local transaction on the wrong connection. SqlTransaction myTrans = conn3.BeginTransaction(); string errorMsg = SystemDataResourceManager.Instance.SQL_BulkLoadConflictingTransactionOption; - DataTestUtility.AssertThrowsWrapper(() => new SqlBulkCopy(dstConn, SqlBulkCopyOptions.UseInternalTransaction, myTrans), exceptionMessage: errorMsg); + DataTestUtility.AssertThrows(() => new SqlBulkCopy(dstConn, SqlBulkCopyOptions.UseInternalTransaction, myTrans), exceptionMessage: errorMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs index ac28a9d883..729dac74a9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs @@ -14,7 +14,7 @@ public class TransactionTestAsync public static void Test(string srcConstr, string dstConstr, string dstTable) { Task t = TestAsync(srcConstr, dstConstr, dstTable); - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInner(() => t.Wait()); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs index 97c225c9ca..552fd4cea5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs @@ -65,7 +65,7 @@ private static void PlainCancel(string connString) using (SqlDataReader reader = cmd.ExecuteReader()) { cmd.Cancel(); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => { do @@ -88,7 +88,7 @@ private static void PlainCancelAsync(string connString) { conn.Open(); Task readerTask = cmd.ExecuteReaderAsync(); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => { readerTask.Wait(2000); @@ -214,7 +214,7 @@ private static void CancelFollowedByTransaction(string constr) private static void CancelFollowedByAlert(string constr) { var alertName = "myAlert" + Guid.NewGuid().ToString(); - // Since Alert conditions are randomly generated, + // Since Alert conditions are randomly generated, // we will retry on unexpected error messages to avoid collision in pipelines. var n = new Random().Next(1, 100); bool retry = true; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs index 98fa8997de..e4bd1133ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs @@ -71,7 +71,7 @@ public void Test_DoubleStart_DifferentConnStr() try { - DataTestUtility.AssertThrowsWrapper(() => SqlDependency.Start(cb.ToString())); + DataTestUtility.AssertThrows(() => SqlDependency.Start(cb.ToString())); } finally { @@ -116,7 +116,7 @@ public void Test_SingleDependency_NoStart() Console.WriteLine("4 Notification callback. Type={0}, Info={1}, Source={2}", args.Type, args.Info, args.Source); }; - DataTestUtility.AssertThrowsWrapper(() => cmd.ExecuteReader()); + DataTestUtility.AssertThrows(() => cmd.ExecuteReader()); } } @@ -138,7 +138,7 @@ public void Test_SingleDependency_Stopped() Console.WriteLine("5 Notification callback. Type={0}, Info={1}, Source={2}", args.Type, args.Info, args.Source); }; - DataTestUtility.AssertThrowsWrapper(() => cmd.ExecuteReader()); + DataTestUtility.AssertThrows(() => cmd.ExecuteReader()); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs index 1a97d6df31..0adff2c8ae 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs @@ -339,13 +339,13 @@ private void ExceptionTest() string executeCommandWithoutTransactionMessage = SystemDataResourceManager.Instance.ADP_TransactionRequired("ExecuteNonQuery"); string transactionConflictErrorMessage = SystemDataResourceManager.Instance.ADP_TransactionConnectionMismatch; string parallelTransactionErrorMessage = SystemDataResourceManager.Instance.ADP_ParallelTransactionsNotSupported("SqlConnection"); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { SqlCommand command = new SqlCommand("sql", connection); command.ExecuteNonQuery(); }, executeCommandWithoutTransactionMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { using (SqlConnection con1 = new SqlConnection(_connectionString)) { @@ -357,32 +357,32 @@ private void ExceptionTest() } }, transactionConflictErrorMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { connection.BeginTransaction(null); }, parallelTransactionErrorMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { connection.BeginTransaction(""); }, parallelTransactionErrorMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Rollback(null); }, invalidSaveStateMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Rollback(""); }, invalidSaveStateMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Save(null); }, invalidSaveStateMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Save(""); }, invalidSaveStateMessage); @@ -456,7 +456,7 @@ private void ReadCommitedIsolationLevel_ShouldReceiveTimeoutExceptionBecauseItWa SqlTransaction tx2 = connection2.BeginTransaction(IsolationLevel.ReadCommitted); command2.Transaction = tx2; - DataTestUtility.AssertThrowsWrapper(() => command2.ExecuteReader(), SystemDataResourceManager.Instance.SQL_Timeout_Execution as string); + DataTestUtility.AssertThrows(() => command2.ExecuteReader(), SystemDataResourceManager.Instance.SQL_Timeout_Execution as string); tx2.Rollback(); connection2.Close(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs index 79f38262cc..1dae314730 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -75,7 +75,7 @@ public void UDTParams_Binary() value[7] = 0; p.Value = new SqlBinary(value); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteReader(), "Error converting data type varbinary to Point."); } @@ -110,7 +110,7 @@ public void UDTParams_Invalid2() p.Value = addr; pName.Value = addr; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteReader(), "Failed to convert parameter value from a Address to a String."); } @@ -134,7 +134,7 @@ public void UDTParams_Invalid() p.UdtTypeName = "UdtTestDb.dbo.Point"; p.Value = 32; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteReader(), "Specified type is not registered on the target server. System.Int32"); } @@ -221,7 +221,7 @@ public void UDTParams_NullInput() string errorMsg = "Procedure or function '" + spInsertCustomerNoBrackets + "' expects parameter '@addr', which was not supplied."; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteNonQuery(), errorMsg); } @@ -317,7 +317,7 @@ public void UDTFields_WrongType() reader.Read(); // retrieve the UDT as a string - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => reader.GetString(1), "Unable to cast object of type 'System.Byte[]' to type 'System.String'."); } @@ -584,12 +584,12 @@ Func create string udtError = SystemDataResourceManager.Instance.SQLUDT_MaxByteSizeValue; string errorMessage = (new ArgumentOutOfRangeException("MaxByteSize", 8001, udtError)).Message; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => create(SqlUserDefinedAggregateAttribute.MaxByteSizeValue + 1), errorMessage); errorMessage = (new ArgumentOutOfRangeException("MaxByteSize", -2, udtError)).Message; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => create(-2), errorMessage); } @@ -702,4 +702,3 @@ public void UDTParams_DeriveParameters_CheckAutoFixOverride() } } } - From 4e1d0d49af40d00a151150a751db0fc626b6d5c0 Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Wed, 1 Apr 2026 11:20:48 -0700 Subject: [PATCH 20/22] =?UTF-8?q?Revert=20"Trim=20docs=20using=20pwsh=20vi?= =?UTF-8?q?a=20dotnet=20tool=20to=20support=20cross-platform=20local=20?= =?UTF-8?q?=E2=80=A6"=20(#4127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 75b33ad53c18d53fe8bcfbb8287f5b3ce78ee11b. --- dotnet-tools.json | 7 ------- .../ref/Microsoft.Data.SqlClient.csproj | 9 +++------ tools/targets/TrimDocsForIntelliSense.targets | 4 ++-- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/dotnet-tools.json b/dotnet-tools.json index 11e3494512..1f59e06063 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -15,13 +15,6 @@ "apicompat" ], "rollForward": false - }, - "powershell": { - "version": "7.6.0", - "commands": [ - "pwsh" - ], - "rollForward": false } } } \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj index 252e8ba0b0..dfad9e601c 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj @@ -36,10 +36,6 @@ $(ArtifactPath)$(AssemblyName).ref/$(ReferenceType)-$(Configuration)/ - - - - + powershell.exe + pwsh - dotnet tool run pwsh + $(PowerShellCommand) -NonInteractive -ExecutionPolicy Unrestricted -Command "$(RepoRoot)tools\intellisense\TrimDocs.ps1 -inputFile '$(DocumentationFile)' -outputFile '$(DocumentationFile)'" diff --git a/tools/targets/TrimDocsForIntelliSense.targets b/tools/targets/TrimDocsForIntelliSense.targets index fd0ca40ea8..05feb26739 100644 --- a/tools/targets/TrimDocsForIntelliSense.targets +++ b/tools/targets/TrimDocsForIntelliSense.targets @@ -3,7 +3,7 @@ The .NET Foundation licenses this file to you under the MIT license. --> - - + + From 90463215f3d9c69993d547d02287ba0b55aa9802 Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Thu, 2 Apr 2026 15:39:24 -0700 Subject: [PATCH 21/22] Test | Connection pool transaction tests (#3805) --- .../prompts/generate-doc-comments.prompt.md | 2 +- .github/prompts/generate-prompt.prompt.md | 63 +- .github/prompts/refine-test-overlap.prompt.md | 12 +- .../ConnectionPool/ChannelDbConnectionPool.cs | 4 + .../ConnectionPool/DbConnectionPoolOptions.cs | 3 + .../ConnectionPool/IDbConnectionPool.cs | 7 +- .../TransactedConnectionPool.cs | 31 +- .../WaitHandleDbConnectionPool.cs | 8 +- .../SqlClientTestGroup.cs | 1 + .../TransactedConnectionPoolTest.cs | 5 +- ...itHandleDbConnectionPoolTransactionTest.cs | 951 ++++++++++++++++++ 11 files changed, 1059 insertions(+), 28 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/WaitHandleDbConnectionPoolTransactionTest.cs diff --git a/.github/prompts/generate-doc-comments.prompt.md b/.github/prompts/generate-doc-comments.prompt.md index 33ced936de..9361d3c7ad 100644 --- a/.github/prompts/generate-doc-comments.prompt.md +++ b/.github/prompts/generate-doc-comments.prompt.md @@ -1,5 +1,5 @@ --- -name: doc-comments +name: generate-doc-comments description: Generate XML documentation comments for C# code following .NET best practices. argument-hint: agent: agent diff --git a/.github/prompts/generate-prompt.prompt.md b/.github/prompts/generate-prompt.prompt.md index a4aa8cd40f..73d083947b 100644 --- a/.github/prompts/generate-prompt.prompt.md +++ b/.github/prompts/generate-prompt.prompt.md @@ -34,6 +34,7 @@ Before generating the prompt, review the available skills in the `.github/skills * `name`: A concise, kebab-case name for the prompt. * `description`: A clear, short description of what the prompt does. * `argument-hint`: (Optional) A hint for what arguments the user can provide when using the prompt. + * `tools`: (Recommended) A list of tool identifiers the prompt is allowed to use. See the **Tool Scoping** section below. * **Body Structure**: * **Role**: Define the AI's persona (e.g., "You are an expert C# developer..."). * **Context**: Include specific context instructions or references. @@ -50,12 +51,70 @@ Before generating the prompt, review the available skills in the `.github/skills * Use `${input:variableName}` for user inputs (e.g., `${input:methodName}`). * Use built-in variables like `${selection}`, `${file}`, or `${workspaceFolder}` where appropriate context is needed. -6. **Best Practices**: +6. **Scope Tools**: Restrict the tools available to each prompt using the `tools` frontmatter field. See the **Tool Scoping** section below for detailed guidance. + +7. **Best Practices**: * Be specific and explicit. * Encourage chain-of-thought reasoning if the task is complex. * Reference workspace files using Markdown links `[path/to/file.cs](path/to/file.cs)` only if they are static and necessary for *all* invocations of this prompt. * Prefer referencing skills over duplicating instructions that already exist in skills. +## Tool Scoping + +Every generated prompt **should** include a `tools` list in its YAML frontmatter. Scoping tools keeps the model focused by limiting it to approved, known-effective tools for the task. Without tool scoping, the model may invoke irrelevant tools, waste context, or produce unpredictable results. + +### Why scope tools? +- **Focus**: Fewer tools means the model spends less reasoning on tool selection and more on the task. +- **Reliability**: Restricting to tested tools avoids unexpected side effects (e.g., a read-only review prompt shouldn't have edit tools). +- **Safety**: Prevents prompts from accidentally running terminal commands or making file changes when they shouldn't. + +### How to choose tools +Apply the **principle of least privilege** — include only the tools the prompt actually needs: + +| Prompt type | Recommended tools | +|---|---| +| **Read-only analysis** (review, triage, explain) | `read/readFile`, `search/codebase`, `search/textSearch` | +| **Code editing** (bug fix, feature, refactor) | `edit/editFiles`, `edit/createFile`, `read/readFile`, `search/codebase` | +| **Needs terminal** (build, test, scripts) | All of the above + `execute/runInTerminal`, `execute/getTerminalOutput` | +| **Needs GitHub data** (triage, release notes) | All of the above + `github/search_issues` or other GitHub tools | +| **Needs web content** (docs lookup) | `web/fetch` | + +### Available built-in tool identifiers + +You can specify individual tools or tool sets (which include all tools in that group). + +**Tool sets** (use these to include all tools in a category): +- `edit` — File creation and editing tools +- `read` — File and notebook reading tools +- `search` — Codebase, text, and file search tools +- `execute` — Terminal, task, and notebook execution tools +- `web` — Web content fetching tools + +**Commonly used individual tools:** + +| Tool identifier | Purpose | +|---|---| +| `edit/editFiles` | Apply edits to existing files | +| `edit/createFile` | Create a new file | +| `read/readFile` | Read file contents | +| `read/problems` | Get workspace problems/diagnostics | +| `search/codebase` | Semantic code search | +| `search/textSearch` | Text/regex search in files | +| `search/fileSearch` | Search for files by glob pattern | +| `search/listDirectory` | List directory contents | +| `search/usages` | Find references and implementations | +| `execute/runInTerminal` | Run a shell command | +| `execute/getTerminalOutput` | Get terminal output | +| `execute/testFailure` | Get test failure details | +| `web/fetch` | Fetch a web page | + +**Extension / MCP tools** can also be included using their identifier (e.g., `github/search_issues`). Use `/*` to include all tools from an MCP server. + +### Frontmatter syntax +```yaml +tools: ['read/readFile', 'search/codebase', 'edit/editFiles'] +``` + ## Example Output Structure (with skill reference) ```markdown @@ -63,6 +122,7 @@ Before generating the prompt, review the available skills in the `.github/skills name: my-new-prompt description: specialized task description argument-hint: input parameter hint +tools: ['edit/editFiles', 'read/readFile', 'search/codebase', 'execute/runInTerminal'] --- You are a specialized agent for... @@ -89,6 +149,7 @@ Use ${input:param1} to... name: my-new-prompt description: specialized task description argument-hint: input parameter hint +tools: ['read/readFile', 'search/codebase'] --- You are a specialized agent for... diff --git a/.github/prompts/refine-test-overlap.prompt.md b/.github/prompts/refine-test-overlap.prompt.md index 824e18f145..f4d0339c27 100644 --- a/.github/prompts/refine-test-overlap.prompt.md +++ b/.github/prompts/refine-test-overlap.prompt.md @@ -1,7 +1,9 @@ --- -name: test-minimize-overlap +name: refine-test-overlap description: Run coverage overlap analysis and suggest test suite optimizations argument-hint: Test filter (e.g. FullyQualifiedName~MyTests) or describe the tests you want to analyze +agent: agent +tools: ['edit/editFiles', 'read/readFile', 'search/codebase', 'execute/runInTerminal', 'execute/getTerminalOutput'] --- You are an expert .NET Test Engineer specialized in optimizing test coverage and reducing technical debt. @@ -10,19 +12,19 @@ Your task is to analyze the user's test suite using the `AnalyzeTestOverlap.ps1` ## Skills This prompt leverages the following skills for specific sub-tasks: -- [generate-mstest-filter](../skills/generate-mstest-filter/SKILL.md) - For generating well-formed MSTest filter expressions +- [generate-mstest-filter](.github/skills/generate-mstest-filter/SKILL.md) - For generating well-formed MSTest filter expressions ## Tools -You have access to the analysis script at `[AnalyzeTestOverlap.ps1](./scripts/AnalyzeTestOverlap.ps1)`. +You have access to the analysis script at [AnalyzeTestOverlap.ps1](.github/prompts/scripts/AnalyzeTestOverlap.ps1). ## Workflow 1. **Parse or Generate Test Filter**: * If `${input:filter}` is a valid MSTest filter expression (e.g., `FullyQualifiedName~MyTests`), use it directly. - * If `${input:filter}` is a loose description (e.g., "connection tests" or "SqlCommand class"), follow the instructions in the [generate-mstest-filter](../skills/generate-mstest-filter/SKILL.md) skill to generate a proper filter expression. + * If `${input:filter}` is a loose description (e.g., "connection tests" or "SqlCommand class"), follow the instructions in the [generate-mstest-filter](.github/skills/generate-mstest-filter/SKILL.md) skill to generate a proper filter expression. * If `${input:filter}` is empty, ask the user for a test filter or description to target specific tests. 2. **Run Analysis**: - * Run the script using the filter: `.\scripts\AnalyzeTestOverlap.ps1 -Filter ""`. + * Run the script from the workspace root: `.\.github\prompts\scripts\AnalyzeTestOverlap.ps1 -Filter ""`. * *Note*: The script produces a console summary and a `test-coverage-analysis.json` file. 3. **Review Overlap**: diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs index 63815e8200..553e6cd395 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/ChannelDbConnectionPool.cs @@ -96,6 +96,7 @@ internal ChannelDbConnectionPool( Identity = identity; AuthenticationContexts = new(); MaxPoolSize = Convert.ToUInt32(PoolGroupOptions.MaxPoolSize); + TransactedConnectionPool = new(this); _connectionSlots = new(MaxPoolSize); @@ -148,6 +149,9 @@ public ConcurrentDictionary< /// public DbConnectionPoolState State { get; private set; } + /// + public TransactedConnectionPool TransactedConnectionPool { get; } + /// public bool UseLoadBalancing => PoolGroupOptions.UseLoadBalancing; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolOptions.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolOptions.cs index 7adf8abdc2..5ac6f4d565 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolOptions.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolOptions.cs @@ -39,6 +39,9 @@ bool hasTransactionAffinity _hasTransactionAffinity = hasTransactionAffinity; } + /// + /// The time (in milliseconds) to wait for a connection to be created/returned before terminating the attempt. + /// public int CreationTimeout { get { return _creationTimeout; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs index b684bb24bb..bfc5789d3f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/IDbConnectionPool.cs @@ -85,6 +85,11 @@ internal interface IDbConnectionPool /// DbConnectionPoolState State { get; } + /// + /// Holds connections that are currently enlisted in a transaction. + /// + TransactedConnectionPool TransactedConnectionPool { get; } + /// /// Indicates whether the connection pool is using load balancing. /// @@ -106,7 +111,7 @@ internal interface IDbConnectionPool /// The user options to use if a new connection must be opened. /// The retrieved connection will be passed out via this parameter. /// True if a connection was set in the out parameter, otherwise returns false. - bool TryGetConnection(DbConnection owningObject, TaskCompletionSource taskCompletionSource, DbConnectionOptions userOptions, out DbConnectionInternal? connection); + bool TryGetConnection(DbConnection owningObject, TaskCompletionSource? taskCompletionSource, DbConnectionOptions userOptions, out DbConnectionInternal? connection); /// /// Replaces the internal connection currently associated with owningObject with a new internal connection from the pool. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/TransactedConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/TransactedConnectionPool.cs index 7245164f2c..3ce203c071 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/TransactedConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/TransactedConnectionPool.cs @@ -31,7 +31,7 @@ internal class TransactedConnectionPool /// A specialized list that holds database connections associated with a specific transaction. /// Maintains a reference to the transaction for proper cleanup when the transaction completes. /// - private sealed class TransactedConnectionList : List + internal sealed class TransactedConnectionList : List { private readonly Transaction _transaction; @@ -60,9 +60,6 @@ internal void Dispose() } #region Fields - - private readonly Dictionary _transactedCxns; - private static int _objectTypeCount; internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); @@ -80,7 +77,7 @@ internal void Dispose() internal TransactedConnectionPool(IDbConnectionPool pool) { Pool = pool; - _transactedCxns = new Dictionary(); + TransactedConnections = new Dictionary(); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Constructed for connection pool {1}", Id, Pool.Id); } @@ -98,6 +95,8 @@ internal TransactedConnectionPool(IDbConnectionPool pool) /// The IDbConnectionPool instance that owns this transacted pool. internal IDbConnectionPool Pool { get; } + internal Dictionary TransactedConnections { get; } + #endregion #region Methods @@ -123,9 +122,9 @@ internal TransactedConnectionPool(IDbConnectionPool pool) TransactedConnectionList? connections; bool txnFound = false; - lock (_transactedCxns) + lock (TransactedConnections) { - txnFound = _transactedCxns.TryGetValue(transaction, out connections); + txnFound = TransactedConnections.TryGetValue(transaction, out connections); } // NOTE: GetTransactedObject is only used when AutoEnlist = True and the ambient transaction @@ -182,10 +181,10 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal // NOTE: because TransactionEnded is an asynchronous notification, there's no guarantee // around the order in which PutTransactionObject and TransactionEnded are called. - lock (_transactedCxns) + lock (TransactedConnections) { // Check if a transacted pool has been created for this transaction - if ((txnFound = _transactedCxns.TryGetValue(transaction, out connections)) + if ((txnFound = TransactedConnections.TryGetValue(transaction, out connections)) && connections is not null) { // synchronize multi-threaded access with GetTransactedObject @@ -213,14 +212,14 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal transactionClone = transaction.Clone(); newConnections = new TransactedConnectionList(2, transactionClone); // start with only two connections in the list; most times we won't need that many. - lock (_transactedCxns) + lock (TransactedConnections) { // NOTE: in the interim between the locks on the transacted pool (this) during // execution of this method, another thread (threadB) may have attempted to // add a different connection to the transacted pool under the same // transaction. As a result, threadB may have completed creating the // transacted pool while threadA was processing the above instructions. - if (_transactedCxns.TryGetValue(transaction, out connections) + if (TransactedConnections.TryGetValue(transaction, out connections) && connections is not null) { // synchronize multi-threaded access with GetTransactedObject @@ -238,7 +237,7 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal // add the connection/transacted object to the list newConnections.Add(transactedObject); - _transactedCxns.Add(transactionClone, newConnections); + TransactedConnections.Add(transactionClone, newConnections); transactionClone = null; // we've used it -- don't throw it or the TransactedConnectionList that references it away. } } @@ -297,9 +296,9 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra // TODO: that the pending creation of a transacted pool for this transaction is aborted when // TODO: PutTransactedObject finally gets some CPU time? - lock (_transactedCxns) + lock (TransactedConnections) { - if (_transactedCxns.TryGetValue(transaction, out connections) + if (TransactedConnections.TryGetValue(transaction, out connections) && connections is not null) { bool shouldDisposeConnections = false; @@ -319,7 +318,7 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra if (0 >= connections.Count) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Removing List from transacted pool.", Id, transaction.GetHashCode()); - _transactedCxns.Remove(transaction); + TransactedConnections.Remove(transaction); // we really need to dispose our connection list; it may have // native resources via the tx and GC may not happen soon enough. @@ -351,4 +350,4 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra } #endregion -} \ No newline at end of file +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs index 194054e58e..8d2311b5cd 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/WaitHandleDbConnectionPool.cs @@ -305,9 +305,9 @@ public bool IsRunning get { return State is Running; } } - private int MaxPoolSize => PoolGroupOptions.MaxPoolSize; + internal int MaxPoolSize => PoolGroupOptions.MaxPoolSize; - private int MinPoolSize => PoolGroupOptions.MinPoolSize; + internal int MinPoolSize => PoolGroupOptions.MinPoolSize; public DbConnectionPoolGroup PoolGroup => _connectionPoolGroup; @@ -324,6 +324,8 @@ public bool IsRunning private bool UsingIntegrateSecurity => _identity != null && DbConnectionPoolIdentity.NoIdentity != _identity; + public TransactedConnectionPool TransactedConnectionPool => _transactedConnectionPool; + private void CleanupCallback(object state) { // Called when the cleanup-timer ticks over. @@ -940,6 +942,8 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj // If automatic transaction enlistment is required, then we try to // get the connection from the transacted connection pool first. + // If automatic enlistment is not enabled, then we cannot vend connections + // from the transacted pool. if (HasTransactionAffinity) { obj = GetFromTransactedPool(out transaction); diff --git a/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Tests/SqlClientTestGroup.cs b/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Tests/SqlClientTestGroup.cs index e48c13d49b..7c7597ba8a 100644 --- a/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Tests/SqlClientTestGroup.cs +++ b/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Tests/SqlClientTestGroup.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using System.Diagnostics; using System.Data; +using System.Transactions; using Microsoft.Data.SqlClient; using System.Xml; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/TransactedConnectionPoolTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/TransactedConnectionPoolTest.cs index 3fc31338cd..18bd9c5ea3 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/TransactedConnectionPoolTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/TransactedConnectionPoolTest.cs @@ -668,13 +668,14 @@ internal class MockDbConnectionPool : IDbConnectionPool public DbConnectionPoolGroupOptions PoolGroupOptions => throw new NotImplementedException(); public DbConnectionPoolProviderInfo ProviderInfo => throw new NotImplementedException(); public DbConnectionPoolState State => throw new NotImplementedException(); + public TransactedConnectionPool TransactedConnectionPool => throw new NotImplementedException(); public bool UseLoadBalancing => throw new NotImplementedException(); public ConcurrentBag ReturnedConnections { get; } = new(); public void Clear() => throw new NotImplementedException(); - public bool TryGetConnection(DbConnection owningObject, TaskCompletionSource taskCompletionSource, DbConnectionOptions userOptions, out DbConnectionInternal? connection) + public bool TryGetConnection(DbConnection owningObject, TaskCompletionSource? taskCompletionSource, DbConnectionOptions userOptions, out DbConnectionInternal? connection) { throw new NotImplementedException(); } @@ -739,4 +740,4 @@ internal override void ResetConnection() } #endregion -} \ No newline at end of file +} diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/WaitHandleDbConnectionPoolTransactionTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/WaitHandleDbConnectionPoolTransactionTest.cs new file mode 100644 index 0000000000..cafea0dad4 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/ConnectionPool/WaitHandleDbConnectionPoolTransactionTest.cs @@ -0,0 +1,951 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using System.Transactions; +using Microsoft.Data.Common.ConnectionString; +using Microsoft.Data.ProviderBase; +using Microsoft.Data.SqlClient.ConnectionPool; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests.ConnectionPool; + +/// +/// Deterministic tests for WaitHandleDbConnectionPool transaction functionality. +/// These tests exercise transacted connection pathways with controlled synchronization +/// to verify correct behavior without relying on probabilistic concurrency. +/// +public class WaitHandleDbConnectionPoolTransactionTest : IDisposable +{ + private const int DefaultMaxPoolSize = 50; + private const int DefaultMinPoolSize = 0; + private const int DefaultCreationTimeoutInMilliseconds = 15000; + + private IDbConnectionPool _pool = null!; + + public WaitHandleDbConnectionPoolTransactionTest() + { + _pool = CreatePool(); + } + + public void Dispose() + { + // Verify no leaked transactions before cleanup + Assert.Empty(_pool.TransactedConnectionPool.TransactedConnections); + + _pool?.Shutdown(); + _pool?.Clear(); + } + + #region Helper Methods + + private WaitHandleDbConnectionPool CreatePool( + int maxPoolSize = DefaultMaxPoolSize, + int minPoolSize = DefaultMinPoolSize, + bool hasTransactionAffinity = true) + { + var poolGroupOptions = new DbConnectionPoolGroupOptions( + poolByIdentity: false, + minPoolSize: minPoolSize, + maxPoolSize: maxPoolSize, + creationTimeout: DefaultCreationTimeoutInMilliseconds, + loadBalanceTimeout: 0, + hasTransactionAffinity: hasTransactionAffinity + ); + + var dbConnectionPoolGroup = new DbConnectionPoolGroup( + new DbConnectionOptions("DataSource=localhost;", null), + new DbConnectionPoolKey("TestDataSource"), + poolGroupOptions + ); + + var connectionFactory = new MockSqlConnectionFactory(); + + var pool = new WaitHandleDbConnectionPool( + connectionFactory, + dbConnectionPoolGroup, + DbConnectionPoolIdentity.NoIdentity, + new DbConnectionPoolProviderInfo() + ); + + pool.Startup(); + return pool; + } + + private DbConnectionInternal GetConnection(SqlConnection owner) + { + _pool.TryGetConnection( + owner, + taskCompletionSource: null, + new DbConnectionOptions("", null), + out DbConnectionInternal? connection); + return connection!; + } + + private async Task GetConnectionAsync( + SqlConnection owner, + Transaction? transaction = null) + { + var tcs = new TaskCompletionSource(transaction); + _pool.TryGetConnection( + owner, + taskCompletionSource: tcs, + new DbConnectionOptions("", null), + out DbConnectionInternal? connection); + return connection ?? await tcs.Task; + } + + private void ReturnConnection(DbConnectionInternal connection, SqlConnection owner) + { + _pool.ReturnInternalConnection(connection, owner); + } + + private void AssertPoolMetrics() + { + Assert.True(_pool.Count <= _pool.PoolGroupOptions.MaxPoolSize, + $"Pool count ({_pool.Count}) exceeded max pool size ({_pool.PoolGroupOptions.MaxPoolSize})"); + Assert.True(_pool.Count >= 0, + $"Pool count ({_pool.Count}) is negative"); + Assert.Empty(_pool.TransactedConnectionPool.TransactedConnections); + } + + #endregion + + #region Transaction Routing Tests + + [Fact] + public void GetConnection_UnderTransaction_RoutesToTransactedPool() + { + // Arrange & Act + using var scope = new TransactionScope(); + var transaction = Transaction.Current; + Assert.NotNull(transaction); + + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + + ReturnConnection(conn, owner); + + // Assert - connection should be in the transacted pool + Assert.True(_pool.TransactedConnectionPool.TransactedConnections.ContainsKey(transaction)); + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections[transaction]); + + scope.Complete(); + } + + [Fact] + public void GetConnection_WithoutTransaction_RoutesToGeneralPool() + { + // Arrange & Act (no TransactionScope) + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + + ReturnConnection(conn, owner); + + // Assert - transacted pool should be empty + Assert.Empty(_pool.TransactedConnectionPool.TransactedConnections); + } + + [Fact] + public void GetConnection_UnderTransaction_ReturnsSameConnectionFromTransactedPool() + { + // Arrange + using var scope = new TransactionScope(); + + // Act - first call creates a new connection + var owner1 = new SqlConnection(); + var conn1 = GetConnection(owner1); + Assert.NotNull(conn1); + ReturnConnection(conn1, owner1); + + // Second call should retrieve the SAME connection from the transacted pool (LIFO) + var owner2 = new SqlConnection(); + var conn2 = GetConnection(owner2); + Assert.NotNull(conn2); + Assert.Same(conn1, conn2); + + ReturnConnection(conn2, owner2); + scope.Complete(); + } + + [Fact] + public async Task GetConnectionAsync_UnderTransaction_ReturnsSameConnectionFromTransactedPool() + { + // Arrange + using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var transaction = Transaction.Current; + + // Act - first call creates a new connection + var owner1 = new SqlConnection(); + var conn1 = await GetConnectionAsync(owner1, transaction: transaction); + Assert.NotNull(conn1); + ReturnConnection(conn1, owner1); + + // Second call should retrieve the SAME connection from the transacted pool + var owner2 = new SqlConnection(); + var conn2 = await GetConnectionAsync(owner2, transaction: transaction); + Assert.NotNull(conn2); + Assert.Same(conn1, conn2); + + ReturnConnection(conn2, owner2); + scope.Complete(); + } + + [Fact] + public void GetConnection_WithTransactionAffinityDisabled_SkipsTransactedPool() + { + // Arrange + _pool.Shutdown(); + _pool.Clear(); + _pool = CreatePool(hasTransactionAffinity: false); + + using var scope = new TransactionScope(); + + // Act + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + // Assert - even though a transaction is active, transacted pool is not used + Assert.Empty(_pool.TransactedConnectionPool.TransactedConnections); + + scope.Complete(); + } + + #endregion + + #region Transaction Lifecycle Tests + + [Fact] + public void TransactionCommit_ClearsTransactedPool() + { + // Arrange & Act + using (var scope = new TransactionScope()) + { + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + // While transaction is active, connection should be in transacted pool + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + + scope.Complete(); + } + + // Assert - after transaction completes, transacted pool should be empty + AssertPoolMetrics(); + } + + [Fact] + public void TransactionRollback_ClearsTransactedPool() + { + // Arrange & Act + using (var scope = new TransactionScope()) + { + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + + // Don't call scope.Complete() — triggers rollback + } + + // Assert - transacted pool should be empty after rollback too + AssertPoolMetrics(); + } + + [Fact] + public void MultipleGetReturn_SameTransaction_ReusesConnection() + { + // Arrange + using var scope = new TransactionScope(); + var transaction = Transaction.Current; + Assert.NotNull(transaction); + + // Act - get and return multiple times within same transaction + for (int i = 0; i < 10; i++) + { + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + } + + // Assert - only one connection should be in the transacted pool + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections[transaction]); + + scope.Complete(); + } + + [Fact] + public async Task MultipleGetReturn_SameTransaction_Async_ReusesConnection() + { + // Arrange + using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var transaction = Transaction.Current; + Assert.NotNull(transaction); + + // Act - get and return multiple times within same transaction + for (int i = 0; i < 10; i++) + { + var owner = new SqlConnection(); + var conn = await GetConnectionAsync(owner, transaction: transaction); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + } + + // Assert - only one connection should be in the transacted pool + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections[transaction]); + + scope.Complete(); + } + + [Fact] + public void AlternatingCommitAndRollback_MaintainsConsistentState() + { + // Act - alternate between commit and rollback + for (int i = 0; i < 20; i++) + { + using var scope = new TransactionScope(); + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + if (i % 2 == 0) + { + scope.Complete(); + } + // else: rollback (no Complete) + } + + // Assert + AssertPoolMetrics(); + } + + #endregion + + #region Nested Transaction Tests + + [Fact] + public void NestedTransaction_Required_SharesSameTransactedEntry() + { + // Arrange + using var outerScope = new TransactionScope(); + var outerTxn = Transaction.Current; + Assert.NotNull(outerTxn); + + var owner1 = new SqlConnection(); + var conn1 = GetConnection(owner1); + Assert.NotNull(conn1); + ReturnConnection(conn1, owner1); + + // Act - nested scope with Required shares the same transaction + using (var innerScope = new TransactionScope(TransactionScopeOption.Required)) + { + Assert.Same(outerTxn, Transaction.Current); + + var owner2 = new SqlConnection(); + var conn2 = GetConnection(owner2); + Assert.NotNull(conn2); + Assert.Same(conn1, conn2); // Same transaction -> same connection from transacted pool + ReturnConnection(conn2, owner2); + + // Only one transaction tracked + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + + innerScope.Complete(); + } + + outerScope.Complete(); + } + + [Fact] + public void NestedTransaction_RequiresNew_CreatesSeparateTransactedEntry() + { + // Arrange + using var outerScope = new TransactionScope(); + var outerTxn = Transaction.Current; + Assert.NotNull(outerTxn); + + var owner1 = new SqlConnection(); + var conn1 = GetConnection(owner1); + Assert.NotNull(conn1); + ReturnConnection(conn1, owner1); + + // Act - nested scope with RequiresNew creates a new transaction + using (var innerScope = new TransactionScope(TransactionScopeOption.RequiresNew)) + { + var innerTxn = Transaction.Current; + Assert.NotNull(innerTxn); + Assert.NotEqual(outerTxn, innerTxn); + + var owner2 = new SqlConnection(); + var conn2 = GetConnection(owner2); + Assert.NotNull(conn2); + Assert.NotSame(conn1, conn2); // Different transaction -> different connection + ReturnConnection(conn2, owner2); + + // Two separate transactions tracked + Assert.Equal(2, _pool.TransactedConnectionPool.TransactedConnections.Count); + + innerScope.Complete(); + } + + outerScope.Complete(); + } + + [Fact] + public void NestedTransaction_RequiresNew_CompletesIndependently() + { + // Arrange & Act + using (var outerScope = new TransactionScope()) + { + var owner1 = new SqlConnection(); + var conn1 = GetConnection(owner1); + Assert.NotNull(conn1); + ReturnConnection(conn1, owner1); + + using (var innerScope = new TransactionScope(TransactionScopeOption.RequiresNew)) + { + var owner2 = new SqlConnection(); + var conn2 = GetConnection(owner2); + Assert.NotNull(conn2); + ReturnConnection(conn2, owner2); + innerScope.Complete(); + } + + // Inner transaction completed - its entry should be cleared + // Outer transaction entry should still exist + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + + outerScope.Complete(); + } + + // Both completed + AssertPoolMetrics(); + } + + [Fact] + public void DeeplyNestedTransactions_RequiresNew_AllTrackedSeparately() + { + // Arrange & Act + using var scope1 = new TransactionScope(); + var owner1 = new SqlConnection(); + var conn1 = GetConnection(owner1); + ReturnConnection(conn1, owner1); + + using var scope2 = new TransactionScope(TransactionScopeOption.RequiresNew); + var owner2 = new SqlConnection(); + var conn2 = GetConnection(owner2); + ReturnConnection(conn2, owner2); + + using var scope3 = new TransactionScope(TransactionScopeOption.RequiresNew); + var owner3 = new SqlConnection(); + var conn3 = GetConnection(owner3); + ReturnConnection(conn3, owner3); + + // Assert - three separate transactions tracked + Assert.Equal(3, _pool.TransactedConnectionPool.TransactedConnections.Count); + + scope3.Complete(); + scope2.Complete(); + scope1.Complete(); + } + + [Fact] + public void DeeplyNestedTransactions_Required_AllShareOneEntry() + { + // Arrange & Act + using var scope1 = new TransactionScope(); + var txn = Transaction.Current; + var owner1 = new SqlConnection(); + var conn1 = GetConnection(owner1); + ReturnConnection(conn1, owner1); + + using var scope2 = new TransactionScope(TransactionScopeOption.Required); + Assert.Same(txn, Transaction.Current); + var owner2 = new SqlConnection(); + var conn2 = GetConnection(owner2); + Assert.Same(conn1, conn2); + ReturnConnection(conn2, owner2); + + using var scope3 = new TransactionScope(TransactionScopeOption.Required); + Assert.Same(txn, Transaction.Current); + var owner3 = new SqlConnection(); + var conn3 = GetConnection(owner3); + Assert.Same(conn1, conn3); + ReturnConnection(conn3, owner3); + + // Assert - single transaction entry + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + + scope3.Complete(); + scope2.Complete(); + scope1.Complete(); + } + + #endregion + + #region Mixed Transacted and Non-Transacted Tests + + [Fact] + public void MixedWorkload_AlternatingTransactedAndNonTransacted() + { + // Act - alternate between transacted and non-transacted + for (int i = 0; i < 10; i++) + { + if (i % 2 == 0) + { + using var scope = new TransactionScope(); + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + scope.Complete(); + } + else + { + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + } + } + + // Assert + AssertPoolMetrics(); + } + + #endregion + + #region Shared Transaction Tests + + [Fact] + public void SharedTransaction_DependentScopes_UseTransactedPool() + { + // Arrange + using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var transaction = Transaction.Current; + Assert.NotNull(transaction); + + // Act - first connection + var owner1 = new SqlConnection(); + var conn1 = GetConnection(owner1); + Assert.NotNull(conn1); + ReturnConnection(conn1, owner1); + + // Use dependent scope on same transaction + using (var innerScope = new TransactionScope(transaction)) + { + Assert.Same(transaction, Transaction.Current); + var owner2 = new SqlConnection(); + var conn2 = GetConnection(owner2); + Assert.NotNull(conn2); + Assert.Same(conn1, conn2); // Same transaction -> same connection + ReturnConnection(conn2, owner2); + innerScope.Complete(); + } + + // Assert - still one transaction entry + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections[transaction]); + + scope.Complete(); + } + + #endregion + + #region Pool Saturation with Transactions Tests + + [Fact] + public void PoolSaturation_BlocksUntilConnectionAvailable() + { + // Arrange - small pool + _pool.Shutdown(); + _pool.Clear(); + _pool = CreatePool(maxPoolSize: 1); + + using var allAcquired = new ManualResetEventSlim(false); + using var releaseFirst = new ManualResetEventSlim(false); + + var saturatingTask = Task.Run(() => + { + using var scope = new TransactionScope(); + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + + allAcquired.Set(); // Signal that this connection is held + + Assert.True(releaseFirst.Wait(TimeSpan.FromSeconds(15)), + "Timed out waiting for releaseFirst signal."); + + ReturnConnection(conn, owner); + scope.Complete(); + }); + + Assert.True(allAcquired.Wait(TimeSpan.FromSeconds(10)), + "Timed out waiting for connection to be acquired."); + Assert.Equal(1, _pool.Count); + + using var acquired = new ManualResetEventSlim(false); + var waitingTask = Task.Run(() => + { + using var scope = new TransactionScope(); + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + acquired.Set(); + ReturnConnection(conn, owner); + scope.Complete(); + }); + + // Give the waiting task time to block — it should NOT complete yet + Assert.False(acquired.Wait(TimeSpan.FromMilliseconds(500)), + "Waiting task should not have acquired a connection while pool is saturated"); + + // Release one connection to unblock the waiting task + releaseFirst.Set(); + + // Now the waiting task should complete + Assert.True(waitingTask.Wait(TimeSpan.FromSeconds(15)), + "Waiting task should have completed after a connection was released"); + Assert.True(acquired.IsSet); + + // Cleanup remaining held connections + Task.WaitAll(saturatingTask); + } + + #endregion + + #region Controlled Concurrency Tests + + [Fact] + public void TwoThreads_SharedTransaction_AccessSameTransactedEntry() + { + // Arrange + // Use 3-phase synchronization so task1 gets AND returns before task2 requests. + // This ensures the connection is back in the transacted pool for task2 to reuse. + using var task1Returned = new ManualResetEventSlim(false); + using var task2Done = new ManualResetEventSlim(false); + DbConnectionInternal? connFromTask1 = null; + DbConnectionInternal? connFromTask2 = null; + + using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var transaction = Transaction.Current; + Assert.NotNull(transaction); + + // Act - two threads sharing the same transaction, sequenced so the + // transacted pool can vend the same connection to both. + var task1 = Task.Run(() => + { + using var innerScope = new TransactionScope(transaction); + var owner = new SqlConnection(); + connFromTask1 = GetConnection(owner); + Assert.NotNull(connFromTask1); + + // Return the connection so it's available in the transacted pool + ReturnConnection(connFromTask1, owner); + innerScope.Complete(); + + task1Returned.Set(); // Signal: connection is back in the transacted pool + }); + + var task2 = Task.Run(() => + { + // Wait until task1 has returned the connection to the transacted pool + Assert.True(task1Returned.Wait(TimeSpan.FromSeconds(10)), + "Timed out waiting for task1 to return its connection."); + + using var innerScope = new TransactionScope(transaction); + var owner = new SqlConnection(); + connFromTask2 = GetConnection(owner); + Assert.NotNull(connFromTask2); + ReturnConnection(connFromTask2, owner); + innerScope.Complete(); + }); + + Task.WaitAll(task1, task2); + + // Both tasks should have received the same connection via the transacted pool + Assert.Same(connFromTask1, connFromTask2); + scope.Complete(); + } + + [Fact] + public async Task TwoThreads_SeparateTransactions_Async_IsolatedTransactedEntries() + { + // Arrange + using var barrier = new SemaphoreSlim(0, 2); + + // Act + var task1 = Task.Run(async () => + { + using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var transaction = Transaction.Current; + var owner = new SqlConnection(); + var conn = await GetConnectionAsync(owner, transaction: transaction); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + barrier.Release(); // Signal ready + await barrier.WaitAsync(); // Wait for other task + + scope.Complete(); + }); + + var task2 = Task.Run(async () => + { + using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var transaction = Transaction.Current; + var owner = new SqlConnection(); + var conn = await GetConnectionAsync(owner, transaction: transaction); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + barrier.Release(); // Signal ready + await barrier.WaitAsync(); // Wait for other task + + scope.Complete(); + }); + + await Task.WhenAll(task1, task2); + + // Assert + AssertPoolMetrics(); + } + + #endregion + + #region Pool Shutdown with Transactions Tests + + [Fact] + public void PoolShutdown_AfterTransactionComplete_NoLeaks() + { + // Arrange + using (var scope = new TransactionScope()) + { + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + scope.Complete(); + } + + // Act + _pool.Shutdown(); + + // Assert + AssertPoolMetrics(); + } + + [Fact] + public void PoolShutdown_WhileConnectionHeld_NoException() + { + // Arrange + using var scope = new TransactionScope(); + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + + // Act - shutdown while connection is held (not yet returned) + _pool.Shutdown(); + + // Return after shutdown — the pool deactivates and disposes the connection + // rather than returning it to the pool. Verify this doesn't throw. + ReturnConnection(conn, owner); + + // Assert + // The connection should have been deactivated and disposed (not returned to the pool). + // After Dispose(), IsConnectionDoomed is set to true and Pool is set to null. + Assert.True(conn.IsConnectionDoomed, + "Connection should be doomed after returning to a shut-down pool."); + Assert.Null(conn.Pool); + } + + #endregion + + #region Transaction Complete Before Return Tests + + [Fact] + public void TransactionComplete_ThenReturn_ConnectionStillReturned() + { + // Arrange + var owner = new SqlConnection(); + DbConnectionInternal conn; + + using (var scope = new TransactionScope()) + { + conn = GetConnection(owner); + Assert.NotNull(conn); + scope.Complete(); + } + // Transaction is fully disposed here + + // Act - return connection after transaction ended + ReturnConnection(conn, owner); + + // Assert - no leak, pool metrics consistent + AssertPoolMetrics(); + Assert.True(_pool.Count > 0, "Pool should still have the connection"); + } + + #endregion + + #region Sequential Transaction Isolation Tests + + [Fact] + public void SequentialTransactions_EachGetsOwnTransactedEntry() + { + // Act - create multiple sequential transactions + for (int i = 0; i < 5; i++) + { + using var scope = new TransactionScope(); + var transaction = Transaction.Current; + Assert.NotNull(transaction); + + var owner = new SqlConnection(); + var conn = GetConnection(owner); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + // Only the current transaction should be tracked + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + Assert.True(_pool.TransactedConnectionPool.TransactedConnections.ContainsKey(transaction)); + + scope.Complete(); + } + + // Assert - after all are done, pool should be clean + AssertPoolMetrics(); + } + + [Fact] + public async Task SequentialTransactions_Async_EachGetsOwnTransactedEntry() + { + // Act - create multiple sequential transactions + for (int i = 0; i < 5; i++) + { + using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); + var transaction = Transaction.Current; + Assert.NotNull(transaction); + + var owner = new SqlConnection(); + var conn = await GetConnectionAsync(owner, transaction: transaction); + Assert.NotNull(conn); + ReturnConnection(conn, owner); + + Assert.Single(_pool.TransactedConnectionPool.TransactedConnections); + Assert.True(_pool.TransactedConnectionPool.TransactedConnections.ContainsKey(transaction)); + + scope.Complete(); + } + + // Assert + AssertPoolMetrics(); + } + + [Fact] + public void SequentialTransactions_CanReuseConnections() + { + // Act + DbConnectionInternal conn1; + DbConnectionInternal conn2; + Transaction? txn1; + Transaction? txn2; + + using (var scope1 = new TransactionScope()) + { + txn1 = Transaction.Current; + var owner1 = new SqlConnection(); + conn1 = GetConnection(owner1); + Assert.NotNull(conn1); + ReturnConnection(conn1, owner1); + scope1.Complete(); + } + + using (var scope2 = new TransactionScope()) + { + txn2 = Transaction.Current; + var owner2 = new SqlConnection(); + conn2 = GetConnection(owner2); + Assert.NotNull(conn2); + ReturnConnection(conn2, owner2); + scope2.Complete(); + } + + // Assert + // The connection was returned to the general pool and picked up by the second transaction + Assert.NotSame(txn1, txn2); + Assert.Same(conn1, conn2); + AssertPoolMetrics(); + } + + #endregion + + #region Mock Classes + + internal class MockSqlConnectionFactory : SqlConnectionFactory + { + protected override DbConnectionInternal CreateConnection( + DbConnectionOptions options, + DbConnectionPoolKey poolKey, + DbConnectionPoolGroupProviderInfo poolGroupProviderInfo, + IDbConnectionPool pool, + DbConnection owningConnection, + DbConnectionOptions userOptions) + { + return new MockDbConnectionInternal(); + } + } + + internal class MockDbConnectionInternal : DbConnectionInternal + { + private static int s_nextId = 1; + public int MockId { get; } = Interlocked.Increment(ref s_nextId); + + public override string ServerVersion => "Mock"; + + public override DbTransaction BeginTransaction(System.Data.IsolationLevel il) + { + throw new NotImplementedException(); + } + + public override void EnlistTransaction(Transaction? transaction) + { + if (transaction != null) + { + EnlistedTransaction = transaction; + } + } + + protected override void Activate(Transaction? transaction) + { + EnlistedTransaction = transaction; + } + + protected override void Deactivate() + { + } + + public override string ToString() => $"MockConnection_{MockId}"; + + internal override void ResetConnection() + { + } + } + + #endregion +} From e500f3af2afbac6ade4ebb37ce70fdeca40fbc8c Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:25:09 -0300 Subject: [PATCH 22/22] Address flaky DEBUG assertions (#4085) Co-authored-by: paulmedynski <31868385+paulmedynski@users.noreply.github.com> --- .../ManualTests/DataCommon/DataTestUtility.cs | 89 ++++--- .../SQL/AdapterTest/AdapterTest.cs | 6 +- .../SQL/DataStreamTest/DataStreamTest.cs | 222 ++++++++++-------- .../SQL/LocalDBTest/LocalDBTest.cs | 2 +- .../ManualTests/SQL/MARSTest/MARSTest.cs | 4 +- .../ParallelTransactionsTest.cs | 6 +- .../SQL/ParameterTest/ParametersTest.cs | 22 +- .../CopyAllFromReaderCancelAsync.cs | 2 +- .../CopyAllFromReaderConnectionCloseAsync.cs | 2 +- ...llFromReaderConnectionCloseOnEventAsync.cs | 3 +- .../SQL/SqlBulkCopyTest/CopyWithEvent1.cs | 2 +- .../SqlBulkCopyTest/MissingTargetColumn.cs | 2 +- .../SqlBulkCopyTest/MissingTargetColumns.cs | 2 +- .../OrderHintDuplicateColumn.cs | 2 +- .../OrderHintMissingTargetColumn.cs | 4 +- .../SQL/SqlBulkCopyTest/Transaction.cs | 2 +- .../SQL/SqlBulkCopyTest/Transaction1.cs | 2 +- .../SQL/SqlBulkCopyTest/Transaction3.cs | 4 +- .../SQL/SqlBulkCopyTest/Transaction4.cs | 2 +- .../SqlBulkCopyTest/TransactionTestAsync.cs | 2 +- .../SQL/SqlCommand/SqlCommandCancelTest.cs | 6 +- .../SqlNotificationTest.cs | 6 +- .../SQL/TransactionTest/TransactionTest.cs | 18 +- .../tests/ManualTests/SQL/UdtTest/UdtTest2.cs | 15 +- 24 files changed, 245 insertions(+), 182 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 3b4245d84f..5bceb81b58 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -302,9 +302,9 @@ private static Task AcquireTokenAsync(string authorityURL, string userID SecureString securePassword = new SecureString(); securePassword.MakeReadOnly(); -#pragma warning disable CS0618 // Type or member is obsolete + #pragma warning disable CS0618 // Type or member is obsolete result = app.AcquireTokenByUsernamePassword(scopes, userID, password).ExecuteAsync().Result; -#pragma warning restore CS0618 // Type or member is obsolete + #pragma warning restore CS0618 // Type or member is obsolete return result.AccessToken; }); @@ -389,7 +389,7 @@ public static string GetSqlServerProperty(SqlConnection connection, ServerProper } } - #nullable disable + #nullable restore private static bool GetSQLServerStatusOnTDS8(string connectionString) { @@ -951,35 +951,48 @@ public static void AssertEqualsWithDescription(object expectedValue, object actu } } - public static TException AssertThrowsWrapper(Action actionThatFails, string exceptionMessage = null, bool innerExceptionMustBeNull = false, Func customExceptionVerifier = null) where TException : Exception + #nullable enable + + /// + /// Asserts that throws an exception of type + /// and optionally verifies that its message contains + /// . + /// + public static TException AssertThrows( + Action actionThatFails, + string? exceptionMessage = null) + where TException : Exception { TException ex = Assert.Throws(actionThatFails); + if (exceptionMessage != null) { Assert.True(ex.Message.Contains(exceptionMessage), string.Format("FAILED: Exception did not contain expected message.\nExpected: {0}\nActual: {1}", exceptionMessage, ex.Message)); } - if (innerExceptionMustBeNull) - { - Assert.True(ex.InnerException == null, "FAILED: Expected InnerException to be null."); - } - - if (customExceptionVerifier != null) - { - Assert.True(customExceptionVerifier(ex), "FAILED: Custom exception verifier returned false for this exception."); - } - return ex; } - public static TException AssertThrowsWrapper(Action actionThatFails, string exceptionMessage = null, string innerExceptionMessage = null, bool innerExceptionMustBeNull = false, Func customExceptionVerifier = null) where TException : Exception + /// + /// Asserts that throws + /// whose is of type . + /// Optionally verifies message text on both the outer and inner exceptions. + /// + public static TException AssertThrowsInner( + Action actionThatFails, + string? exceptionMessage = null, + string? innerExceptionMessage = null) + where TException : Exception + where TInnerException : Exception { - TException ex = AssertThrowsWrapper(actionThatFails, exceptionMessage, innerExceptionMustBeNull, customExceptionVerifier); + TException ex = AssertThrows(actionThatFails, exceptionMessage); + + Assert.NotNull(ex.InnerException); + Assert.IsAssignableFrom(ex.InnerException); if (innerExceptionMessage != null) { - Assert.True(ex.InnerException != null, "FAILED: Cannot check innerExceptionMessage because InnerException is null."); Assert.True(ex.InnerException.Message.Contains(innerExceptionMessage), string.Format("FAILED: Inner Exception did not contain expected message.\nExpected: {0}\nActual: {1}", innerExceptionMessage, ex.InnerException.Message)); } @@ -987,26 +1000,40 @@ public static TException AssertThrowsWrapper(Action return ex; } - public static TException AssertThrowsWrapper(Action actionThatFails, string exceptionMessage = null, string innerExceptionMessage = null, string innerInnerExceptionMessage = null, bool innerInnerInnerExceptionMustBeNull = false) where TException : Exception where TInnerException : Exception where TInnerInnerException : Exception + /// + /// Asserts that throws + /// whose is either + /// or . Use this when a race condition + /// (e.g. disposal during an async read) may cause the inner exception type to vary + /// between runs. The is only verified when the + /// inner exception is . + /// + public static TException AssertThrowsInnerWithAlternate( + Action actionThatFails, + string? exceptionMessage = null, + string? innerExceptionMessage = null) + where TException : Exception + where TInnerException : Exception + where TAlternateInnerException : Exception { - TException ex = AssertThrowsWrapper(actionThatFails, exceptionMessage, innerExceptionMessage); - if (innerInnerInnerExceptionMustBeNull) - { - Assert.True(ex.InnerException != null, "FAILED: Cannot check innerInnerInnerExceptionMustBeNull since InnerException is null"); - Assert.True(ex.InnerException.InnerException == null, "FAILED: Expected InnerInnerException to be null."); - } + TException ex = AssertThrows(actionThatFails, exceptionMessage); - if (innerInnerExceptionMessage != null) + Assert.NotNull(ex.InnerException); + Assert.True( + ex.InnerException is TInnerException or TAlternateInnerException, + $"Expected {typeof(TInnerException).Name} or {typeof(TAlternateInnerException).Name}, got: {ex.InnerException?.GetType()}"); + + if (innerExceptionMessage != null && ex.InnerException is TInnerException) { - Assert.True(ex.InnerException != null, "FAILED: Cannot check innerInnerExceptionMessage since InnerException is null"); - Assert.True(ex.InnerException.InnerException != null, "FAILED: Cannot check innerInnerExceptionMessage since InnerInnerException is null"); - Assert.True(ex.InnerException.InnerException.Message.Contains(innerInnerExceptionMessage), - string.Format("FAILED: Inner Exception did not contain expected message.\nExpected: {0}\nActual: {1}", innerInnerExceptionMessage, ex.InnerException.InnerException.Message)); + Assert.True(ex.InnerException.Message.Contains(innerExceptionMessage), + string.Format("FAILED: Inner Exception did not contain expected message.\nExpected: {0}\nActual: {1}", innerExceptionMessage, ex.InnerException.Message)); } return ex; } + #nullable restore + public static TException ExpectFailure(Action actionThatFails, string[] exceptionMessages, bool innerExceptionMustBeNull = false, Func customExceptionVerifier = null) where TException : Exception { try @@ -1320,7 +1347,7 @@ public static string GetMachineFQDN(string hostname) } return fqdn.ToString(); } - } - #nullable disable + #nullable restore + } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs index ed9d9ab541..9bf5351a83 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AdapterTest/AdapterTest.cs @@ -1318,13 +1318,13 @@ public void TestDeriveParameters() using (SqlCommand cmd = new SqlCommand(procName, connection)) { string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DeriveParametersNotSupported, "SqlCommand", cmd.CommandType); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => SqlCommandBuilder.DeriveParameters(cmd), errorMessage); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_OpenConnectionRequired, "DeriveParameters", ""); cmd.CommandType = CommandType.StoredProcedure; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => SqlCommandBuilder.DeriveParameters(cmd), errorMessage); @@ -1335,7 +1335,7 @@ public void TestDeriveParameters() cmd.CommandText = "Test_EmployeeSalesBy"; errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NoStoredProcedureExists, cmd.CommandText); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => SqlCommandBuilder.DeriveParameters(cmd), errorMessage); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index 1c6d851ecc..2de13c98ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -276,7 +276,7 @@ static async Task LocalCopyTo(Stream source, Stream destination, int bufferSize, { await destination.WriteAsync(new ReadOnlyMemory(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false); } -#endif +#endif } finally { @@ -320,7 +320,7 @@ private static byte[] CreateBinaryTable(SqlConnection connection, string tableNa { cmd.CommandText = $@" IF OBJECT_ID('dbo.{tableName}', 'U') IS NOT NULL -DROP TABLE {tableName}; +DROP TABLE {tableName}; CREATE TABLE {tableName} (id INT, foo VARBINARY(MAX)) "; cmd.ExecuteNonQuery(); @@ -377,7 +377,7 @@ private static void MultipleResults(string connectionString) { Assert.True(numBatches < expectedResults.Length, "ERROR: Received more batches than were expected."); object[] values = new object[r1.FieldCount]; - // Current "column" in expected row is (valuesChecked MOD FieldCount), since + // Current "column" in expected row is (valuesChecked MOD FieldCount), since // expected rows for current batch are appended together for easy formatting int valuesChecked = 0; while (r1.Read()) @@ -410,7 +410,7 @@ private static void InvalidRead(string connectionString) using (SqlDataReader reader = cmd.ExecuteReader()) { string errorMessage = SystemDataResourceManager.Instance.SQL_InvalidRead; - DataTestUtility.AssertThrowsWrapper(() => reader.GetInt32(0), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetInt32(0), errorMessage); } } } @@ -497,7 +497,7 @@ private static void TypeRead(string connectionString) s = rdr.GetString(10); //ShipCity; // should get an exception here string errorMessage = SystemDataResourceManager.Instance.SqlMisc_NullValueMessage; - DataTestUtility.AssertThrowsWrapper(() => rdr.GetString(11), errorMessage); + DataTestUtility.AssertThrows(() => rdr.GetString(11), errorMessage); s = rdr.GetString(12); //ShipPostalCode; s = rdr.GetString(13); //ShipCountry; @@ -533,7 +533,7 @@ private static void GetValueOfTRead(string connectionString) rdr.IsDBNull(10); rdr.GetFieldValue(10); //ShipCity; // should get an exception here - DataTestUtility.AssertThrowsWrapper(() => rdr.GetFieldValue(11), errorMessage); + DataTestUtility.AssertThrows(() => rdr.GetFieldValue(11), errorMessage); rdr.IsDBNull(11); rdr.GetFieldValue(11); rdr.IsDBNull(11); @@ -542,7 +542,7 @@ private static void GetValueOfTRead(string connectionString) rdr.IsDBNull(12); rdr.GetFieldValue(13);//ShipCountry; rdr.GetFieldValue(14); - DataTestUtility.AssertThrowsWrapper(() => rdr.GetFieldValue(15), errorMessage); + DataTestUtility.AssertThrows(() => rdr.GetFieldValue(15), errorMessage); rdr.Read(); // read data out of buffer @@ -559,7 +559,7 @@ private static void GetValueOfTRead(string connectionString) Assert.False(rdr.IsDBNullAsync(10).Result, "FAILED: IsDBNull was true for a non-null value"); rdr.GetFieldValueAsync(10).Wait(); //ShipCity; // should get an exception here - DataTestUtility.AssertThrowsWrapper(() => rdr.GetFieldValueAsync(11).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInner(() => rdr.GetFieldValueAsync(11).Wait(), innerExceptionMessage: errorMessage); Assert.True(rdr.IsDBNullAsync(11).Result, "FAILED: IsDBNull was false for a null value"); rdr.IsDBNullAsync(11).Wait(); @@ -787,7 +787,7 @@ private static void OrphanReader(string connectionString) conn.Close(); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "CheckDataIsReady"); - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); Assert.True(reader.IsClosed, "FAILED: Stream was not closed by connection close (Scenario: Read)"); conn.Open(); } @@ -798,7 +798,7 @@ private static void OrphanReader(string connectionString) value = reader[0]; conn.Close(); - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); Assert.True(reader.IsClosed, "FAILED: Stream was not closed by connection close (Scenario: Read Partial Data)"); conn.Open(); } @@ -817,7 +817,7 @@ private static void OrphanReader(string connectionString) } while (reader.NextResult()); conn.Close(); - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); Assert.True(reader.IsClosed, "FAILED: Stream was not closed by connection close (Scenario: Read All Data)"); } } @@ -866,7 +866,7 @@ private static void ExecuteXmlReaderTest(string connectionString) // make sure we get an exception if we try to get another reader errorMessage = SystemDataResourceManager.Instance.ADP_OpenReaderExists("Connection"); - DataTestUtility.AssertThrowsWrapper(() => xr = cmd.ExecuteXmlReader(), errorMessage); + DataTestUtility.AssertThrows(() => xr = cmd.ExecuteXmlReader(), errorMessage); } // use a big result to fill up the pipe and do a partial read @@ -943,12 +943,12 @@ private static void ExecuteXmlReaderTest(string connectionString) // multiple columns cmd.CommandText = "select * from customers"; errorMessage = SystemDataResourceManager.Instance.SQL_NonXmlResult; - DataTestUtility.AssertThrowsWrapper(() => xr = cmd.ExecuteXmlReader(), errorMessage); + DataTestUtility.AssertThrows(() => xr = cmd.ExecuteXmlReader(), errorMessage); // non-ntext column cmd.CommandText = "select employeeID from employees"; errorMessage = SystemDataResourceManager.Instance.SQL_NonXmlResult; - DataTestUtility.AssertThrowsWrapper(() => xr = cmd.ExecuteXmlReader(), errorMessage); + DataTestUtility.AssertThrows(() => xr = cmd.ExecuteXmlReader(), errorMessage); } } } @@ -1114,31 +1114,31 @@ private static void SequentialAccess(string connectionString) i = reader.GetOrdinal("notes"); reader.GetChars(i, 14, chars, 0, 14); string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, i, i + 1); - DataTestUtility.AssertThrowsWrapper(() => reader.GetString(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetString(i), errorMessage); // Tests GetValue before GetBytes\Chars reader.Read(); i = reader.GetOrdinal("photo"); reader.GetSqlBinary(i); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, i, i + 1); - DataTestUtility.AssertThrowsWrapper(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); i = reader.GetOrdinal("notes"); reader.GetString(i); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, i, i + 1); - DataTestUtility.AssertThrowsWrapper(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); // Tests GetBytes\GetChars re-reading same characters reader.Read(); i = reader.GetOrdinal("photo"); reader.GetBytes(i, 0, data, 0, 13); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSeqByteAccess, 0, 13, "GetBytes"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetBytes(i, 0, data, 0, 13), errorMessage); i = reader.GetOrdinal("notes"); reader.GetChars(i, 0, chars, 0, 14); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSeqByteAccess, 0, 14, "GetChars"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetChars(i, 0, chars, 0, 14), errorMessage); } using (reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) @@ -1150,12 +1150,12 @@ private static void SequentialAccess(string connectionString) int columnToTry = 0; string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_NonSequentialColumnAccess, columnToTry, sqldata.Length); - DataTestUtility.AssertThrowsWrapper(() => reader.GetInt32(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrows(() => reader.GetInt32(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); reader.Read(); columnToTry = 17; @@ -1163,12 +1163,12 @@ private static void SequentialAccess(string connectionString) s = reader.GetString(columnToTry); DataTestUtility.AssertEqualsWithDescription("http://accweb/emmployees/fuller.bmp", s, "FAILED: Did not receive expected string."); - DataTestUtility.AssertThrowsWrapper(() => reader.GetInt32(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(columnToTry), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrows(() => reader.GetInt32(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(columnToTry), errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInner(() => reader.GetFieldValueAsync(columnToTry).Wait(), innerExceptionMessage: errorMessage); reader.Read(); // skip all columns up to photo, and read from it partially @@ -1189,14 +1189,14 @@ private static void SequentialAccess(string connectionString) // now try to read one more byte string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "GetBytes"); - DataTestUtility.AssertThrowsWrapper(() => cb = reader.GetBytes(i, 51, data, 0, 1), errorMessage); + DataTestUtility.AssertThrows(() => cb = reader.GetBytes(i, 51, data, 0, 1), errorMessage); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "CheckDataIsReady"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetValue(i), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(i), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValue(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetValue(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(i), errorMessage); + DataTestUtility.AssertThrows(() => reader.GetFieldValue(i), errorMessage); errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "GetFieldValueAsync"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); - DataTestUtility.AssertThrowsWrapper(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInnerWithAlternate(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInnerWithAlternate(() => reader.GetFieldValueAsync(i).Wait(), innerExceptionMessage: errorMessage); } } } @@ -1236,10 +1236,10 @@ private static void NumericRead(string connectionString) // Em object value; string errorMessage = SystemDataResourceManager.Instance.SqlMisc_ConversionOverflowMessage; - DataTestUtility.AssertThrowsWrapper(() => value = reader[0], errorMessage); - DataTestUtility.AssertThrowsWrapper(() => value = reader[1], errorMessage); - DataTestUtility.AssertThrowsWrapper(() => value = reader.GetDecimal(0), errorMessage); - DataTestUtility.AssertThrowsWrapper(() => value = reader.GetDecimal(1), errorMessage); + DataTestUtility.AssertThrows(() => value = reader[0], errorMessage); + DataTestUtility.AssertThrows(() => value = reader[1], errorMessage); + DataTestUtility.AssertThrows(() => value = reader.GetDecimal(0), errorMessage); + DataTestUtility.AssertThrows(() => value = reader.GetDecimal(1), errorMessage); } } finally @@ -1297,7 +1297,7 @@ private static void HasRowsTest(string connectionString) bool result; string errorMessage = string.Format(SystemDataResourceManager.Instance.ADP_DataReaderClosed, "HasRows"); - DataTestUtility.AssertThrowsWrapper(() => result = reader.HasRows, errorMessage); + DataTestUtility.AssertThrows(() => result = reader.HasRows, errorMessage); } } } @@ -1382,7 +1382,7 @@ private static void SeqAccessFailureWrapper(Action action, CommandBe { if (behavior == CommandBehavior.SequentialAccess) { - DataTestUtility.AssertThrowsWrapper(action); + DataTestUtility.AssertThrows(action); } else { @@ -1390,6 +1390,24 @@ private static void SeqAccessFailureWrapper(Action action, CommandBe } } + /// + /// Waits for a task that may or may not throw due to a race condition, and if they do + /// throw, they may throw one of a few acceptable exceptions. + /// + /// See: https://github.com/dotnet/SqlClient/issues/4088 + /// + private static void WaitIgnoringFlakyException(Task task) + { + try { task.Wait(); } + catch (AggregateException) + { + // A faulted Task stores its exception permanently. Calling .Wait() again is + // guaranteed to re-throw the same AggregateException, so we can safely pass it to + // the assert helper for inner-exception validation. + DataTestUtility.AssertThrowsInnerWithAlternate(() => task.Wait()); + } + } + private static void GetStream(string connectionString) { using (SqlConnection connection = new SqlConnection(connectionString)) @@ -1410,7 +1428,7 @@ private static void GetStream(string connectionString) reader.GetStream(1); // Bad values - DataTestUtility.AssertThrowsWrapper(() => reader.GetStream(2)); + DataTestUtility.AssertThrows(() => reader.GetStream(2)); // Null stream Stream stream = reader.GetStream(3); Assert.False(stream.Read(buffer, 0, buffer.Length) > 0, "FAILED: Read more than 0 bytes from a null stream"); @@ -1446,12 +1464,12 @@ private static void GetStream(string connectionString) { t = reader.ReadAsync(); Assert.False(t.Wait(1), "FAILED: Read completed immediately"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetStream(8)); + DataTestUtility.AssertThrows(() => reader.GetStream(8)); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); - // GetStream after Read - DataTestUtility.AssertThrowsWrapper(() => reader.GetStream(0)); + // GetStream after Read + DataTestUtility.AssertThrows(() => reader.GetStream(0)); #endif } @@ -1486,8 +1504,7 @@ private static void GetStream(string connectionString) Assert.True(t.IsCompleted, "FAILED: Failed to get stream within 1 second"); t = reader.ReadAsync(); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); } #endif } @@ -1523,7 +1540,7 @@ private static void GetTextReader(string connectionString) reader.GetTextReader(1); // Bad values - DataTestUtility.AssertThrowsWrapper(() => reader.GetTextReader(2)); + DataTestUtility.AssertThrows(() => reader.GetTextReader(2)); // Null stream TextReader textReader = reader.GetTextReader(3); Assert.False(textReader.Read(buffer, 0, buffer.Length) > 0, "FAILED: Read more than 0 chars from a null TextReader"); @@ -1559,13 +1576,12 @@ private static void GetTextReader(string connectionString) { t = reader.ReadAsync(); Assert.False(t.IsCompleted, "FAILED: Read completed immediately"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetTextReader(8)); + DataTestUtility.AssertThrows(() => reader.GetTextReader(8)); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); - // GetTextReader after Read - DataTestUtility.AssertThrowsWrapper(() => reader.GetTextReader(0)); + // GetTextReader after Read + DataTestUtility.AssertThrows(() => reader.GetTextReader(0)); #endif } @@ -1601,8 +1617,7 @@ private static void GetTextReader(string connectionString) Assert.True(t.IsCompleted, "FAILED: Failed to get TextReader within 1 second"); t = reader.ReadAsync(); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); } #endif } @@ -1631,7 +1646,7 @@ private static void GetXmlReader(string connectionString) reader.GetXmlReader(1); // Bad values - DataTestUtility.AssertThrowsWrapper(() => reader.GetXmlReader(2)); + DataTestUtility.AssertThrows(() => reader.GetXmlReader(2)); // Null stream XmlReader xmlReader = reader.GetXmlReader(3); Assert.False(xmlReader.Read(), "FAILED: Successfully read on a null XmlReader"); @@ -1651,13 +1666,12 @@ private static void GetXmlReader(string connectionString) { t = reader.ReadAsync(); Assert.False(t.IsCompleted, "FAILED: Read completed immediately"); - DataTestUtility.AssertThrowsWrapper(() => reader.GetXmlReader(6)); + DataTestUtility.AssertThrows(() => reader.GetXmlReader(6)); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); - // GetXmlReader after Read - DataTestUtility.AssertThrowsWrapper(() => reader.GetXmlReader(0)); + // GetXmlReader after Read + DataTestUtility.AssertThrows(() => reader.GetXmlReader(0)); #endif } } @@ -1691,16 +1705,16 @@ private static void ReadStream(string connectionString) // Testing stream properties stream.Flush(); - DataTestUtility.AssertThrowsWrapper(() => stream.SetLength(1)); + DataTestUtility.AssertThrows(() => stream.SetLength(1)); Action performOnStream = ((s) => { int i = s.WriteTimeout; }); - DataTestUtility.AssertThrowsWrapper(() => performOnStream(stream)); + DataTestUtility.AssertThrows(() => performOnStream(stream)); if (behavior == CommandBehavior.SequentialAccess) { - DataTestUtility.AssertThrowsWrapper(() => stream.Seek(0, SeekOrigin.Begin)); + DataTestUtility.AssertThrows(() => stream.Seek(0, SeekOrigin.Begin)); performOnStream = ((s) => { long i = s.Position; }); - DataTestUtility.AssertThrowsWrapper(() => performOnStream(stream)); + DataTestUtility.AssertThrows(() => performOnStream(stream)); performOnStream = ((s) => { long i = s.Length; }); - DataTestUtility.AssertThrowsWrapper(() => performOnStream(stream)); + DataTestUtility.AssertThrows(() => performOnStream(stream)); } else { @@ -1711,7 +1725,7 @@ private static void ReadStream(string connectionString) } // Once Stream is closed - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(buffer, 0, buffer.Length); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(buffer, 0, buffer.Length); }); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1723,9 +1737,9 @@ private static void ReadStream(string connectionString) _ = stream.Read(buffer, 0, buffer.Length); // Argument exceptions - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(null, 0, 1); }); - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(buffer, -1, 2); }); - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(buffer, 2, -1); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(null, 0, 1); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(buffer, -1, 2); }); + DataTestUtility.AssertThrows(() => { _ = stream.Read(buffer, 2, -1); }); // Prior to net6 comment:ArgumentException is thrown in net5 and earlier. ArgumentOutOfRangeException in net6 and later ArgumentException ex = Assert.ThrowsAny(() => { _ = stream.Read(buffer, buffer.Length, buffer.Length); }); @@ -1777,10 +1791,10 @@ private static void ReadStream(string connectionString) { // Read during async t = stream.ReadAsync(largeBuffer, 0, largeBuffer.Length); - DataTestUtility.AssertThrowsWrapper(() => { _ = stream.Read(largeBuffer, 0, largeBuffer.Length); }); - DataTestUtility.AssertThrowsWrapper(() => reader.Read()); + DataTestUtility.AssertThrows(() => { _ = stream.Read(largeBuffer, 0, largeBuffer.Length); }); + DataTestUtility.AssertThrows(() => reader.Read()); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInnerWithAlternate(() => t.Wait()); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) { @@ -1796,7 +1810,7 @@ private static void ReadStream(string connectionString) // Guarantee that timeout occurs: Thread.Sleep(stream.ReadTimeout * 4); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInnerWithAlternate(() => t.Wait()); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1811,7 +1825,10 @@ private static void ReadStream(string connectionString) t = stream.ReadAsync(largeBuffer, 0, largeBuffer.Length, tokenSource.Token); tokenSource.Cancel(); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + // Normally the cancellation wins (TaskCanceledException), but if the + // PendAsyncReadsScope disposal completes the read first, the inner + // exception may be InvalidOperationException instead (GH-4088). + DataTestUtility.AssertThrowsInnerWithAlternate(() => t.Wait()); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1824,7 +1841,18 @@ private static void ReadStream(string connectionString) // Error during read t = stream.ReadAsync(largeBuffer, 0, largeBuffer.Length); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + // PendAsyncReadsScope(errorCode: 11) injects a network error, normally producing + // AggregateException -> IOException -> SqlException. In rare race conditions + // the inner exception may be ObjectDisposedException instead (GH-4088). + AggregateException aex = Assert.Throws(() => t.Wait()); + if (aex.InnerException is IOException ioEx) + { + Assert.IsAssignableFrom(ioEx.InnerException); + } + else + { + Assert.IsAssignableFrom(aex.InnerException); + } } #endif } @@ -1876,7 +1904,7 @@ private static void ReadTextReader(string connectionString) } // Once Reader is closed - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, 0, buffer.Length)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, 0, buffer.Length)); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1890,11 +1918,11 @@ private static void ReadTextReader(string connectionString) textReader.Peek(); // Argument exceptions - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(null, 0, 1)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, -1, 2)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, 2, -1)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, buffer.Length, buffer.Length)); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(buffer, int.MaxValue, int.MaxValue)); + DataTestUtility.AssertThrows(() => textReader.Read(null, 0, 1)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, -1, 2)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, 2, -1)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, buffer.Length, buffer.Length)); + DataTestUtility.AssertThrows(() => textReader.Read(buffer, int.MaxValue, int.MaxValue)); } // Once Reader is closed @@ -1936,11 +1964,10 @@ private static void ReadTextReader(string connectionString) { // Read during async t = textReader.ReadAsync(largeBuffer, 0, largeBuffer.Length); - DataTestUtility.AssertThrowsWrapper(() => textReader.Read(largeBuffer, 0, largeBuffer.Length)); - DataTestUtility.AssertThrowsWrapper(() => reader.Read()); + DataTestUtility.AssertThrows(() => textReader.Read(largeBuffer, 0, largeBuffer.Length)); + DataTestUtility.AssertThrows(() => reader.Read()); } - // TODO(GH-3604): Fix this failing assertion. - // DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + WaitIgnoringFlakyException(t); } using (SqlDataReader reader = cmd.ExecuteReader(behavior)) @@ -1953,7 +1980,18 @@ private static void ReadTextReader(string connectionString) // Error during read t = textReader.ReadAsync(largeBuffer, 0, largeBuffer.Length); } - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + // PendAsyncReadsScope(errorCode: 11) injects a network error, normally producing + // AggregateException -> IOException -> SqlException. In rare race conditions + // the inner exception may be ObjectDisposedException instead (GH-4088). + AggregateException aex = Assert.Throws(() => t.Wait()); + if (aex.InnerException is IOException ioEx) + { + Assert.IsAssignableFrom(ioEx.InnerException); + } + else + { + Assert.IsAssignableFrom(aex.InnerException); + } } #endif } @@ -2118,7 +2156,7 @@ private void TestXEventsStreaming(string connectionString) byte[] bytes = new byte[cb]; long read = reader.GetBytes(1, 0, bytes, 0, cb); - // Don't send data on the first read because there is already data in the buffer. + // Don't send data on the first read because there is already data in the buffer. // Don't send data on the last iteration. We will not be reading that data. if (i == 0 || i == streamXeventCount - 1) { @@ -2164,7 +2202,7 @@ private static void TimeoutDuringReadAsyncWithClosedReaderTest(string connection // Wait for the task to see the timeout string errorMessage = SystemDataResourceManager.Instance.SQL_Timeout_Execution; - DataTestUtility.AssertThrowsWrapper(() => task.Wait(), innerExceptionMessage: errorMessage); + DataTestUtility.AssertThrowsInnerWithAlternate(() => task.Wait(), innerExceptionMessage: errorMessage); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs index ad84ac7881..c3aed471c0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -54,7 +54,7 @@ public static void LocalDBMarsTest() public static void InvalidLocalDBTest() { using var connection = new SqlConnection(s_badConnectionString); - DataTestUtility.AssertThrowsWrapper(() => connection.Open()); + DataTestUtility.AssertThrows(() => connection.Open()); } #endregion diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs index 69265c4e75..1213c5bfa4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs @@ -591,7 +591,7 @@ public static void MARSMultiDataReaderErrTest() { using (SqlDataReader reader1 = command.ExecuteReader()) { - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { SqlDataReader reader2 = command.ExecuteReader(); }, openReaderExistsMessage); @@ -611,7 +611,7 @@ public static void MARSMultiDataReaderErrTest() { using (SqlDataReader reader1 = command1.ExecuteReader()) { - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { SqlDataReader reader2 = command2.ExecuteReader(); }, openReaderExistsMessage); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs index efc99ea757..73fbd8bdc6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParallelTransactionsTest/ParallelTransactionsTest.cs @@ -20,7 +20,7 @@ public static void BasicParallelTest_shouldThrowsUnsupported() try { tempTableName = CreateTempTable(connectionString); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( actionThatFails: () => { BasicParallelTest(connectionString, tempTableName); }, exceptionMessage: expectedErrorMessage); } @@ -77,7 +77,7 @@ public static void MultipleExecutesInSameTransactionTest_shouldThrowsUnsupported try { tempTableName = CreateTempTable(connectionString); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( actionThatFails: () => { MultipleExecutesInSameTransactionTest(connectionString, tempTableName); }, exceptionMessage: expectedErrorMessage); } @@ -157,5 +157,3 @@ private static void DropTempTable(string connectionString, string tempTableName) } } } - - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index a4cd63b0c1..d53209e912 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -42,14 +42,14 @@ public static void CodeCoverageSqlClient() { string failValue; - DataTestUtility.AssertThrowsWrapper(() => failValue = opc[0].ParameterName, "Invalid index 0 for this SqlParameterCollection with Count=0."); + DataTestUtility.AssertThrows(() => failValue = opc[0].ParameterName, "Invalid index 0 for this SqlParameterCollection with Count=0."); - DataTestUtility.AssertThrowsWrapper(() => failValue = opc["@p1"].ParameterName, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); + DataTestUtility.AssertThrows(() => failValue = opc["@p1"].ParameterName, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); - DataTestUtility.AssertThrowsWrapper(() => opc["@p1"] = null, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); + DataTestUtility.AssertThrows(() => opc["@p1"] = null, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); } - DataTestUtility.AssertThrowsWrapper(() => opc.Add(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); + DataTestUtility.AssertThrows(() => opc.Add(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); opc.Add((object)new SqlParameter()); IEnumerator enm = opc.GetEnumerator(); @@ -70,11 +70,11 @@ public static void CodeCoverageSqlClient() SqlParameter p = opc[0]; - DataTestUtility.AssertThrowsWrapper(() => opc.Add((object)p), "The SqlParameter is already contained by another SqlParameterCollection."); + DataTestUtility.AssertThrows(() => opc.Add((object)p), "The SqlParameter is already contained by another SqlParameterCollection."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Add(p), "The SqlParameter is already contained by another SqlParameterCollection."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Add(p), "The SqlParameter is already contained by another SqlParameterCollection."); - DataTestUtility.AssertThrowsWrapper(() => opc.Remove(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); + DataTestUtility.AssertThrows(() => opc.Remove(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); string pname = p.ParameterName; p.ParameterName = pname; @@ -106,13 +106,13 @@ public static void CodeCoverageSqlClient() new SqlCommand().Parameters.CopyTo(new object[0], 0); Assert.False(new SqlCommand().Parameters.GetEnumerator().MoveNext(), "FAILED: Expected MoveNext to be false"); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Add(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Add(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Insert(0, 0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Insert(0, 0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Remove(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(new SqlParameter()), "Attempted to remove an SqlParameter that is not contained by this SqlParameterCollection."); + DataTestUtility.AssertThrows(() => new SqlCommand().Parameters.Remove(new SqlParameter()), "Attempted to remove an SqlParameter that is not contained by this SqlParameterCollection."); } // TODO Synapse: Parse error at line: 1, column: 12: Incorrect syntax near 'IF'. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs index 3ae79dbe31..3fb3d051a1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderCancelAsync.cs @@ -19,7 +19,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) cts = new CancellationTokenSource(); cts.Cancel(); Task t = TestAsync(srcConstr, dstConstr, dstTable, cts.Token); - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInner(() => t.Wait()); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs index fd6062659a..ede16437d3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseAsync.cs @@ -14,7 +14,7 @@ public class CopyAllFromReaderConnectionClosedAsync public static void Test(string srcConstr, string dstConstr, string dstTable) { Task t = TestAsync(srcConstr, dstConstr, dstTable); - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInner(() => t.Wait()); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs index 1afd2d93d0..3d832925a6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyAllFromReaderConnectionCloseOnEventAsync.cs @@ -4,6 +4,7 @@ using System; using System.Data.Common; +using System.IO; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { @@ -49,7 +50,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) // Check that the copying fails string message = string.Format(SystemDataResourceManager.Instance.ADP_OpenConnectionRequired, "WriteToServer", SystemDataResourceManager.Instance.ADP_ConnectionStateMsg_Closed); - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServerAsync(reader).Wait(5000), innerExceptionMessage: message); + DataTestUtility.AssertThrowsInnerWithAlternate(() => bulkcopy.WriteToServerAsync(reader).Wait(5000), innerExceptionMessage: message); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs index e89f2f83a3..8bc72435d0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/CopyWithEvent1.cs @@ -66,7 +66,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) ColumnMappings.Add("ShipName", "shipname"); bulkcopy.NotifyAfter = 3; - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader)); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader)); bulkcopy.SqlRowsCopied -= new SqlRowsCopiedEventHandler(OnRowCopied); bulkcopy.Close(); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs index d8fe8f66da..e4df366137 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumn.cs @@ -38,7 +38,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) string errorMsg = SystemDataResourceManager.Instance.SQL_BulkLoadNonMatchingColumnName; errorMsg = string.Format(errorMsg, "col2"); - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs index 2696770f69..5fb47f44c1 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/MissingTargetColumns.cs @@ -38,7 +38,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) string errorMsg = SystemDataResourceManager.Instance.SQL_BulkLoadNonMatchingColumnName; errorMsg = string.Format(errorMsg, "col3,col4"); - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader), exceptionMessage: errorMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs index 9e535ccae1..ebed5e9918 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs @@ -45,7 +45,7 @@ public static void Test(string connStr, string dstTable) string expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintDuplicateColumn, destColumn); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => bulkcopy.ColumnOrderHints.Add(destColumn, SortOrder.Ascending), exceptionMessage: expectedErrorMsg); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs index 46a7f39eee..cc486e9e4e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs @@ -44,7 +44,7 @@ public static void Test(string connStr, string dstTable) string expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintInvalidColumn, nonexistentColumn); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => bulkcopy.WriteToServer(reader), exceptionMessage: expectedErrorMsg); @@ -56,7 +56,7 @@ public static void Test(string connStr, string dstTable) expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintInvalidColumn, sourceColumn); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => bulkcopy.WriteToServer(reader), exceptionMessage: expectedErrorMsg); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs index e7632044b0..0738f32010 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction.cs @@ -34,7 +34,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) SqlTransaction myTrans = dstConn.BeginTransaction(); try { - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader)); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader)); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs index 1f6ba5e823..930e1ac6f5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction1.cs @@ -37,7 +37,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) try { - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader)); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader)); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs index 8e53989ed5..4b65f2a5d2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction3.cs @@ -37,13 +37,13 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) bulkcopy.DestinationTableName = dstTable; string exceptionMsg = SystemDataResourceManager.Instance.ADP_TransactionConnectionMismatch; - DataTestUtility.AssertThrowsWrapper(() => bulkcopy.WriteToServer(reader), exceptionMessage: exceptionMsg); + DataTestUtility.AssertThrows(() => bulkcopy.WriteToServer(reader), exceptionMessage: exceptionMsg); SqlCommand myCmd = dstConn.CreateCommand(); myCmd.CommandText = "select * from " + dstTable; myCmd.Transaction = myTrans; - DataTestUtility.AssertThrowsWrapper(() => myCmd.ExecuteReader(), exceptionMessage: exceptionMsg); + DataTestUtility.AssertThrows(() => myCmd.ExecuteReader(), exceptionMessage: exceptionMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs index c8028ca5c5..40b2d2c251 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/Transaction4.cs @@ -31,7 +31,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) // Start a local transaction on the wrong connection. SqlTransaction myTrans = conn3.BeginTransaction(); string errorMsg = SystemDataResourceManager.Instance.SQL_BulkLoadConflictingTransactionOption; - DataTestUtility.AssertThrowsWrapper(() => new SqlBulkCopy(dstConn, SqlBulkCopyOptions.UseInternalTransaction, myTrans), exceptionMessage: errorMsg); + DataTestUtility.AssertThrows(() => new SqlBulkCopy(dstConn, SqlBulkCopyOptions.UseInternalTransaction, myTrans), exceptionMessage: errorMsg); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs index ac28a9d883..729dac74a9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TransactionTestAsync.cs @@ -14,7 +14,7 @@ public class TransactionTestAsync public static void Test(string srcConstr, string dstConstr, string dstTable) { Task t = TestAsync(srcConstr, dstConstr, dstTable); - DataTestUtility.AssertThrowsWrapper(() => t.Wait()); + DataTestUtility.AssertThrowsInner(() => t.Wait()); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs index 97c225c9ca..552fd4cea5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs @@ -65,7 +65,7 @@ private static void PlainCancel(string connString) using (SqlDataReader reader = cmd.ExecuteReader()) { cmd.Cancel(); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => { do @@ -88,7 +88,7 @@ private static void PlainCancelAsync(string connString) { conn.Open(); Task readerTask = cmd.ExecuteReaderAsync(); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => { readerTask.Wait(2000); @@ -214,7 +214,7 @@ private static void CancelFollowedByTransaction(string constr) private static void CancelFollowedByAlert(string constr) { var alertName = "myAlert" + Guid.NewGuid().ToString(); - // Since Alert conditions are randomly generated, + // Since Alert conditions are randomly generated, // we will retry on unexpected error messages to avoid collision in pipelines. var n = new Random().Next(1, 100); bool retry = true; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs index 98fa8997de..e4bd1133ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlNotificationTest/SqlNotificationTest.cs @@ -71,7 +71,7 @@ public void Test_DoubleStart_DifferentConnStr() try { - DataTestUtility.AssertThrowsWrapper(() => SqlDependency.Start(cb.ToString())); + DataTestUtility.AssertThrows(() => SqlDependency.Start(cb.ToString())); } finally { @@ -116,7 +116,7 @@ public void Test_SingleDependency_NoStart() Console.WriteLine("4 Notification callback. Type={0}, Info={1}, Source={2}", args.Type, args.Info, args.Source); }; - DataTestUtility.AssertThrowsWrapper(() => cmd.ExecuteReader()); + DataTestUtility.AssertThrows(() => cmd.ExecuteReader()); } } @@ -138,7 +138,7 @@ public void Test_SingleDependency_Stopped() Console.WriteLine("5 Notification callback. Type={0}, Info={1}, Source={2}", args.Type, args.Info, args.Source); }; - DataTestUtility.AssertThrowsWrapper(() => cmd.ExecuteReader()); + DataTestUtility.AssertThrows(() => cmd.ExecuteReader()); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs index 1a97d6df31..0adff2c8ae 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/TransactionTest/TransactionTest.cs @@ -339,13 +339,13 @@ private void ExceptionTest() string executeCommandWithoutTransactionMessage = SystemDataResourceManager.Instance.ADP_TransactionRequired("ExecuteNonQuery"); string transactionConflictErrorMessage = SystemDataResourceManager.Instance.ADP_TransactionConnectionMismatch; string parallelTransactionErrorMessage = SystemDataResourceManager.Instance.ADP_ParallelTransactionsNotSupported("SqlConnection"); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { SqlCommand command = new SqlCommand("sql", connection); command.ExecuteNonQuery(); }, executeCommandWithoutTransactionMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { using (SqlConnection con1 = new SqlConnection(_connectionString)) { @@ -357,32 +357,32 @@ private void ExceptionTest() } }, transactionConflictErrorMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { connection.BeginTransaction(null); }, parallelTransactionErrorMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { connection.BeginTransaction(""); }, parallelTransactionErrorMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Rollback(null); }, invalidSaveStateMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Rollback(""); }, invalidSaveStateMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Save(null); }, invalidSaveStateMessage); - DataTestUtility.AssertThrowsWrapper(() => + DataTestUtility.AssertThrows(() => { tx.Save(""); }, invalidSaveStateMessage); @@ -456,7 +456,7 @@ private void ReadCommitedIsolationLevel_ShouldReceiveTimeoutExceptionBecauseItWa SqlTransaction tx2 = connection2.BeginTransaction(IsolationLevel.ReadCommitted); command2.Transaction = tx2; - DataTestUtility.AssertThrowsWrapper(() => command2.ExecuteReader(), SystemDataResourceManager.Instance.SQL_Timeout_Execution as string); + DataTestUtility.AssertThrows(() => command2.ExecuteReader(), SystemDataResourceManager.Instance.SQL_Timeout_Execution as string); tx2.Rollback(); connection2.Close(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs index 79f38262cc..1dae314730 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -75,7 +75,7 @@ public void UDTParams_Binary() value[7] = 0; p.Value = new SqlBinary(value); - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteReader(), "Error converting data type varbinary to Point."); } @@ -110,7 +110,7 @@ public void UDTParams_Invalid2() p.Value = addr; pName.Value = addr; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteReader(), "Failed to convert parameter value from a Address to a String."); } @@ -134,7 +134,7 @@ public void UDTParams_Invalid() p.UdtTypeName = "UdtTestDb.dbo.Point"; p.Value = 32; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteReader(), "Specified type is not registered on the target server. System.Int32"); } @@ -221,7 +221,7 @@ public void UDTParams_NullInput() string errorMsg = "Procedure or function '" + spInsertCustomerNoBrackets + "' expects parameter '@addr', which was not supplied."; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => cmd.ExecuteNonQuery(), errorMsg); } @@ -317,7 +317,7 @@ public void UDTFields_WrongType() reader.Read(); // retrieve the UDT as a string - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => reader.GetString(1), "Unable to cast object of type 'System.Byte[]' to type 'System.String'."); } @@ -584,12 +584,12 @@ Func create string udtError = SystemDataResourceManager.Instance.SQLUDT_MaxByteSizeValue; string errorMessage = (new ArgumentOutOfRangeException("MaxByteSize", 8001, udtError)).Message; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => create(SqlUserDefinedAggregateAttribute.MaxByteSizeValue + 1), errorMessage); errorMessage = (new ArgumentOutOfRangeException("MaxByteSize", -2, udtError)).Message; - DataTestUtility.AssertThrowsWrapper( + DataTestUtility.AssertThrows( () => create(-2), errorMessage); } @@ -702,4 +702,3 @@ public void UDTParams_DeriveParameters_CheckAutoFixOverride() } } } -