diff --git a/.editorconfig b/.editorconfig index f5c888e04a..db85dde7fd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -140,6 +140,9 @@ dotnet_diagnostic.CA1063.severity = silent # CA2100: Review SQL queries for security vulnerabilities dotnet_diagnostic.CA2100.severity = silent +# CA1416: Validate platform compatibility +dotnet_diagnostic.CA1416.severity = silent + [*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] indent_size = 2 @@ -163,3 +166,6 @@ end_of_line = crlf # Analyzers dotnet_code_quality.ca1802.api_surface = private, internal + +[*.cs] +dotnet_code_quality.CA2100.excluded_type_names_with_derived_types = Microsoft.Data.SqlClient.ManualTesting.Tests.* diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index 41ff39445d..47cacc9fb7 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -5,6 +5,7 @@ This document provides all the necessary details to build the driver and run tes ## Visual Studio Pre-Requisites This project should be built with Visual Studio 2019+ for the best compatibility. The required set of components are provided in the below file: + - **Visual Studio 2019** with imported components: [VS19Components](/tools/vsconfig/VS19Components.vsconfig) Once the environment is setup properly, execute the desired set of commands below from the _root_ folder to perform the respective operations: @@ -14,137 +15,217 @@ Once the environment is setup properly, execute the desired set of commands belo ```bash # Default Build Configuration: -> msbuild +msbuild # Builds the driver for the Client OS in 'Debug' Configuration for 'AnyCPU' platform. -# Both .NET Framework (NetFx) and .NET Core drivers are built by default (as supported by Client OS). +# Both .NET Framework (NetFx) and .NET (CoreFx) drivers are built by default (as supported by Client OS). ``` ```bash -> msbuild /p:Configuration=Release -# Builds the driver in 'Release' Configuration. +msbuild -t:clean +# Cleans all build directories. ``` ```bash -> msbuild /p:Platform=x86 -# Builds the .NET Framework (NetFx) driver for Win32 (x86) platform on Windows. +msbuild -p:Configuration=Release +# Builds the driver in 'Release' Configuration for `AnyCPU` platform. ``` ```bash -> msbuild /t:clean -# Cleans all build directories. -``` - -```bash -> msbuild /t:restore +msbuild -t:restore # Restores Nuget Packages. ``` ```bash -> msbuild /t:BuildAllConfigurations +msbuild -t:BuildAllConfigurations # Builds the driver for all target OSes and supported platforms. ``` ```bash -> msbuild /p:BuildNetFx=false +msbuild -p:BuildNetFx=false # Skips building the .NET Framework (NetFx) Driver on Windows. # On Unix the netfx driver build is automatically skipped. ``` ```bash -> msbuild /p:OSGroup=Unix +msbuild -p:OSGroup=Unix # Builds the driver for the Unix platform. ``` ```bash -> msbuild /t:BuildNetCoreAllOS -# Builds the .NET Core driver for all Operating Systems. +msbuild -t:BuildNetCoreAllOS +# Builds the .NET driver for all Operating Systems. ``` ## Building Tests ```bash -> msbuild /t:BuildTestsNetCore -# Build the tests for the .NET Core driver. Default .NET Core version is 2.1. +msbuild -t:BuildTestsNetCore +# Build the tests for the .NET driver in 'Debug' Configuration. Default .NET version is 6.0. +``` + +```bash +msbuild -t:BuildTestsNetFx +# Build the tests for the .NET Framework (NetFx) driver in 'Debug' Configuration. Default .NET Framework version is 4.6.2. +``` + +```bash +msbuild -t:BuildTestsNetCore -p:TestSet=1 +# Build a subset of the manual tests. Valid values: '1', '2', '3', 'AE'. Omit to build all tests. +``` + +## Running Tests + +There are 2 ways to run tests, using MsBuild or Dotnet SDK. + +### Running from Build.proj + +```bash +msbuild -t:RunFunctionalTests +# Run all functional tests in Debug configuration for *default* target framework (.NET 6.0). ``` ```bash -> msbuild /t:BuildTestsNetFx -# Build the tests for the .NET Framework (NetFx) driver. Default .NET Framework version is 4.6. +msbuild -t:RunManualTests +# Run all manual tests in Debug configuration for *default* target framework (.NET 6.0). ``` -## Run Functional Tests +```bash +msbuild -t:RunTests -p:configuration=Release +# Run both functional and manual tests in Release configuration for *default* target framework (.NET 6.0). +``` + +```bash +msbuild -t:RunTests -p:configuration=Release -p:DotnetPath=C:\net6-win-x86\ +# Run both functional and manual tests in Release configuration for *default* target framework (.NET 6.0) against the installed dotnet tool in the provided path. +``` + +To specify custom target framework, use `TF` property: + +```bash +msbuild -t:RunTests -p:configuration=Release -p:TF=net7.0 +msbuild -t:RunTests -p:configuration=Release -p:TF=net48 +# Runs tests for specified target framework. +# TargetNetCoreVersion and TargetNetFxVersion are not to be used with TF property, they will take precedence over TF if provided. +``` + +To capture test and code coverage results in a custom directory: + +```bash +msbuild -t:RunTests -p:ResultsDirectory=MyDirectory +# Runs tests with test and code coverage results placed in provided results directory. +# Default results directory is "TestResults". +``` + +Other properties can be set alongside as needed. + +### Running using Dotnet SDK (traditional) + +#### Run Functional Tests + +- Windows (`netfx x86`): -Windows (`netfx x86`): ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x86" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" ``` -Windows (`netfx x64`): +- Windows (`netfx x64`): + ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" ``` -Windows (`netcoreapp`): +- AnyCPU: + + Project reference only builds Driver with `AnyCPU` platform, and underlying process decides to run the tests with a compatible architecture (x64, x86, ARM64). + + Windows (`netcoreapp`): + ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" +dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" ``` -Unix (`netcoreapp`): + Unix (`netcoreapp`): + ```bash -> dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" +dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" ``` -## Run Manual Tests +#### Run Manual Tests + +### Pre-Requisites for running Manual tests -### Pre-Requisites for running Manual tests: Manual Tests require the below setup to run: -* SQL Server with enabled Shared Memory, TCP and Named Pipes Protocols and access to the Client OS. -* Databases "NORTHWIND" and "UdtTestDb" present in SQL Server, created using SQL scripts [createNorthwindDb.sql](tools/testsql/createNorthwindDb.sql) and [createUdtTestDb.sql](tools/testsql/createUdtTestDb.sql). -* Make a copy of the configuration file [config.default.json](src/Microsoft.Data.SqlClient/tests/ManualTests/config.default.json) and rename it to `config.json`. Update the values in `config.json`: -|Property|Description|Value| -|------|--------|-------------------| -|TCPConnectionString | Connection String for a TCP enabled SQL Server instance. | `Server={servername};Database={Database_Name};Trusted_Connection=True;`
OR `Data Source={servername};Initial Catalog={Database_Name};Integrated Security=True;`| -|NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;`
OR
`Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`| -|TCPConnectionStringHGSVBS | (Optional) Connection String for a TCP enabled SQL Server with Host Guardian Service (HGS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = HGS; Enclave Attestation Url = {AttestationURL};`| -|AADAuthorityURL | (Optional) Identifies the OAuth2 authority resource for `Server` specified in `AADPasswordConnectionString` | `https://login.windows.net/`, where `` is the tenant ID of the Azure Active Directory (Azure AD) tenant | -|AADPasswordConnectionString | (Optional) Connection String for testing Azure Active Directory Password Authentication. | `Data Source={server.database.windows.net}; Initial Catalog={Azure_DB_Name};Authentication=Active Directory Password; User ID={AAD_User}; Password={AAD_User_Password};`| -|AADSecurePrincipalId | (Optional) The Application Id of a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Application ID} | -|AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} | -|AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` | -|AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ | -|AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ | -|SupportsLocalDb | (Optional) Whether or not a LocalDb instance of SQL Server is installed on the machine running the tests. |`true` OR `false`| -|SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`| -|SupportsFileStream | (Optional) Whether or not FileStream is enabled on SQL Server| `true` OR `false`| -|UseManagedSNIOnWindows | (Optional) Enables testing with Managed SNI on Windows| `true` OR `false`| -|IsAzureSynpase | (Optional) When set to 'true', test suite runs compatible tests for Azure Synapse/Parallel Data Warehouse. | `true` OR `false`| +- SQL Server with enabled Shared Memory, TCP and Named Pipes Protocols and access to the Client OS. +- Databases "NORTHWIND" and "UdtTestDb" present in SQL Server, created using SQL scripts [createNorthwindDb.sql](tools/testsql/createNorthwindDb.sql) and [createUdtTestDb.sql](tools/testsql/createUdtTestDb.sql). To setup an Azure Database with "NORTHWIND" tables, use SQL Script: [createNorthwindAzureDb.sql](tools/testsql/createNorthwindAzureDb.sql). +- Make a copy of the configuration file [config.default.json](src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json) and rename it to `config.json`. Update the values in `config.json`: + + |Property|Description|Value| + |------|--------|-------------------| + |TCPConnectionString | Connection String for a TCP enabled SQL Server instance. | `Server={servername};Database={Database_Name};Trusted_Connection=True;`
OR `Data Source={servername};Initial Catalog={Database_Name};Integrated Security=True;`| + |NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;`
OR
`Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`| + |TCPConnectionStringHGSVBS | (Optional) Connection String for a TCP enabled SQL Server with Host Guardian Service (HGS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = HGS; Enclave Attestation Url = {AttestationURL};`| + |TCPConnectionStringAASVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`| + |TCPConnectionStringNoneVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using None Attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = NONE;`| + |TCPConnectionStringAASSGX | (Optional) Connection String for a TCP enabled SQL Server with a SGX Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`| + |EnclaveEnabled | Enables tests requiring an enclave-configured server.| + |TracingEnabled | Enables EventSource related tests | + |AADAuthorityURL | (Optional) Identifies the OAuth2 authority resource for `Server` specified in `AADPasswordConnectionString` | `https://login.windows.net/`, where `` is the tenant ID of the Azure Active Directory (Azure AD) tenant | + |AADPasswordConnectionString | (Optional) Connection String for testing Azure Active Directory Password Authentication. | `Data Source={server.database.windows.net}; Initial Catalog={Azure_DB_Name};Authentication=Active Directory Password; User ID={AAD_User}; Password={AAD_User_Password};`| + |AADSecurePrincipalId | (Optional) The Application Id of a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Application ID} | + |AADSecurePrincipalSecret | (Optional) A Secret defined for a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Secret} | + |AzureKeyVaultURL | (Optional) Azure Key Vault Identifier URL | `https://{keyvaultname}.vault.azure.net/` | + |AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ | + |AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ | + |AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ | + |SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`| + |LocalDbAppName | (Optional) If Local Db Testing is supported, this property configures the name of Local DB App instance available in client environment. Empty string value disables Local Db testing. | Name of Local Db App to connect to.| + |LocalDbSharedInstanceName | (Optional) If LocalDB testing is supported and the instance is shared, this property configures the name of the shared instance of LocalDB to connect to. | Name of shared instance of LocalDB. | + |FileStreamDirectory | (Optional) If File Stream is enabled on SQL Server, pass local directory path to be used for setting up File Stream enabled database. | `D:\\escaped\\absolute\\path\\to\\directory\\` | + |UseManagedSNIOnWindows | (Optional) Enables testing with Managed SNI on Windows| `true` OR `false`| + |DNSCachingConnString | Connection string for a server that supports DNS Caching| + |IsAzureSynpase | (Optional) When set to 'true', test suite runs compatible tests for Azure Synapse/Parallel Data Warehouse. | `true` OR `false`| + |EnclaveAzureDatabaseConnString | (Optional) Connection string for Azure database with enclaves | + |ManagedIdentitySupported | (Optional) When set to `false` **Managed Identity** related tests won't run. The default value is `true`. | + |IsManagedInstance | (Optional) When set to `true` **TVP** related tests will use on non-Azure bs files to compare test results. this is needed when testing against Managed Instances or TVP Tests will fail on Test set 3. The default value is `false`. | + |PowerShellPath | The full path to PowerShell.exe. This is not required if the path is present in the PATH environment variable. | `D:\\escaped\\absolute\\path\\to\\PowerShell.exe` | + +### Commands to run Manual Tests + +- Windows (`netfx x86`): -Commands to run tests: +```bash +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x86" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" + ``` + +- Windows (`netfx x64`): -Windows (`netfx x86`): ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" ``` -Windows (`netfx x64`): +- Windows (`netfx`): + ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" ``` -Windows (`netcoreapp`): +- Windows (`netcoreapp`): + ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" ``` -Unix (`netcoreapp`): +- Unix (`netcoreapp`): + ```bash -> dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" +dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" ``` ## Run A Single Test + ```bash -> dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "FullyQualifiedName=Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.CspProviderExt.TestKeysFromCertificatesCreatedWithMultipleCryptoProviders" +dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "FullyQualifiedName=Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.CspProviderExt.TestKeysFromCertificatesCreatedWithMultipleCryptoProviders" ``` ## Testing with Custom ReferenceType @@ -158,82 +239,98 @@ Tests can be built and run with custom "Reference Type" property that enables di > ************** IMPORTANT NOTE BEFORE PROCEEDING WITH "PACKAGE" AND "NETSTANDARDPACKAGE" REFERENCE TYPES *************** > CREATE A NUGET PACKAGE WITH BELOW COMMAND AND ADD TO LOCAL FOLDER + UPDATE NUGET CONFIG FILE TO READ FROM THAT LOCATION -> ``` -> > msbuild /p:configuration=Release +> +> ```bash +> msbuild -p:configuration=Release > ``` -### Building Tests: +A non-AnyCPU platform reference can only be used with package and NetStandardPackage reference types. Otherwise, the specified platform will be replaced with AnyCPU in the build process. -For .NET Core, all 4 reference types are supported: +### Building Tests with Reference Type + +For .NET, all 4 reference types are supported: ```bash -> msbuild /t:BuildTestsNetCore /p:ReferenceType=Project +msbuild -t:BuildTestsNetCore -p:ReferenceType=Project # Default setting uses Project Reference. -> msbuild /t:BuildTestsNetCore /p:ReferenceType=Package +msbuild -t:BuildTestsNetCore -p:ReferenceType=Package -> msbuild /t:BuildTestsNetCore /p:ReferenceType=NetStandard +msbuild -t:BuildTestsNetCore -p:ReferenceType=NetStandard -> msbuild /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage +msbuild -t:BuildTestsNetCore -p:ReferenceType=NetStandardPackage ``` For .NET Framework, below reference types are supported: ```bash -> msbuild /t:BuildTestsNetFx /p:ReferenceType=Project +msbuild -t:BuildTestsNetFx -p:ReferenceType=Project # Default setting uses Project Reference. -> msbuild /t:BuildTestsNetFx /p:ReferenceType=Package +msbuild -t:BuildTestsNetFx -p:ReferenceType=Package ``` -### Running Tests: +### Running Tests with Reference Type Provide property to `dotnet test` commands for testing desired reference type. -``` -dotnet test /p:ReferenceType=Project ... + +```bash +dotnet test -p:ReferenceType=Project ... ``` -## Testing with Custom TargetFramework +## Testing with Custom TargetFramework (traditional) Tests can be built and run with custom Target Frameworks. See the below examples. -### Building Tests: +### Building Tests with custom target framework ```bash -> msbuild /t:BuildTestsNetFx /p:TargetNetFxVersion=net461 +msbuild -t:BuildTestsNetFx -p:TargetNetFxVersion=net462 # Build the tests for custom TargetFramework (.NET Framework) -# Applicable values: net46 (Default) | net461 | net462 | net47 | net471 net472 | net48 +# Applicable values: net462 (Default) | net47 | net471 net472 | net48 | net481 ``` ```bash -> msbuild /t:BuildTestsNetCore /p:TargetNetCoreVersion=netcoreapp3.1 -# Build the tests for custom TargetFramework (.NET Core) -# Applicable values: netcoreapp2.1 | netcoreapp2.2 | netcoreapp3.1 | netcoreapp5.0 +msbuild -t:BuildTestsNetCore -p:TargetNetCoreVersion=net6.0 +# Build the tests for custom TargetFramework (.NET) +# Applicable values: net6.0 | net7.0 ``` -### Running Tests: +### Running Tests with custom target framework (traditional) ```bash -> dotnet test /p:TargetNetFxVersion=net461 ... +dotnet test -p:TargetNetFxVersion=net462 ... # Use above property to run Functional Tests with custom TargetFramework (.NET Framework) -# Applicable values: net46 (Default) | net461 | net462 | net47 | net471 net472 | net48 +# Applicable values: net462 (Default) | net47 | net471 net472 | net48 | net481 -> dotnet test /p:TargetNetCoreVersion=netcoreapp3.1 ... -# Use above property to run Functional Tests with custom TargetFramework (.NET Core) -# Applicable values: netcoreapp2.1 | netcoreapp2.2 | netcoreapp3.1 | netcoreapp5.0 +dotnet test -p:TargetNetCoreVersion=net6.0 ... +# Use above property to run Functional Tests with custom TargetFramework (.NET) +# Applicable values: net6.0 | net7.0 ``` ## Using Managed SNI on Windows Managed SNI can be enabled on Windows by enabling the below AppContext switch: -**"Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows"** +`Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows` ## Set truncation on for scaled decimal parameters Scaled decimal parameter truncation can be enabled by enabling the below AppContext switch: -**"Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal"** +`Switch.Microsoft.Data.SqlClient.TruncateScaledDecimal` + +## Enabling row version null behavior + +`SqlDataReader` returns a `DBNull` value instead of an empty `byte[]`. To enable the legacy behavior, you must enable the following AppContext switch on application startup: + +`Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior` + +## Suppressing TLS security warning + +When connecting to a server, if a protocol lower than TLS 1.2 is negotiated, a security warning is output to the console. This warning can be suppressed on SQL connections with `Encrypt = false` by enabling the following AppContext switch on application startup: + +`Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning` ## Debugging SqlClient on Linux from Windows @@ -242,14 +339,17 @@ For enhanced developer experience, we support debugging SqlClient on Linux from This project is also included in `docker-compose.yml` to demonstrate connectivity with SQL Server docker image. To run the same: + 1. Build the Solution in Visual Studio 2. Set `docker-compose` as Startup Project 3. Run "Docker-Compose" launch configuration. 4. You will see similar message in Debug window: + ```log Connected to SQL Server v15.00.4023 from Unix 4.19.76.0 The program 'dotnet' has exited with code 0 (0x0). ``` + 5. Now you can write code in [Program.cs](/src/Microsoft.Data.SqlClient/tests/DockerLinuxTest/Program.cs) to debug SqlClient on Linux! ### Troubleshooting Docker issues @@ -259,6 +359,7 @@ There may be times where connection cannot be made to SQL Server, we found below - Clear Docker images to create clean image from time-to-time, and clear docker cache if needed by running `docker system prune` in Command Prompt. - If you face `Microsoft.Data.SqlClient.SNI.dll not found` errors when debugging, try updating the below properties in the netcore\Microsoft.Data.SqlClient.csproj file and try again: + ```xml Unix false @@ -278,3 +379,17 @@ dotnet test --collect:"Code Coverage" ```bash dotnet test --collect:"XPlat Code Coverage" ``` + +## Run Performance Tests + +### Running Performance test project directly + +Project location from Root: `src\Microsoft.Data.SqlClient\tests\PerformanceTests\Microsoft.Data.SqlClient.PerformanceTests.csproj` +Configure `runnerconfig.json` file with connection string and preferred settings to run Benchmark Jobs. + +```bash +cd src\Microsoft.Data.SqlClient\tests\PerformanceTests +dotnet run -c Release -f net6.0|net7.0 +``` + +_Only "**Release** Configuration" applies to Performance Tests_ diff --git a/CHANGELOG.md b/CHANGELOG.md index dceaa7c412..129ca46b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,50 +4,584 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [Stable release 5.1.0] - 2023-01-19 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed thread safety of transient error list in configurable retry logic. [#1882](https://github.com/dotnet/SqlClient/pull/1882) +- Fixed deadlock when using SinglePhaseCommit with distributed transactions. [#1801](https://github.com/dotnet/SqlClient/pull/1801) +- Fixed Dedicated Admin Connections (DAC) to localhost in managed SNI. [#1865](https://github.com/dotnet/SqlClient/pull/1865) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.0`. [#1889](https://github.com/dotnet/SqlClient/pull/1889) which includes fix for AppDomain crash in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418), TLS 1.3 Support, removal of ARM32 binaries, and support for the `ServerCertificate` option. +- Code health improvements [#1867](https://github.com/dotnet/SqlClient/pull/1867) [#1849](https://github.com/dotnet/SqlClient/pull/1849) + +## [Preview Release 5.1.0-preview2.22314.2] - 2022-11-10 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.1.0-preview1 + +- Dropped support for .NET Core 3.1. [#1704](https://github.com/dotnet/SqlClient/pull/1704) [#1823](https://github.com/dotnet/SqlClient/pull/1823) + +### Added + +- Added support for .NET 6.0. [#1704](https://github.com/dotnet/SqlClient/pull/1704) +- Added support for `DateOnly` and `TimeOnly` for `SqlParameter` value and `GetFieldValue`. [#1813](https://github.com/dotnet/SqlClient/pull/1813) +- Added support for TLS 1.3 for .NET Core and SNI Native. [#1821](https://github.com/dotnet/SqlClient/pull/1821) +- Added `ServerCertificate` support for `Encrypt=Mandatory` or `Encrypt=Strict`. [#1822](https://github.com/dotnet/SqlClient/pull/1822) +- Added Windows ARM64 support when targeting .NET Framework. [#1828](https://github.com/dotnet/SqlClient/pull/1828) + +### Fixed + +- Fixed memory leak regression from [#1781](https://github.com/dotnet/SqlClient/pull/1781) using a `DisposableTemporaryOnStack` struct. [#1818](https://github.com/dotnet/SqlClient/pull/1818) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.0-preview2.22311.2`. [#1831](https://github.com/dotnet/SqlClient/pull/1831) which includes the fix for the TLS 1.3 timeout and double handshake issue, removal of ARM32 binaries, and support for the `ServerCertificate` option. [#1822](https://github.com/dotnet/SqlClient/issues/1822) +- Reverted "Excluding unsupported TLS protocols" for issue [#1151](https://github.com/dotnet/SqlClient/issues/1151) (i.e. removed `Switch.Microsoft.Data.SqlClient.EnableSecureProtocolsByOS`) by adding support for TLS 1.3. [#1824](https://github.com/dotnet/SqlClient/issues/1824) +- Code health improvements [#1812](https://github.com/dotnet/SqlClient/pull/1812) [#1520](https://github.com/dotnet/SqlClient/pull/1520) + +## [Preview Release 5.1.0-preview1.22279.3] - 2022-10-19 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed `ReadAsync()` behavior to register Cancellation token action before streaming results. [#1781](https://github.com/dotnet/SqlClient/pull/1781) +- Fixed `NullReferenceException` when assigning `null` to `SqlConnectionStringBuilder.Encrypt`. [#1778](https://github.com/dotnet/SqlClient/pull/1778) +- Fixed missing `HostNameInCertificate` property in .NET Framework Reference Project. [#1776](https://github.com/dotnet/SqlClient/pull/1776) +- Fixed async deadlock issue when sending attention fails due to network failure. [#1766](https://github.com/dotnet/SqlClient/pull/1766) +- Fixed failed connection requests in ConnectionPool in case of PoolBlock. [#1768](https://github.com/dotnet/SqlClient/pull/1768) +- Fixed hang on infinite timeout and managed SNI. [#1742](https://github.com/dotnet/SqlClient/pull/1742) +- Fixed Default UTF8 collation conflict. [#1739](https://github.com/dotnet/SqlClient/pull/1739) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.0-preview1.22278.1`. [#1787](https://github.com/dotnet/SqlClient/pull/1787) which includes TLS 1.3 Support and fix for AppDomain crash in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Changed the `SqlConnectionEncryptOption` string parser to public. [#1771](https://github.com/dotnet/SqlClient/pull/1771) +- Converted `ExecuteNonQueryAsync` to use async context object. [#1692](https://github.com/dotnet/SqlClient/pull/1692) +- Code health improvements [#1604](https://github.com/dotnet/SqlClient/pull/1604) [#1598](https://github.com/dotnet/SqlClient/pull/1598) [#1595](https://github.com/dotnet/SqlClient/pull/1595) [#1443](https://github.com/dotnet/SqlClient/pull/1443) + +### Known issues + +- When using `Encrypt=Strict` with TLS v1.3, the TLS handshake occurs twice on initial connection on .NET Framework due to a timeout during the TLS handshake and a retry helper re-establishes the connection; however, on .NET Core, it will throw a `System.ComponentModel.Win32Exception (258): The wait operation timed out.` and is being investigated. If you're using Microsoft.Data.SqlClient with .NET Core on Windows 11, you will need to enable the managed SNI on Windows context switch using following statement `AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", true);` to use TLS v1.3 or disabling TLS 1.3 from the registry by assigning `0` to the following `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Client\Enabled` registry key and it'll use TLS v1.2 for the connection. This will be fixed in a future release. + +## [Stable release 5.0.1] - 2022-10-07 + +### Fixed + +- Fixed missing `HostNameInCertificate` connection string property in .NET Framework. [#1782](https://github.com/dotnet/SqlClient/pull/1782) +- Fixed async deadlock issue when sending attention fails due to network failure. [#1783](https://github.com/dotnet/SqlClient/pull/1783) +- Fixed **Null Reference Exception** on assigning `null` to `SqlConnectionStringBuilder.Encrypt`. [#1784](https://github.com/dotnet/SqlClient/pull/1784) +- Fixed `ReadAsync()` behavior to register Cancellation token action before streaming results. [#1785](https://github.com/dotnet/SqlClient/pull/1785) +- Fixed hang on infinite timeout and managed SNI. [#1798](https://github.com/dotnet/SqlClient/pull/1798) +- Fixed Default UTF8 collation conflict. [#1799](https://github.com/dotnet/SqlClient/pull/1799) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.1` [#1795](https://github.com/dotnet/SqlClient/pull/1795), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418). + +## [Stable release 5.0.0] - 2022-08-05 + +This update brings the below changes over the previous release: + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify `Encrypt=Strict` in the connection string. [#1608](https://github.com/dotnet/SqlClient/pull/1608) +- Added `TDS 8` version for TDSLogin. [#1657](https://github.com/dotnet/SqlClient/pull/1657) + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1688](https://github.com/dotnet/SqlClient/pull/1688) +- Fixed **KeyNotFoundException** for the `FailoverPartner` key on SQL servers with availability group configured. [#1614](https://github.com/dotnet/SqlClient/pull/1614) +- Fixed small inconsistency between netcore and netfx for `EncryptionOptions`. [#1672](https://github.com/dotnet/SqlClient/pull/1672) +- Fixed `Microsoft.SqlServer.Server` netcore project package reference. [#1654](https://github.com/dotnet/SqlClient/pull/1654) + +### Changed + +- Updated `AuthProviderInfo` struct to be matched the changes in native SNI for `TDS 8` server certificate validation. [#1680](https://github.com/dotnet/SqlClient/pull/1680) +- Updated default system protocol for `TDS 8` on managed code. [#1678](https://github.com/dotnet/SqlClient/pull/1678) +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0`. [#1680](https://github.com/dotnet/SqlClient/pull/1680) +- Updated **IdentityModel** dependency from 6.8.0 to 6.21.0 and **IdentityClient** from 4.32.2 to 4.45.0. [#1646](https://github.com/dotnet/SqlClient/pull/1646) +- Changed from union overlay design to reflected interfaces for SqlTypes. [1647](https://github.com/dotnet/SqlClient/pull/1647) + +## [Preview Release 5.0.0-preview3.22168.1] - 2022-06-16 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview2 + +- Dropped classes from the `Microsoft.Data.SqlClient.Server` namespace and replaced them with supported types from the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package.[#1585](https://github.com/dotnet/SqlClient/pull/1585) The affected classes and enums are: + - Microsoft.Data.SqlClient.Server.IBinarySerialize -> Microsoft.SqlServer.Server.IBinarySerialize + - Microsoft.Data.SqlClient.Server.InvalidUdtException -> Microsoft.SqlServer.Server.InvalidUdtException + - Microsoft.Data.SqlClient.Server.SqlFacetAttribute -> Microsoft.SqlServer.Server.SqlFacetAttribute + - Microsoft.Data.SqlClient.Server.SqlFunctionAttribute -> Microsoft.SqlServer.Server.SqlFunctionAttribute + - Microsoft.Data.SqlClient.Server.SqlMethodAttribute -> Microsoft.SqlServer.Server.SqlMethodAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregateAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute + - (enum) Microsoft.Data.SqlClient.Server.DataAccessKind -> Microsoft.SqlServer.Server.DataAccessKind + - (enum) Microsoft.Data.SqlClient.Server.Format -> Microsoft.SqlServer.Server.Format + - (enum) Microsoft.Data.SqlClient.Server.SystemDataAccessKind -> Microsoft.SqlServer.Server.SystemDataAccessKind + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify Encrypt=Strict in the connection string. Strict mode disables TrustServerCertificate (always treated as False in Strict mode). HostNameInCertificate has been added to help some Strict mode scenarios. [#1608](https://github.com/dotnet/SqlClient/pull/1608) +- Added support for specifying Server SPN and Failover Server SPN on the connection. [#1607](https://github.com/dotnet/SqlClient/pull/1607) +- Added support for aliases when targeting .NET Core on Windows. [#1588](https://github.com/dotnet/SqlClient/pull/1588) + +### Fixed + +- Fixed naming, order, and formatting for `SqlDiagnosticsListener` on .NET Core and .NET. [#1637](https://github.com/dotnet/SqlClient/pull/1637) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1625](https://github.com/dotnet/SqlClient/pull/1625) +- Added CommandText length validation when using stored procedure command types. [#1484](https://github.com/dotnet/SqlClient/pull/1484) +- Fixed `GetSchema("StructuredTypeMembers")` to return correct schema information. [#1500](https://github.com/dotnet/SqlClient/pull/1500), [#1639](https://github.com/dotnet/SqlClient/pull/1639) +- Fixed NullReferenceException when using `SqlDependency.Start` against an Azure SQL Database.[#1294](https://github.com/dotnet/SqlClient/pull/1294) +- Send the correct retained transaction descriptor in the MARS TDS Header when there is no current transaction on .NET 5+ and .NET Core. [#1624](https://github.com/dotnet/SqlClient/pull/1624) +- Parallelize SSRP requests (instance name resolution) on Linux and macOS when MultiSubNetFailover is specified. [#1578](https://github.com/dotnet/SqlClient/pull/1578) +- Adjust the default ConnectRetryCount against Azure Synapse OnDemand endpoints [#1626](https://github.com/dotnet/SqlClient/pull/1626) + +### Changed + +- Code health improvements [#1353](https://github.com/dotnet/SqlClient/pull/1353) [#1354](https://github.com/dotnet/SqlClient/pull/1354) [#1525](https://github.com/dotnet/SqlClient/pull/1525) [#1186](https://github.com/dotnet/SqlClient/pull/1186) +- Update Azure Identity dependency from 1.5.0 to 1.6.0.[#1611](https://github.com/dotnet/SqlClient/pull/1611) +- Improved Regex for SqlCommandSet [#1548](https://github.com/dotnet/SqlClient/pull/1548) +- Rework on `TdsParserStateObjectManaged` with nullable annotations. [#1555](https://github.com/dotnet/SqlClient/pull/1555) + +## [Preview Release 5.0.0-preview2.22096.2] - 2022-04-06 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview1 + +- Dropped support for .NET Framework 4.6.1 [#1574](https://github.com/dotnet/SqlClient/pull/1574) + +### Fixed + +- Fixed connection failure by skipping Certificate Revocation List (CRL) check during authentication [#1559](https://github.com/dotnet/SqlClient/pull/1559) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0-preview2.22084.1`. [#1563](https://github.com/dotnet/SqlClient/pull/1563) +- Updated `Azure.Identity` version to `1.5.0` and `Microsoft.Identity.Client` version to `4.30.1` [#1462](https://github.com/dotnet/SqlClient/pull/1462) +- Replaced AlwaysEncryptedAttestationException with SqlException [#1515](https://github.com/dotnet/SqlClient/pull/1515) +- Improved error message when adding wrong type to SqlParameterCollection [#1547](https://github.com/dotnet/SqlClient/pull/1547) +- Code health improvements [#1343](https://github.com/dotnet/SqlClient/pull/1343) [#1370](https://github.com/dotnet/SqlClient/pull/1370) [#1371](https://github.com/dotnet/SqlClient/pull/1371) [#1438](https://github.com/dotnet/SqlClient/pull/1438) [#1483](https://github.com/dotnet/SqlClient/pull/1483) + +## [Preview Release 5.0.0-preview1.22069.1] - 2022-03-09 + +### Added + +- Added SqlDataSourceEnumerator. [#1430](https://github.com/dotnet/SqlClient/pull/1430) +- Added new attestation protocol `None` option to forgo enclave attestation when using VBS enclaves. [#1425](https://github.com/dotnet/SqlClient/pull/1425) and [#1419](https://github.com/dotnet/SqlClient/pull/1419) +- Added a new AppContext switch to suppress insecure TLS warnings. [#1457](https://github.com/dotnet/SqlClient/pull/1457) + +### Fixed + +- Fixed all documentation paths to Unix format path. [#1442](https://github.com/dotnet/SqlClient/pull/1442) +- Fixed thread safety issue for `GetEnclaveProvider` by converting dictionary to concurrent dictionary. [#1451](https://github.com/dotnet/SqlClient/pull/1451) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v5.0.0-preview1.22062.1`. [#1537](https://github.com/dotnet/SqlClient/pull/1537) +- Modernized style in ValueUtilSmi. [#1351](https://github.com/dotnet/SqlClient/pull/1351) +- Changed SQL server codenames to version names. [#1439](https://github.com/dotnet/SqlClient/pull/1439) +- Prevented subtype generation in project files. [#1452](https://github.com/dotnet/SqlClient/pull/1452) +- Changed `Array.Copy` to `Buffer.BlockCopy` for byte arrays. [#1366](https://github.com/dotnet/SqlClient/pull/1366) +- Changed files in csproj to be alphabetically sorted in netfx and netcore. [#1364](https://github.com/dotnet/SqlClient/pull/1364) +- Sqlstream, SqlInternalTransaction and MetaDataUtilsSmi are moved to shared folder. [#1337](https://github.com/dotnet/SqlClient/pull/1337), [#1346](https://github.com/dotnet/SqlClient/pull/1346) and [#1339](https://github.com/dotnet/SqlClient/pull/1339) +- Various code improvements: [#1197](https://github.com/dotnet/SqlClient/pull/1197), [#1313](https://github.com/dotnet/SqlClient/pull/1313),[#1330](https://github.com/dotnet/SqlClient/pull/1330),[#1366](https://github.com/dotnet/SqlClient/pull/1366), [#1435](https://github.com/dotnet/SqlClient/pull/1435),[#1478](https://github.com/dotnet/SqlClient/pull/1478) + +## [Stable release 4.1.1] - 2022-09-13 + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1706](https://github.com/dotnet/SqlClient/pull/1706) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1708](https://github.com/dotnet/SqlClient/pull/1708), [#1746](https://github.com/dotnet/SqlClient/pull/1746) +- Added CommandText length validation when using stored procedure command types. [#1709](https://github.com/dotnet/SqlClient/pull/1709) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1710](https://github.com/dotnet/SqlClient/pull/1710) +- Fixed null SqlBinary as rowversion. [#1712](https://github.com/dotnet/SqlClient/pull/1712) +- Fixed table's collation overriding with default UTF8 collation. [#1749](https://github.com/dotnet/SqlClient/pull/1749) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1755](https://github.com/dotnet/SqlClient/pull/1755), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1711](https://github.com/dotnet/SqlClient/pull/1711) + +## [Stable release 4.1.0] - 2022-01-31 + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1419](https://github.com/dotnet/SqlClient/pull/1419) [#1425](https://github.com/dotnet/SqlClient/pull/1425) + +## [Stable release 4.0.2] - 2022-09-13 + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1718](https://github.com/dotnet/SqlClient/pull/1718) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1720](https://github.com/dotnet/SqlClient/pull/1720), [#1747](https://github.com/dotnet/SqlClient/pull/1747) +- Added CommandText length validation when using stored procedure command types. [#1721](https://github.com/dotnet/SqlClient/pull/1721) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1722](https://github.com/dotnet/SqlClient/pull/1722) +- Fixed null SqlBinary as rowversion. [#1724](https://github.com/dotnet/SqlClient/pull/1724) +- Fixed table's collation overriding with default UTF8 collation. [#1750](https://github.com/dotnet/SqlClient/pull/1750) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1754](https://github.com/dotnet/SqlClient/pull/1754), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1723](https://github.com/dotnet/SqlClient/pull/1723) + +## [Stable release 4.0.1] - 2022-01-17 + +### Added + +Added AppContext switch `SuppressInsecureTLSWarning` to allow suppression of TLS security warning when using `Encrypt=false` in the connection string. [#1457](https://github.com/dotnet/SqlClient/pull/1457) + +### Fixed + +- Fixed Kerberos authentication failure when using .NET 6. [#1411](https://github.com/dotnet/SqlClient/pull/1411) +- Fixed connection failure when using `SqlLocalDB` instance pipe name. [#1433](https://github.com/dotnet/SqlClient/pull/1433) +- Fixed a failure when executing concurrent queries requiring enclaves. [#1451](https://github.com/dotnet/SqlClient/pull/1451) +- Updated obsolete API calls targeting .NET 6. [#1401](https://github.com/dotnet/SqlClient/pull/1401) + +## [Stable Release 4.0.0] - 2021-11-18 + +### Added + +- Added missing `SqlClientLogger` class to .NET Core refs and missing `SqlClientLogger.LogWarning` method in .NET Framework refs [#1392](https://github.com/dotnet/SqlClient/pull/1392) + +### Changed + +- Avoid throwing unnecessary exception when an invalid `SqlNotificationInfo` value is received from SQL Server [#1378](https://github.com/dotnet/SqlClient/pull/1378) +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.0` [#1391](https://github.com/dotnet/SqlClient/pull/1391) + +## [Preview Release 4.0.0-preview3.21293.2] - 2021-10-20 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v4.0.0-preview2 + +- Dropped support for .NET Core 2.1 [#1272](https://github.com/dotnet/SqlClient/pull/1272) +- [.NET Framework] Exception will not be thrown if a User ID is provided in the connection string when using `Active Directory Integrated` authentication [#1359](https://github.com/dotnet/SqlClient/pull/1359) + +### Added + +- Add `GetFieldValueAsync` and `GetFieldValue` support for `XmlReader`, `TextReader`, `Stream` [#1019](https://github.com/dotnet/SqlClient/pull/1019) + +### Fixed + +- Fixed `FormatException` when opening a connection with event tracing enabled [#1291](https://github.com/dotnet/SqlClient/pull/1291) +- Fixed improper initialization of `ActiveDirectoryAuthenticationProvider` [#1328](https://github.com/dotnet/SqlClient/pull/1328) +- Fixed `MissingMethodException` when accessing `SqlAuthenticationParameters.ConnectionTimeout` [#1336](https://github.com/dotnet/SqlClient/pull/1336) +- Fixed data corruption issues by reverting changes to async cancellations [#1352](https://github.com/dotnet/SqlClient/pull/1352) +- Fixed performance degradation by reverting changes to MARS state machine [#1357](https://github.com/dotnet/SqlClient/pull/1357) +- Fixed bug where environment variables are ignored when using `Active Directory Default` authentication [#1360](https://github.com/dotnet/SqlClient/pull/1360) + +### Changed + +- Removed attributes for classes used in Microsoft.VSDesigner due to lack of support for Microsoft.Data.SqlClient [#1296](https://github.com/dotnet/SqlClient/pull/1296) +- Disable encryption when connecting to SQL LocalDB [#1312](https://github.com/dotnet/SqlClient/pull/1312) +- Various code health and performance improvements. See [milestone](https://github.com/dotnet/SqlClient/milestone/31?closed=1) for more info. + +## [Preview Release 4.0.0-preview2.21264.2] - 2021-09-21 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v4.0.0-preview1 + +- Removed `Configurable Retry Logic` safety switch. [#1254](https://github.com/dotnet/SqlClient/pull/1254) + +### Added + +- Added support for `SqlFileStream` on Windows using .NET Standard 2.0 and above. [#1240](https://github.com/dotnet/SqlClient/pull/1240) +- Added support for **localdb** `shared instance` using managed SNI. [#1237](https://github.com/dotnet/SqlClient/pull/1237) + +### Fixed + +- Fixed `.NET decimal` conversion from `SqlDecimal`. [#1179](https://github.com/dotnet/SqlClient/pull/1179) +- Fixed `Event Source` changes on **TryBeginExecuteEvent** and **WriteEndExecuteEvent** to address the failure on other MS products such as OpenTelemetry and Application Insight. [#1258](https://github.com/dotnet/SqlClient/pull/1258) +- Fixed command's async cancellation. [#956](https://github.com/dotnet/SqlClient/pull/956) +- Fixed deadlock in transaction using .NET Framework. [#1242](https://github.com/dotnet/SqlClient/pull/1242) +- Fixed unknown transaction state issues when prompting delegated transaction. [1216](https://github.com/dotnet/SqlClient/pull/1216) + +### Changed + +- Various code improvements [#1155](https://github.com/dotnet/SqlClient/pull/1155) [#1236](https://github.com/dotnet/SqlClient/pull/1236) [#1251](https://github.com/dotnet/SqlClient/pull/1251) [#1266](https://github.com/dotnet/SqlClient/pull/1266) + +## [Preview Release 4.0.0-preview1.21237.2] - 2021-08-25 + +### Breaking changes over stable release 3.0.0 + +- Changed `Encrypt` connection string property to be `true` by default. [#1210](https://github.com/dotnet/SqlClient/pull/1210) +- The driver now throws `SqlException` replacing `AggregateException` for active directory authentication modes. [#1213](https://github.com/dotnet/SqlClient/pull/1213) +- Dropped obsolete `Asynchronous Processing` connection property from .NET Framework. [#1148](https://github.com/dotnet/SqlClient/pull/1148) + +### Added + +- Added `SqlCommand.EnableOptimizedParameterBinding` property that when enabled increases performance for commands with very large numbers of parameters. [#1041](https://github.com/dotnet/SqlClient/pull/1041) +- Included `42108` and `42109` error codes to retriable transient errors list. [#1215](https://github.com/dotnet/SqlClient/pull/1215) +- Added new App Context switch to use OS enabled client protocols only. [#1168](https://github.com/dotnet/SqlClient/pull/1168) +- Added `PoolBlockingPeriod` connection property support in .NET Standard. [#1181](https://github.com/dotnet/SqlClient/pull/1181) +- Added support for `SqlDataReader.GetColumnSchema()` in .NET Standard. [#1181](https://github.com/dotnet/SqlClient/pull/1181) +- Added PropertyGrid support with component model annotations to `SqlConnectionStringBuilder` properties for .NET Core. [#1152](https://github.com/dotnet/SqlClient/pull/1152) + +### Fixed + +- Fixed issue with connectivity when TLS 1.3 is enabled on client and server. [#1168](https://github.com/dotnet/SqlClient/pull/1168) +- Fixed issue with connection encryption to ensure connections fail when encryption is required. [#1210](https://github.com/dotnet/SqlClient/pull/1210) +- Fixed issue where connection goes to unusable state. [#1128](https://github.com/dotnet/SqlClient/pull/1128) +- Fixed recursive calls to `RetryLogicProvider` when calling `SqlCommand.ExecuteScalarAsync`. [#1220](https://github.com/dotnet/SqlClient/pull/1220) +- Fixed async deadlock scenarios in web contexts with configurable retry logic provider. [#1220](https://github.com/dotnet/SqlClient/pull/1220) +- Fixed `EntryPointNotFoundException` in `InOutOfProcHelper` constructor. [#1120](https://github.com/dotnet/SqlClient/pull/1120) +- Fixed async thread blocking issues on `SqlConnection.Open()` for active directory authentication modes. [#1213](https://github.com/dotnet/SqlClient/pull/1213) +- Fixed driver behavior for Always Encrypted with secure enclaves to not fail when no user parameters have been provided. [#1115](https://github.com/dotnet/SqlClient/pull/1115) +- Fixed bug with `LegacyRowVersionNullBehavior` App Context switch. [#1182](https://github.com/dotnet/SqlClient/pull/1182) +- Fixed issues in Strings.resx file containing error messages. [#1136](https://github.com/dotnet/SqlClient/pull/1136) [#1178](https://github.com/dotnet/SqlClient/pull/1178) + +### Changed + +- Updated error code to match with Windows when certificate validation fails in non-Windows client environments. [#1130](https://github.com/dotnet/SqlClient/pull/1130) +- Removed designer attributes from `SqlCommand` and `SqlDataAdapter`. [#1132](https://github.com/dotnet/SqlClient/pull/1132) +- Updated configurable retry logic default retriable error list. [#1125](https://github.com/dotnet/SqlClient/pull/1125) +- Improved performance by changing `SqlParameter` bool fields to flags. [#1064](https://github.com/dotnet/SqlClient/pull/1064) +- Improved performance by implementing static delegates. [#1060](https://github.com/dotnet/SqlClient/pull/1060) +- Optimized async method allocations in .NET Framework by porting changes from .NET Core. [#1084](https://github.com/dotnet/SqlClient/pull/1084) +- Various code improvements [#902](https://github.com/dotnet/SqlClient/pull/902) [#925](https://github.com/dotnet/SqlClient/pull/925) [#933](https://github.com/dotnet/SqlClient/pull/933) [#934](https://github.com/dotnet/SqlClient/pull/934) [#1024](https://github.com/dotnet/SqlClient/pull/1024) [#1057](https://github.com/dotnet/SqlClient/pull/1057) [#1122](https://github.com/dotnet/SqlClient/pull/1122) [#1133](https://github.com/dotnet/SqlClient/pull/1133) [#1134](https://github.com/dotnet/SqlClient/pull/1134) [#1141](https://github.com/dotnet/SqlClient/pull/1141) [#1187](https://github.com/dotnet/SqlClient/pull/1187) [#1188](https://github.com/dotnet/SqlClient/pull/1188) [#1223](https://github.com/dotnet/SqlClient/pull/1223) [#1225](https://github.com/dotnet/SqlClient/pull/1225) [#1226](https://github.com/dotnet/SqlClient/pull/1226) + +## [Stable release 3.1.1] - 2022-08-12 + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1700](https://github.com/dotnet/SqlClient/pull/1700) +- Fixed Kerberos authentication failure when using .NET 6. [#1696](https://github.com/dotnet/SqlClient/pull/1696) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1695](https://github.com/dotnet/SqlClient/pull/1695) +- Removed union overlay design and use reflection in `SqlTypeWorkarounds`. [#1699](https://github.com/dotnet/SqlClient/pull/1699) + +## [Stable release 3.1.0] - 2022-03-30 + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1539](https://github.com/dotnet/SqlClient/pull/1539) +- Included `42108` and `42109` error codes to retriable transient errors list. [#1560](https://github.com/dotnet/SqlClient/pull/1560) + +### Fixed + +- Changed EnclaveDelegate.Crypto GetEnclaveProvider to use a thread safe concurrent dictionary. [#1564](https://github.com/dotnet/SqlClient/pull/1564 + +## [Stable Release 3.0.1] - 2021-09-24 + +### Fixed + +- Fixed async thread blocking issues on `SqlConnection.Open()` for active directory authentication modes. [#1270](https://github.com/dotnet/SqlClient/pull/1270) +- Fixed unknown transaction state issues when prompting delegated transaction. [1247](https://github.com/dotnet/SqlClient/pull/1247) +- Fixed issue with connection encryption to ensure connections fail when encryption is required. [#1233](https://github.com/dotnet/SqlClient/pull/1233) +- Fixed bug with `LegacyRowVersionNullBehavior` App Context switch. [#1246](https://github.com/dotnet/SqlClient/pull/1246) +- Fixed recursive calls to `RetryLogicProvider` when calling `SqlCommand.ExecuteScalarAsync`. [#1245](https://github.com/dotnet/SqlClient/pull/1245) +- Fixed async deadlock scenarios in web contexts with configurable retry logic provider. [#1245](https://github.com/dotnet/SqlClient/pull/1245) +- Fixed deadlock in transaction using .NET Framework. [#1243](https://github.com/dotnet/SqlClient/pull/1243) +- Fixed issue where connection goes to unusable state. [#1238](https://github.com/dotnet/SqlClient/pull/1238) + +## [Stable Release 3.0.0] - 2021-06-09 + +### Added + +- Added support for column encryption key caching when the server supports retrying queries that require enclave computations [#1062](https://github.com/dotnet/SqlClient/pull/1062) +- Added support for configurable retry logic configuration file in .NET Standard [#1090](https://github.com/dotnet/SqlClient/pull/1090) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v3.0.0` [#1102](https://github.com/dotnet/SqlClient/pull/1102) +- Improved event counter display information [#1091](https://github.com/dotnet/SqlClient/pull/1091) + +### Breaking Changes + +- Modified column encryption key store provider registrations to give built-in system providers precedence over providers registered on connection and command instances. [#1101](https://github.com/dotnet/SqlClient/pull/1101) + +## [Stable Release 2.1.5] - 2022-08-30 + +### Fixed + +- Added CommandText length validation when using stored procedure command types. [#1726](https://github.com/dotnet/SqlClient/pull/1726) +- Fixed Kerberos authentication failure when using .NET 6. [#1727](https://github.com/dotnet/SqlClient/pull/1727) +- Removed union overlay design and use reflection in `SqlTypeWorkarounds`. [#1729](https://github.com/dotnet/SqlClient/pull/1729) + +## [Stable Release 2.1.4] - 2021-09-20 + +### Fixed + +- Fixed issue with connection encryption to ensure connections fail when encryption is required. [#1232](https://github.com/dotnet/SqlClient/pull/1232) +- Fixed issue where connection goes to unusable state. [#1239](https://github.com/dotnet/SqlClient/pull/1239) + +## [Stable Release 2.1.3] - 2021-05-21 + +### Fixed + +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1051](https://github.com/dotnet/SqlClient/pull/1051) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1049](https://github.com/dotnet/SqlClient/pull/1049) + +## [Preview Release 3.0.0-preview3.21140.5] - 2021-05-20 + +### Added + +- Added support for "Active Directory Default" authentication mode [#1043](https://github.com/dotnet/SqlClient/pull/1043) +- Added support for connection-level and command-level registration of custom key store providers to enable multi-tenant applications to control key store access [#1045](https://github.com/dotnet/SqlClient/pull/1045) [#1056](https://github.com/dotnet/SqlClient/pull/1056) [#1078](https://github.com/dotnet/SqlClient/pull/1078) +- Added IP address preference support for TCP connections [#1015](https://github.com/dotnet/SqlClient/pull/1015) + +### Fixed + +- Fixed corrupted connection issue when an exception occurs during RPC execution with TVP types [#1068](https://github.com/dotnet/SqlClient/pull/1068) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1042](https://github.com/dotnet/SqlClient/pull/1042) + +### Changed + +- Updated error messages for enclave exceptions to include a link to a troubleshooting guide. [#994](https://github.com/dotnet/SqlClient/pull/994) +- Changes to share common files between projects [#1022](https://github.com/dotnet/SqlClient/pull/1022) [#1038](https://github.com/dotnet/SqlClient/pull/1038) [#1040](https://github.com/dotnet/SqlClient/pull/1040) [#1033](https://github.com/dotnet/SqlClient/pull/1033) [#1028](https://github.com/dotnet/SqlClient/pull/1028) [#1039](https://github.com/dotnet/SqlClient/pull/1039) + +## [Preview Release 3.0.0-preview2.21106.5] - 2021-04-16 + +### Breaking Changes over preview release v3.0.0-preview1 + +- `User Id` connection property now requires `Client Id` instead of `Object Id` for **User-Assigned Managed Identity** [#1010](https://github.com/dotnet/SqlClient/pull/1010) +- `SqlDataReader` now returns a `DBNull` value instead of an empty `byte[]`. Legacy behavior can be enabled by setting `AppContext` switch **Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior** [#998](https://github.com/dotnet/SqlClient/pull/998) + +### Added + +- **Microsoft.Data.SqlClient** now depends on **Azure.Identity** library to acquire a token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. [#1010](https://github.com/dotnet/SqlClient/pull/1010) +- Upgraded Native SNI dependency to **v3.0.0-preview1** along with enhanced event tracing support [#1006](https://github.com/dotnet/SqlClient/pull/1006) + +### Fixed + +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set[#1023](https://github.com/dotnet/SqlClient/pull/1023) +- Fixed derived parameters containing incorrect TypeName [#1020](https://github.com/dotnet/SqlClient/pull/1020) +- Fixed server connection leak possibilities when an exception occurs in pooling layer [#890](https://github.com/dotnet/SqlClient/pull/890) +- Fixed IP connection resolving logic in .NET Core [#1016](https://github.com/dotnet/SqlClient/pull/1016) [#1031](https://github.com/dotnet/SqlClient/pull/1031) + +### Changed + +- Performance improvements in `SqlDateTime` to `DateTime` internal conversion method [#912](https://github.com/dotnet/SqlClient/pull/912) +- Improved memory allocation by avoiding unnecessary context switching [1008](https://github.com/dotnet/SqlClient/pull/1008) +- Updated `Microsoft.Identity.Client` version from **4.21.1** to **4.22.0** [#1036](https://github.com/dotnet/SqlClient/pull/1036) +- Various performance improvements [#963](https://github.com/dotnet/SqlClient/pull/963) [#996](https://github.com/dotnet/SqlClient/pull/996) [#1004](https://github.com/dotnet/SqlClient/pull/1004) [#1012](https://github.com/dotnet/SqlClient/pull/1012) [#1017](https://github.com/dotnet/SqlClient/pull/1017) +- Event source tracing improvements [#1018](https://github.com/dotnet/SqlClient/pull/1018) +- Changes to share common files between NetFx and NetCore source code [#871](https://github.com/dotnet/SqlClient/pull/871) [#887](https://github.com/dotnet/SqlClient/pull/887) + +## [Preview Release 3.0.0-preview1.21075.2] - 2021-03-15 + +### Breaking Changes over stable release v2.1 + +- The minimum supported .NET Framework version has been increased to v4.6.1. .NET Framework v4.6.0 is no longer supported. [#899](https://github.com/dotnet/SqlClient/pull/899) + +### Added + +- Added support for Configurable Retry Logic [#693](https://github.com/dotnet/SqlClient/pull/693) [#966](https://github.com/dotnet/SqlClient/pull/966) +- Added support for Event counters in .NET Core 3.1+ and .NET Standard 2.1+ [#719](https://github.com/dotnet/SqlClient/pull/719) +- Added support for Assembly Context Unloading in .NET Core [#913](https://github.com/dotnet/SqlClient/pull/913) +- Added missing `System.Runtime.Caching` dependency for .NET Standard assemblies [#877](https://github.com/dotnet/SqlClient/pull/877) + +### Fixed + +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#906](https://github.com/dotnet/SqlClient/pull/906) +- Fixed Kerberos authentication issues when configured Server Principal Name (SPN) didn't contain default port [#930](https://github.com/dotnet/SqlClient/pull/930) +- Fixed MARS header errors when `MakeReadAsyncBlocking` App Context switch is set to `false` [#910](https://github.com/dotnet/SqlClient/pull/910) [#922](https://github.com/dotnet/SqlClient/pull/922) +- Fixed unwanted exceptions being thrown from `SqlDataReader.Dispose` [#920](https://github.com/dotnet/SqlClient/pull/920) +- Fixed issues connecting to SQL Server instance with instance name specified from Unix environment [#870](https://github.com/dotnet/SqlClient/pull/870) +- Fixed TCP Keep Alive issues in .NET Core [#854](https://github.com/dotnet/SqlClient/pull/854) +- Fixed Kerberos Authentication issues caused due to regression [#845](https://github.com/dotnet/SqlClient/pull/845) +- Fixed issues with System-Assigned Managed Identity in Azure Functions [#829](https://github.com/dotnet/SqlClient/pull/829) +- Fixed missing error messages in Managed SNI [#882](https://github.com/dotnet/SqlClient/pull/882) +- Fixed event source trace string issue [#940](https://github.com/dotnet/SqlClient/pull/940) + +### Changed + +- Changed App Context switch `MakeReadAsyncBlocking` default to `false` [#937](https://github.com/dotnet/SqlClient/pull/937) +- Replaced usage of `BinaryFormatter` with `DataContractSerializer` [#869](https://github.com/dotnet/SqlClient/pull/869) +- Prohibited `DtdProcessing` on `XmlTextReader` instance in .NET Core [#884](https://github.com/dotnet/SqlClient/pull/884) +- Improved performance by reducing memory allocations in `SerializeEncodingChar`/`WriteEncodingChar` and some options boxing [#785](https://github.com/dotnet/SqlClient/pull/785) +- Improved performance by preventing orphaned active packets being GC'ed without clear [#888](https://github.com/dotnet/SqlClient/pull/888) +- Various performance improvements [#889](https://github.com/dotnet/SqlClient/pull/889) [#900](https://github.com/dotnet/SqlClient/pull/900) +- Partial event source tracing improvements in .NET Core [#867](https://github.com/dotnet/SqlClient/pull/867) [#897](https://github.com/dotnet/SqlClient/pull/897) +- Changes to share common files between NetFx and NetCore source code [#827](https://github.com/dotnet/SqlClient/pull/827) [#835](https://github.com/dotnet/SqlClient/pull/835) [#838](https://github.com/dotnet/SqlClient/pull/838) [#881](https://github.com/dotnet/SqlClient/pull/881) + +## [Stable Release 1.1.4] - 2021-03-10 + +### Fixed + +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#950](https://github.com/dotnet/SqlClient/pull/950) +- Fixed MARS header contains errors issue against .NET Framework 4.8+ [#959](https://github.com/dotnet/SqlClient/pull/959) + +## [Stable Release 2.1.2] - 2021-03-03 + +### Fixed + +- Fixed issue connecting with instance name from a Linux/macOS environment [#874](https://github.com/dotnet/SqlClient/pull/874) +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#929](https://github.com/dotnet/SqlClient/pull/929) +- Fixed a vulnerability by prohibiting `DtdProcessing` on `XmlTextReader` instances in .NET Core [#885](https://github.com/dotnet/SqlClient/pull/885) +- Fixed Kerberos authentication when an SPN does not contain the port [#935](https://github.com/dotnet/SqlClient/pull/935) +- Fixed missing error messages in Managed SNI [#883](https://github.com/dotnet/SqlClient/pull/883) +- Fixed missing `System.Runtime.Caching` dependency for .NET Standard assemblies [#878](https://github.com/dotnet/SqlClient/pull/878) +- Fixed event source tracing issues [#941](https://github.com/dotnet/SqlClient/pull/941) +- Fixed MARS header contains errors issue against .NET Framework 4.8.1 [#928](https://github.com/dotnet/SqlClient/pull/928) + +## [Stable Release 2.1.1] - 2020-12-18 + +### Fixed + +- Fixed issue with System-Assigned Managed Identity in Azure Functions [#841](https://github.com/dotnet/SqlClient/pull/841) +- Fixed issue with Kerberos Authentication for .NET Core in Unix environments [#848](https://github.com/dotnet/SqlClient/pull/848) +- Fixed issue with TCP Keep Alive for .NET Core in Unix environments [#855](https://github.com/dotnet/SqlClient/pull/855) + ## [Stable Release 2.1.0] - 2020-11-19 ### Added + - Microsoft.Data.SqlClient symbols are now source-linked [#789](https://github.com/dotnet/SqlClient/pull/789) - Added an API to clear cached access tokens from the token provider [#800](https://github.com/dotnet/SqlClient/pull/800) - Added `SqlFacetAttribute` implementation [#757](https://github.com/dotnet/SqlClient/pull/757) ### Fixed + - Fixed `InvalidOperationException` and `NotSupportedException` errors due to `WriteAsync` collisions [#796](https://github.com/dotnet/SqlClient/pull/796) - Fixed incorrect Settings.Async flag in `ExecuteXmlReaderAsync` [#782](https://github.com/dotnet/SqlClient/pull/782) - Fixed a regression in Windows Integrated authentication when using managed networking [#777](https://github.com/dotnet/SqlClient/pull/777) - Fixed Bulk Copy Async deadlock issues with custom `IDataReader` when using `SqlDataReader` internally [#779](https://github.com/dotnet/SqlClient/pull/779) - Fixed a serialization issue with `SqlException` in .NET Core [#780](https://github.com/dotnet/SqlClient/pull/780) -### Changes -- Updated versions of `Microsoft.IdentityModel` package dependencies [#794](https://github.com/dotnet/SqlClient/pull/794) +### Changed +- Updated versions of `Microsoft.IdentityModel` package dependencies [#794](https://github.com/dotnet/SqlClient/pull/794) ## [Preview Release 2.1.0-preview2.20297.7] - 2020-10-23 ### Added + - Added support for Azure Active Directory Managed Identity authentication [#730](https://github.com/dotnet/SqlClient/pull/730) - Added support to provide a user-defined application client ID when using Active Directory authentication [#740](https://github.com/dotnet/SqlClient/pull/740) - Added the "Command Timeout" connection string property to set a default timeout for all commands executed with the connection [#722](https://github.com/dotnet/SqlClient/pull/722) - Added support for Always Encrypted on all supported platforms for .NET Standard 2.0 [#756](https://github.com/dotnet/SqlClient/pull/756) ### Fixed + - Fixed unobserved exception issue when a timeout occurs before a faulted task completes with an exception [#688](https://github.com/dotnet/SqlClient/pull/688) [#773](https://github.com/dotnet/SqlClient/pull/773) - Fixed an issue where driver continues to prompt for credentials when using Azure Active Directory authentication [#770](https://github.com/dotnet/SqlClient/pull/770) -### Changes +### Changed + - Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v2.1.1` and removed symbols from `Microsoft.Data.SqlClient.SNI.runtime`, which are now published to Microsoft Symbols Server [#764](https://github.com/dotnet/SqlClient/pull/764) - Updated `Microsoft.Identity.Client` dependency version to `v4.21.1` [#765](https://github.com/dotnet/SqlClient/pull/765) - Performance improvements when establishing an encrypted channel by removing sync over async method calls [#541](https://github.com/dotnet/SqlClient/pull/541) - Performance improvements by replacing heap-allocated arrays with Spans [#667](https://github.com/dotnet/SqlClient/pull/667) - Moved common files to shared folder between .NET Framework and .NET Core implementation [#734](https://github.com/dotnet/SqlClient/pull/734) [#753](https://github.com/dotnet/SqlClient/pull/753) - ## [Stable Release 2.0.1] - 2020-08-25 ### Added + - Added support for a new Configuration Section, `SqlClientAuthenticationProviders` (duplicate of existing `SqlAuthenticationProviders`), to allow co-existence of configurations for both drivers, "System.Data.SqlClient" and "Microsoft.Data.SqlClient" [#701](https://github.com/dotnet/SqlClient/pull/701) ### Fixed + - Fixed pooled connection re-use on access token expiry issue when using Active Directory authentication modes [#639](https://github.com/dotnet/SqlClient/pull/639) - Fixed transient fault handling for Pooled connections [#638](https://github.com/dotnet/SqlClient/pull/638) - Fixed Enclave session cache issue with Azure Database [#628](https://github.com/dotnet/SqlClient/pull/628) @@ -55,10 +589,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed configuration section collision issue with System.Data.SqlClient type [#701](https://github.com/dotnet/SqlClient/pull/701) - Fixed blank error message [HTTP Provider] issues due to unexpected pre-login failures when using Native SNI. Fixed with Microsoft.Data.SqlClient.SNI v2.0.1 and Microsoft.Data.SqlClient.SNI.runtime v2.0.1 release versions. - ## [Preview Release 2.1.0-preview1.20235.1] - 2020-08-21 ### Added + - Added support for Always Encrypted with secure enclaves on Unix for .NET Core 2.1+ and on all supported platforms for .NET Standard 2.1+ [#676](https://github.com/dotnet/SqlClient/pull/676) - Added support for Azure Active Directory Device Code Flow authentication [#597](https://github.com/dotnet/SqlClient/pull/597) - Added Sensitivity Rank support in Sensitivity Classification information [#626](https://github.com/dotnet/SqlClient/pull/626) @@ -68,13 +602,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Updated Microsoft.Data.SqlClient.SNI (.NET Framework dependency) and Microsoft.Data.SqlClient.SNI.runtime (.NET Core/Standard dependency) version to v2.1.0 with trace logging implementation [#705](https://github.com/dotnet/SqlClient/pull/705) ### Fixed + - Fixed Enclave session cache issue with Azure Database [#686](https://github.com/dotnet/SqlClient/pull/686) - Fixed pooled connection re-use on access token expiry issue when using Active Directory authentication modes [#635](https://github.com/dotnet/SqlClient/pull/635) - Fixed transient fault handling for Pooled connections [#637](https://github.com/dotnet/SqlClient/pull/637) - Fixed SPN generation issue when no port is provided [#629](https://github.com/dotnet/SqlClient/pull/629) - Fixed missing null checks for `SqlErrors` in `SqlException` for .NET Framework implementation [#698](https://github.com/dotnet/SqlClient/pull/698) -### Changes +### Changed + - Performance improvements by fixing unnecessary allocations with EventSource implementation [#684](https://github.com/dotnet/SqlClient/pull/684) - Reverted changes to return empty DataTable from GetSchemaTable to return null as before. [#696](https://github.com/dotnet/SqlClient/pull/696) - Removed multiple `CacheConnectionStringProperties` calls when setting `ConnectionString` properties [#683](https://github.com/dotnet/SqlClient/pull/683) @@ -85,16 +621,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Performance improvements by moving `DataReader` caches to internal connection [#499](https://github.com/dotnet/SqlClient/pull/499) - Moved common files to shared folder between .NET Framework and .NET Core implementation [#618](https://github.com/dotnet/SqlClient/pull/618) [#625](https://github.com/dotnet/SqlClient/pull/625) - ## [Stable Release 2.0.0] - 2020-06-16 ### Added + - Added internal driver support to provide resiliency to DNS failures [#594](https://github.com/dotnet/SqlClient/pull/594) - Added support for `Active Directory Integrated`, `Active Directory Interactive` and `Active Directory Service Principal` authentication mode for .NET Core and .NET Standard [#560](https://github.com/dotnet/SqlClient/pull/560) - Added support for `Active Directory Service Principal` authentication mode for .NET Framework [#560](https://github.com/dotnet/SqlClient/pull/560) - Added support for optional `ORDER` hints in `SqlBulkCopy` for improved performance [#540](https://github.com/dotnet/SqlClient/pull/540) ### Fixed + - Fixed `SqlSequentialStream` multipacket read stalling issue in .NET Core [#603](https://github.com/dotnet/SqlClient/pull/603) - Fixed code page issue for Kazakh collation in SQL Server [#584](https://github.com/dotnet/SqlClient/pull/584) - Fixed stalled application issues when end of stream is reached [#577](https://github.com/dotnet/SqlClient/pull/577) @@ -102,81 +639,91 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed Object null reference issue when failover partner is set [#588](https://github.com/dotnet/SqlClient/pull/588) - Fixed `applicationintent` connection string property issue [#585](https://github.com/dotnet/SqlClient/pull/585) -### Changes +### Changed + - Raise warning message when insecure TLS protocols are in use [#591](https://github.com/dotnet/SqlClient/pull/591) ### Breaking Changes + - Modified enclave provider interface `SqlColumnEncryptionEnclaveProvider` to be internal [#602](https://github.com/dotnet/SqlClient/pull/602) - _This change is not likely to impact customer applications since secure enclaves is a relatively new feature and they would have had to implement their own enclave provider, which is not a trivial task_. - Updated `SqlClientMetaDataCollectionNames` exposed constants by removing non-existing constants and adding new to the metadata collection [#580](https://github.com/dotnet/SqlClient/pull/580) - ## [Preview Release 2.0.0-preview4.20142.4] - 2020-05-21 ### Added + - Microsoft.Data.SqlClient (.NET Core and .NET Standard) on Windows is now dependent on **Microsoft.Data.SqlClient.SNI.runtime**, replacing the previous dependency on **runtime.native.System.Data.SqlClient.SNI** [#570](https://github.com/dotnet/SqlClient/pull/570) -- The new **Microsoft.Data.SqlClient.SNI.runtime** dependency adds support for the *ARM* platform along with the already supported platforms *ARM64*, *x64* and *x86* on Windows [#570](https://github.com/dotnet/SqlClient/pull/570) +- The new **Microsoft.Data.SqlClient.SNI.runtime** dependency adds support for the _ARM_ platform along with the already supported platforms _ARM64_, _x64_ and _x86_ on Windows [#570](https://github.com/dotnet/SqlClient/pull/570) - Improved driver performance by introducing managed packet recycling [#389](https://github.com/dotnet/SqlClient/pull/389) ### Fixed + - Fixed `SqlBulkCopy` to work with database columns containing metadata about data classification [#568](https://github.com/dotnet/SqlClient/pull/568) - Fixed unsafe cast in `SqlException` for `SerializationEntry.Value` - Fixed null reference exceptions in `SqlDelegatedTransaction` methods [#563](https://github.com/dotnet/SqlClient/pull/563) -### Changes +### Changed + - Standardized connection string properties for enhanced user experience [#534](https://github.com/dotnet/SqlClient/pull/534) - Improved performance by reducing eventsource tracing related to allocations from TVP write methods [#557](https://github.com/dotnet/SqlClient/pull/557) [#564](https://github.com/dotnet/SqlClient/pull/564) ### Breaking Changes -- For .NET Framework applications consuming **Microsoft.Data.SqlClient**, the `SNI.dll` files previously downloaded to the `bin\x64` and `bin\x86` folders are now named `Microsoft.Data.SqlClient.SNI.x64.dll` and `Microsoft.Data.SqlClient.SNI.x86.dll` and will be downloaded to the `bin` directory, to support auto-loading in the application process [#570](https://github.com/dotnet/SqlClient/pull/570). This change is not going to impact client applications unless a direct reference has been made to `SNI.dll` or the x86 and x64 folders. +- For .NET Framework applications consuming **Microsoft.Data.SqlClient**, the `SNI.dll` files previously downloaded to the `bin\x64` and `bin\x86` folders are now named `Microsoft.Data.SqlClient.SNI.x64.dll` and `Microsoft.Data.SqlClient.SNI.x86.dll` and will be downloaded to the `bin` directory, to support auto-loading in the application process [#570](https://github.com/dotnet/SqlClient/pull/570). This change is not going to impact client applications unless a direct reference has been made to `SNI.dll` or the x86 and x64 folders. ## [Stable Release 1.1.3] - 2020-05-15 ### Fixed + - Fixed driver behavior to not perform enlistment of pooled connection on aborted transaction [#551](https://github.com/dotnet/SqlClient/pull/551) - Fixed issues introduced with MARS TDS Header fix in last release by reverting original change that caused issues. [#550](https://github.com/dotnet/SqlClient/pull/550) - ## [Preview Release 2.0.0-preview3.20122.2] - 2020-05-01 ### Added + - Allow passing username with Active Directory Interactive Authentication in .NET Framework [#492](https://github.com/dotnet/SqlClient/pull/492) - Allow large UDT buffers for .NET Framework [#456](https://github.com/dotnet/SqlClient/pull/456) - Added "Transaction Id" and "Client Version" in Diagnostic Source traces [#515](https://github.com/dotnet/SqlClient/pull/515) - Added new `SqlConnectionOverrides` APIs to perform `SqlConnection.Open()` with fail fast option [#463](https://github.com/dotnet/SqlClient/pull/463) ### Fixed + - Addressed MARS TDS Header errors by reverting changes to make `SqlDataReader.ReadAsync()` non-blocking [#547](https://github.com/dotnet/SqlClient/pull/547) - Fixed driver behavior to not perform enlistment of pooled connection in aborted transaction [#543](https://github.com/dotnet/SqlClient/pull/543) - Fixed wrong application domain selected when starting `SqlDependencyListener` [#410](https://github.com/dotnet/SqlClient/pull/410) - Added missing refs for `RowCopied` property in `SqlBulkCopy` [#508](https://github.com/dotnet/SqlClient/pull/508) -### Changes +### Changed + - Improved performance by removing unwanted method calls in Event Source tracing [#506](https://github.com/dotnet/SqlClient/pull/506) - Removed Diagnostic Source and Configuration Manager dependencies from .NET Standard implementation [#535](https://github.com/dotnet/SqlClient/pull/535) - Removed redundant calls to `DbConnectionPoolKey.GetType()` [#512](https://github.com/dotnet/SqlClient/pull/512) ### Breaking Changes + - Updated driver to perform decimal scale rounding to match SQL Server behavior [#470](https://github.com/dotnet/SqlClient/pull/470) - Standardized App Context switch name that enables Managed SNI on Windows for .NET Core and .NET Standard (break only applies to 2.0 preview releases that introduced the switch) [#548](https://github.com/dotnet/SqlClient/pull/548) - ## [Stable Release 1.1.2] - 2020-04-15 ### Added + - Allowed passing username with Active Directory Interactive Authentication [#493](https://github.com/dotnet/SqlClient/pull/493) [#516](https://github.com/dotnet/SqlClient/pull/516) ### Fixed + - Fixed the ConnectionString's password persistence in .NET Core. [#489](https://github.com/dotnet/SqlClient/pull/489) - Addressed MARS TDS header containing errors [#510](https://github.com/dotnet/SqlClient/pull/510) ### Changed -- Updated driver libraries to be CLS Compliant [#522](https://github.com/dotnet/SqlClient/pull/522) +- Updated driver libraries to be CLS Compliant [#522](https://github.com/dotnet/SqlClient/pull/522) ## [Preview Release 2.0.0-preview2.20084.1] - 2020-03-24 ### Added + - Added support for capturing EventSource traces in .NET Framework, .NET Core, and .NET Standard applications [#399](https://github.com/dotnet/SqlClient/pull/399) [#461](https://github.com/dotnet/SqlClient/pull/461) [#479](https://github.com/dotnet/SqlClient/pull/479) [#483](https://github.com/dotnet/SqlClient/pull/483) [#484](https://github.com/dotnet/SqlClient/pull/484) - Added support for Cross-platform TCP Keep Alive applicable to .NET Core 3.1+ applications [#395](https://github.com/dotnet/SqlClient/pull/395) - Added support for enabling Managed networking implementation on Windows applicable to .NET Core and .NET Standard applications [#477](https://github.com/dotnet/SqlClient/pull/477) @@ -186,11 +733,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added cached `SqlReferenceCollection.FindLiveReaderContext` objects [#380](https://github.com/dotnet/SqlClient/pull/380) ### Fixed + - Fixed Access Token behavior in connection pool to perform string comparison [#443](https://github.com/dotnet/SqlClient/pull/443) - Fixed concurrent connection speed issues when connecting with Azure Active Directory Authentication modes in .NET Core [#466](https://github.com/dotnet/SqlClient/pull/466) - Fixed issues with `Password` persistence in Connection String [#453](https://github.com/dotnet/SqlClient/pull/453) -### Changes +### Changed + - Updated all driver assemblies to be CLS Compliant [#396](https://github.com/dotnet/SqlClient/pull/396) - Updated Bulk Copy error messages to also include Column, Row and non-encrypted Data information [#437](https://github.com/dotnet/SqlClient/pull/437) - Updated error messages for "Always Encrypted - Secure Enclaves" to handle 'Attestation Protocol' and fixed typos [#421](https://github.com/dotnet/SqlClient/pull/421) [#397](https://github.com/dotnet/SqlClient/pull/397) @@ -199,25 +748,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Multiple performance improvements [#377](https://github.com/dotnet/SqlClient/pull/377) [#378](https://github.com/dotnet/SqlClient/pull/378) [#379](https://github.com/dotnet/SqlClient/pull/379) ### Breaking Changes + - The driver will now perform Server Certificate validation when TLS encryption is enforced by the target Server, which is the default for Azure connections [#391](https://github.com/dotnet/SqlClient/pull/391) - `SqlDataReader.GetSchemaTable()` now returns an empty `DataTable` instead of returning `null` [#419](https://github.com/dotnet/SqlClient/pull/419) - ## [Stable Release 1.1.1] - 2020-02-14 ### Fixed + - Fixed deadlock issues by reverting async changes to `SNIPacket` [#425](https://github.com/dotnet/SqlClient/pull/425) ### Changed -- Updated SNI package reference to include version range [#425](https://github.com/dotnet/SqlClient/pull/425) +- Updated SNI package reference to include version range [#425](https://github.com/dotnet/SqlClient/pull/425) ## [Preview Release 2.0.0-preview1.20021.1] - 2020-01-21 ### Added + - Added support to allow large UDT buffer size (_upto_ `Int.MaxValue`) as supported by SQL Server starting TDS 7.3 [#340](https://github.com/dotnet/SqlClient/pull/340) ### Fixed + - Fixed issues with `SqlCommandSet` not working with Byte Array parameters [#360](https://github.com/dotnet/SqlClient/pull/360) - Fixed Statement command cancellation in Managed SNI [#248](https://github.com/dotnet/SqlClient/pull/248) - Ported [dotnet/corefx#38271](https://github.com/dotnet/corefx/pull/38271) - Fixed zero connection timeout issue in Managed SNI [#332](https://github.com/dotnet/SqlClient/pull/332) @@ -226,7 +778,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed `ConnectionTime` and `ClientConnectionId` reported by `SqlStatistics` when connection is closed [#341](https://github.com/dotnet/SqlClient/pull/341) - Fixed deadlock issues by reverting async changes to `SNIPacket` [#349](https://github.com/dotnet/SqlClient/pull/349) -### Changes +### Changed + - Improved performance of Managed SNI by removing double fetch of domain name [#366](https://github.com/dotnet/SqlClient/pull/366) - Improved performance of Async Method Allocations in Managed SNI [#328](https://github.com/dotnet/SqlClient/pull/328) - Improved performance of Managed SNI by enhancing utilization of resources [#173](https://github.com/dotnet/SqlClient/pull/173) - Ported [dotnet/corefx#35363](https://github.com/dotnet/corefx/pull/35363) and [dotnet/corefx#40732](https://github.com/dotnet/corefx/pull/40732) @@ -235,31 +788,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Changed `Recieve()` and `ReceiveAsync()` implementation to receive null packets on failure [#350](https://github.com/dotnet/SqlClient/pull/350) - Changed `EnclaveProviderBase` caching implementation to support Async Scenarios _(Introduces breaking changes)_ [#346](https://github.com/dotnet/SqlClient/pull/346) - ## [Stable Release 1.1.0] - 2019-11-20 ### Added + - Added support for |DataDirectory| macro in `AttachDBFilename` for .NET Core client [#284](https://github.com/dotnet/SqlClient/pull/284) ### Fixed + - Fixed connection resiliency check [#310](https://github.com/dotnet/SqlClient/pull/310) - Fixed `SNIPacket.ReadFromStreamAsync` to not consume same `ValueTask` twice [#295](https://github.com/dotnet/SqlClient/pull/295) - Fixed driver behavior to not send Attention signal for successful Bulk Copy operation [#308](https://github.com/dotnet/SqlClient/pull/308) - Fixed driver behavior to abort connection when encountering `SqlException` on `SqlTransaction.Commit` [#299](https://github.com/dotnet/SqlClient/pull/299) -- Fixed driver behavior to not throw exception on invalid *app.config* files [#319](https://github.com/dotnet/SqlClient/pull/319) +- Fixed driver behavior to not throw exception on invalid _app.config_ files [#319](https://github.com/dotnet/SqlClient/pull/319) + +### Changed -### Changes - Improved async read performance by adding multi-packet target buffer caching [#285](https://github.com/dotnet/SqlClient/pull/285) - Improved performance of `TdsParserStateObject` and `SqlDataReader` snapshot mechanisms [#198](https://github.com/dotnet/SqlClient/pull/198) - Updated `SqlDataReader.Close` documentation [#314](https://github.com/dotnet/SqlClient/pull/314) - ## [Preview Release 1.1.0-preview2.19309.1] - 2019-11-04 ### Added + - Add support for secure enclaves with Always Encrypted [#293](https://github.com/dotnet/SqlClient/pull/293) ### Fixed + - Setting the value `DbParameter.DbType` to `DbType.Time` property fails after setting the Value property [#5](https://github.com/dotnet/SqlClient/issues/5) - `SQLDataAdapter.FillSchema` doesn't mark computed columns as readonly [#275](https://github.com/dotnet/SqlClient/issues/275) - `SqlDependency.Start` throws `FileNotFoundException` [#260](https://github.com/dotnet/SqlClient/issues/260) @@ -270,7 +826,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Exception message grammar: "An SqlParameter [...] is not contained by this `SqlParameterCollection`" [#159](https://github.com/dotnet/SqlClient/issues/159) - Fixing incorrect event id and opcode for the `SqlEventSource` [#241](https://github.com/dotnet/SqlClient/pull/241) -### Changes +### Changed + - Update dependency to Microsoft.Data.SqlClient.SNI v1.1.0 [#276](https://github.com/dotnet/SqlClient/pull/276) - Correct timeout remarks for async command methods [#264](https://github.com/dotnet/SqlClient/pull/264) - Improve `SqlBulkCopy` truncation error message [#256](https://github.com/dotnet/SqlClient/issues/256) @@ -278,23 +835,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Enable SQL Command text for non-stored procs in EventSource events for .NET Framework [242](https://github.com/dotnet/SqlClient/pull/242) - Many test changes to support a public CI - ## [Preview Release 1.1.0-preview1.19275.1] - 2019-10-02 ### Added + - Added `SqlFileStream` support for .NET Framework with `Microsoft.Data.SqlTypes.SqlFileStream` class introduced. [#210](https://github.com/dotnet/SqlClient/pull/210) - Added support for Visual Studio Intellisense with XML Documentation. [#210](https://github.com/dotnet/SqlClient/pull/210) -### Changes +### Changed + - Synchronized ref definitions with driver classes. [#180](https://github.com/dotnet/SqlClient/pull/180) - Updated `SNINativeMethodWrapper` to provide the underlying error in the inner exception when we fail to load SNI.dll. [#225](https://github.com/dotnet/SqlClient/pull/225) - Added .editorconfig file and set formatting rules. [#193](https://github.com/dotnet/SqlClient/pull/193) - Changes done to handle statistics well and to cleanup `AutoResetEvent` on disconnect. [#232](https://github.com/dotnet/SqlClient/pull/232) - ## [Hotfix & Stable Release 1.0.19269.1] - 2019-09-26 ### Fixed Issues + - `SqlCommand.StatementCompleted` event never being fired [#212](https://github.com/dotnet/SqlClient/issues/212) - Added missing `Authentication` property to `SqlConnectionStringBuilder` reference assembly - Reverted API changes in `SqlAuthenticationParameters` which had changed the `public string Resource` property to `public string[] Scopes` @@ -302,6 +860,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Hotfix & Stable Release 1.0.19249.1] - 2019-09-06 ### Fixed Issues + - Fixed issues with large data reading in Unix applications when data is spanned over multiple packets. [#171](https://github.com/dotnet/SqlClient/pull/171) ## [Stable Release 1.0.19239.1] - 2019-08-27 diff --git a/README.md b/README.md index 3c390cac58..29f8950a49 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/dotnet/sqlclient/master/LICENSE) [![Nuget](https://img.shields.io/nuget/dt/Microsoft.Data.SqlClient?label=Nuget.org%20Downloads&style=flat-square&color=blue)](https://www.nuget.org/packages/Microsoft.Data.SqlClient) [![Gitter](https://img.shields.io/gitter/room/badges/shields.svg?style=flat-square&color=blue)](https://gitter.im/Microsoft/mssql-developers) +[![Build status](https://sqlclientdrivers.visualstudio.com/public/_apis/build/status/ADO/CI-SqlClient)](https://sqlclientdrivers.visualstudio.com/public/_build/latest?definitionId=1139) # Microsoft SqlClient Data Provider for SQL Server @@ -12,9 +13,9 @@ Microsoft.Data.SqlClient is a data provider for Microsoft SQL Server and Azure S The Microsoft.Data.SqlClient package supports the below environments: -- .NET Framework 4.6+ -- .NET Core 2.1+ -- .NET Standard 2.0+. +- .NET Framework 4.6.2+ +- .NET Core 3.1+ +- .NET Standard 2.0+ The source code of this library is now available under the MIT license. @@ -118,4 +119,4 @@ The Microsoft.Data.SqlClient Driver for SQL Server is licensed under the MIT lic ## Trademarks -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. \ No newline at end of file +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. diff --git a/RunPackageReferenceTests.cmd b/RunPackageReferenceTests.cmd new file mode 100644 index 0000000000..fd37b9c7ba --- /dev/null +++ b/RunPackageReferenceTests.cmd @@ -0,0 +1,146 @@ +@echo off + +:: .NET CORE + .NET STANDARD LIBRARY TEST CASES +echo Building .NET Core Tests +call :pauseOnError msbuild -t:Clean + +:: ************** IMPORTANT NOTE BEFORE PROCEEDING WITH "PACKAGE" AND "NETSTANDARDPACKAGE" REFERENCE TYPES *************** +:: THESE ARE NOT STAND ALONE TEST COMMANDS AND NEED A DEVELOPER'S SPECIAL ATTENTION TO WORK. ATTEMPTING TO RUN THE ENTIRE SET OF COMMANDS AS-IS IS LIKELY TO FAIL! + +:: CREATE A NUGET PACKAGE WITH BELOW COMMAND AND ADD TO LOCAL FOLDER + UPDATE NUGET CONFIG FILE TO READ FROM THAT LOCATION +:: > MSBuild -p:Configuration=Release + +:: Based on `dotnet test` documentation, the `Platform` property has no effect on choosing the underlying architecture for the test execution environment. +:: You need to install and run the `dotnet` command for a specific architecture (x64, x86, Arm64). + +:: REFERENCE TYPE "PACKAGE" +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetFx -p:ReferenceType=Package +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetCore -p:ReferenceType=Package +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetSt -p:ReferenceType=Package + +:: .NET - REFERENCE TYPE "PACKAGE" + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=Package -p:TargetNetCoreVersion=net6.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net6.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net6.0-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=Package -p:TargetNetCoreVersion=net7.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net7.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net7.0-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=Package -p:Platform=x64 -p:TargetNetCoreVersion=net6.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net6.0-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net6.0-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=Package -p:Platform=x64 -p:TargetNetCoreVersion=net7.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net7.0-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net7.0-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=Package -p:Platform=Win32 -p:TargetNetCoreVersion=net6.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net6.0-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net6.0-manual-win32.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=Package -p:Platform=Win32 -p:TargetNetCoreVersion=net7.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net7.0-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net7.0-manual-win32.xml + +:: .NET Framework - REFERENCE TYPE "PACKAGE" + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=Package -p:Platform=AnyCPU -p:TargetNetFxVersion=net462 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=Package -p:Platform=AnyCPU -p:TargetNetFxVersion=net48 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=Package -p:Platform=x64 -p:TargetNetFxVersion=net462 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:TargetNetFxVersion=net48 -p:Platform=x64 -p:ReferenceType=Package +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=Package -p:Platform=Win32 -p:TargetNetFxVersion=net462 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-manual-win32.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:TargetNetFxVersion=net48 -p:Platform=Win32 -p:ReferenceType=Package +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-Win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-Win32.xml + +:: REFERENCE TYPE "NETSTANDARDPACKAGE" + +:: .NET - REFERENCE TYPE "NETSTANDARDPACKAGE" + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandardPackage -p:TargetNetCoreVersion=net6.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net6.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net6.0-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandardPackage -p:TargetNetCoreVersion=net7.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net7.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net7.0-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandardPackage -p:Platform=x64 -p:TargetNetCoreVersion=net6.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net6.0-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net6.0-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandardPackage -p:Platform=x64 -p:TargetNetCoreVersion=net7.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net7.0-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net7.0-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandardPackage -p:Platform=Win32 -p:TargetNetCoreVersion=net6.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net6.0-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net6.0-manual-win32.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandardPackage -p:Platform=Win32 -p:TargetNetCoreVersion=net7.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net7.0-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net7.0-manual-win32.xml + +:: .NET Framework - REFERENCE TYPE "NETSTANDARDPACKAGE" + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=NetStandardPackage -p:TargetNetFxVersion=net462 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetFxVersion=net462 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net462-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetFxVersion=net462 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net462-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=NetStandardPackage -p:TargetNetFxVersion=net48 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetFxVersion=net48 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetFxVersion=net48 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=NetStandardPackage -p:Platform=x64 -p:TargetNetFxVersion=net462 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetFxVersion=net462 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net462-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetFxVersion=net462 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net462-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=NetStandardPackage -p:Platform=x64 -p:TargetNetFxVersion=net48 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetFxVersion=net48 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="x64" -p:TargetNetFxVersion=net48 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-manual-x64.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=NetStandardPackage -p:Platform=Win32 -p:TargetNetFxVersion=net462 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetFxVersion=net462 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net462-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetFxVersion=net462 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net462-manual-win32.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=NetStandardPackage -p:Platform=Win32 -p:TargetNetFxVersion=net48 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetFxVersion=net48 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:Platform="Win32" -p:TargetNetFxVersion=net48 -p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-manual-win32.xml + +:: REFERENCE TYPE "NETSTANDARD" (We only build and test AnyCPU with Project Reference) +:: NUGET PACKAGE GENERATION IS NOT SUPPORTED FOR REFERNCE TYPE 'NETSTANDARD' +call :pauseOnError msbuild -p:Configuration="Release" -p:ReferenceType=NetStandard -p:GenerateNuget=false +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandard -p:TargetNetCoreVersion=net6.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-net6.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net6.0 -p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-net6.0-manual-anycpu.xml + +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:ReferenceType=NetStandard -p:TargetNetCoreVersion=net7.0 +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-net7.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net7.0 -p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-net7.0-manual-anycpu.xml + +:: TESTING 'NETSTANDARD' REFERENCE TYPE WITH .NET FRAMEWORK 4.6.2+ AS TARGET FRAMEWORK IS INVALID CASE AS PROJECT REFERENCE DOES NOT LOAD SNI.DLL IN .NET FRAMEWORK RUNTIME. +:: CASE IS VERIFIED WITH RUNTIME.NATIVE.SYSTEM.DATA.SQLCLIENT.SNI AS WELL. TO TEST .NET FRAMEWORK TARGETS, USE 'NETSTANDARDPACKAGE' REFERENCE TYPE ONLY. + +goto :eof + +:pauseOnError +%* +if ERRORLEVEL 1 pause +goto :eof diff --git a/RunProjectReferenceTests.cmd b/RunProjectReferenceTests.cmd new file mode 100644 index 0000000000..3b2208e82d --- /dev/null +++ b/RunProjectReferenceTests.cmd @@ -0,0 +1,47 @@ +@echo off + +call :pauseOnError msbuild -t:Clean +:: .NET FRAMEWORK - REFERENCE TYPE "PROJECT" +:: Only Builds AnyCPU for project reference! + +:: Based on `dotnet test` documentation, the `Platform` property has no effect on choosing the underlying architecture for the test execution environment. +:: You need to install and run the `dotnet` command for a specific architecture (x64, x86, Arm64). + +echo Building .NET Framework Tests ... +call :pauseOnError msbuild -p:Configuration="Release" +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetStAllOS +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetFx +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:TargetNetFxVersion=net462 +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:TargetNetFxVersion=net48 + +echo Running .NET Framework Tests ... +call pause +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net462 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net462 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-manual-anycpu.xml + +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj"-p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-manual-anycpu.xml + +echo Building .NET Tests ... +call pause +call :pauseOnError msbuild -t:Clean +call :pauseOnError msbuild -p:Configuration="Release" +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetStAllOS +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetCoreAllOS +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:TargetNetCoreVersion=net6.0 +call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:TargetNetCoreVersion=net7.0 + +echo Running .NET Tests ... +call pause +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" -p:TargetNetCoreVersion=net6.0 --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net6.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" -p:TargetNetCoreVersion=net6.0 --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net6.0-manual-anycpu.xml + +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" -p:TargetNetCoreVersion=net7.0 --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net7.0-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" -p:TargetNetCoreVersion=net7.0 --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net7.0-manual-anycpu.xml + +goto :eof + +:pauseOnError +%* +if ERRORLEVEL 1 pause +goto :eof diff --git a/RunTests.cmd b/RunTests.cmd deleted file mode 100644 index 20dbd88287..0000000000 --- a/RunTests.cmd +++ /dev/null @@ -1,207 +0,0 @@ -@echo off - -:: .NET CORE + .NET STANDARD LIBRARY TEST CASES -echo Building .NET Core Tests -call :pauseOnError msbuild /t:Clean - -:: ************** IMPORTANT NOTE BEFORE PROCEEDING WITH "PACKAGE" AND "NETSTANDARDPACKAGE" REFERENCE TYPES *************** -:: CREATE A NUGET PACKAGE WITH BELOW COMMAND AND ADD TO LOCAL FOLDER + UPDATE NUGET CONFIG FILE TO READ FROM THAT LOCATION -:: msbuild /p:configuration=Release - -:: REFERENCE TYPE "PACKAGE" -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildAKVNetFx /p:ReferenceType=Package -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildAKVNetCore /p:ReferenceType=Package - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore2.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore2.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore3.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore3.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore5.0-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore5.0-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:Platform=x64 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore2.1-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore2.1-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:Platform=x64 /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore3.1-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore3.1-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:Platform=x64 /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore5.0-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore5.0-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:Platform=Win32 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore2.1-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore2.1-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:Platform=Win32 /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore3.1-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore3.1-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=Package /p:Platform=Win32 /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore5.0-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-netcore5.0-manual-win32.xml - -:: REFERENCE TYPE "NETSTANDARDPACKAGE" -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore2.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore2.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore3.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore3.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore5.0-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore5.0-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:ReferenceType=NetStandardPackage /p:TargetNetFxVersion=net461 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetFxVersion=net461 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net461-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetFxVersion=net461 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net461-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:ReferenceType=NetStandardPackage /p:TargetNetFxVersion=net48 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetFxVersion=net48 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetFxVersion=net48 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:Platform=x64 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore2.1-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore2.1-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:Platform=x64 /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore3.1-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore3.1-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:Platform=x64 /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore5.0-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore5.0-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:ReferenceType=NetStandardPackage /p:Platform=x64 /p:TargetNetFxVersion=net461 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetFxVersion=net461 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net461-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetFxVersion=net461 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net461-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:ReferenceType=NetStandardPackage /p:Platform=x64 /p:TargetNetFxVersion=net48 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetFxVersion=net48 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="x64" /p:TargetNetFxVersion=net48 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:Platform=Win32 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore2.1-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore2.1-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:Platform=Win32 /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore3.1-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore3.1-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandardPackage /p:Platform=Win32 /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore5.0-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-netcore5.0-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:ReferenceType=NetStandardPackage /p:Platform=Win32 /p:TargetNetFxVersion=net461 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetFxVersion=net461 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net461-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetFxVersion=net461 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net461-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:ReferenceType=NetStandardPackage /p:Platform=Win32 /p:TargetNetFxVersion=net48 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetFxVersion=net48 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:Platform="Win32" /p:TargetNetFxVersion=net48 /p:ReferenceType=NetStandardPackage -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandardpackage-net48-manual-win32.xml - -:: REFERENCE TYPE "NETSTANDARD" (We only build and test AnyCPU with Project Reference) -:: NUGET PACKAGE GENERATION IS NOT SUPPORTED FOR REFERNCE TYPE 'NETSTANDARD' -call :pauseOnError msbuild /p:Configuration="Release" /p:ReferenceType=NetStandard /p:GenerateNuget=false -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandard -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore2.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore2.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandard /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore3.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore3.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:ReferenceType=NetStandard /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore5.0-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore5.0-manual-anycpu.xml - -:: TESTING 'NETSTANDARD' REFERENCE TYPE WITH .NET FRAMEWORK 4.6.1+ AS TARGET FRAMEWORK IS INVALID CASE AS PROJECT REFERENCE DOES NOT LOAD SNI.DLL IN .NET FRAMEWORK RUNTIME. -:: CASE IS VERIFIED WITH RUNTIME.NATIVE.SYSTEM.DATA.SQLCLIENT.SNI AS WELL. TO TEST .NET FRAMEWORK TARGETS, USE 'NETSTANDARDPACKAGE' REFERENCE TYPE ONLY. - -:: REFERENCE TYPE "PROJECT" (We only build and test AnyCPU with Project Reference) -call :pauseOnError msbuild /p:Configuration="Release" -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildAKVNetFx -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildAKVNetCoreAllOS -call :pauseOnError msbuild /p:Configuration="Release" /t:GenerateAKVProviderNugetPackage -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Project -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-netcore2.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp2.1 /p:ReferenceType=Project -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-netcore2.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:TargetNetCoreVersion=netcoreapp3.1 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Project -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-netcore3.1-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp3.1 /p:ReferenceType=Project -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-netcore3.1-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:TargetNetCoreVersion=netcoreapp5.0 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Project -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-netcore5.0-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Configuration="Release" /p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" /p:Platform="AnyCPU" /p:TargetNetCoreVersion=netcoreapp5.0 /p:ReferenceType=Project -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-netcore5.0-manual-anycpu.xml - -:: .NET FRAMEWORK REFERENCE TYPE "PROJECT" -echo Building .NET Framework Tests -call :pauseOnError msbuild /p:Configuration="Release" -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildAKVNetFx -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildAKVNetCoreAllOS -call :pauseOnError msbuild /p:Configuration="Release" /t:GenerateAKVProviderNugetPackage -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net46-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net46-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:TargetNetFxVersion=net48 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=x64 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net46-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net46-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=x64 /p:TargetNetFxVersion=net48 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=Win32 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net46-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net46-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=Win32 /p:TargetNetFxVersion=net48 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-functional-Win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-manual-Win32.xml - -:: .NET FRAMEWORK REFERENCE TYPE "PACKAGE" -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net46-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net46-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:TargetNetFxVersion=net48 /p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-anycpu.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=x64 /p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net46-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net46-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=x64 /p:TargetNetFxVersion=net48 /p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="x64" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-x64.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=Win32 /p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net46-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net46-manual-win32.xml - -call :pauseOnError msbuild /p:Configuration="Release" /t:BuildTestsNetFx /p:Platform=Win32 /p:TargetNetFxVersion=net48 /p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-Win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="Win32" /p:Configuration="Release" /p:TestTargetOS="Windowsnetfx" /p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" /p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-Win32.xml - -goto :eof - -:pauseOnError -%* -if ERRORLEVEL 1 pause -goto :eof diff --git a/RunTests.sh b/RunTests.sh index 1c4201371d..ed3f22a9a1 100644 --- a/RunTests.sh +++ b/RunTests.sh @@ -1,8 +1,8 @@ -dotnet msbuild /p:Configuration="Release" /t:Clean,BuildAll /p:GenerateDocumentationFile=false +dotnet msbuild -p:Configuration="Release" -t:Clean,BuildAll -p:GenerateDocumentationFile=false echo Building Add-Ons -dotnet msbuild /p:Configuration="Release" /t:BuildAKVNetCore /p:OSGroup=Unix /p:Platform=AnyCPU +dotnet msbuild -p:Configuration="Release" -t:BuildAKVNetCore -p:OSGroup=Unix -p:Platform=AnyCPU echo Building tests -dotnet msbuild /p:Configuration="Release" /t:BuildTestsNetCore /p:OSGroup=Unix /p:Platform=AnyCPU +dotnet msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:OSGroup=Unix -p:Platform=AnyCPU echo Running SqlClient test suite -dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" -dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" /p:Platform="AnyCPU" /p:Configuration="Release" /p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" +dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" +dotnet test "src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests" diff --git a/SUPPORT.md b/SUPPORT.md index 861787eb5b..51a3996cca 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -4,7 +4,7 @@ Microsoft.Data.SqlClient library follows the latest .NET Core support policy for [View the .NET Core Support Policy](https://dotnet.microsoft.com/platform/support/policy/dotnet-core) -[View SqlClient Support Policy on Microsoft Documentation](https://docs.microsoft.com/sql/connect/ado-net/sqlclient-driver-support-lifecycle) +View GA released version LTS/Current status and support dates here: [SqlClient Support Policy on Microsoft Documentation](https://docs.microsoft.com/sql/connect/ado-net/sqlclient-driver-support-lifecycle) ## Microsoft.Data.SqlClient release cadence diff --git a/build.proj b/build.proj index 2dcc4ae354..1b3372bc95 100644 --- a/build.proj +++ b/build.proj @@ -9,19 +9,42 @@ src\NuGet.config Debug AnyCPU + + true true false Windows Unix + net6.0 + netfx + netcore + netfx + netcoreapp + $(TF) + $(TF) true + Configuration=$(Configuration);AssemblyVersion=$(SqlServerAssemblyVersion);AssemblyFileVersion=$(SqlServerAssemblyFileVersion);Version=$(SqlServerPackageVersion); Configuration=$(Configuration);AssemblyFileVersion=$(AssemblyFileVersion);TargetsWindows=$(TargetsWindows);TargetsUnix=$(TargetsUnix); - BuildProjectReferences=false;$(ProjectProperties); - ContinuousIntegrationBuild=true + BuildProjectReferences=false;$(ProjectProperties);BuildForRelease=false;TargetNetCoreVersion=$(TargetNetCoreVersion);TargetNetFxVersion=$(TargetNetFxVersion) + TestResults + + + + + + + true + ContinuousIntegrationBuild=$(BuildForRelease);EmbedUntrackedSources=$(BuildForRelease) + @@ -33,7 +56,8 @@ - + + @@ -43,38 +67,66 @@ + + - - - - - + + + + + + + + + + + - + - - + + - + - - - + + + + + + + + $(DotNetCmd) dotnet build -c Release -p:ReferenceType=$(ReferenceType)" + + + + + + + + + + + + + + + @@ -86,23 +138,55 @@ - + + + + - + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + @@ -113,18 +197,32 @@ - + - + + + + + + - + + + + + + + + + + @@ -132,4 +230,10 @@ + + + + + + diff --git a/buildAddons.cmd b/buildAddons.cmd index e958ed14cb..e8d41ea3a8 100644 --- a/buildAddons.cmd +++ b/buildAddons.cmd @@ -1,8 +1,9 @@ -call :pauseOnError msbuild /p:configuration=Release /t:clean -call :pauseOnError msbuild /p:configuration=Release /t:BuildAllConfigurations -call :pauseOnError msbuild /p:configuration=Release /t:BuildAKVNetFx -call :pauseOnError msbuild /p:configuration=Release /t:BuildAKVNetCoreAllOS -call :pauseOnError msbuild /p:configuration=Release /t:GenerateAKVProviderNugetPackage +call :pauseOnError msbuild -p:configuration=Release -t:clean +call :pauseOnError msbuild -p:configuration=Release -t:BuildAllConfigurations +call :pauseOnError msbuild -p:configuration=Release -t:BuildAKVNetFx +call :pauseOnError msbuild -p:configuration=Release -t:BuildAKVNetCoreAllOS +call :pauseOnError msbuild -p:configuration=Release -t:BuildAKVNetStAllOS +call :pauseOnError msbuild -p:configuration=Release -t:GenerateAKVProviderNugetPackage goto :eof diff --git a/contributing-workflow.md b/contributing-workflow.md index 374e843ae1..52ee5e5e15 100644 --- a/contributing-workflow.md +++ b/contributing-workflow.md @@ -6,7 +6,7 @@ You can contribute to Microsoft.Data.SqlClient with issues and PRs. Simply filin We use and recommend the following workflow: -1. Create an issue for your work. +1. Create an issue for your work. - You can skip this step for trivial changes. - Reuse an existing issue on the topic, if there is one. - Get agreement from the team and the community that your proposed change is a good one. @@ -15,8 +15,8 @@ We use and recommend the following workflow: - For any other improvements in the driver, add a Label "**Enhancement**" to your issue. - Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person. 2. Create a personal fork of the repository on GitHub (if you don't already have one). -3. Create a branch off of master (`git checkout -b mybranch`). - - Name the branch so that it clearly communicates your intentions, such as issue-123 or githubhandle-issue. +3. Create a branch off of master (`git checkout -b mybranch`). + - Name the branch so that it clearly communicates your intentions, such as issue-123 or githubhandle-issue. - Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork. 4. Make and commit your changes. - Please follow our [Commit Messages](contributing.md#commit-messages) guidance. @@ -41,7 +41,7 @@ If the CI build fails for any reason, the PR issue will be updated with a link t ## PR Feedback -Microsoft team and community members will provide feedback on your change. Community feedback is highly valued. You will often see the absence of team feedback if the community has already provided good review feedback. +Microsoft team and community members will provide feedback on your change. Community feedback is highly valued. You will often see the absence of team feedback if the community has already provided good review feedback. 1 or more Microsoft team members will review every PR prior to merge. They will often reply with "LGTM, modulo comments". That means that the PR will be merged once the feedback is resolved. "LGTM" == "looks good to me". @@ -61,6 +61,16 @@ For testing PR changes and ensure they work fine, we maintain a public feed that **Add this feed to NuGet Sources** +```xml + + + + + + + +``` +OR ```cmd nuget.exe sources Add -Name "Microsoft.Data.SqlClient.Commits" -Source "https://pkgs.dev.azure.com/sqlclientdrivers-ci/sqlclient/_packaging/Microsoft.Data.SqlClient.Commits/nuget/v3/index.json" ``` @@ -80,8 +90,26 @@ The package naming conventions follow SemVer 2.0.0 and also provide changeset in Package names will be like: `Microsoft.Data.SqlClient.1.1.0-build.19250.1-c21aa7c.nupkg` Breaking down: -- `1.1.0-build` - Identitier for currently active driver version in Build. +- `1.1.0-build` - Identifier for currently active driver version in Build. - `19250.1` - Unique identifier to keep latest PRs on top of the feed. - `c21aa7c` - Short Commit Id to identify merged commit in `master`. > Note: This public feed is only for testing and validating new PR changes. Packages from feed will be eventually removed when the maximum NuGet Package limit of **50** is reached. We do not recommend using packages from this feed in client applications. + +## Nightly builds + +We also maintain NuGet feed for Nightly builds that are generated when new changes are merged in the repository. To access Nightly builds, add below configuration to package sources: + +```xml + + + + + + + +``` +OR +```cmd +nuget.exe sources Add -Name "Microsoft.Data.SqlClient.dev" -Source "https://pkgs.dev.azure.com/sqlclientdrivers-ci/sqlclient/_packaging/Microsoft.Data.SqlClient.dev/nuget/v3/index.json" +``` diff --git a/doc/samples/AzureKeyVaultProviderExample.cs b/doc/samples/AzureKeyVaultProviderExample.cs index 84c9ffb2da..e16a9a63a7 100644 --- a/doc/samples/AzureKeyVaultProviderExample.cs +++ b/doc/samples/AzureKeyVaultProviderExample.cs @@ -130,8 +130,8 @@ WITH VALUES ( private static string GetEncryptedValue(SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) { byte[] plainTextColumnEncryptionKey = new byte[32]; - RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); - rngCsp.GetBytes(plainTextColumnEncryptionKey); + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(plainTextColumnEncryptionKey); byte[] encryptedColumnEncryptionKey = sqlColumnEncryptionAzureKeyVaultProvider.EncryptColumnEncryptionKey(s_akvUrl, s_algorithm, plainTextColumnEncryptionKey); string EncryptedValue = string.Concat("0x", BitConverter.ToString(encryptedColumnEncryptionKey).Replace("-", string.Empty)); diff --git a/doc/samples/AzureKeyVaultProviderExample_2_0.cs b/doc/samples/AzureKeyVaultProviderExample_2_0.cs new file mode 100644 index 0000000000..d4c64f9684 --- /dev/null +++ b/doc/samples/AzureKeyVaultProviderExample_2_0.cs @@ -0,0 +1,246 @@ +// +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using Azure.Identity; +using Microsoft.Data.SqlClient; +using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; + +namespace Microsoft.Data.SqlClient.Samples +{ + public class AzureKeyVaultProviderExample_2_0 + { + static readonly string s_algorithm = "RSA_OAEP"; + + // ********* Provide details here *********** + static readonly string s_akvUrl = "https://{KeyVaultName}.vault.azure.net/keys/{Key}/{KeyIdentifier}"; + static readonly string s_connectionString = "Server={Server}; Database={database}; Integrated Security=true; Column Encryption Setting=Enabled;"; + // ****************************************** + + public static void Main(string[] args) + { + // Initialize Token Credential instance using InteractiveBrowserCredential. For other authentication options, + // see classes derived from TokenCredential: https://docs.microsoft.com/dotnet/api/azure.core.tokencredential + InteractiveBrowserCredential interactiveBrowserCredential = new InteractiveBrowserCredential(); + + // Initialize AKV provider + SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(interactiveBrowserCredential); + + // Register AKV provider + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders: new Dictionary(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase) + { + { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, akvProvider} + }); + Console.WriteLine("AKV provider Registered"); + + // Create connection to database + using (SqlConnection sqlConnection = new SqlConnection(s_connectionString)) + { + string cmkName = "CMK_WITH_AKV"; + string cekName = "CEK_WITH_AKV"; + string tblName = "AKV_TEST_TABLE"; + + CustomerRecord customer = new CustomerRecord(1, @"Microsoft", @"Corporation"); + + try + { + sqlConnection.Open(); + + // Drop Objects if exists + dropObjects(sqlConnection, cmkName, cekName, tblName); + + // Create Column Master Key with AKV Url + createCMK(sqlConnection, cmkName); + Console.WriteLine("Column Master Key created."); + + // Create Column Encryption Key + createCEK(sqlConnection, cmkName, cekName, akvProvider); + Console.WriteLine("Column Encryption Key created."); + + // Create Table with Encrypted Columns + createTbl(sqlConnection, cekName, tblName); + Console.WriteLine("Table created with Encrypted columns."); + + // Insert Customer Record in table + insertData(sqlConnection, tblName, customer); + Console.WriteLine("Encryted data inserted."); + + // Read data from table + verifyData(sqlConnection, tblName, customer); + Console.WriteLine("Data validated successfully."); + } + finally + { + // Drop table and keys + dropObjects(sqlConnection, cmkName, cekName, tblName); + Console.WriteLine("Dropped Table, CEK and CMK"); + } + + Console.WriteLine("Completed AKV provider Sample."); + } + } + + private static void createCMK(SqlConnection sqlConnection, string cmkName) + { + string KeyStoreProviderName = SqlColumnEncryptionAzureKeyVaultProvider.ProviderName; + + string sql = + $@"CREATE COLUMN MASTER KEY [{cmkName}] + WITH ( + KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', + KEY_PATH = N'{s_akvUrl}' + );"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static void createCEK(SqlConnection sqlConnection, string cmkName, string cekName, SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) + { + string sql = + $@"CREATE COLUMN ENCRYPTION KEY [{cekName}] + WITH VALUES ( + COLUMN_MASTER_KEY = [{cmkName}], + ALGORITHM = '{s_algorithm}', + ENCRYPTED_VALUE = {GetEncryptedValue(sqlColumnEncryptionAzureKeyVaultProvider)} + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static string GetEncryptedValue(SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) + { + byte[] plainTextColumnEncryptionKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(plainTextColumnEncryptionKey); + + byte[] encryptedColumnEncryptionKey = sqlColumnEncryptionAzureKeyVaultProvider.EncryptColumnEncryptionKey(s_akvUrl, s_algorithm, plainTextColumnEncryptionKey); + string EncryptedValue = string.Concat("0x", BitConverter.ToString(encryptedColumnEncryptionKey).Replace("-", string.Empty)); + return EncryptedValue; + } + + private static void createTbl(SqlConnection sqlConnection, string cekName, string tblName) + { + string ColumnEncryptionAlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA_256"; + + string sql = + $@"CREATE TABLE [dbo].[{tblName}] + ( + [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}') + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static void insertData(SqlConnection sqlConnection, string tblName, CustomerRecord customer) + { + string insertSql = $"INSERT INTO [{tblName}] (CustomerId, FirstName, LastName) VALUES (@CustomerId, @FirstName, @LastName);"; + + using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction()) + using (SqlCommand sqlCommand = new SqlCommand(insertSql, + connection: sqlConnection, transaction: sqlTransaction, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id); + sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); + sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); + + sqlCommand.ExecuteNonQuery(); + sqlTransaction.Commit(); + } + } + + private static void verifyData(SqlConnection sqlConnection, string tblName, CustomerRecord customer) + { + // Test INPUT parameter on an encrypted parameter + using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tblName}] WHERE FirstName = @firstName", + sqlConnection)) + { + SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); + customerFirstParam.Direction = System.Data.ParameterDirection.Input; + customerFirstParam.ForceColumnEncryption = true; + + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + { + ValidateResultSet(sqlDataReader); + } + } + } + + private static void ValidateResultSet(SqlDataReader sqlDataReader) + { + Console.WriteLine(" * Row available: " + sqlDataReader.HasRows); + + while (sqlDataReader.Read()) + { + if (sqlDataReader.GetInt32(0) == 1) + { + Console.WriteLine(" * Employee Id received as sent: " + sqlDataReader.GetInt32(0)); + } + else + { + Console.WriteLine("Employee Id didn't match"); + } + + if (sqlDataReader.GetString(1) == @"Microsoft") + { + Console.WriteLine(" * Employee Firstname received as sent: " + sqlDataReader.GetString(1)); + } + else + { + Console.WriteLine("Employee FirstName didn't match."); + } + + if (sqlDataReader.GetString(2) == @"Corporation") + { + Console.WriteLine(" * Employee LastName received as sent: " + sqlDataReader.GetString(2)); + } + else + { + Console.WriteLine("Employee LastName didn't match."); + } + } + } + + private static void dropObjects(SqlConnection sqlConnection, string cmkName, string cekName, string tblName) + { + using (SqlCommand cmd = sqlConnection.CreateCommand()) + { + cmd.CommandText = $@"IF EXISTS (select * from sys.objects where name = '{tblName}') BEGIN DROP TABLE [{tblName}] END"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $@"IF EXISTS (select * from sys.column_encryption_keys where name = '{cekName}') BEGIN DROP COLUMN ENCRYPTION KEY [{cekName}] END"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $@"IF EXISTS (select * from sys.column_master_keys where name = '{cmkName}') BEGIN DROP COLUMN MASTER KEY [{cmkName}] END"; + cmd.ExecuteNonQuery(); + } + } + + private class CustomerRecord + { + internal int Id { get; set; } + internal string FirstName { get; set; } + internal string LastName { get; set; } + + public CustomerRecord(int id, string fName, string lName) + { + Id = id; + FirstName = fName; + LastName = lName; + } + } + } +} +// diff --git a/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs b/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs new file mode 100644 index 0000000000..d691455f06 --- /dev/null +++ b/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs @@ -0,0 +1,368 @@ +// +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using Azure.Identity; +using Microsoft.Data.SqlClient; +using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; + +namespace Microsoft.Data.SqlClient.Samples +{ + public class AzureKeyVaultProviderLegacyExample_2_0 + { + const string s_algorithm = "RSA_OAEP"; + + // ********* Provide details here *********** + static readonly string s_akvUrl = "https://{KeyVaultName}.vault.azure.net/keys/{Key}/{KeyIdentifier}"; + static readonly string s_clientId = "{Application_Client_ID}"; + static readonly string s_clientSecret = "{Application_Client_Secret}"; + static readonly string s_connectionString = "Server={Server}; Database={database}; Integrated Security=true; Column Encryption Setting=Enabled;"; + // ****************************************** + + public static void Main() + { + // Initialize AKV provider + SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new LegacyAuthCallbackTokenCredential()); + + // Register AKV provider + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders: new Dictionary(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase) + { + { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, akvProvider} + }); + Console.WriteLine("AKV provider Registered"); + + // Create connection to database + using (SqlConnection sqlConnection = new SqlConnection(s_connectionString)) + { + string cmkName = "CMK_WITH_AKV"; + string cekName = "CEK_WITH_AKV"; + string tblName = "AKV_TEST_TABLE"; + + CustomerRecord customer = new CustomerRecord(1, @"Microsoft", @"Corporation"); + + try + { + sqlConnection.Open(); + + // Drop Objects if exists + dropObjects(sqlConnection, cmkName, cekName, tblName); + + // Create Column Master Key with AKV Url + createCMK(sqlConnection, cmkName); + Console.WriteLine("Column Master Key created."); + + // Create Column Encryption Key + createCEK(sqlConnection, cmkName, cekName, akvProvider); + Console.WriteLine("Column Encryption Key created."); + + // Create Table with Encrypted Columns + createTbl(sqlConnection, cekName, tblName); + Console.WriteLine("Table created with Encrypted columns."); + + // Insert Customer Record in table + insertData(sqlConnection, tblName, customer); + Console.WriteLine("Encryted data inserted."); + + // Read data from table + verifyData(sqlConnection, tblName, customer); + Console.WriteLine("Data validated successfully."); + } + finally + { + // Drop table and keys + dropObjects(sqlConnection, cmkName, cekName, tblName); + Console.WriteLine("Dropped Table, CEK and CMK"); + } + + Console.WriteLine("Completed AKV provider Sample."); + } + } + + private static void createCMK(SqlConnection sqlConnection, string cmkName) + { + string KeyStoreProviderName = SqlColumnEncryptionAzureKeyVaultProvider.ProviderName; + + string sql = + $@"CREATE COLUMN MASTER KEY [{cmkName}] + WITH ( + KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', + KEY_PATH = N'{s_akvUrl}' + );"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static void createCEK(SqlConnection sqlConnection, string cmkName, string cekName, SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) + { + string sql = + $@"CREATE COLUMN ENCRYPTION KEY [{cekName}] + WITH VALUES ( + COLUMN_MASTER_KEY = [{cmkName}], + ALGORITHM = '{s_algorithm}', + ENCRYPTED_VALUE = {GetEncryptedValue(sqlColumnEncryptionAzureKeyVaultProvider)} + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static string GetEncryptedValue(SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) + { + byte[] plainTextColumnEncryptionKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(plainTextColumnEncryptionKey); + + byte[] encryptedColumnEncryptionKey = sqlColumnEncryptionAzureKeyVaultProvider.EncryptColumnEncryptionKey(s_akvUrl, s_algorithm, plainTextColumnEncryptionKey); + string EncryptedValue = string.Concat("0x", BitConverter.ToString(encryptedColumnEncryptionKey).Replace("-", string.Empty)); + return EncryptedValue; + } + + private static void createTbl(SqlConnection sqlConnection, string cekName, string tblName) + { + string ColumnEncryptionAlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA_256"; + + string sql = + $@"CREATE TABLE [dbo].[{tblName}] + ( + [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = '{ColumnEncryptionAlgorithmName}') + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static void insertData(SqlConnection sqlConnection, string tblName, CustomerRecord customer) + { + string insertSql = $"INSERT INTO [{tblName}] (CustomerId, FirstName, LastName) VALUES (@CustomerId, @FirstName, @LastName);"; + + using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction()) + using (SqlCommand sqlCommand = new SqlCommand(insertSql, + connection: sqlConnection, transaction: sqlTransaction, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id); + sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); + sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); + + sqlCommand.ExecuteNonQuery(); + sqlTransaction.Commit(); + } + } + + private static void verifyData(SqlConnection sqlConnection, string tblName, CustomerRecord customer) + { + // Test INPUT parameter on an encrypted parameter + using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tblName}] WHERE FirstName = @firstName", + sqlConnection)) + { + SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); + customerFirstParam.Direction = System.Data.ParameterDirection.Input; + customerFirstParam.ForceColumnEncryption = true; + + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + { + ValidateResultSet(sqlDataReader); + } + } + } + + private static void ValidateResultSet(SqlDataReader sqlDataReader) + { + Console.WriteLine(" * Row available: " + sqlDataReader.HasRows); + + while (sqlDataReader.Read()) + { + if (sqlDataReader.GetInt32(0) == 1) + { + Console.WriteLine(" * Employee Id received as sent: " + sqlDataReader.GetInt32(0)); + } + else + { + Console.WriteLine("Employee Id didn't match"); + } + + if (sqlDataReader.GetString(1) == @"Microsoft") + { + Console.WriteLine(" * Employee Firstname received as sent: " + sqlDataReader.GetString(1)); + } + else + { + Console.WriteLine("Employee FirstName didn't match."); + } + + if (sqlDataReader.GetString(2) == @"Corporation") + { + Console.WriteLine(" * Employee LastName received as sent: " + sqlDataReader.GetString(2)); + } + else + { + Console.WriteLine("Employee LastName didn't match."); + } + } + } + + private static void dropObjects(SqlConnection sqlConnection, string cmkName, string cekName, string tblName) + { + using (SqlCommand cmd = sqlConnection.CreateCommand()) + { + cmd.CommandText = $@"IF EXISTS (select * from sys.objects where name = '{tblName}') BEGIN DROP TABLE [{tblName}] END"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $@"IF EXISTS (select * from sys.column_encryption_keys where name = '{cekName}') BEGIN DROP COLUMN ENCRYPTION KEY [{cekName}] END"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $@"IF EXISTS (select * from sys.column_master_keys where name = '{cmkName}') BEGIN DROP COLUMN MASTER KEY [{cmkName}] END"; + cmd.ExecuteNonQuery(); + } + } + + private class CustomerRecord + { + internal int Id { get; set; } + internal string FirstName { get; set; } + internal string LastName { get; set; } + + public CustomerRecord(int id, string fName, string lName) + { + Id = id; + FirstName = fName; + LastName = lName; + } + } + + private class LegacyAuthCallbackTokenCredential : TokenCredential + { + string _authority = ""; + string _resource = ""; + string _akvUrl = ""; + + public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) => + AcquireTokenAsync().GetAwaiter().GetResult(); + + public override async ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) => + await AcquireTokenAsync(); + + private async Task AcquireTokenAsync() + { + // Added to reduce HttpClient calls. + // For multi-user support, a better design can be implemented as needed. + if (_akvUrl != s_akvUrl) + { + using (HttpClient httpClient = new HttpClient()) + { + HttpResponseMessage response = await httpClient.GetAsync(s_akvUrl); + string challenge = response?.Headers.WwwAuthenticate.FirstOrDefault()?.ToString(); + string trimmedChallenge = ValidateChallenge(challenge); + string[] pairs = trimmedChallenge.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); + + if (pairs != null && pairs.Length > 0) + { + for (int i = 0; i < pairs.Length; i++) + { + string[] pair = pairs[i]?.Split('='); + + if (pair.Length == 2) + { + string key = pair[0]?.Trim().Trim(new char[] { '\"' }); + string value = pair[1]?.Trim().Trim(new char[] { '\"' }); + + if (!string.IsNullOrEmpty(key)) + { + if (key.Equals("authorization", StringComparison.InvariantCultureIgnoreCase)) + { + _authority = value; + } + else if (key.Equals("resource", StringComparison.InvariantCultureIgnoreCase)) + { + _resource = value; + } + } + } + } + } + } + _akvUrl = s_akvUrl; + } + + string strAccessToken = await AzureActiveDirectoryAuthenticationCallback(_authority, _resource); + DateTime expiryTime = InterceptAccessTokenForExpiry(strAccessToken); + return new AccessToken(strAccessToken, new DateTimeOffset(expiryTime)); + } + + private DateTime InterceptAccessTokenForExpiry(string accessToken) + { + if (null == accessToken) + { + throw new ArgumentNullException(accessToken); + } + + var jwtHandler = new JwtSecurityTokenHandler(); + var jwtOutput = string.Empty; + + // Check Token Format + if (!jwtHandler.CanReadToken(accessToken)) + throw new FormatException(accessToken); + + JwtSecurityToken token = jwtHandler.ReadJwtToken(accessToken); + + // Re-serialize the Token Headers to just Key and Values + var jwtHeader = JsonConvert.SerializeObject(token.Header.Select(h => new { h.Key, h.Value })); + jwtOutput = $"{{\r\n\"Header\":\r\n{JToken.Parse(jwtHeader)},"; + + // Re-serialize the Token Claims to just Type and Values + var jwtPayload = JsonConvert.SerializeObject(token.Claims.Select(c => new { c.Type, c.Value })); + jwtOutput += $"\r\n\"Payload\":\r\n{JToken.Parse(jwtPayload)}\r\n}}"; + + // Output the whole thing to pretty JSON object formatted. + string jToken = JToken.Parse(jwtOutput).ToString(Formatting.Indented); + JToken payload = JObject.Parse(jToken).GetValue("Payload"); + + return new DateTime(1970, 1, 1).AddSeconds((long)payload[4]["Value"]); + } + + private static string ValidateChallenge(string challenge) + { + string Bearer = "Bearer "; + if (string.IsNullOrEmpty(challenge)) + throw new ArgumentNullException(nameof(challenge)); + + string trimmedChallenge = challenge.Trim(); + + if (!trimmedChallenge.StartsWith(Bearer)) + throw new ArgumentException("Challenge is not Bearer", nameof(challenge)); + + return trimmedChallenge.Substring(Bearer.Length); + } + + /// + /// Legacy implementation of Authentication Callback, used by Azure Key Vault provider 1.0. + /// This can be leveraged to support multi-user authentication support in the same Azure Key Vault Provider. + /// + /// Authorization URL + /// Resource + /// + public static async Task AzureActiveDirectoryAuthenticationCallback(string authority, string resource) + { + var authContext = new AuthenticationContext(authority); + ClientCredential clientCred = new ClientCredential(s_clientId, s_clientSecret); + AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred); + if (result == null) + { + throw new InvalidOperationException($"Failed to retrieve an access token for {resource}"); + } + return result.AccessToken; + } + } + } +} +// diff --git a/doc/samples/AzureKeyVaultProviderWithEnclaveProviderExample.cs b/doc/samples/AzureKeyVaultProviderWithEnclaveProviderExample.cs index 628a2e663b..2091dab322 100644 --- a/doc/samples/AzureKeyVaultProviderWithEnclaveProviderExample.cs +++ b/doc/samples/AzureKeyVaultProviderWithEnclaveProviderExample.cs @@ -136,8 +136,8 @@ WITH VALUES ( private static string GetEncryptedValue(SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) { byte[] plainTextColumnEncryptionKey = new byte[32]; - RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); - rngCsp.GetBytes(plainTextColumnEncryptionKey); + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(plainTextColumnEncryptionKey); byte[] encryptedColumnEncryptionKey = sqlColumnEncryptionAzureKeyVaultProvider.EncryptColumnEncryptionKey(s_akvUrl, s_algorithm, plainTextColumnEncryptionKey); string EncryptedValue = string.Concat("0x", BitConverter.ToString(encryptedColumnEncryptionKey).Replace("-", string.Empty)); diff --git a/doc/samples/AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs b/doc/samples/AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs new file mode 100644 index 0000000000..27f97dac38 --- /dev/null +++ b/doc/samples/AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs @@ -0,0 +1,250 @@ +using System; +// +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Threading.Tasks; +using Azure.Identity; +using Microsoft.Data.SqlClient; +using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; + +namespace AKVEnclaveExample +{ + class Program + { + static readonly string s_algorithm = "RSA_OAEP"; + + // ********* Provide details here *********** + static readonly string s_akvUrl = "https://{KeyVaultName}.vault.azure.net/keys/{Key}/{KeyIdentifier}"; + static readonly string s_connectionString = "Server={Server}; Database={database}; Integrated Security=true; Column Encryption Setting=Enabled; Attestation Protocol=HGS; Enclave Attestation Url = {attestation_url_for_HGS};"; + // ****************************************** + + static void Main(string[] args) + { + // Initialize Token Credential instance using InteractiveBrowserCredential. For other authentication options, + // see classes derived from TokenCredential: https://docs.microsoft.com/dotnet/api/azure.core.tokencredential + InteractiveBrowserCredential interactiveBrowserCredential = new InteractiveBrowserCredential(); + + // Initialize AKV provider + SqlColumnEncryptionAzureKeyVaultProvider akvProvider = new SqlColumnEncryptionAzureKeyVaultProvider(interactiveBrowserCredential); + + // Register AKV provider + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders: new Dictionary(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase) + { + { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, akvProvider} + }); + Console.WriteLine("AKV provider Registered"); + + // Create connection to database + using (SqlConnection sqlConnection = new SqlConnection(s_connectionString)) + { + string cmkName = "CMK_WITH_AKV"; + string cekName = "CEK_WITH_AKV"; + string tblName = "AKV_TEST_TABLE"; + + CustomerRecord customer = new CustomerRecord(1, @"Microsoft", @"Corporation"); + + try + { + sqlConnection.Open(); + + // Drop Objects if exists + dropObjects(sqlConnection, cmkName, cekName, tblName); + + // Create Column Master Key with AKV Url + createCMK(sqlConnection, cmkName, akvProvider); + Console.WriteLine("Column Master Key created."); + + // Create Column Encryption Key + createCEK(sqlConnection, cmkName, cekName, akvProvider); + Console.WriteLine("Column Encryption Key created."); + + // Create Table with Encrypted Columns + createTbl(sqlConnection, cekName, tblName); + Console.WriteLine("Table created with Encrypted columns."); + + // Insert Customer Record in table + insertData(sqlConnection, tblName, customer); + Console.WriteLine("Encryted data inserted."); + + // Read data from table + verifyData(sqlConnection, tblName, customer); + Console.WriteLine("Data validated successfully."); + } + finally + { + // Drop table and keys + dropObjects(sqlConnection, cmkName, cekName, tblName); + Console.WriteLine("Dropped Table, CEK and CMK"); + } + + Console.WriteLine("Completed AKV provider Sample."); + } + } + + private static void createCMK(SqlConnection sqlConnection, string cmkName, SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) + { + string KeyStoreProviderName = SqlColumnEncryptionAzureKeyVaultProvider.ProviderName; + + byte[] cmkSign = sqlColumnEncryptionAzureKeyVaultProvider.SignColumnMasterKeyMetadata(s_akvUrl, true); + string cmkSignStr = string.Concat("0x", BitConverter.ToString(cmkSign).Replace("-", string.Empty)); + + string sql = + $@"CREATE COLUMN MASTER KEY [{cmkName}] + WITH ( + KEY_STORE_PROVIDER_NAME = N'{KeyStoreProviderName}', + KEY_PATH = N'{s_akvUrl}', + ENCLAVE_COMPUTATIONS (SIGNATURE = {cmkSignStr}) + );"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static void createCEK(SqlConnection sqlConnection, string cmkName, string cekName, SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) + { + string sql = + $@"CREATE COLUMN ENCRYPTION KEY [{cekName}] + WITH VALUES ( + COLUMN_MASTER_KEY = [{cmkName}], + ALGORITHM = '{s_algorithm}', + ENCRYPTED_VALUE = {GetEncryptedValue(sqlColumnEncryptionAzureKeyVaultProvider)} + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static string GetEncryptedValue(SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider) + { + byte[] plainTextColumnEncryptionKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(plainTextColumnEncryptionKey); + + byte[] encryptedColumnEncryptionKey = sqlColumnEncryptionAzureKeyVaultProvider.EncryptColumnEncryptionKey(s_akvUrl, s_algorithm, plainTextColumnEncryptionKey); + string EncryptedValue = string.Concat("0x", BitConverter.ToString(encryptedColumnEncryptionKey).Replace("-", string.Empty)); + return EncryptedValue; + } + + private static void createTbl(SqlConnection sqlConnection, string cekName, string tblName) + { + string ColumnEncryptionAlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA_256"; + + string sql = + $@"CREATE TABLE [dbo].[{tblName}] + ( + [CustomerId] [int] ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [FirstName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}'), + [LastName] [nvarchar](50) COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [{cekName}], ENCRYPTION_TYPE = RANDOMIZED, ALGORITHM = '{ColumnEncryptionAlgorithmName}') + )"; + + using (SqlCommand command = sqlConnection.CreateCommand()) + { + command.CommandText = sql; + command.ExecuteNonQuery(); + } + } + + private static void insertData(SqlConnection sqlConnection, string tblName, CustomerRecord customer) + { + string insertSql = $"INSERT INTO [{tblName}] (CustomerId, FirstName, LastName) VALUES (@CustomerId, @FirstName, @LastName);"; + + using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction()) + using (SqlCommand sqlCommand = new SqlCommand(insertSql, + connection: sqlConnection, transaction: sqlTransaction, + columnEncryptionSetting: SqlCommandColumnEncryptionSetting.Enabled)) + { + sqlCommand.Parameters.AddWithValue(@"CustomerId", customer.Id); + sqlCommand.Parameters.AddWithValue(@"FirstName", customer.FirstName); + sqlCommand.Parameters.AddWithValue(@"LastName", customer.LastName); + + sqlCommand.ExecuteNonQuery(); + sqlTransaction.Commit(); + } + } + + private static void verifyData(SqlConnection sqlConnection, string tblName, CustomerRecord customer) + { + // Test INPUT parameter on an encrypted parameter + using (SqlCommand sqlCommand = new SqlCommand($"SELECT CustomerId, FirstName, LastName FROM [{tblName}] WHERE FirstName = @firstName", sqlConnection)) + { + SqlParameter customerFirstParam = sqlCommand.Parameters.AddWithValue(@"firstName", @"Microsoft"); + customerFirstParam.Direction = System.Data.ParameterDirection.Input; + customerFirstParam.ForceColumnEncryption = true; + + using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader()) + { + ValidateResultSet(sqlDataReader); + } + } + } + + private static void ValidateResultSet(SqlDataReader sqlDataReader) + { + Console.WriteLine(" * Row available: " + sqlDataReader.HasRows); + + while (sqlDataReader.Read()) + { + if (sqlDataReader.GetInt32(0) == 1) + { + Console.WriteLine(" * Employee Id received as sent: " + sqlDataReader.GetInt32(0)); + } + else + { + Console.WriteLine("Employee Id didn't match"); + } + + if (sqlDataReader.GetString(1) == @"Microsoft") + { + Console.WriteLine(" * Employee Firstname received as sent: " + sqlDataReader.GetString(1)); + } + else + { + Console.WriteLine("Employee FirstName didn't match."); + } + + if (sqlDataReader.GetString(2) == @"Corporation") + { + Console.WriteLine(" * Employee LastName received as sent: " + sqlDataReader.GetString(2)); + } + else + { + Console.WriteLine("Employee LastName didn't match."); + } + } + } + + private static void dropObjects(SqlConnection sqlConnection, string cmkName, string cekName, string tblName) + { + using (SqlCommand cmd = sqlConnection.CreateCommand()) + { + cmd.CommandText = $@"IF EXISTS (select * from sys.objects where name = '{tblName}') BEGIN DROP TABLE [{tblName}] END"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $@"IF EXISTS (select * from sys.column_encryption_keys where name = '{cekName}') BEGIN DROP COLUMN ENCRYPTION KEY [{cekName}] END"; + cmd.ExecuteNonQuery(); + cmd.CommandText = $@"IF EXISTS (select * from sys.column_master_keys where name = '{cmkName}') BEGIN DROP COLUMN MASTER KEY [{cmkName}] END"; + cmd.ExecuteNonQuery(); + } + } + + private class CustomerRecord + { + internal int Id { get; set; } + internal string FirstName { get; set; } + internal string LastName { get; set; } + + public CustomerRecord(int id, string fName, string lName) + { + Id = id; + FirstName = fName; + LastName = lName; + } + } + } +} +// diff --git a/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs b/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs new file mode 100644 index 0000000000..c91e3edd7c --- /dev/null +++ b/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs @@ -0,0 +1,20 @@ +// +class Program +{ + static void Main() + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + using (SqlCommand command = connection.CreateCommand()) + { + Dictionary customKeyStoreProviders = new Dictionary(); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders); + // Perform database operation using Azure Key Vault Provider + // Any decrypted column encryption keys will be cached + } // Column encryption key cache of "azureKeyVaultProvider" is cleared when "azureKeyVaultProvider" goes out of scope + } + } +} +// diff --git a/doc/samples/CustomDeviceCodeFlowAzureAuthenticationProvider.cs b/doc/samples/CustomDeviceCodeFlowAzureAuthenticationProvider.cs index 19bc1f849d..68d792fef5 100644 --- a/doc/samples/CustomDeviceCodeFlowAzureAuthenticationProvider.cs +++ b/doc/samples/CustomDeviceCodeFlowAzureAuthenticationProvider.cs @@ -44,6 +44,7 @@ public class Program { public static void Main() { + // Register our custom authentication provider class to override Active Directory Device Code Flow SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, new CustomDeviceCodeFlowAzureAuthenticationProvider()); using (SqlConnection sqlConnection = new SqlConnection("Server=.database.windows.net;Authentication=Active Directory Device Code Flow;Database=;")) { diff --git a/doc/samples/IBinarySerialize.cs b/doc/samples/IBinarySerialize.cs index 749ee5f3f6..64f314f86d 100644 --- a/doc/samples/IBinarySerialize.cs +++ b/doc/samples/IBinarySerialize.cs @@ -2,7 +2,7 @@ using System.IO; using System.Data; using System.Data.SqlTypes; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; using System.Text; namespace test @@ -45,7 +45,7 @@ static void Main(string[] args) // Bytes 0 - 19: string text, padded to the right with null characters // Bytes 20+: Double value - // using Microsoft.Data.SqlClient.Server; + // using Microsoft.SqlServer.Server; public void Read(System.IO.BinaryReader r) { @@ -84,7 +84,7 @@ public void Read(System.IO.BinaryReader r) // Bytes 0 - 19: string text, padded to the right with null characters // Bytes 20+: Double value - // using Microsoft.Data.SqlClient.Server; + // using Microsoft.SqlServer.Server; public void Write(System.IO.BinaryWriter w) { int maxStringSize = 20; diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs new file mode 100644 index 0000000000..3631912ba6 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using Microsoft.SqlServer.Server; + +namespace test +{ + public class Class1 : IBinarySerialize + { + [STAThread] + static void Main(string[] args) + { + string fileName = "info.dat"; + Class1 temp = new Class1(); + + FileStream fs = new FileStream(fileName, FileMode.Create); + BinaryWriter w = new BinaryWriter(fs); + + temp.Write(w); + + w.Close(); + fs.Close(); + + fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); + BinaryReader r = new BinaryReader(fs); + + temp.Read(r); + + Console.WriteLine("String value: " + temp.StringValue); + Console.WriteLine("Double value: " + temp.DoubleValue); + + r.Close(); + fs.Close(); + } + + public string StringValue; + public double DoubleValue; + + // + // The binary layout is as follows: + // Bytes 0 - 19: string text, padded to the right with null characters + // Bytes 20+: Double value + + // using Microsoft.SqlServer.Server; + public void Read(System.IO.BinaryReader r) + { + + int maxStringSize = 20; + char[] chars; + int stringEnd; + string stringValue; + double doubleValue; + + // Read the characters from the binary stream. + chars = r.ReadChars(maxStringSize); + + // Find the start of the null character padding. + stringEnd = Array.IndexOf(chars, '\0'); + + if (stringEnd == 0) + { + stringValue = null; + return; + } + + // Build the string from the array of characters. + stringValue = new String(chars, 0, stringEnd); + + // Read the double value from the binary stream. + doubleValue = r.ReadDouble(); + + // Set the object's properties equal to the values. + this.StringValue = stringValue; + this.DoubleValue = doubleValue; + } + // + + // + // The binary layout is as follows: + // Bytes 0 - 19: string text, padded to the right with null characters + // Bytes 20+: Double value + + // using Microsoft.SqlServer.Server; + public void Write(System.IO.BinaryWriter w) + { + int maxStringSize = 20; + string stringValue = "The value of PI: "; + string paddedString; + double value = 3.14159; + + // Pad the string from the right with null characters. + paddedString = stringValue.PadRight(maxStringSize, '\0'); + + // Write the string value one byte at a time. + for (int i = 0; i < paddedString.Length; i++) + { + w.Write(paddedString[i]); + } + + // Write the double value. + w.Write(value); + } + // + } +} diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs new file mode 100644 index 0000000000..e35b9cad21 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs @@ -0,0 +1,23 @@ +using System.IO; +using System.Collections; +using Microsoft.SqlServer.Server; + +public class Class1 +{ + +// +[SqlFunctionAttribute(FillRowMethodName = "FillFileRow")] +public static IEnumerable GetFileDetails(string directoryPath) +{ + try + { + DirectoryInfo di = new DirectoryInfo(directoryPath); + return di.GetFiles(); + } + catch (DirectoryNotFoundException dnf) + { + return new string[1] { dnf.ToString() }; + } +} +// +} \ No newline at end of file diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs new file mode 100644 index 0000000000..34999b5dd5 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs @@ -0,0 +1,23 @@ +// +using System; +using System.IO; +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedAggregate(Microsoft.SqlServer.Server.Format.UserDefined, + IsInvariantToNulls = true, + IsInvariantToDuplicates = false, + IsInvariantToOrder = false, + MaxByteSize = 8000) + ] +public class Concatenate : Microsoft.SqlServer.Server.IBinarySerialize +{ + public void Read(BinaryReader r) + { + } + + public void Write(BinaryWriter w) + { + } +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs new file mode 100644 index 0000000000..90bfccfce4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs @@ -0,0 +1,120 @@ +using System; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; +using System.Text; + +// +[Serializable] +[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native, + IsByteOrdered=true, + Name="Point",ValidationMethodName = "ValidatePoint")] +public struct Point : INullable +{ +// + private bool is_Null; + private int _x; + private int _y; + + public bool IsNull + { + get + { + return (is_Null); + } + } + + public static Point Null + { + get + { + Point pt = new Point(); + pt.is_Null = true; + return pt; + } + } + + // Use StringBuilder to provide string representation of UDT. + public override string ToString() + { + // Since InvokeIfReceiverIsNull defaults to 'true' + // this test is unnecessary if Point is only being called + // from SQL. + if (this.IsNull) + { + return "NULL"; + } + else + { + StringBuilder builder = new StringBuilder(); + builder.Append(_x); + builder.Append(","); + builder.Append(_y); + return builder.ToString(); + } + } + + [SqlMethod(OnNullCall = false)] + public static Point Parse(SqlString s) + { + // With OnNullCall=false, this check is unnecessary if + // Point only called from SQL. + if (s.IsNull) + return Null; + + // Parse input string to separate out points. + Point pt = new Point(); + string[] xy = s.Value.Split(",".ToCharArray()); + pt.X = int.Parse(xy[0]); + pt.Y = int.Parse(xy[1]); + + // Call ValidatePoint to enforce validation + // for string conversions. + if (!pt.ValidatePoint()) + throw new ArgumentException("Invalid XY coordinate values."); + return pt; + } + + // X and Y coordinates exposed as properties. + public int X + { + get + { + return this._x; + } + // Call ValidatePoint to ensure valid range of Point values. + set + { + int temp = _x; + _x = value; + if (!ValidatePoint()) + { + _x = temp; + throw new ArgumentException("Invalid X coordinate value."); + } + } + } + + public int Y + { + get + { + return this._y; + } + set + { + int temp = _y; + _y = value; + if (!ValidatePoint()) + { + _y = temp; + throw new ArgumentException("Invalid Y coordinate value."); + } + } + } + + // Validation method to enforce valid X and Y values. + private bool ValidatePoint() + { + return true; + } +} diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs new file mode 100644 index 0000000000..7317b1917b --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs @@ -0,0 +1,45 @@ +using System.Collections; +//----------------------------------------------------------------------------- +// +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; + +public partial class UserDefinedFunctions +{ + public const double SALES_TAX = .086; + + [SqlFunction()] + public static SqlDouble addTax(SqlDouble originalAmount) + { + SqlDouble taxAmount = originalAmount * SALES_TAX; + + return originalAmount + taxAmount; + } +} +// + +//----------------------------------------------------------------------------- +// +public partial class UserDefinedFunctions +{ + [SqlFunction(Name="sp_scalarFunc")] + public static SqlString SampleScalarFunction(SqlString s) + { + //... + return ""; + } +} +// + +//----------------------------------------------------------------------------- +// +public partial class UserDefinedFunctions +{ + [SqlFunction(Name="sp_tableFunc", TableDefinition="letter nchar(1)")] + public static IEnumerable SampleTableFunction(SqlString s) + { + //... + return new ArrayList(new char[3] {'a', 'b', 'c'}); + } +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs new file mode 100644 index 0000000000..9e41150c99 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs @@ -0,0 +1,126 @@ +using System; +// +using Microsoft.SqlServer.Server; +using System.Data.SqlTypes; +using System.Text; + +[Serializable] +[SqlUserDefinedType(Format.Native, + IsByteOrdered = true, + Name = "Point", ValidationMethodName = "ValidatePoint")] +public struct Point : INullable +{ + + private bool is_Null; + private int _x; + private int _y; + + // Distance from Point to the specified x and y values method. + [SqlMethod(OnNullCall = false, IsMutator = false, InvokeIfReceiverIsNull = false)] + public Double DistanceFromXY(int iX, int iY) + { + return Math.Sqrt(Math.Pow(iX - _x, 2.0) + Math.Pow(iY - _y, 2.0)); + } + // + + public bool IsNull + { + get + { + return (is_Null); + } + } + + public static Point Null + { + get + { + Point pt = new Point(); + pt.is_Null = true; + return pt; + } + } + + // Use StringBuilder to provide string representation of UDT. + public override string ToString() + { + // Since InvokeIfReceiverIsNull defaults to 'true' + // this test is unnecessary if Point is only being called + // from SQL. + if (this.IsNull) + return "NULL"; + else + { + StringBuilder builder = new StringBuilder(); + builder.Append(_x); + builder.Append(","); + builder.Append(_y); + return builder.ToString(); + } + } + + [SqlMethod(OnNullCall = false)] + public static Point Parse(SqlString s) + { + // With OnNullCall=false, this check is unnecessary if + // Point only called from SQL. + if (s.IsNull) + return Null; + + // Parse input string to separate out points. + Point pt = new Point(); + string[] xy = s.Value.Split(",".ToCharArray()); + pt.X = int.Parse(xy[0]); + pt.Y = int.Parse(xy[1]); + + // Call ValidatePoint to enforce validation + // for string conversions. + if (!pt.ValidatePoint()) + throw new ArgumentException("Invalid XY coordinate values."); + return pt; + } + + // X and Y coordinates exposed as properties. + public int X + { + get + { + return this._x; + } + // Call ValidatePoint to ensure valid range of Point values. + set + { + int temp = _x; + _x = value; + if (!ValidatePoint()) + { + _x = temp; + throw new ArgumentException("Invalid X coordinate value."); + } + } + } + + public int Y + { + get + { + return this._y; + } + set + { + int temp = _y; + _y = value; + if (!ValidatePoint()) + { + _y = temp; + throw new ArgumentException("Invalid Y coordinate value."); + } + } + } + + // Validation method to enforce valid X and Y values. + private bool ValidatePoint() + { + return true; + } +} diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedAggregateAttribute_Aggregate1.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedAggregateAttribute_Aggregate1.cs new file mode 100644 index 0000000000..edf119d940 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedAggregateAttribute_Aggregate1.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// +using System; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedAggregate(Format.Native)] +public struct CountVowels +{ + // count only the vowels in the passed-in strings + private SqlInt32 countOfVowels; + + public void Init() + { + countOfVowels = 0; + } + + public void Accumulate(SqlString value) + { + // list of vowels to look for + string vowels = "aeiou"; + + // for each character in the given parameter + for (int i=0; i < value.ToString().Length; i++) + { + // for each character in the vowels string + for (int j=0; j < vowels.Length; j++) + { + // convert parameter character to lowercase and compare to vowel + if (value.Value.Substring(i,1).ToLower() == vowels.Substring(j,1)) + { + // it is a vowel, increment the count + countOfVowels+=1; + } + } + } + } + + public void Merge(CountVowels value) + { + Accumulate(value.Terminate()); + } + + public SqlString Terminate() + { + return countOfVowels.ToString(); + } +} +// + +//----------------------------------------------------------------------------- +// +[SqlUserDefinedAggregate(Format.Native)] +public class SampleAggregate +{ + //... +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedTypeAttribute_Type1.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedTypeAttribute_Type1.cs new file mode 100644 index 0000000000..70f8d0ae7b --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedTypeAttribute_Type1.cs @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// +using System; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; + +[Serializable()] +[SqlUserDefinedType(Format.Native)] +public struct Point : INullable +{ + private int m_x; + private int m_y; + private bool is_Null; + + public int X + { + get + { + return (this.m_x); + } + set + { + m_x = value; + } + } + + public int Y + { + get + { + return (this.m_y); + } + set + { + m_y = value; + } + } + + public bool IsNull + { + get + { + return is_Null; + } + } + + public static Point Null + { + get + { + Point pt = new Point(); + pt.is_Null = true; + return (pt); + } + } + + public override string ToString() + { + if (this.IsNull) + { + return "NULL"; + } + else + { + return this.m_x + ":" + this.m_y; + } + } + + public static Point Parse(SqlString s) + { + if (s.IsNull) + { + return Null; + } + + // Parse input string here to separate out coordinates + string str = Convert.ToString(s); + string[] xy = str.Split(':'); + + Point pt = new Point(); + pt.X = Convert.Toint(xy[0]); + pt.Y = Convert.Toint(xy[1]); + return (pt); + } + + public SqlString Quadrant() + { + if (m_x == 0 && m_y == 0) + { + return "centered"; + } + + SqlString stringReturn = ""; + + if (m_x == 0) + { + stringReturn = "center"; + } + else if (m_x > 0) + { + stringReturn = "right"; + } + else if (m_x < 0) + { + stringReturn = "left"; + } + + if (m_y == 0) + { + stringReturn = stringReturn + " center"; + } + else if (m_y > 0) + { + stringReturn = stringReturn + " top"; + } + else if (m_y < 0) + { + stringReturn = stringReturn + " bottom"; + } + + return stringReturn; + } +} +// + +//----------------------------------------------------------------------------- +// +[SqlUserDefinedType(Format.Native, MaxByteSize=8000)] +public class SampleType +{ + //... +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb new file mode 100644 index 0000000000..8f96b056a4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb @@ -0,0 +1,103 @@ +Option Explicit On +Option Strict On + +Imports System +Imports System.IO +Imports Microsoft.SqlServer.Server +Imports System.Text + +Public Class Class1:Implements Microsoft.SqlServer.Server.IBinarySerialize + +Dim StringValue As String +Dim DoubleValue As Double + +Shared Sub Main() + + Dim fileName As String = "info.dat" + Dim temp As New Class1() + + Dim fs As New FileStream(fileName, FileMode.Create) + Dim w As New BinaryWriter(fs) + + temp.Write(w) + + w.Close() + fs.Close() + + fs = New FileStream(fileName, FileMode.Open, FileAccess.Read) + Dim r As New BinaryReader(fs) + + temp.Read(r) + + Console.WriteLine("String Value: " & temp.StringValue) + Console.WriteLine("Double value: " & temp.DoubleValue) + +End Sub + +' +' The binary layout is as follows: +' Bytes 0 - 19: string text, padded to the right with null +' characters +' Bytes 20+: double value +Public Sub Read(ByVal r As System.IO.BinaryReader) _ + Implements Microsoft.SqlServer.Server.IBinarySerialize.Read + + Dim maxStringSize As Integer = 20 + Dim chars As Char() + Dim stringEnd As Integer + Dim stringValue As String + Dim value As double + + ' Read the characters from the binary stream. + chars = r.ReadChars(maxStringSize) + + ' Find the start of the null character padding. + stringEnd = Array.IndexOf(chars, ControlChars.NullChar) + + If StringEnd = 0 Then + stringValue = Nothing + Exit Sub + End If + + ' Build the string from the array of characters. + stringValue = new String(chars, 0, stringEnd) + + ' Read the double value from the binary stream. + value = r.ReadDouble() + + ' Set the object's properties equal to the values. + Me.StringValue = stringValue + Me.DoubleValue = value + +End Sub +' + +' +' The binary layout is as follows: +' Bytes 0 - 19: string text, padded to the right with null characters +' Bytes 20+: Double value +Public Sub Write(ByVal w As System.IO.BinaryWriter) _ + Implements Microsoft.SqlServer.Server.IBinarySerialize.Write + + Dim maxStringSize As Integer = 20 + Dim stringValue As String = "The value of PI: " + Dim paddedString As String + Dim value As Double = 3.14159 + + ' Pad the string from the right with null characters. + paddedString = stringValue.PadRight(maxStringSize, ControlChars.NullChar) + + + ' Write the string value one byte at a time. + Dim i As Integer + For i = 0 To paddedString.Length - 1 + w.Write(paddedString(i)) + Next + + ' Write the double value. + w.Write(value) + +End Sub +' + +End Class diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb new file mode 100644 index 0000000000..41c7bcffcc --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb @@ -0,0 +1,31 @@ +Option Explicit On +Option Strict On + +Imports System.IO +Imports System.Collections +Imports Microsoft.SqlServer.Server + +Public Class Class1 + +' + _ +Public Shared Function GetFileDetails(ByVal directoryPath As String) As IEnumerable + + Try + + Dim di As DirectoryInfo = new DirectoryInfo(directoryPath) + return di.GetFiles() + + Catch dnf As DirectoryNotFoundException + + Dim message As String() = {dnf.ToString() } + return message + + End Try +End Function +' + + + + +End Class \ No newline at end of file diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb new file mode 100644 index 0000000000..2bb1e5af98 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb @@ -0,0 +1,21 @@ +' +Imports System.IO +Imports Microsoft.SqlServer.Server + + _ +Public Class Concatenate + Implements Microsoft.SqlServer.Server.IBinarySerialize + +Public Sub Read(ByVal r As BinaryReader) Implements Microsoft.SqlServer.Server.IBinarySerialize.Read + + End Sub + + Public Sub Write(ByVal w As BinaryWriter) Implements Microsoft.SqlServer.Server.IBinarySerialize.Write + + End Sub +End Class +' diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb new file mode 100644 index 0000000000..e5b2368aa9 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb @@ -0,0 +1,114 @@ +Option Explicit On +Option Strict On + +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server +Imports System.Text + +' + _ + Public Structure Point + Implements INullable +' + Private is_Null As Boolean + Private _x As Integer + Private _y As Integer + + Public ReadOnly Property IsNull() As Boolean _ + Implements INullable.IsNull + Get + Return (is_Null) + End Get + End Property + + Public Shared ReadOnly Property Null() As Point + Get + Dim pt As New Point + pt.is_Null = True + Return (pt) + End Get + End Property + + ' Use StringBuilder to provide string representation of UDT. + Public Overrides Function ToString() As String + ' Since InvokeIfReceiverIsNull defaults to 'true' + ' this test is unneccesary if Point is only being called + ' from SQL. + If Me.IsNull Then + Return "NULL" + Else + Dim builder As StringBuilder = New StringBuilder + builder.Append(_x) + builder.Append(",") + builder.Append(_y) + Return builder.ToString + End If + End Function + + _ + Public Shared Function Parse(ByVal s As SqlString) As Point + ' With OnNullCall=False, this check is unnecessary if + ' Point only being called from SQL. + If s.IsNull Then + Return Null + End If + + ' Parse input string here to separate out points. + Dim pt As New Point() + Dim xy() As String = s.Value.Split(",".ToCharArray()) + pt.X = Integer.Parse(xy(0)) + pt.Y = Integer.Parse(xy(1)) + + ' Call ValidatePoint to enforce validation + ' for string conversions. + If Not pt.ValidatePoint() Then + Throw New ArgumentException("Invalid XY coordinate values.") + End If + Return pt + End Function + + ' X and Y coordinates are exposed as properties. + Public Property X() As Integer + Get + Return (Me._x) + End Get + + Set(ByVal Value As Integer) + Dim temp As Integer = _x + _x = Value + If Not ValidatePoint() Then + _x = temp + Throw New ArgumentException("Invalid X coordinate value.") + End If + End Set + End Property + + Public Property Y() As Integer + Get + Return (Me._y) + End Get + + Set(ByVal Value As Integer) + Dim temp As Integer = _y + _y = Value + If Not ValidatePoint() Then + _y = temp + Throw New ArgumentException("Invalid Y coordinate value.") + End If + End Set + End Property + + ' Validation method to enforce valid X and Y values. + Private Function ValidatePoint() As Boolean + ' Allow only zero or positive integers for X and Y coordinates. + If (_x >= 0) And (_y >= 0) Then + Return True + Else + Return False + End If + End Function + +End Structure diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb new file mode 100644 index 0000000000..b308062cd4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb @@ -0,0 +1,47 @@ +Imports System.Collections +'------------------------------------------------------------------------------ +' +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server + +Partial Public Class UserDefinedFunctions + + Public Const SALES_TAX As Double = 0.086 + + + Public Shared Function addTax(ByVal originalAmount As SqlDouble) As SqlDouble + + Dim taxAmount As SqlDouble = originalAmount * SALES_TAX + + Return originalAmount + taxAmount + End Function +End Class +' + + +'------------------------------------------------------------------------------ +' +Partial Public Class UserDefinedFunctions + + + Public Shared Function SampleScalarFunction(ByVal s As SqlString) As SqlString + + '... + Return "" + End Function +End Class +' + + +'------------------------------------------------------------------------------ +' +Partial Public Class UserDefinedFunctions + + + Public Shared Function SampleTableFunction(ByVal s As SqlString) As IEnumerable + + '... + Return New Char(2) {"a"c, "b"c, "c"c} + End Function +End Class +' diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedAggregateAttribute_Aggregate1.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedAggregateAttribute_Aggregate1.vb new file mode 100644 index 0000000000..b2e7727c51 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedAggregateAttribute_Aggregate1.vb @@ -0,0 +1,57 @@ +'------------------------------------------------------------------------------ +' +Imports System +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server + + + +Public Structure CountVowels + + ' count only the vowels in the passed-in strings + Private countOfVowels As SqlInt32 + + + Public Sub Init() + countOfVowels = 0 + End Sub + + + Public Sub Accumulate(ByVal value As SqlString) + Dim stringChar As String + Dim indexChar As Integer + + ' for each character in the given parameter + For indexChar = 0 To Len(value.ToString()) - 1 + + stringChar = value.ToString().Substring(indexChar, 1) + + If stringChar.ToLower() Like "[aeiou]" Then + + ' it is a vowel, increment the count + countOfVowels = countOfVowels + 1 + End If + Next + End Sub + + + Public Sub Merge(ByVal value As CountVowels) + + Accumulate(value.Terminate()) + End Sub + + + Public Function Terminate() As SqlString + + Return countOfVowels.ToString() + End Function +End Structure +' + +'------------------------------------------------------------------------------ +' + +Public Class SampleAggregate + '... +End Class +' diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedTypeAttribute_Type1.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedTypeAttribute_Type1.vb new file mode 100644 index 0000000000..effdf6bda4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedTypeAttribute_Type1.vb @@ -0,0 +1,116 @@ +'------------------------------------------------------------------------------ +' +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server + + + +Public Structure Point + Implements INullable + + Private m_x As Integer + Private m_y As Integer + Private is_Null As Boolean + + Public Property X() As Integer + Get + Return (Me.m_x) + End Get + Set(ByVal Value As Integer) + m_x = Value + End Set + End Property + + Public Property Y() As Integer + Get + Return (Me.m_y) + End Get + Set(ByVal Value As Integer) + m_y = Value + End Set + End Property + + Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull + Get + Return is_Null + End Get + End Property + + Public Shared ReadOnly Property Null() As Point + Get + Dim pt As Point = New Point + pt.is_Null = True + Return pt + End Get + End Property + + Public Overrides Function ToString() As String + If Me.IsNull() Then + Return Nothing + Else + Return Me.m_x & ":" & Me.m_y + End If + End Function + + Public Shared Function Parse(ByVal s As SqlString) As Point + If s = SqlString.Null Then + Return Null + End If + + If s.ToString() = SqlString.Null.ToString() Then + Return Null + End If + + If s.IsNull Then + Return Null + End If + + 'Parse input string here to separate out coordinates + Dim str As String = Convert.ToString(s) + Dim xy() As String = str.Split(":"c) + + Dim pt As New Point() + pt.X = CType(xy(0), Integer) + pt.Y = CType(xy(1), Integer) + Return (pt) + End Function + + Public Function Quadrant() As SqlString + + If m_x = 0 And m_y = 0 Then + Return "centered" + End If + + Dim stringResult As String = "" + + Select Case m_x + Case 0 + stringResult = "center" + Case Is > 0 + stringResult = "right" + Case Is < 0 + stringResult = "left" + End Select + + Select Case m_y + Case 0 + stringResult = stringResult & " center" + Case Is > 0 + stringResult = stringResult & " top" + Case Is < 0 + stringResult = stringResult & " bottom" + End Select + + Return stringResult + End Function +End Structure +' + +'------------------------------------------------------------------------------ +' + +Public Class SampleType + + '... +End Class +' diff --git a/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs b/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs new file mode 100644 index 0000000000..d70410df59 --- /dev/null +++ b/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs @@ -0,0 +1,32 @@ +// +class Program +{ + static void Main() + { + Dictionary customKeyStoreProviders = new Dictionary(); + MyCustomKeyStoreProvider firstProvider = new MyCustomKeyStoreProvider(); + customKeyStoreProviders.Add("FIRST_CUSTOM_STORE", firstProvider); + // Registers the provider globally + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders); + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + customKeyStoreProviders.Clear(); + MyCustomKeyStoreProvider secondProvider = new MyCustomKeyStoreProvider(); + customKeyStoreProviders.Add("SECOND_CUSTOM_STORE", secondProvider); + // Registers the provider on the connection + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders); + + using (SqlCommand command = connection.CreateCommand()) + { + customKeyStoreProviders.Clear(); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + // Registers the provider on the command + // These providers will take precedence over connection-level providers and globally registered providers + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders); + } + } + } +} +// diff --git a/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs b/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs new file mode 100644 index 0000000000..e51ec09c83 --- /dev/null +++ b/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs @@ -0,0 +1,23 @@ +// +class Program +{ + static void Main() + { + Dictionary customKeyStoreProviders = new Dictionary(); + MyCustomKeyStoreProvider myProvider = new MyCustomKeyStoreProvider(); + customKeyStoreProviders.Add("MY_CUSTOM_STORE", myProvider); + // Registers the provider globally + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders); + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + customKeyStoreProviders.Clear(); + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + customKeyStoreProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + // Registers the provider on the connection + // These providers will take precedence over globally registered providers + connection.RegisterColumnEncryptionKeyStoreProvidersOnConnection(customKeyStoreProviders); + } + } +} +// diff --git a/doc/samples/RegisterCustomKeyStoreProvider_Example.cs b/doc/samples/RegisterCustomKeyStoreProvider_Example.cs new file mode 100644 index 0000000000..334b1264c1 --- /dev/null +++ b/doc/samples/RegisterCustomKeyStoreProvider_Example.cs @@ -0,0 +1,51 @@ +// +using Microsoft.Data.SqlClient; +using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; +using System.Collections.Generic; + +class Program +{ + // Maps a SqlColumnEncryptionAzureKeyVaultProvider to some object that represents a user + static Dictionary providerByUser = new(); + + void ExecuteSelectQuery(object user, SqlConnection connection) + { + // Check if the user already has a SqlColumnEncryptionAzureKeyVaultProvider + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = providerByUser[user]; + if (azureKeyVaultProvider is null) + { + // Create a new SqlColumnEncryptionAzureKeyVaultProvider with the user's credentials and save it for future use + azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + providerByUser[user] = azureKeyVaultProvider; + } + + Dictionary customProviders = new(); + customProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + + using SqlCommand command = new("SELECT * FROM Customers", connection); + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProviders); + // Perform database operations + // Any decrypted column encryption keys will be cached by azureKeyVaultProvider + } + + void ExecuteUpdateQuery(object user, SqlConnection connection) + { + // Check if the user already has a SqlColumnEncryptionAzureKeyVaultProvider + SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = providerByUser[user]; + if (azureKeyVaultProvider is null) + { + // Create a new SqlColumnEncryptionAzureKeyVaultProvider with the user's credentials and save it for future use + azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(); + providerByUser[user] = azureKeyVaultProvider; + } + + Dictionary customProviders = new(); + customProviders.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider); + + using SqlCommand command = new("UPDATE Customers SET Name = 'NewName' WHERE CustomerId = 1", connection); + command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customProviders); + // Perform database operations + // Any decrypted column encryption keys will be cached by azureKeyVaultProvider + } +} +// diff --git a/doc/samples/SqlClientDiagnosticCounter.cs b/doc/samples/SqlClientDiagnosticCounter.cs new file mode 100644 index 0000000000..576563f6fd --- /dev/null +++ b/doc/samples/SqlClientDiagnosticCounter.cs @@ -0,0 +1,62 @@ +// +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using System.Linq; + +// This listener class will listen for events from the SqlClientEventSource class. +// SqlClientEventSource is an implementation of the EventSource class which gives +// it the ability to create events. +public class EventCounterListener : EventListener +{ + protected override void OnEventSourceCreated(EventSource eventSource) + { + // Only enable events from SqlClientEventSource. + if (eventSource.Name.Equals("Microsoft.Data.SqlClient.EventSource")) + { + var options = new Dictionary(); + // define time interval 1 second + // without defining this parameter event counters will not enabled + options.Add("EventCounterIntervalSec", "1"); + // enable for the None keyword + EnableEvents(eventSource, EventLevel.Informational, EventKeywords.None, options); + } + } + + // This callback runs whenever an event is written by SqlClientEventSource. + // Event data is accessed through the EventWrittenEventArgs parameter. + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + if (eventData.Payload.FirstOrDefault(p => p is IDictionary x && x.ContainsKey("Name")) is IDictionary counters) + { + if (counters.TryGetValue("DisplayName", out object name) && name is string cntName + && counters.TryGetValue("Mean", out object value) && value is double cntValue) + { + // print event counter's name and mean value + Console.WriteLine($"{cntName}\t\t{cntValue}"); + } + } + } +} + +class Program +{ + static void Main(string[] args) + { + // Create a new event listener + using (var listener = new EventCounterListener()) + { + string connectionString = "Data Source=localhost; Integrated Security=true"; + + for (int i = 0; i < 50; i++) + { + // Open a connection + SqlConnection cnn = new SqlConnection(connectionString); + cnn.Open(); + // wait for sampling interval happens + System.Threading.Thread.Sleep(500); + } + } + } +} +// diff --git a/doc/samples/SqlClientFactory_Netcoreapp.cs b/doc/samples/SqlClientFactory_Netcoreapp.cs new file mode 100644 index 0000000000..e72d623067 --- /dev/null +++ b/doc/samples/SqlClientFactory_Netcoreapp.cs @@ -0,0 +1,20 @@ +using System; +using System.Data.Common; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + } + + // + private static DbProviderFactory GetFactory() + { + // register SqlClientFactory in provider factories + DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", SqlClientFactory.Instance); + + return DbProviderFactories.GetFactory("Microsoft.Data.SqlClient"); + } + // +} diff --git a/doc/samples/SqlClient_AsyncProgramming_Cancellation.cs b/doc/samples/SqlClient_AsyncProgramming_Cancellation.cs new file mode 100644 index 0000000000..d60fc6f99b --- /dev/null +++ b/doc/samples/SqlClient_AsyncProgramming_Cancellation.cs @@ -0,0 +1,45 @@ +using System; +// +using Microsoft.Data.SqlClient; +using System.Threading; +using System.Threading.Tasks; + +namespace Samples +{ + class CancellationSample + { + public static void Main(string[] args) + { + CancellationTokenSource source = new CancellationTokenSource(); + source.CancelAfter(2000); // give up after 2 seconds + try + { + Task result = CancellingAsynchronousOperations(source.Token); + result.Wait(); + } + catch (AggregateException exception) + { + if (exception.InnerException is SqlException) + { + Console.WriteLine("Operation canceled"); + } + else + { + throw; + } + } + } + + static async Task CancellingAsynchronousOperations(CancellationToken cancellationToken) + { + using (SqlConnection connection = new SqlConnection("Server=(local);Integrated Security=true")) + { + await connection.OpenAsync(cancellationToken); + + SqlCommand command = new SqlCommand("WAITFOR DELAY '00:10:00'", connection); + await command.ExecuteNonQueryAsync(cancellationToken); + } + } + } +} +// diff --git a/doc/samples/SqlClient_AsyncProgramming_DistributedTransaction.cs b/doc/samples/SqlClient_AsyncProgramming_DistributedTransaction.cs new file mode 100644 index 0000000000..a517e0ea40 --- /dev/null +++ b/doc/samples/SqlClient_AsyncProgramming_DistributedTransaction.cs @@ -0,0 +1,68 @@ +using System; +// +using Microsoft.Data.SqlClient; +using System.Threading.Tasks; +using System.Transactions; + +class Program +{ + public static void Main() + { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); + // replace these with your own values + // create two tables RegionTable1 and RegionTable2 + // and add a constraint in one of these tables + // to avoid duplicate RegionID + builder.DataSource = "localhost"; + builder.InitialCatalog = "Northwind"; + builder.IntegratedSecurity = true; + + Task task = ExecuteDistributedTransaction(builder.ConnectionString, builder.ConnectionString); + task.Wait(); + } + + static async Task ExecuteDistributedTransaction(string connectionString1, string connectionString2) + { + using (SqlConnection connection1 = new SqlConnection(connectionString1)) + using (SqlConnection connection2 = new SqlConnection(connectionString2)) + { + using (CommittableTransaction transaction = new CommittableTransaction()) + { + await connection1.OpenAsync(); + connection1.EnlistTransaction(transaction); + + await connection2.OpenAsync(); + connection2.EnlistTransaction(transaction); + + try + { + SqlCommand command1 = connection1.CreateCommand(); + command1.CommandText = "Insert into RegionTable1 (RegionID, RegionDescription) VALUES (100, 'Description')"; + await command1.ExecuteNonQueryAsync(); + + SqlCommand command2 = connection2.CreateCommand(); + command2.CommandText = "Insert into RegionTable2 (RegionID, RegionDescription) VALUES (100, 'Description')"; + await command2.ExecuteNonQueryAsync(); + + transaction.Commit(); + } + catch (Exception ex) + { + Console.WriteLine("Exception Type: {0}", ex.GetType()); + Console.WriteLine(" Message: {0}", ex.Message); + + try + { + transaction.Rollback(); + } + catch (Exception ex2) + { + Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType()); + Console.WriteLine(" Message: {0}", ex2.Message); + } + } + } + } + } +} +// diff --git a/doc/samples/SqlClient_AsyncProgramming_MultipleCommands.cs b/doc/samples/SqlClient_AsyncProgramming_MultipleCommands.cs new file mode 100644 index 0000000000..6f53f2b55c --- /dev/null +++ b/doc/samples/SqlClient_AsyncProgramming_MultipleCommands.cs @@ -0,0 +1,72 @@ +using System; +// +using System.Data.Common; +using Microsoft.Data.SqlClient; +using System.Threading.Tasks; + +class Class1 +{ + static void Main() + { + Task task = MultipleCommands(); + task.Wait(); + } + + static async Task MultipleCommands() + { + // By default, MARS is disabled when connecting to a MARS-enabled. + // It must be enabled in the connection string. + string connectionString = GetConnectionString(); + + int vendorID; + SqlDataReader productReader = null; + string vendorSQL = + "SELECT BusinessEntityID, Name FROM Purchasing.Vendor"; + string productSQL = + "SELECT Production.Product.Name FROM Production.Product " + + "INNER JOIN Purchasing.ProductVendor " + + "ON Production.Product.ProductID = " + + "Purchasing.ProductVendor.ProductID " + + "WHERE Purchasing.ProductVendor.BusinessEntityID = @VendorId"; + + using (SqlConnection awConnection = + new SqlConnection(connectionString)) + { + SqlCommand vendorCmd = new SqlCommand(vendorSQL, awConnection); + SqlCommand productCmd = + new SqlCommand(productSQL, awConnection); + + productCmd.Parameters.Add("@VendorId", SqlDbType.Int); + + await awConnection.OpenAsync(); + using (SqlDataReader vendorReader = await vendorCmd.ExecuteReaderAsync()) + { + while (await vendorReader.ReadAsync()) + { + Console.WriteLine(vendorReader["Name"]); + + vendorID = (int)vendorReader["BusinessEntityID"]; + + productCmd.Parameters["@VendorId"].Value = vendorID; + // The following line of code requires a MARS-enabled connection. + productReader = await productCmd.ExecuteReaderAsync(); + using (productReader) + { + while (await productReader.ReadAsync()) + { + Console.WriteLine(" " + + productReader["Name"].ToString()); + } + } + } + } + } + } + + private static string GetConnectionString() + { + // 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;MultipleActiveResultSets=True"; + } +} +// diff --git a/doc/samples/SqlClient_AsyncProgramming_MultipleCommandsEx.cs b/doc/samples/SqlClient_AsyncProgramming_MultipleCommandsEx.cs new file mode 100644 index 0000000000..47376ca18c --- /dev/null +++ b/doc/samples/SqlClient_AsyncProgramming_MultipleCommandsEx.cs @@ -0,0 +1,116 @@ +using System; +// +using System.Data.Common; +using Microsoft.Data.SqlClient; +using System.Threading.Tasks; + +class Program +{ + static void Main() + { + Task task = ReadingAndUpdatingData(); + task.Wait(); + } + + static async Task ReadingAndUpdatingData() + { + // By default, MARS is disabled when connecting to a MARS-enabled host. + // It must be enabled in the connection string. + string connectionString = GetConnectionString(); + + SqlTransaction updateTx = null; + SqlCommand vendorCmd = null; + SqlCommand prodVendCmd = null; + SqlCommand updateCmd = null; + + SqlDataReader prodVendReader = null; + + int vendorID = 0; + int productID = 0; + int minOrderQty = 0; + int maxOrderQty = 0; + int onOrderQty = 0; + int recordsUpdated = 0; + int totalRecordsUpdated = 0; + + string vendorSQL = + "SELECT BusinessEntityID, Name FROM Purchasing.Vendor " + + "WHERE CreditRating = 5"; + string prodVendSQL = + "SELECT ProductID, MaxOrderQty, MinOrderQty, OnOrderQty " + + "FROM Purchasing.ProductVendor " + + "WHERE BusinessEntityID = @VendorID"; + string updateSQL = + "UPDATE Purchasing.ProductVendor " + + "SET OnOrderQty = @OrderQty " + + "WHERE ProductID = @ProductID AND BusinessEntityID = @VendorID"; + + using (SqlConnection awConnection = + new SqlConnection(connectionString)) + { + await awConnection.OpenAsync(); + updateTx = await Task.Run(() => awConnection.BeginTransaction()); + + vendorCmd = new SqlCommand(vendorSQL, awConnection); + vendorCmd.Transaction = updateTx; + + prodVendCmd = new SqlCommand(prodVendSQL, awConnection); + prodVendCmd.Transaction = updateTx; + prodVendCmd.Parameters.Add("@VendorId", SqlDbType.Int); + + updateCmd = new SqlCommand(updateSQL, awConnection); + updateCmd.Transaction = updateTx; + updateCmd.Parameters.Add("@OrderQty", SqlDbType.Int); + updateCmd.Parameters.Add("@ProductID", SqlDbType.Int); + updateCmd.Parameters.Add("@VendorID", SqlDbType.Int); + + using (SqlDataReader vendorReader = await vendorCmd.ExecuteReaderAsync()) + { + while (await vendorReader.ReadAsync()) + { + Console.WriteLine(vendorReader["Name"]); + + vendorID = (int)vendorReader["BusinessEntityID"]; + prodVendCmd.Parameters["@VendorID"].Value = vendorID; + prodVendReader = await prodVendCmd.ExecuteReaderAsync(); + + using (prodVendReader) + { + while (await prodVendReader.ReadAsync()) + { + productID = (int)prodVendReader["ProductID"]; + + if (prodVendReader["OnOrderQty"] == DBNull.Value) + { + minOrderQty = (int)prodVendReader["MinOrderQty"]; + onOrderQty = minOrderQty; + } + else + { + maxOrderQty = (int)prodVendReader["MaxOrderQty"]; + onOrderQty = (int)(maxOrderQty / 2); + } + + updateCmd.Parameters["@OrderQty"].Value = onOrderQty; + updateCmd.Parameters["@ProductID"].Value = productID; + updateCmd.Parameters["@VendorID"].Value = vendorID; + + recordsUpdated = await updateCmd.ExecuteNonQueryAsync(); + totalRecordsUpdated += recordsUpdated; + } + } + } + } + Console.WriteLine("Total Records Updated: ", totalRecordsUpdated.ToString()); + await Task.Run(() => updateTx.Rollback()); + Console.WriteLine("Transaction Rolled Back"); + } + } + + private static string GetConnectionString() + { + // 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;MultipleActiveResultSets=True"; + } +} +// diff --git a/doc/samples/SqlClient_AsyncProgramming_ProviderModel.cs b/doc/samples/SqlClient_AsyncProgramming_ProviderModel.cs new file mode 100644 index 0000000000..7653e3947d --- /dev/null +++ b/doc/samples/SqlClient_AsyncProgramming_ProviderModel.cs @@ -0,0 +1,46 @@ +using System; +// +using System.Data.Common; +using Microsoft.Data.SqlClient; +using System.Threading.Tasks; + +class program +{ + static async Task PerformDBOperationsUsingProviderModel(string connectionString) + { + using (DbConnection connection = SqlClientFactory.Instance.CreateConnection()) + { + connection.ConnectionString = connectionString; + await connection.OpenAsync(); + + DbCommand command = connection.CreateCommand(); + command.CommandText = "SELECT * FROM AUTHORS"; + + using (DbDataReader reader = await command.ExecuteReaderAsync()) + { + while (await reader.ReadAsync()) + { + for (int i = 0; i < reader.FieldCount; i++) + { + // Process each column as appropriate + object obj = await reader.GetFieldValueAsync(i); + Console.WriteLine(obj); + } + } + } + } + } + + public static void Main() + { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); + // replace these with your own values + builder.DataSource = "localhost"; + builder.InitialCatalog = "pubs"; + builder.IntegratedSecurity = true; + + Task task = PerformDBOperationsUsingProviderModel(builder.ConnectionString); + task.Wait(); + } +} +// diff --git a/doc/samples/SqlClient_AsyncProgramming_SqlBulkCopy.cs b/doc/samples/SqlClient_AsyncProgramming_SqlBulkCopy.cs new file mode 100644 index 0000000000..7a383410fc --- /dev/null +++ b/doc/samples/SqlClient_AsyncProgramming_SqlBulkCopy.cs @@ -0,0 +1,275 @@ +using System; +// +using System.Data; +using Microsoft.Data.SqlClient; +using System.Threading; +using System.Threading.Tasks; + +namespace SqlBulkCopyAsyncCodeSample +{ + class Program + { + static string selectStatement = "SELECT * FROM [pubs].[dbo].[titles]"; + static string createDestTableStatement = + @"CREATE TABLE {0} ( + [title_id] [varchar](6) NOT NULL, + [title] [varchar](80) NOT NULL, + [type] [char](12) NOT NULL, + [pub_id] [char](4) NULL, + [price] [money] NULL, + [advance] [money] NULL, + [royalty] [int] NULL, + [ytd_sales] [int] NULL, + [notes] [varchar](200) NULL, + [pubdate] [datetime] NOT NULL)"; + + // Replace the connection string if needed, for instance to connect to SQL Express: @"Server=(local)\SQLEXPRESS;Database=Demo;Integrated Security=true" + // static string connectionString = @"Server=(localdb)\V11.0;Database=Demo"; + static string connectionString = @"Server=(local);Database=Demo;Integrated Security=true"; + + // static string marsConnectionString = @"Server=(localdb)\V11.0;Database=Demo;MultipleActiveResultSets=true;"; + static string marsConnectionString = @"Server=(local);Database=Demo;MultipleActiveResultSets=true;Integrated Security=true"; + + // Replace the Server name with your actual sql azure server name and User ID/Password + static string azureConnectionString = @"Server=SqlAzure;User ID=;Password=;Database=Demo"; + + static void Main(string[] args) + { + SynchronousSqlBulkCopy(); + AsyncSqlBulkCopy().Wait(); + MixSyncAsyncSqlBulkCopy().Wait(); + AsyncSqlBulkCopyNotifyAfter().Wait(); + AsyncSqlBulkCopyDataRows().Wait(); + AsyncSqlBulkCopySqlServerToSqlAzure().Wait(); + AsyncSqlBulkCopyCancel().Wait(); + AsyncSqlBulkCopyMARS().Wait(); + } + + // 3.1.1 Synchronous bulk copy in .NET 4.5 + private static void SynchronousSqlBulkCopy() + { + using (SqlConnection conn = new SqlConnection(connectionString)) + { + conn.Open(); + DataTable dt = new DataTable(); + using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) + { + SqlDataAdapter adapter = new SqlDataAdapter(cmd); + adapter.Fill(dt); + + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + cmd.CommandText = string.Format(createDestTableStatement, temptable); + cmd.ExecuteNonQuery(); + + using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) + { + bcp.DestinationTableName = temptable; + bcp.WriteToServer(dt); + } + } + } + + } + + // 3.1.2 Asynchronous bulk copy in .NET 4.5 + private static async Task AsyncSqlBulkCopy() + { + using (SqlConnection conn = new SqlConnection(connectionString)) + { + await conn.OpenAsync(); + DataTable dt = new DataTable(); + using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) + { + SqlDataAdapter adapter = new SqlDataAdapter(cmd); + adapter.Fill(dt); + + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + cmd.CommandText = string.Format(createDestTableStatement, temptable); + await cmd.ExecuteNonQueryAsync(); + + using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) + { + bcp.DestinationTableName = temptable; + await bcp.WriteToServerAsync(dt); + } + } + } + } + + // 3.2 Add new Async.NET capabilities in an existing application (Mixing synchronous and asynchronous calls) + private static async Task MixSyncAsyncSqlBulkCopy() + { + using (SqlConnection conn1 = new SqlConnection(connectionString)) + { + conn1.Open(); + using (SqlCommand cmd = new SqlCommand(selectStatement, conn1)) + { + using (SqlDataReader reader = cmd.ExecuteReader()) + { + using (SqlConnection conn2 = new SqlConnection(connectionString)) + { + await conn2.OpenAsync(); + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + SqlCommand createCmd = new SqlCommand(string.Format(createDestTableStatement, temptable), conn2); + await createCmd.ExecuteNonQueryAsync(); + using (SqlBulkCopy bcp = new SqlBulkCopy(conn2)) + { + bcp.DestinationTableName = temptable; + await bcp.WriteToServerAsync(reader); + } + } + } + } + } + } + + // 3.3 Using the NotifyAfter property + private static async Task AsyncSqlBulkCopyNotifyAfter() + { + using (SqlConnection conn = new SqlConnection(connectionString)) + { + await conn.OpenAsync(); + DataTable dt = new DataTable(); + using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) + { + SqlDataAdapter adapter = new SqlDataAdapter(cmd); + adapter.Fill(dt); + + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + cmd.CommandText = string.Format(createDestTableStatement, temptable); + await cmd.ExecuteNonQueryAsync(); + + using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) + { + bcp.DestinationTableName = temptable; + bcp.NotifyAfter = 5; + bcp.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied); + await bcp.WriteToServerAsync(dt); + } + } + } + } + + private static void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e) + { + Console.WriteLine("Copied {0} so far...", e.RowsCopied); + } + + // 3.4 Using the new SqlBulkCopy Async.NET capabilities with DataRow[] + private static async Task AsyncSqlBulkCopyDataRows() + { + using (SqlConnection conn = new SqlConnection(connectionString)) + { + await conn.OpenAsync(); + DataTable dt = new DataTable(); + using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) + { + SqlDataAdapter adapter = new SqlDataAdapter(cmd); + adapter.Fill(dt); + DataRow[] rows = dt.Select(); + + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + cmd.CommandText = string.Format(createDestTableStatement, temptable); + await cmd.ExecuteNonQueryAsync(); + + using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) + { + bcp.DestinationTableName = temptable; + await bcp.WriteToServerAsync(rows); + } + } + } + } + + // 3.5 Copying data from SQL Server to SQL Azure in .NET 4.5 + private static async Task AsyncSqlBulkCopySqlServerToSqlAzure() + { + using (SqlConnection srcConn = new SqlConnection(connectionString)) + using (SqlConnection destConn = new SqlConnection(azureConnectionString)) + { + await srcConn.OpenAsync(); + await destConn.OpenAsync(); + using (SqlCommand srcCmd = new SqlCommand(selectStatement, srcConn)) + { + using (SqlDataReader reader = await srcCmd.ExecuteReaderAsync()) + { + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement, temptable), destConn)) + { + await destCmd.ExecuteNonQueryAsync(); + using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) + { + bcp.DestinationTableName = temptable; + await bcp.WriteToServerAsync(reader); + } + } + } + } + } + } + + // 3.6 Cancelling an Asynchronous Operation to SQL Azure + private static async Task AsyncSqlBulkCopyCancel() + { + CancellationTokenSource cts = new CancellationTokenSource(); + using (SqlConnection srcConn = new SqlConnection(connectionString)) + using (SqlConnection destConn = new SqlConnection(azureConnectionString)) + { + await srcConn.OpenAsync(cts.Token); + await destConn.OpenAsync(cts.Token); + using (SqlCommand srcCmd = new SqlCommand(selectStatement, srcConn)) + { + using (SqlDataReader reader = await srcCmd.ExecuteReaderAsync(cts.Token)) + { + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement, temptable), destConn)) + { + await destCmd.ExecuteNonQueryAsync(cts.Token); + using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) + { + bcp.DestinationTableName = temptable; + await bcp.WriteToServerAsync(reader, cts.Token); + //Cancel Async SqlBulCopy Operation after 200 ms + cts.CancelAfter(200); + } + } + } + } + } + } + + // 3.7 Using Async.Net and MARS + private static async Task AsyncSqlBulkCopyMARS() + { + using (SqlConnection marsConn = new SqlConnection(marsConnectionString)) + { + await marsConn.OpenAsync(); + + SqlCommand titlesCmd = new SqlCommand("SELECT * FROM [pubs].[dbo].[titles]", marsConn); + SqlCommand authorsCmd = new SqlCommand("SELECT * FROM [pubs].[dbo].[authors]", marsConn); + //With MARS we can have multiple active results sets on the same connection + using (SqlDataReader titlesReader = await titlesCmd.ExecuteReaderAsync()) + using (SqlDataReader authorsReader = await authorsCmd.ExecuteReaderAsync()) + { + await authorsReader.ReadAsync(); + + string temptable = "[#" + Guid.NewGuid().ToString("N") + "]"; + using (SqlConnection destConn = new SqlConnection(connectionString)) + { + await destConn.OpenAsync(); + using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement, temptable), destConn)) + { + await destCmd.ExecuteNonQueryAsync(); + using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) + { + bcp.DestinationTableName = temptable; + await bcp.WriteToServerAsync(titlesReader); + } + } + } + } + } + } + } +} +// diff --git a/doc/samples/SqlClient_AsyncProgramming_SqlTransaction.cs b/doc/samples/SqlClient_AsyncProgramming_SqlTransaction.cs new file mode 100644 index 0000000000..86659b3c98 --- /dev/null +++ b/doc/samples/SqlClient_AsyncProgramming_SqlTransaction.cs @@ -0,0 +1,70 @@ +using System; +// +using Microsoft.Data.SqlClient; +using System.Threading.Tasks; + +class Program +{ + static void Main() + { + string connectionString = + "Persist Security Info=False;Integrated Security=SSPI;database=Northwind;server=(local)"; + Task task = ExecuteSqlTransaction(connectionString); + task.Wait(); + } + + static async Task ExecuteSqlTransaction(string connectionString) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + await connection.OpenAsync(); + + SqlCommand command = connection.CreateCommand(); + SqlTransaction transaction = null; + + // Start a local transaction. + transaction = await Task.Run( + () => connection.BeginTransaction("SampleTransaction") + ); + + // Must assign both transaction object and connection + // to Command object for a pending local transaction + command.Connection = connection; + command.Transaction = transaction; + + try { + command.CommandText = + "Insert into Region (RegionID, RegionDescription) VALUES (555, 'Description')"; + await command.ExecuteNonQueryAsync(); + + command.CommandText = + "Insert into Region (RegionID, RegionDescription) VALUES (556, 'Description')"; + await command.ExecuteNonQueryAsync(); + + // Attempt to commit the transaction. + await Task.Run(() => transaction.Commit()); + Console.WriteLine("Both records are written to database."); + } + catch (Exception ex) + { + Console.WriteLine("Commit Exception Type: {0}", ex.GetType()); + Console.WriteLine(" Message: {0}", ex.Message); + + // Attempt to roll back the transaction. + try + { + transaction.Rollback(); + } + catch (Exception ex2) + { + // This catch block will handle any errors that may have occurred + // on the server that would cause the rollback to fail, such as + // a closed connection. + Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType()); + Console.WriteLine(" Message: {0}", ex2.Message); + } + } + } + } +} +// diff --git a/doc/samples/SqlClient_PerformanceCounter.cs b/doc/samples/SqlClient_PerformanceCounter.cs new file mode 100644 index 0000000000..e015d6f6b1 --- /dev/null +++ b/doc/samples/SqlClient_PerformanceCounter.cs @@ -0,0 +1,173 @@ +// +using System; +using Microsoft.Data.SqlClient; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace PerformanceCounterTest +{ + class Program + { + PerformanceCounter[] PerfCounters = new PerformanceCounter[10]; + SqlConnection connection = new SqlConnection(); + + static void Main() + { + Program prog = new Program(); + // Open a connection and create the performance counters. + prog.connection.ConnectionString = + GetIntegratedSecurityConnectionString(); + prog.SetUpPerformanceCounters(); + Console.WriteLine("Available Performance Counters:"); + + // Create the connections and display the results. + prog.CreateConnections(); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + + private void CreateConnections() + { + // List the Performance counters. + WritePerformanceCounters(); + + // Create 4 connections and display counter information. + SqlConnection connection1 = new SqlConnection( + GetIntegratedSecurityConnectionString()); + connection1.Open(); + Console.WriteLine("Opened the 1st Connection:"); + WritePerformanceCounters(); + + SqlConnection connection2 = new SqlConnection( + GetSqlConnectionStringDifferent()); + connection2.Open(); + Console.WriteLine("Opened the 2nd Connection:"); + WritePerformanceCounters(); + + SqlConnection connection3 = new SqlConnection( + GetSqlConnectionString()); + connection3.Open(); + Console.WriteLine("Opened the 3rd Connection:"); + WritePerformanceCounters(); + + SqlConnection connection4 = new SqlConnection( + GetSqlConnectionString()); + connection4.Open(); + Console.WriteLine("Opened the 4th Connection:"); + WritePerformanceCounters(); + + connection1.Close(); + Console.WriteLine("Closed the 1st Connection:"); + WritePerformanceCounters(); + + connection2.Close(); + Console.WriteLine("Closed the 2nd Connection:"); + WritePerformanceCounters(); + + connection3.Close(); + Console.WriteLine("Closed the 3rd Connection:"); + WritePerformanceCounters(); + + connection4.Close(); + Console.WriteLine("Closed the 4th Connection:"); + WritePerformanceCounters(); + } + + private enum ADO_Net_Performance_Counters + { + NumberOfActiveConnectionPools, + NumberOfReclaimedConnections, + HardConnectsPerSecond, + HardDisconnectsPerSecond, + NumberOfActiveConnectionPoolGroups, + NumberOfInactiveConnectionPoolGroups, + NumberOfInactiveConnectionPools, + NumberOfNonPooledConnections, + NumberOfPooledConnections, + NumberOfStasisConnections + // The following performance counters are more expensive to track. + // Enable ConnectionPoolPerformanceCounterDetail in your config file. + // SoftConnectsPerSecond + // SoftDisconnectsPerSecond + // NumberOfActiveConnections + // NumberOfFreeConnections + } + + private void SetUpPerformanceCounters() + { + connection.Close(); + this.PerfCounters = new PerformanceCounter[10]; + string instanceName = GetInstanceName(); + Type apc = typeof(ADO_Net_Performance_Counters); + int i = 0; + foreach (string s in Enum.GetNames(apc)) + { + this.PerfCounters[i] = new PerformanceCounter(); + this.PerfCounters[i].CategoryName = ".NET Data Provider for SqlServer"; + this.PerfCounters[i].CounterName = s; + this.PerfCounters[i].InstanceName = instanceName; + i++; + } + } + + [DllImport("kernel32.dll", SetLastError = true)] + static extern int GetCurrentProcessId(); + + private string GetInstanceName() + { + //This works for Winforms apps. + string instanceName = + System.Reflection.Assembly.GetEntryAssembly().GetName().Name; + + // Must replace special characters like (, ), #, /, \\ + string instanceName2 = + AppDomain.CurrentDomain.FriendlyName.ToString().Replace('(', '[') + .Replace(')', ']').Replace('#', '_').Replace('/', '_').Replace('\\', '_'); + + // For ASP.NET applications your instanceName will be your CurrentDomain's + // FriendlyName. Replace the line above that sets the instanceName with this: + // instanceName = AppDomain.CurrentDomain.FriendlyName.ToString().Replace('(','[') + // .Replace(')',']').Replace('#','_').Replace('/','_').Replace('\\','_'); + + string pid = GetCurrentProcessId().ToString(); + instanceName = instanceName + "[" + pid + "]"; + Console.WriteLine("Instance Name: {0}", instanceName); + Console.WriteLine("---------------------------"); + return instanceName; + } + + private void WritePerformanceCounters() + { + Console.WriteLine("---------------------------"); + foreach (PerformanceCounter p in this.PerfCounters) + { + Console.WriteLine("{0} = {1}", p.CounterName, p.NextValue()); + } + Console.WriteLine("---------------------------"); + } + + private static string GetIntegratedSecurityConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return @"Data Source=.;Integrated Security=True;" + + "Initial Catalog=AdventureWorks"; + } + private static string GetSqlConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return @"Data Source=.;User Id=;Password=;" + + "Initial Catalog=AdventureWorks"; + } + + private static string GetSqlConnectionStringDifferent() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return @"Initial Catalog=AdventureWorks;Data Source=.;" + + "User Id=;Password=;"; + } + } +} +// diff --git a/doc/samples/SqlClient_RetrieveIdentity.cs b/doc/samples/SqlClient_RetrieveIdentity.cs new file mode 100644 index 0000000000..ba08be607a --- /dev/null +++ b/doc/samples/SqlClient_RetrieveIdentity.cs @@ -0,0 +1,107 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +namespace AutoNumberTest +{ + class Program + { + // + static void Main(string[] args) + { + String SqlDbConnectionString = "Data Source=(local);Initial Catalog=MySchool;Integrated Security=True;"; + + InsertPersonInCommand(SqlDbConnectionString, "Janice", "Galvin"); + Console.WriteLine(); + + InsertPersonInAdapter(SqlDbConnectionString, "Peter", "Krebs"); + Console.WriteLine(); + + Console.WriteLine("Please press any key to exit....."); + Console.ReadKey(); + } + + // Using stored procedure to insert a new row and retrieve the identity value + static void InsertPersonInCommand(String connectionString, String firstName, String lastName) + { + String commandText = "dbo.InsertPerson"; + + using (SqlConnection conn = new SqlConnection(connectionString)) + { + using (SqlCommand cmd = new SqlCommand(commandText, conn)) + { + cmd.CommandType = CommandType.StoredProcedure; + + cmd.Parameters.Add(new SqlParameter("@FirstName", firstName)); + cmd.Parameters.Add(new SqlParameter("@LastName", lastName)); + SqlParameter personId = new SqlParameter("@PersonID", SqlDbType.Int); + personId.Direction = ParameterDirection.Output; + cmd.Parameters.Add(personId); + + conn.Open(); + cmd.ExecuteNonQuery(); + + Console.WriteLine("Person Id of new person:{0}", personId.Value); + } + } + } + + // Using stored procedure in adapter to insert new rows and update the identity value. + static void InsertPersonInAdapter(String connectionString, String firstName, String lastName) + { + String commandText = "dbo.InsertPerson"; + using (SqlConnection conn = new SqlConnection(connectionString)) + { + SqlDataAdapter mySchool = new SqlDataAdapter("Select PersonID,FirstName,LastName from [dbo].[Person]", conn); + + mySchool.InsertCommand = new SqlCommand(commandText, conn); + mySchool.InsertCommand.CommandType = CommandType.StoredProcedure; + + mySchool.InsertCommand.Parameters.Add( + new SqlParameter("@FirstName", SqlDbType.NVarChar, 50, "FirstName")); + mySchool.InsertCommand.Parameters.Add( + new SqlParameter("@LastName", SqlDbType.NVarChar, 50, "LastName")); + + SqlParameter personId = mySchool.InsertCommand.Parameters.Add(new SqlParameter("@PersonID", SqlDbType.Int, 0, "PersonID")); + personId.Direction = ParameterDirection.Output; + + DataTable persons = new DataTable(); + mySchool.Fill(persons); + + DataRow newPerson = persons.NewRow(); + newPerson["FirstName"] = firstName; + newPerson["LastName"] = lastName; + persons.Rows.Add(newPerson); + + mySchool.Update(persons); + Console.WriteLine("Show all persons:"); + ShowDataTable(persons, 14); + } + } + + private static void ShowDataTable(DataTable table, Int32 length) + { + foreach (DataColumn col in table.Columns) + { + Console.Write("{0,-" + length + "}", col.ColumnName); + } + Console.WriteLine(); + + foreach (DataRow row in table.Rows) + { + foreach (DataColumn col in table.Columns) + { + if (col.DataType.Equals(typeof(DateTime))) + Console.Write("{0,-" + length + ":d}", row[col]); + else if (col.DataType.Equals(typeof(Decimal))) + Console.Write("{0,-" + length + ":C}", row[col]); + else + Console.Write("{0,-" + length + "}", row[col]); + } + + Console.WriteLine(); + } + } + // + } +} diff --git a/doc/samples/SqlClient_Streaming_FromServer.cs b/doc/samples/SqlClient_Streaming_FromServer.cs new file mode 100644 index 0000000000..a139b828e6 --- /dev/null +++ b/doc/samples/SqlClient_Streaming_FromServer.cs @@ -0,0 +1,226 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; +using System.IO; +using System.Threading.Tasks; +using System.Xml; + +namespace StreamingFromServer +{ + class Program + { + private const string connectionString = @"Server=localhost;Database=Demo;Integrated Security=true"; + + static void Main(string[] args) + { + CopyBinaryValueToFile().Wait(); + PrintTextValues().Wait(); + PrintXmlValues().Wait(); + PrintXmlValuesViaNVarChar().Wait(); + + Console.WriteLine("Done"); + } + + // Application retrieving a large BLOB from SQL Server in .NET 4.5 using the new asynchronous capability + private static async Task CopyBinaryValueToFile() + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + await connection.OpenAsync(); + using (SqlCommand command = new SqlCommand("SELECT [bindata] FROM [Streams] WHERE [id]=@id", connection)) + { + command.Parameters.AddWithValue("id", 1); + + // The reader needs to be executed with the SequentialAccess behavior to enable network streaming + // Otherwise ReadAsync will buffer the entire BLOB into memory which can cause scalability issues or even OutOfMemoryExceptions + using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) + { + if (await reader.ReadAsync()) + { + if (!(await reader.IsDBNullAsync(0))) + { + using (FileStream file = new FileStream("binarydata.bin", FileMode.Create, FileAccess.Write)) + { + using (Stream data = reader.GetStream(0)) + { + + // Asynchronously copy the stream from the server to the file we just created + await data.CopyToAsync(file); + } + } + } + } + } + } + } + } + + // Application transferring a large Text File from SQL Server + private static async Task PrintTextValues() + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + await connection.OpenAsync(); + using (SqlCommand command = new SqlCommand("SELECT [id], [textdata] FROM [Streams]", connection)) + { + + // The reader needs to be executed with the SequentialAccess behavior to enable network streaming + // Otherwise ReadAsync will buffer the entire text document into memory which can cause scalability issues or even OutOfMemoryExceptions + using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) + { + while (await reader.ReadAsync()) + { + Console.Write("{0}: ", reader.GetInt32(0)); + + if (await reader.IsDBNullAsync(1)) + { + Console.Write("(NULL)"); + } + else + { + char[] buffer = new char[4096]; + int charsRead = 0; + using (TextReader data = reader.GetTextReader(1)) + { + do + { + // Grab each chunk of text and write it to the console + // If you are writing to a TextWriter you should use WriteAsync or WriteLineAsync + charsRead = await data.ReadAsync(buffer, 0, buffer.Length); + Console.Write(buffer, 0, charsRead); + } while (charsRead > 0); + } + } + + Console.WriteLine(); + } + } + } + } + } + + // Application transferring a large Xml Document from SQL Server + private static async Task PrintXmlValues() + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + await connection.OpenAsync(); + using (SqlCommand command = new SqlCommand("SELECT [id], [xmldata] FROM [Streams]", connection)) + { + + // The reader needs to be executed with the SequentialAccess behavior to enable network streaming + // Otherwise ReadAsync will buffer the entire Xml Document into memory which can cause scalability issues or even OutOfMemoryExceptions + using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) + { + while (await reader.ReadAsync()) + { + Console.WriteLine("{0}: ", reader.GetInt32(0)); + + if (await reader.IsDBNullAsync(1)) + { + Console.WriteLine("\t(NULL)"); + } + else + { + using (XmlReader xmlReader = reader.GetXmlReader(1)) + { + int depth = 1; + // NOTE: The XmlReader returned by GetXmlReader does NOT support async operations + // See the example below (PrintXmlValuesViaNVarChar) for how to get an XmlReader with asynchronous capabilities + while (xmlReader.Read()) + { + switch (xmlReader.NodeType) + { + case XmlNodeType.Element: + Console.WriteLine("{0}<{1}>", new string('\t', depth), xmlReader.Name); + depth++; + break; + case XmlNodeType.Text: + Console.WriteLine("{0}{1}", new string('\t', depth), xmlReader.Value); + break; + case XmlNodeType.EndElement: + depth--; + Console.WriteLine("{0}", new string('\t', depth), xmlReader.Name); + break; + } + } + } + } + } + } + } + } + } + + // Application transferring a large Xml Document from SQL Server + // This goes via NVarChar and TextReader to enable asynchronous reading + private static async Task PrintXmlValuesViaNVarChar() + { + XmlReaderSettings xmlSettings = new XmlReaderSettings() + { + // Async must be explicitly enabled in the XmlReaderSettings otherwise the XmlReader will throw exceptions when async methods are called + Async = true, + // Since we will immediately wrap the TextReader we are creating in an XmlReader, we will permit the XmlReader to take care of closing\disposing it + CloseInput = true, + // If the Xml you are reading is not a valid document (as per ) you will need to set the conformance level to Fragment + ConformanceLevel = ConformanceLevel.Fragment + }; + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + await connection.OpenAsync(); + + // Cast the XML into NVarChar to enable GetTextReader - trying to use GetTextReader on an XML type will throw an exception + using (SqlCommand command = new SqlCommand("SELECT [id], CAST([xmldata] AS NVARCHAR(MAX)) FROM [Streams]", connection)) + { + + // The reader needs to be executed with the SequentialAccess behavior to enable network streaming + // Otherwise ReadAsync will buffer the entire Xml Document into memory which can cause scalability issues or even OutOfMemoryExceptions + using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) + { + while (await reader.ReadAsync()) + { + Console.WriteLine("{0}:", reader.GetInt32(0)); + + if (await reader.IsDBNullAsync(1)) + { + Console.WriteLine("\t(NULL)"); + } + else + { + // Grab the row as a TextReader, then create an XmlReader on top of it + // We are not keeping a reference to the TextReader since the XmlReader is created with the "CloseInput" setting (so it will close the TextReader when needed) + using (XmlReader xmlReader = XmlReader.Create(reader.GetTextReader(1), xmlSettings)) + { + int depth = 1; + // The XmlReader above now supports asynchronous operations, so we can use ReadAsync here + while (await xmlReader.ReadAsync()) + { + switch (xmlReader.NodeType) + { + case XmlNodeType.Element: + Console.WriteLine("{0}<{1}>", new string('\t', depth), xmlReader.Name); + depth++; + break; + case XmlNodeType.Text: + // Depending on what your data looks like, you should either use Value or GetValueAsync + // Value has less overhead (since it doesn't create a Task), but it may also block if additional data is required + Console.WriteLine("{0}{1}", new string('\t', depth), await xmlReader.GetValueAsync()); + break; + case XmlNodeType.EndElement: + depth--; + Console.WriteLine("{0}", new string('\t', depth), xmlReader.Name); + break; + } + } + } + } + } + } + } + } + } + } +} +// diff --git a/doc/samples/SqlClient_Streaming_ServerToServer.cs b/doc/samples/SqlClient_Streaming_ServerToServer.cs new file mode 100644 index 0000000000..42efe6fa32 --- /dev/null +++ b/doc/samples/SqlClient_Streaming_ServerToServer.cs @@ -0,0 +1,72 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace StreamingFromServerToAnother +{ + class Program + { + private const string connectionString = @"Server=localhost;Database=Demo2;Integrated Security=true"; + + static void Main(string[] args) + { + // For this example, we don't want to cancel + // So we can pass in a "blank" cancellation token + E2EStream(CancellationToken.None).Wait(); + + Console.WriteLine("Done"); + } + + // Streaming from one SQL Server to Another One + private static async Task E2EStream(CancellationToken cancellationToken) + { + using (SqlConnection readConn = new SqlConnection(connectionString)) + { + using (SqlConnection writeConn = new SqlConnection(connectionString)) + { + + // Note that we are using the same cancellation token for calls to both connections\commands + // Also we can start both the connection opening asynchronously, and then wait for both to complete + Task openReadConn = readConn.OpenAsync(cancellationToken); + Task openWriteConn = writeConn.OpenAsync(cancellationToken); + await Task.WhenAll(openReadConn, openWriteConn); + + using (SqlCommand readCmd = new SqlCommand("SELECT [bindata] FROM [BinaryStreams]", readConn)) + { + using (SqlCommand writeCmd = new SqlCommand("INSERT INTO [BinaryStreamsCopy] (bindata) VALUES (@bindata)", writeConn)) + { + + // Add an empty parameter to the write command which will be used for the streams we are copying + // Size is set to -1 to indicate "MAX" + SqlParameter streamParameter = writeCmd.Parameters.Add("@bindata", SqlDbType.Binary, -1); + + // The reader needs to be executed with the SequentialAccess behavior to enable network streaming + // Otherwise ReadAsync will buffer the entire BLOB into memory which can cause scalability issues or even OutOfMemoryExceptions + using (SqlDataReader reader = await readCmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)) + { + while (await reader.ReadAsync(cancellationToken)) + { + // Grab a stream to the binary data in the source database + using (Stream dataStream = reader.GetStream(0)) + { + + // Set the parameter value to the stream source that was opened + streamParameter.Value = dataStream; + + // Asynchronously send data from one database to another + await writeCmd.ExecuteNonQueryAsync(cancellationToken); + } + } + } + } + } + } + } + } + } +} +// diff --git a/doc/samples/SqlClient_Streaming_ToServer.cs b/doc/samples/SqlClient_Streaming_ToServer.cs new file mode 100644 index 0000000000..6ee858db1c --- /dev/null +++ b/doc/samples/SqlClient_Streaming_ToServer.cs @@ -0,0 +1,140 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace StreamingToServer +{ + class Program + { + private const string connectionString = @"Server=localhost;Database=Demo2;Integrated Security=true"; + + static void Main(string[] args) + { + CreateDemoFiles(); + + StreamBLOBToServer().Wait(); + StreamTextToServer().Wait(); + + // Create a CancellationTokenSource that will be cancelled after 100ms + // Typically this token source will be cancelled by a user request (e.g. a Cancel button) + CancellationTokenSource tokenSource = new CancellationTokenSource(); + tokenSource.CancelAfter(100); + try + { + CancelBLOBStream(tokenSource.Token).Wait(); + } + catch (AggregateException ex) + { + // Cancelling an async operation will throw an exception + // Since we are using the Task's Wait method, this exception will be wrapped in an AggregateException + // If you were using the 'await' keyword, the compiler would take care of unwrapping the AggregateException + // Depending on when the cancellation occurs, you can either get an error from SQL Server or from .Net + if ((ex.InnerException is SqlException) || (ex.InnerException is TaskCanceledException)) + { + // This is an expected exception + Console.WriteLine("Got expected exception: {0}", ex.InnerException.Message); + } + else + { + // Did not expect this exception - re-throw it + throw; + } + } + + Console.WriteLine("Done"); + } + + // This is used to generate the files which are used by the other sample methods + private static void CreateDemoFiles() + { + Random rand = new Random(); + byte[] data = new byte[1024]; + rand.NextBytes(data); + + using (FileStream file = File.Open("binarydata.bin", FileMode.Create)) + { + file.Write(data, 0, data.Length); + } + + using (StreamWriter writer = new StreamWriter(File.Open("textdata.txt", FileMode.Create))) + { + writer.Write(Convert.ToBase64String(data)); + } + } + + // Application transferring a large BLOB to SQL Server + private static async Task StreamBLOBToServer() + { + using (SqlConnection conn = new SqlConnection(connectionString)) + { + await conn.OpenAsync(); + using (SqlCommand cmd = new SqlCommand("INSERT INTO [BinaryStreams] (bindata) VALUES (@bindata)", conn)) + { + using (FileStream file = File.Open("binarydata.bin", FileMode.Open)) + { + + // Add a parameter which uses the FileStream we just opened + // Size is set to -1 to indicate "MAX" + cmd.Parameters.Add("@bindata", SqlDbType.Binary, -1).Value = file; + + // Send the data to the server asynchronously + await cmd.ExecuteNonQueryAsync(); + } + } + } + } + + // Application transferring a large Text File to SQL Server + private static async Task StreamTextToServer() + { + using (SqlConnection conn = new SqlConnection(connectionString)) + { + await conn.OpenAsync(); + using (SqlCommand cmd = new SqlCommand("INSERT INTO [TextStreams] (textdata) VALUES (@textdata)", conn)) + { + using (StreamReader file = File.OpenText("textdata.txt")) + { + + // Add a parameter which uses the StreamReader we just opened + // Size is set to -1 to indicate "MAX" + cmd.Parameters.Add("@textdata", SqlDbType.NVarChar, -1).Value = file; + + // Send the data to the server asynchronously + await cmd.ExecuteNonQueryAsync(); + } + } + } + } + + // Cancelling the transfer of a large BLOB + private static async Task CancelBLOBStream(CancellationToken cancellationToken) + { + using (SqlConnection conn = new SqlConnection(connectionString)) + { + // We can cancel not only sending the data to the server, but also opening the connection + await conn.OpenAsync(cancellationToken); + + // Artificially delay the command by 100ms + using (SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:00:100';INSERT INTO [BinaryStreams] (bindata) VALUES (@bindata)", conn)) + { + using (FileStream file = File.Open("binarydata.bin", FileMode.Open)) + { + + // Add a parameter which uses the FileStream we just opened + // Size is set to -1 to indicate "MAX" + cmd.Parameters.Add("@bindata", SqlDbType.Binary, -1).Value = file; + + // Send the data to the server asynchronously + // Pass the cancellation token such that the command will be cancelled if needed + await cmd.ExecuteNonQueryAsync(cancellationToken); + } + } + } + } + } +} +// diff --git a/doc/samples/SqlCommandBuilder_Create.cs b/doc/samples/SqlCommandBuilder_Create.cs new file mode 100644 index 0000000000..13322368af --- /dev/null +++ b/doc/samples/SqlCommandBuilder_Create.cs @@ -0,0 +1,63 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +namespace SqlCommandBuilderCS +{ + class Program + { + static void Main() + { + string cnnst = "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + string queryst = "SELECT CustomerID, CompanyName, ContactName, Phone FROM Customers"; + string newQueryString = "SELECT CustomerID, City, Region FROM Customers"; + string tablen = "Customers"; + DataSet ds = SelectSqlRows(cnnst, queryst, newQueryString, tablen); + } + + public static DataSet SelectSqlRows(string connectionString, + string queryString, string newQueryString, string tableName) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // + // Assumes that connection is a valid SqlConnection object + // inside of a using block. + SqlDataAdapter adapter = new SqlDataAdapter(); + adapter.SelectCommand = new SqlCommand(queryString, connection); + SqlCommandBuilder builder = new SqlCommandBuilder(adapter); + builder.QuotePrefix = "["; + builder.QuoteSuffix = "]"; + // + + // + // Generate the update command automatically by SqlCommandBuilder + Console.WriteLine(builder.GetUpdateCommand().CommandText); + // + + connection.Open(); + + DataSet dataSet = new DataSet(); + adapter.Fill(dataSet, tableName); + + // + // Assumes an open SqlConnection and SqlDataAdapter inside of a using block. + adapter.SelectCommand.CommandText = newQueryString; + builder.RefreshSchema(); + + dataSet.Tables.Remove(dataSet.Tables[tableName]); + adapter.Fill(dataSet, tableName); + // + + //code to modify data in DataSet here + builder.GetUpdateCommand(); + + //Without the SqlCommandBuilder this line would fail + adapter.Update(dataSet, tableName); + + return dataSet; + } + } + } +} diff --git a/doc/samples/SqlCommand_BeginExecuteNonQuery.cs b/doc/samples/SqlCommand_BeginExecuteNonQuery.cs index 4598ffcd38..7cae30238f 100644 --- a/doc/samples/SqlCommand_BeginExecuteNonQuery.cs +++ b/doc/samples/SqlCommand_BeginExecuteNonQuery.cs @@ -75,11 +75,8 @@ private static string GetConnectionString() // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. - // If you have not included "Asynchronous Processing=true" in the - // connection string, the command is not able - // to execute asynchronously. return "Data Source=(local);Integrated Security=SSPI;" + - "Initial Catalog=AdventureWorks; Asynchronous Processing=true"; + "Initial Catalog=AdventureWorks"; } } // diff --git a/doc/samples/SqlCommand_BeginExecuteNonQueryForm.cs b/doc/samples/SqlCommand_BeginExecuteNonQueryForm.cs index 5bc740978a..d800ebde16 100644 --- a/doc/samples/SqlCommand_BeginExecuteNonQueryForm.cs +++ b/doc/samples/SqlCommand_BeginExecuteNonQueryForm.cs @@ -49,11 +49,8 @@ private static string GetConnectionString() // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. - // If you have not included "Asynchronous Processing=true" in the - // connection string, the command is not able - // to execute asynchronously. return "Data Source=(local);Integrated Security=true;" + - "Initial Catalog=AdventureWorks; Asynchronous Processing=true"; + "Initial Catalog=AdventureWorks"; } private void DisplayStatus(string Text) diff --git a/doc/samples/SqlCommand_BeginExecuteReaderAsyncBehavior.cs b/doc/samples/SqlCommand_BeginExecuteReaderAsyncBehavior.cs index 830bf36be7..a79254fbbc 100644 --- a/doc/samples/SqlCommand_BeginExecuteReaderAsyncBehavior.cs +++ b/doc/samples/SqlCommand_BeginExecuteReaderAsyncBehavior.cs @@ -119,10 +119,8 @@ private string GetConnectionString() // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. - // If you do not include the Asynchronous Processing=true name/value pair, - // you wo not be able to execute the command asynchronously. return "Data Source=(local);Integrated Security=true;" + - "Initial Catalog=AdventureWorks; Asynchronous Processing=true"; + "Initial Catalog=AdventureWorks"; } private void button1_Click(object sender, System.EventArgs e) diff --git a/doc/samples/SqlCommand_BeginExecuteReaderAsyncSimple.cs b/doc/samples/SqlCommand_BeginExecuteReaderAsyncSimple.cs index c010b16c06..2c5d74a8f4 100644 --- a/doc/samples/SqlCommand_BeginExecuteReaderAsyncSimple.cs +++ b/doc/samples/SqlCommand_BeginExecuteReaderAsyncSimple.cs @@ -92,11 +92,8 @@ private static string GetConnectionString() // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. - // If you have not included "Asynchronous Processing=true" in the - // connection string, the command is not able - // to execute asynchronously. return "Data Source=(local);Integrated Security=true;" + - "Initial Catalog=AdventureWorks; Asynchronous Processing=true"; + "Initial Catalog=AdventureWorks"; } } // diff --git a/doc/samples/SqlCommand_BeginExecuteXmlReader.cs b/doc/samples/SqlCommand_BeginExecuteXmlReader.cs index 9f99c070a9..6935d7d117 100644 --- a/doc/samples/SqlCommand_BeginExecuteXmlReader.cs +++ b/doc/samples/SqlCommand_BeginExecuteXmlReader.cs @@ -75,11 +75,8 @@ private static string GetConnectionString() // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. - // If you have not included "Asynchronous Processing=true" in the - // connection string, the command is not able - // to execute asynchronously. return "Data Source=(local);Integrated Security=true;" + - "Initial Catalog=AdventureWorks; Asynchronous Processing=true"; + "Initial Catalog=AdventureWorks"; } } // diff --git a/doc/samples/SqlCommand_BeginExecuteXmlReaderAsync.cs b/doc/samples/SqlCommand_BeginExecuteXmlReaderAsync.cs index b3ba1902ae..35195c95a7 100644 --- a/doc/samples/SqlCommand_BeginExecuteXmlReaderAsync.cs +++ b/doc/samples/SqlCommand_BeginExecuteXmlReaderAsync.cs @@ -37,10 +37,8 @@ private string GetConnectionString() // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. - // If you do not include the Asynchronous Processing=true name/value pair, - // you wo not be able to execute the command asynchronously. return "Data Source=(local);Integrated Security=true;" + - "Initial Catalog=AdventureWorks; Asynchronous Processing=true"; + "Initial Catalog=AdventureWorks"; } private void DisplayStatus(string Text) diff --git a/doc/samples/SqlCommand_ExecuteNonQueryAsync.cs b/doc/samples/SqlCommand_ExecuteNonQueryAsync.cs new file mode 100644 index 0000000000..d1f8fc5db8 --- /dev/null +++ b/doc/samples/SqlCommand_ExecuteNonQueryAsync.cs @@ -0,0 +1,27 @@ +using System; +// +using Microsoft.Data.SqlClient; +using System.Threading.Tasks; + +class A { + public static void Main() + { + using (SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind; Integrated Security=SSPI")) + { + SqlCommand command = new SqlCommand("SELECT TOP 2 * FROM dbo.Orders", conn); + + int result = A.Method(conn, command).Result; + + SqlDataReader reader = command.ExecuteReader(); + while (reader.Read()) + Console.WriteLine(reader[0]); + } + } + + static async Task Method(SqlConnection conn, SqlCommand cmd) { + await conn.OpenAsync(); + await cmd.ExecuteNonQueryAsync(); + return 1; + } +} +// diff --git a/doc/samples/SqlCommand_ExecuteNonQuery_SP_DML.cs b/doc/samples/SqlCommand_ExecuteNonQuery_SP_DML.cs new file mode 100644 index 0000000000..6b35ce142b --- /dev/null +++ b/doc/samples/SqlCommand_ExecuteNonQuery_SP_DML.cs @@ -0,0 +1,77 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +namespace SqlCommandCS +{ + class Program + { + static void Main() + { + string str = "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + + CreateStoredProcedure(str); + CreateCommand(str); + } + + private static void CreateStoredProcedure(string connectionString) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // + // Assumes connection is a valid SqlConnection. + string queryString = "CREATE PROCEDURE InsertCategory " + + "@CategoryName nchar(15), " + + "@Identity int OUT " + + "AS " + + "INSERT INTO Categories (CategoryName) VALUES(@CategoryName) " + + "SET @Identity = @@Identity " + + "RETURN @@ROWCOUNT"; + + SqlCommand command = new SqlCommand(queryString, connection); + command.ExecuteNonQuery(); + // + } + } + + private static void CreateCommand(string connectionString) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + SqlCommand command = new SqlCommand(connection); + + // + // Assumes connection is a valid SqlConnection. + connection.Open(); + + string queryString = "INSERT INTO Customers " + + "(CustomerID, CompanyName) Values('NWIND', 'Northwind Traders')"; + + SqlCommand command = new SqlCommand(queryString, connection); + Int32 recordsAffected = command.ExecuteNonQuery(); + // + + // + // Assumes command is a valid SqlCommand with an open connection. + command.CommandText = "InsertCategory"; + command.CommandType = CommandType.StoredProcedure; + + SqlParameter parameter = command.Parameters.Add("@RowCount", SqlDbType.Int); + parameter.Direction = ParameterDirection.ReturnValue; + + parameter = command.Parameters.Add("@CategoryName", SqlDbType.NChar, 15); + + parameter = command.Parameters.Add("@Identity", SqlDbType.Int); + parameter.Direction = ParameterDirection.Output; + + command.Parameters["@CategoryName"].Value = "New Category"; + command.ExecuteNonQuery(); + + Int32 categoryID = (Int32) command.Parameters["@Identity"].Value; + Int32 rowCount = (Int32) command.Parameters["@RowCount"].Value; + // + } + } + } +} diff --git a/doc/samples/SqlCommand_ExecuteReader_SequentialAccess.cs b/doc/samples/SqlCommand_ExecuteReader_SequentialAccess.cs new file mode 100644 index 0000000000..864e183428 --- /dev/null +++ b/doc/samples/SqlCommand_ExecuteReader_SequentialAccess.cs @@ -0,0 +1,84 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string cnnString = "Data Source=(local);Initial Catalog=pubs;" + + "Integrated Security=SSPI"; + SqlConnection connection = new SqlConnection(cnnString); + RetrievePubLogo(connection); + } + + private static void RetrievePubLogo(SqlConnection connection) + { + // + // Assumes that connection is a valid SqlConnection object. + SqlCommand command = new SqlCommand( + "SELECT pub_id, logo FROM pub_info", connection); + + // Writes the BLOB to a file (*.bmp). + System.IO.FileStream stream; + // Streams the BLOB to the FileStream object. + System.IO.BinaryWriter writer; + + // Size of the BLOB buffer. + int bufferSize = 100; + // The BLOB byte[] buffer to be filled by GetBytes. + byte[] outByte = new byte[bufferSize]; + // The bytes returned from GetBytes. + long retval; + // The starting position in the BLOB output. + long startIndex = 0; + + // The publisher id to use in the file name. + string pubID = ""; + + // Open the connection and read data into the DataReader. + connection.Open(); + SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess); + + while (reader.Read()) + { + // Get the publisher id, which must occur before getting the logo. + pubID = reader.GetString(0); + + // Create a file to hold the output. + stream = new System.IO.FileStream( + "logo" + pubID + ".bmp", System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write); + writer = new System.IO.BinaryWriter(stream); + + // Reset the starting byte for the new BLOB. + startIndex = 0; + + // Read bytes into outByte[] and retain the number of bytes returned. + retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize); + + // Continue while there are bytes beyond the size of the buffer. + while (retval == bufferSize) + { + writer.Write(outByte); + writer.Flush(); + + // Reposition start index to end of last buffer and fill buffer. + startIndex += bufferSize; + retval = reader.GetBytes(1, startIndex, outByte, 0, bufferSize); + } + + // Write the remaining buffer. + writer.Write(outByte, 0, (int)retval); + writer.Flush(); + + // Close the output file. + writer.Close(); + stream.Close(); + } + + // Close the reader and the connection. + reader.Close(); + connection.Close(); + // + } +} diff --git a/doc/samples/SqlCommand_ExecuteScalar_Return_Id.cs b/doc/samples/SqlCommand_ExecuteScalar_Return_Id.cs new file mode 100644 index 0000000000..61f8029a16 --- /dev/null +++ b/doc/samples/SqlCommand_ExecuteScalar_Return_Id.cs @@ -0,0 +1,47 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Class1 +{ + static void Main() + { + int ret = AddProductCategory("newval", GetConnectionString()); + Console.WriteLine(ret.ToString()); + Console.ReadLine(); + } + + // + static public int AddProductCategory(string newName, string connString) + { + Int32 newProdID = 0; + string sql = + "INSERT INTO Production.ProductCategory (Name) VALUES (@Name); " + + "SELECT CAST(scope_identity() AS int)"; + using (SqlConnection conn = new SqlConnection(connString)) + { + SqlCommand cmd = new SqlCommand(sql, conn); + cmd.Parameters.Add("@Name", SqlDbType.VarChar); + cmd.Parameters["@name"].Value = newName; + try + { + conn.Open(); + newProdID = (Int32)cmd.ExecuteScalar(); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + return (int)newProdID; + } + + // + static private string GetConnectionString() + { + // 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=true"; + } +} diff --git a/doc/samples/SqlCommand_Intro.cs b/doc/samples/SqlCommand_Intro.cs index b0af57ffa3..5c16f78fd5 100644 --- a/doc/samples/SqlCommand_Intro.cs +++ b/doc/samples/SqlCommand_Intro.cs @@ -68,7 +68,7 @@ public static SqlDataReader ExecuteReader(String connectionString, String comman static void Main(string[] args) { - String connectionString = "Data Source=(local);Initial Catalog=MySchool;Integrated Security=True;Asynchronous Processing=true;"; + String connectionString = "Data Source=(local);Initial Catalog=MySchool;Integrated Security=True;"; CountCourses(connectionString, 2012); Console.WriteLine(); diff --git a/doc/samples/SqlConfigurableRetryLogic_OpenConnection.cs b/doc/samples/SqlConfigurableRetryLogic_OpenConnection.cs new file mode 100644 index 0000000000..dca259fecf --- /dev/null +++ b/doc/samples/SqlConfigurableRetryLogic_OpenConnection.cs @@ -0,0 +1,105 @@ +using System; +// +using Microsoft.Data.SqlClient; + +/// Detecting retriable exceptions is a vital part of the retry pattern. +/// Before applying retry logic it is important to investigate exceptions and choose a retry provider that best fits your scenario. +/// First, log your exceptions and find transient faults. +/// The purpose of this sample is to illustrate how to use this feature and the condition might not be realistic. +class RetryLogicSample +{ + private const string DefaultDB = "Northwind"; + private const string CnnStringFormat = "Server=localhost; Initial Catalog={0}; Integrated Security=true; pooling=false;"; + private const string DropDatabaseFormat = "DROP DATABASE {0}"; + + // For general use + private static SqlConnection s_generalConnection = new SqlConnection(string.Format(CnnStringFormat, DefaultDB)); + + static void Main(string[] args) + { + // 1. Define the retry logic parameters + var options = new SqlRetryLogicOption() + { + NumberOfTries = 5, + MaxTimeInterval = TimeSpan.FromSeconds(20), + DeltaTime = TimeSpan.FromSeconds(1) + }; + + // 2. Create a retry provider + var provider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(options); + + // define the retrying event to report the execution attempts + provider.Retrying += (object s, SqlRetryingEventArgs e) => + { + int attempts = e.RetryCount + 1; + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"attempt {attempts} - current delay time:{e.Delay} \n"); + Console.ForegroundColor = ConsoleColor.DarkGray; + if (e.Exceptions[e.Exceptions.Count - 1] is SqlException ex) + { + Console.WriteLine($"{ex.Number}-{ex.Message}\n"); + } + else + { + Console.WriteLine($"{e.Exceptions[e.Exceptions.Count - 1].Message}\n"); + } + + // It is not a good practice to do time-consuming tasks inside the retrying event which blocks the running task. + // Use parallel programming patterns to mitigate it. + if (e.RetryCount == provider.RetryLogic.NumberOfTries - 1) + { + Console.WriteLine("This is the last chance to execute the command before throwing the exception."); + Console.WriteLine("Press Enter when you're ready:"); + Console.ReadLine(); + Console.WriteLine("continue ..."); + } + }; + + // Open the general connection. + s_generalConnection.Open(); + + try + { + // Assume the database is being created and other services are going to connect to it. + RetryConnection(provider); + } + catch + { + // exception is thrown if connecting to the database isn't successful. + throw; + } + } + + private static void ExecuteCommand(SqlConnection cn, string command) + { + using var cmd = cn.CreateCommand(); + cmd.CommandText = command; + cmd.ExecuteNonQuery(); + } + + private static void RetryConnection(SqlRetryLogicBaseProvider provider) + { + // Change this if you already have a database with the same name in your database. + string dbName = "Invalid_DB_Open"; + + // Create a connection to an invalid database. + using var cnn = new SqlConnection(string.Format(CnnStringFormat, dbName)); + // 3. Assign the `provider` to the connection + cnn.RetryLogicProvider = provider; + Console.WriteLine($"Connecting to the [{dbName}] ..."); + // Manually execute the following command in SSMS to create the invalid database while the SqlConnection is attempting to connect to it. + // >> CREATE DATABASE Invalid_DB_Open; + Console.WriteLine($"Manually, run the 'CREATE DATABASE {dbName};' in the SQL Server before exceeding the {provider.RetryLogic.NumberOfTries} attempts."); + // the connection tries to connect to the database 5 times + Console.WriteLine("The first attempt, before getting into the retry logic."); + cnn.Open(); + Console.WriteLine($"Connected to the [{dbName}] successfully."); + + cnn.Close(); + + // Drop it after test + ExecuteCommand(s_generalConnection, string.Format(DropDatabaseFormat, dbName)); + Console.WriteLine($"The [{dbName}] is removed."); + } +} +// diff --git a/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs b/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs new file mode 100644 index 0000000000..71ace67def --- /dev/null +++ b/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs @@ -0,0 +1,245 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Data.SqlClient; + +class RetryLogicSample +{ +// +/// Detecting retriable exceptions is a vital part of the retry pattern. +/// Before applying retry logic it is important to investigate exceptions and choose a retry provider that best fits your scenario. +/// First, log your exceptions and find transient faults. +/// The purpose of this sample is to illustrate how to use this feature and the condition might not be realistic. + + private const string DefaultDB = "Northwind"; + private const string CnnStringFormat = "Server=localhost; Initial Catalog={0}; Integrated Security=true; pooling=false;"; + private const string DropDatabaseFormat = "DROP DATABASE {0}"; + private const string CreateDatabaseFormat = "CREATE DATABASE {0}"; + + // For general use + private static SqlConnection s_generalConnection = new SqlConnection(string.Format(CnnStringFormat, DefaultDB)); + + static void Main(string[] args) + { + // 1. Define the retry logic parameters + var options = new SqlRetryLogicOption() + { + NumberOfTries = 5, + MaxTimeInterval = TimeSpan.FromSeconds(20), + DeltaTime = TimeSpan.FromSeconds(1), + AuthorizedSqlCondition = null, + // error number 3702 : Cannot drop database "xxx" because it is currently in use. + TransientErrors = new int[] {3702} + }; + + // 2. Create a retry provider + var provider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(options); + + // define the retrying event to report execution attempts + provider.Retrying += (object s, SqlRetryingEventArgs e) => + { + int attempts = e.RetryCount + 1; + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"attempt {attempts} - current delay time:{e.Delay} \n"); + Console.ForegroundColor = ConsoleColor.DarkGray; + if (e.Exceptions[e.Exceptions.Count - 1] is SqlException ex) + { + Console.WriteLine($"{ex.Number}-{ex.Message}\n"); + } + else + { + Console.WriteLine($"{e.Exceptions[e.Exceptions.Count - 1].Message}\n"); + } + + // It is not good practice to do time-consuming tasks inside the retrying event which blocks the running task. + // Use parallel programming patterns to mitigate it. + if (e.RetryCount == provider.RetryLogic.NumberOfTries - 1) + { + Console.WriteLine("This is the last chance to execute the command before throwing the exception."); + Console.WriteLine("Press Enter when you're ready:"); + Console.ReadLine(); + Console.WriteLine("continue ..."); + } + }; + + // Open a general connection. + s_generalConnection.Open(); + + try + { + // Assume the database is creating and other services are going to connect to it. + RetryCommand(provider); + } + catch + { + s_generalConnection.Close(); + // exception is thrown if connecting to the database isn't successful. + throw; + } + s_generalConnection.Close(); + } + + private static void ExecuteCommand(SqlConnection cn, string command) + { + using var cmd = cn.CreateCommand(); + cmd.CommandText = command; + cmd.ExecuteNonQuery(); + } + + private static void FindActiveSessions(SqlConnection cnn, string dbName) + { + using var cmd = cnn.CreateCommand(); + cmd.CommandText = "DECLARE @query NVARCHAR(max) = '';" + Environment.NewLine + + $"SELECT @query = @query + 'KILL ' + CAST(spid as varchar(50)) + ';' FROM sys.sysprocesses WHERE dbid = DB_ID('{dbName}')" + Environment.NewLine + + "SELECT @query AS Active_sessions;"; + var reader = cmd.ExecuteReader(); + if (reader.Read()) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.Write($">> Execute the '{reader.GetString(0)}' command in SQL Server to unblock the running task."); + Console.ResetColor(); + } + reader.Close(); + } +// +// + private static void RetryCommand(SqlRetryLogicBaseProvider provider) + { + // Change this if you already have a database with the same name in your database. + string dbName = "RetryCommand_TestDatabase"; + + // Subscribe a new event on retry event and discover the active sessions on a database + EventHandler retryEvent = (object s, SqlRetryingEventArgs e) => + { + // Run just at first execution + if (e.RetryCount == 1) + { + FindActiveSessions(s_generalConnection, dbName); + Console.WriteLine($"Before exceeding {provider.RetryLogic.NumberOfTries} attempts."); + } + }; + + provider.Retrying += retryEvent; + + // Create a new database. + ExecuteCommand(s_generalConnection, string.Format(CreateDatabaseFormat, dbName)); + Console.WriteLine($"The '{dbName}' database is created."); + + // Open a connection to the newly created database to block it from being dropped. + using var blockingCnn = new SqlConnection(string.Format(CnnStringFormat, dbName)); + blockingCnn.Open(); + Console.WriteLine($"Established a connection to '{dbName}' to block it from being dropped."); + + Console.WriteLine($"Dropping `{dbName}`..."); + // Try to drop the new database. + RetryCommandSync(provider, dbName); + + Console.WriteLine("Command executed successfully."); + + provider.Retrying -= retryEvent; + } + + private static void RetryCommandSync(SqlRetryLogicBaseProvider provider, string dbName) + { + using var cmd = s_generalConnection.CreateCommand(); + cmd.CommandText = string.Format(DropDatabaseFormat, dbName); + // 3. Assign the `provider` to the command + cmd.RetryLogicProvider = provider; + Console.WriteLine("The first attempt, before getting into the retry logic."); + cmd.ExecuteNonQuery(); + } +// +// + private static void RetryCommand(SqlRetryLogicBaseProvider provider) + { + // Change this if you already have a database with the same name in your database. + string dbName = "RetryCommand_TestDatabase"; + + // Subscribe to the retry event and discover active sessions in a database + EventHandler retryEvent = (object s, SqlRetryingEventArgs e) => + { + // Run just at first execution + if (e.RetryCount == 1) + { + FindActiveSessions(s_generalConnection, dbName); + Console.WriteLine($"Before exceeding {provider.RetryLogic.NumberOfTries} attempts."); + } + }; + + provider.Retrying += retryEvent; + + // Create a new database. + ExecuteCommand(s_generalConnection, string.Format(CreateDatabaseFormat, dbName)); + Console.WriteLine($"The '{dbName}' database is created."); + + // Open a connection to the newly created database to block it from being dropped. + using var blockingCnn = new SqlConnection(string.Format(CnnStringFormat, dbName)); + blockingCnn.Open(); + Console.WriteLine($"Established a connection to '{dbName}' to block it from being dropped."); + + Console.WriteLine("Dropping the database..."); + // Try to drop the new database. + RetryCommandAsync(provider, dbName).Wait(); + + Console.WriteLine("Command executed successfully."); + + provider.Retrying -= retryEvent; + } + + private static async Task RetryCommandAsync(SqlRetryLogicBaseProvider provider, string dbName) + { + using var cmd = s_generalConnection.CreateCommand(); + cmd.CommandText = string.Format(DropDatabaseFormat, dbName); + // 3. Assign the `provider` to the command + cmd.RetryLogicProvider = provider; + Console.WriteLine("The first attempt, before getting into the retry logic."); + await cmd.ExecuteNonQueryAsync(); + } +// +// + private static void RetryCommand(SqlRetryLogicBaseProvider provider) + { + // Change this if you already have a database with the same name in your database. + string dbName = "RetryCommand_TestDatabase"; + + // Subscribe to the retry event and discover the active sessions in a database + EventHandler retryEvent = (object s, SqlRetryingEventArgs e) => + { + // Run just at first execution + if (e.RetryCount == 1) + { + FindActiveSessions(s_generalConnection, dbName); + Console.WriteLine($"Before exceeding {provider.RetryLogic.NumberOfTries} attempts."); + } + }; + + provider.Retrying += retryEvent; + + // Create a new database. + ExecuteCommand(s_generalConnection, string.Format(CreateDatabaseFormat, dbName)); + Console.WriteLine($"The '{dbName}' database is created."); + + // Open a connection to the newly created database to block it from being dropped. + using var blockingCnn = new SqlConnection(string.Format(CnnStringFormat, dbName)); + blockingCnn.Open(); + Console.WriteLine($"Established a connection to '{dbName}' to block it from being dropped."); + + Console.WriteLine("Dropping the database..."); + // Try to drop the new database. + RetryCommandBeginExecuteAsync(provider, dbName).Wait(); + + Console.WriteLine("Command executed successfully."); + + provider.Retrying -= retryEvent; + } + + private static async Task RetryCommandBeginExecuteAsync(SqlRetryLogicBaseProvider provider, string dbName) + { + using var cmd = s_generalConnection.CreateCommand(); + cmd.CommandText = string.Format(DropDatabaseFormat, dbName); + // Execute the BeginExecuteXXX and EndExecuteXXX functions by using Task.Factory.FromAsync(). + // Apply the retry logic by using the ExecuteAsync function of the configurable retry logic provider. + Console.WriteLine("The first attempt, before getting into the retry logic."); + await provider.ExecuteAsync(cmd, () => Task.Factory.FromAsync(cmd.BeginExecuteNonQuery(), cmd.EndExecuteNonQuery)); + } +// +} diff --git a/doc/samples/SqlConfigurableRetryLogic_SqlRetryLogicOptions.cs b/doc/samples/SqlConfigurableRetryLogic_SqlRetryLogicOptions.cs new file mode 100644 index 0000000000..fc03987a9a --- /dev/null +++ b/doc/samples/SqlConfigurableRetryLogic_SqlRetryLogicOptions.cs @@ -0,0 +1,25 @@ +using System; +using System.Text.RegularExpressions; +using Microsoft.Data.SqlClient; + + class RetryLogicSample + { + static void Main(string[] args) + { + // + var RetryLogicOption = new SqlRetryLogicOption() + { + NumberOfTries = 5, + // Declare the error number 102 as a transient error to apply the retry logic when it occurs. + TransientErrors = new int[] { 102 }, + // When a SqlCommand executes out of a transaction, + // the retry logic will apply if it contains a 'select' keyword. + AuthorizedSqlCondition = x => string.IsNullOrEmpty(x) + || Regex.IsMatch(x, @"\b(SELECT)\b", RegexOptions.IgnoreCase), + DeltaTime = TimeSpan.FromSeconds(1), + MaxTimeInterval = TimeSpan.FromSeconds(60), + MinTimeInterval = TimeSpan.FromSeconds(3) + }; + // + } + } diff --git a/doc/samples/SqlConfigurableRetryLogic_StepByStep_CustomProvider.cs b/doc/samples/SqlConfigurableRetryLogic_StepByStep_CustomProvider.cs new file mode 100644 index 0000000000..a30e05389d --- /dev/null +++ b/doc/samples/SqlConfigurableRetryLogic_StepByStep_CustomProvider.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Data.SqlClient; + +namespace CustomCRL_Doc +{ + class Program + { + private const string CnnStringFormat = "Server=localhost; Initial Catalog=Northwind; Integrated Security=true; pooling=false; Timeout=1"; + + static void Main(string[] args) + { + RetryConnection(CnnStringFormat); + } + + private static void RetryConnection(string connectionString) + { + // + // Define the retry logic parameters + var options = new SqlRetryLogicOption() + { + // Tries 5 times before throwing an exception + NumberOfTries = 5, + // Preferred gap time to delay before retry + DeltaTime = TimeSpan.FromSeconds(1), + // Maximum gap time for each delay time before retry + MaxTimeInterval = TimeSpan.FromSeconds(20), + // SqlException retriable error numbers + TransientErrors = new int[] { 4060, 1024, 1025} + }; + // + + // + // Create a custom retry logic provider + SqlRetryLogicBaseProvider provider = CustomRetry.CreateCustomProvider(options); + // + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // + // Assumes that connection is a valid SqlConnection object + // Set the retry logic provider on the connection instance + connection.RetryLogicProvider = provider; + // Establishing the connection will trigger retry if one of the given transient failure occurs. + connection.Open(); + // + } + } + } + + public class CustomRetry + { + // + public static SqlRetryLogicBaseProvider CreateCustomProvider(SqlRetryLogicOption options) + { + // 1. create an enumerator instance + CustomEnumerator customEnumerator = new CustomEnumerator(options.DeltaTime, options.MaxTimeInterval, options.MinTimeInterval); + // 2. Use the enumerator object to create a new RetryLogic instance + CustomRetryLogic customRetryLogic = new CustomRetryLogic(5, customEnumerator, (e) => TransientErrorsCondition(e, options.TransientErrors)); + // 3. Create a provider using the RetryLogic object + CustomProvider customProvider = new CustomProvider(customRetryLogic); + return customProvider; + } + // + + // + // Return true if the exception is a transient fault. + private static bool TransientErrorsCondition(Exception e, IEnumerable retriableConditions) + { + bool result = false; + + // Assess only SqlExceptions + if (retriableConditions != null && e is SqlException ex) + { + foreach (SqlError item in ex.Errors) + { + // Check each error number to see if it is a retriable error number + if (retriableConditions.Contains(item.Number)) + { + result = true; + break; + } + } + } + // Other types of exceptions can also be assessed + else if (e is TimeoutException) + { + result = true; + } + return result; + } + // + } + + // + public class CustomEnumerator : SqlRetryIntervalBaseEnumerator + { + // Set the maximum acceptable time to 4 minutes + private readonly TimeSpan _maxValue = TimeSpan.FromMinutes(4); + + public CustomEnumerator(TimeSpan timeInterval, TimeSpan maxTime, TimeSpan minTime) + : base(timeInterval, maxTime, minTime) {} + + // Return fixed time on each request + protected override TimeSpan GetNextInterval() + { + return GapTimeInterval; + } + + // Override the validate method with the new time range validation + protected override void Validate(TimeSpan timeInterval, TimeSpan maxTimeInterval, TimeSpan minTimeInterval) + { + if (minTimeInterval < TimeSpan.Zero || minTimeInterval > _maxValue) + { + throw new ArgumentOutOfRangeException(nameof(minTimeInterval)); + } + + if (maxTimeInterval < TimeSpan.Zero || maxTimeInterval > _maxValue) + { + throw new ArgumentOutOfRangeException(nameof(maxTimeInterval)); + } + + if (timeInterval < TimeSpan.Zero || timeInterval > _maxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeInterval)); + } + + if (maxTimeInterval < minTimeInterval) + { + throw new ArgumentOutOfRangeException(nameof(minTimeInterval)); + } + } + } + // + + // + public class CustomRetryLogic : SqlRetryLogicBase + { + // Maximum number of attempts + private const int maxAttempts = 20; + + public CustomRetryLogic(int numberOfTries, + SqlRetryIntervalBaseEnumerator enumerator, + Predicate transientPredicate) + { + if (!(numberOfTries > 0 && numberOfTries <= maxAttempts)) + { + // 'numberOfTries' should be between 1 and 20. + throw new ArgumentOutOfRangeException(nameof(numberOfTries)); + } + + // Assign parameters to the relevant properties + NumberOfTries = numberOfTries; + RetryIntervalEnumerator = enumerator; + TransientPredicate = transientPredicate; + Current = 0; + } + + // Prepare this object for the next round + public override void Reset() + { + Current = 0; + RetryIntervalEnumerator.Reset(); + } + + public override bool TryNextInterval(out TimeSpan intervalTime) + { + intervalTime = TimeSpan.Zero; + // First try has occurred before starting the retry process. + // Check if retry is still allowed + bool result = Current < NumberOfTries - 1; + + if (result) + { + // Increase the number of attempts + Current++; + // It's okay if the RetryIntervalEnumerator gets to the last value before we've reached our maximum number of attempts. + // MoveNext() will simply leave the enumerator on the final interval value and we will repeat that for the final attempts. + RetryIntervalEnumerator.MoveNext(); + // Receive the current time from enumerator + intervalTime = RetryIntervalEnumerator.Current; + } + return result; + } + } + // + + // + public class CustomProvider : SqlRetryLogicBaseProvider + { + // Preserve the given retryLogic on creation + public CustomProvider(SqlRetryLogicBase retryLogic) + { + RetryLogic = retryLogic; + } + + public override TResult Execute(object sender, Func function) + { + // Create a list to save transient exceptions to report later if necessary + IList exceptions = new List(); + // Prepare it before reusing + RetryLogic.Reset(); + // Create an infinite loop to attempt the defined maximum number of tries + do + { + try + { + // Try to invoke the function + return function.Invoke(); + } + // Catch any type of exception for further investigation + catch (Exception e) + { + // Ask the RetryLogic object if this exception is a transient error + if (RetryLogic.TransientPredicate(e)) + { + // Add the exception to the list of exceptions we've retried on + exceptions.Add(e); + // Ask the RetryLogic for the next delay time before the next attempt to run the function + if (RetryLogic.TryNextInterval(out TimeSpan gapTime)) + { + Console.WriteLine($"Wait for {gapTime} before next try"); + // Wait before next attempt + Thread.Sleep(gapTime); + } + else + { + // Number of attempts has exceeded the maximum number of tries + throw new AggregateException("The number of retries has exceeded the maximum number of attempts.", exceptions); + } + } + else + { + // If the exception wasn't a transient failure throw the original exception + throw; + } + } + } while (true); + } + + public override Task ExecuteAsync(object sender, Func> function, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public override Task ExecuteAsync(object sender, Func function, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + } + // +} diff --git a/doc/samples/SqlConfigurableRetryLogic_StepByStep_OpenConnection.cs b/doc/samples/SqlConfigurableRetryLogic_StepByStep_OpenConnection.cs new file mode 100644 index 0000000000..4ae3bc4c5f --- /dev/null +++ b/doc/samples/SqlConfigurableRetryLogic_StepByStep_OpenConnection.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.Data.SqlClient; + +// The purpose of this sample is to illustrate how to use this feature and the example conditions might not be realistic. +class RetryLogicSample +{ + private const string CnnStringFormat = "Server=localhost; Initial Catalog=Northwind; Integrated Security=true; pooling=false;"; + + static void Main(string[] args) + { + RetryConnection(CnnStringFormat); + } + private static void RetryConnection(string connectionString) + { + // + // Define the retry logic parameters + var options = new SqlRetryLogicOption() + { + // Tries 5 times before throwing an exception + NumberOfTries = 5, + // Preferred gap time to delay before retry + DeltaTime = TimeSpan.FromSeconds(1), + // Maximum gap time for each delay time before retry + MaxTimeInterval = TimeSpan.FromSeconds(20) + }; + // + + // + // Create a retry logic provider + SqlRetryLogicBaseProvider provider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(options); + // + + using(SqlConnection connection = new SqlConnection(connectionString)) + { + // + // Assumes that connection is a valid SqlConnection object + // Set the retry logic provider on the connection instance + connection.RetryLogicProvider = provider; + // Establishing the connection will retry if a transient failure occurs. + connection.Open(); + // + } + } +} diff --git a/doc/samples/SqlConnectionStringBuilder.cs b/doc/samples/SqlConnectionStringBuilder.cs index bb19d43b16..f1c3252880 100644 --- a/doc/samples/SqlConnectionStringBuilder.cs +++ b/doc/samples/SqlConnectionStringBuilder.cs @@ -27,7 +27,6 @@ static void Main() // you can work with individual items. Console.WriteLine(builder.Password); builder.Password = "new@1Password"; - builder.AsynchronousProcessing = true; // You can refer to connection keys using strings, // as well. When you use this technique (the default diff --git a/doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs b/doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs deleted file mode 100644 index b3706e86bd..0000000000 --- a/doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Data; -// -using Microsoft.Data.SqlClient; -using System.Threading; - -class Program -{ - static void Main() - { - // Create a SqlConnectionStringBuilder instance, - // and ensure that it is set up for asynchronous processing. - SqlConnectionStringBuilder builder = - new SqlConnectionStringBuilder(GetConnectionString()); - // Asynchronous method calls won't work unless you - // have added this option, or have added - // the clause "Asynchronous Processing=true" - // to the connection string. - builder.AsynchronousProcessing = true; - - 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, builder.ConnectionString); - - Console.WriteLine("Press any key to finish."); - Console.ReadLine(); - } - - private static string GetConnectionString() - { - // 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"; - } - - 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's working, verifying the - // asynchronous behavior. - using (SqlConnection connection = new SqlConnection(connectionString)) - { - try - { - 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 - // doesn't consume all available resources - // on the main thread. - Thread.Sleep(100); - count += 1; - } - Console.WriteLine("Command complete. Affected {0} rows.", - command.EndExecuteNonQuery(result)); - - } - catch (SqlException ex) - { - Console.WriteLine( - "Error {0}: Microsoft.Data.SqlClient.SqlConnectionStringBuilder", - ex.Number, ex.Message); - } - catch (InvalidOperationException ex) - { - Console.WriteLine("Error: {0}", ex.Message); - } - catch (Exception ex) - { - // You might want to pass these errors - // back out to the caller. - Console.WriteLine("Error: {0}", ex.Message); - } - } - } -} -// diff --git a/doc/samples/SqlConnectionStringBuilder_Values.cs b/doc/samples/SqlConnectionStringBuilder_Values.cs index ae6f3b0f32..553a02e5fb 100644 --- a/doc/samples/SqlConnectionStringBuilder_Values.cs +++ b/doc/samples/SqlConnectionStringBuilder_Values.cs @@ -23,7 +23,7 @@ private static string GetConnectionString() // 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; Asynchronous Processing=true"; + "Initial Catalog=AdventureWorks"; } } // diff --git a/doc/samples/SqlConnection_BeginTransaction.cs b/doc/samples/SqlConnection_BeginTransaction.cs index 9665c33c12..13f9c0414d 100644 --- a/doc/samples/SqlConnection_BeginTransaction.cs +++ b/doc/samples/SqlConnection_BeginTransaction.cs @@ -24,7 +24,7 @@ private static void ExecuteSqlTransaction(string connectionString) SqlTransaction transaction; // Start a local transaction. - transaction = connection.BeginTransaction("SampleTransaction"); + transaction = connection.BeginTransaction(); // Must assign both transaction object and connection // to Command object for a pending local transaction diff --git a/doc/samples/SqlConnection_BeginTransaction2.cs b/doc/samples/SqlConnection_BeginTransaction2.cs index 9665c33c12..1be9f5987d 100644 --- a/doc/samples/SqlConnection_BeginTransaction2.cs +++ b/doc/samples/SqlConnection_BeginTransaction2.cs @@ -52,7 +52,7 @@ private static void ExecuteSqlTransaction(string connectionString) // Attempt to roll back the transaction. try { - transaction.Rollback(); + transaction.Rollback("SampleTransaction"); } catch (Exception ex2) { diff --git a/doc/samples/SqlConnection_BeginTransaction3.cs b/doc/samples/SqlConnection_BeginTransaction3.cs index 44eb020ff7..c68284f6d9 100644 --- a/doc/samples/SqlConnection_BeginTransaction3.cs +++ b/doc/samples/SqlConnection_BeginTransaction3.cs @@ -47,7 +47,7 @@ private static void ExecuteSqlTransaction(string connectionString) { try { - transaction.Rollback(); + transaction.Rollback("SampleTransaction"); } catch (SqlException ex) { diff --git a/doc/samples/SqlConnection_GetSchema.cs b/doc/samples/SqlConnection_GetSchema.cs index 64b07aaef5..8ca75b225e 100644 --- a/doc/samples/SqlConnection_GetSchema.cs +++ b/doc/samples/SqlConnection_GetSchema.cs @@ -7,7 +7,7 @@ class Program { static void Main(string[] args) { - using (SqlConnection conn = new SqlConnection("Data Source=(local);Initial Catalog=MySchool;Integrated Security=True;Asynchronous Processing=true;")) + using (SqlConnection conn = new SqlConnection("Data Source=(local);Initial Catalog=MySchool;Integrated Security=True;")) { conn.Open(); diff --git a/doc/samples/SqlConnection_GetSchema_Restriction.cs b/doc/samples/SqlConnection_GetSchema_Restriction.cs new file mode 100644 index 0000000000..209b0b8c3a --- /dev/null +++ b/doc/samples/SqlConnection_GetSchema_Restriction.cs @@ -0,0 +1,40 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main(string[] args) + { + string connectionString = "Data Source = localhost; Integrated Security = true; Initial Catalog = AdventureWorks"; + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + connection.Open(); + + // Specify the restrictions. + string[] restrictions = new string[4]; + restrictions[1] = "Sales"; + System.Data.DataTable table = connection.GetSchema("Tables", restrictions); + + // Display the contents of the table. + DisplayData(table); + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +} +// diff --git a/doc/samples/SqlConnection_GetSchema_Tables.cs b/doc/samples/SqlConnection_GetSchema_Tables.cs new file mode 100644 index 0000000000..34f04439a6 --- /dev/null +++ b/doc/samples/SqlConnection_GetSchema_Tables.cs @@ -0,0 +1,36 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main(string[] args) + { + string connectionString = "Data Source = localhost; Integrated Security = true; Initial Catalog = AdventureWorks"; + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + connection.Open(); + DataTable table = connection.GetSchema("Tables"); + + // Display the contents of the table. + DisplayData(table); + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +} +// diff --git a/doc/samples/SqlConnection_OpenAsync_ContinueWith.cs b/doc/samples/SqlConnection_OpenAsync_ContinueWith.cs new file mode 100644 index 0000000000..1bc63d3af0 --- /dev/null +++ b/doc/samples/SqlConnection_OpenAsync_ContinueWith.cs @@ -0,0 +1,43 @@ +using System; +using System.Data; +// +using Microsoft.Data.SqlClient; +using System.Threading.Tasks; + +class A +{ + static void ProductList(IAsyncResult result) { } + + public static void Main() + { + // AsyncCallback productList = new AsyncCallback(ProductList); + // SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind; Integrated Security=SSPI"); + // conn.Open(); + // SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn); + // IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd); + + AsyncCallback productList = new AsyncCallback(ProductList); + SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind; Integrated Security=SSPI"); + conn.OpenAsync().ContinueWith((task) => { + SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn); + IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd); + }, TaskContinuationOptions.OnlyOnRanToCompletion); + } +} +// + +class B +{ + static void ProductList(IAsyncResult result) { } + + public static void Main() + { + // + AsyncCallback productList = new AsyncCallback(ProductList); + SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind; Integrated Security=SSPI"); + conn.Open(); + SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn); + IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd); + // + } +} diff --git a/doc/samples/SqlDataAdapter_Batch.cs b/doc/samples/SqlDataAdapter_Batch.cs new file mode 100644 index 0000000000..f7deb7e87d --- /dev/null +++ b/doc/samples/SqlDataAdapter_Batch.cs @@ -0,0 +1,69 @@ +using System; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace DataAdapterTest +{ + class Program + { + static void Main() + { + } + + // + public static void BatchUpdate(DataTable dataTable, Int32 batchSize) + { + // Assumes GetConnectionString() returns a valid connection string. + string connectionString = GetConnectionString(); + + // Connect to the AdventureWorks database. + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // Create a SqlDataAdapter. + SqlDataAdapter adapter = new SqlDataAdapter(); + + // Set the UPDATE command and parameters. + adapter.UpdateCommand = new SqlCommand( + "UPDATE Production.ProductCategory SET " + + "Name=@Name WHERE ProductCategoryID=@ProdCatID;", + connection); + adapter.UpdateCommand.Parameters.Add("@Name", + SqlDbType.NVarChar, 50, "Name"); + adapter.UpdateCommand.Parameters.Add("@ProdCatID", + SqlDbType.Int, 4, "ProductCategoryID"); + adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None; + + // Set the INSERT command and parameter. + adapter.InsertCommand = new SqlCommand( + "INSERT INTO Production.ProductCategory (Name) VALUES (@Name);", + connection); + adapter.InsertCommand.Parameters.Add("@Name", + SqlDbType.NVarChar, 50, "Name"); + adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None; + + // Set the DELETE command and parameter. + adapter.DeleteCommand = new SqlCommand( + "DELETE FROM Production.ProductCategory " + + "WHERE ProductCategoryID=@ProdCatID;", connection); + adapter.DeleteCommand.Parameters.Add("@ProdCatID", + SqlDbType.Int, 4, "ProductCategoryID"); + adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None; + + // Set the batch size. + adapter.UpdateBatchSize = batchSize; + + // Execute the update. + adapter.Update(dataTable); + } + } + // + + static private string GetConnectionString() + { + // 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"; + } + } +} diff --git a/doc/samples/SqlDataAdapter_Concurrency.cs b/doc/samples/SqlDataAdapter_Concurrency.cs new file mode 100644 index 0000000000..fd40eb9f7f --- /dev/null +++ b/doc/samples/SqlDataAdapter_Concurrency.cs @@ -0,0 +1,62 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main(string[] args) + { + string connectionString = "Data Source = localhost; Integrated Security = true; Initial Catalog = Northwind"; + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // Assumes connection is a valid SqlConnection. + SqlDataAdapter adapter = new SqlDataAdapter( + "SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID", + connection); + + // The Update command checks for optimistic concurrency violations + // in the WHERE clause. + adapter.UpdateCommand = new SqlCommand("UPDATE Customers Set CustomerID = @CustomerID, CompanyName = @CompanyName " + + "WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName", connection); + adapter.UpdateCommand.Parameters.Add( + "@CustomerID", SqlDbType.NChar, 5, "CustomerID"); + adapter.UpdateCommand.Parameters.Add( + "@CompanyName", SqlDbType.NVarChar, 30, "CompanyName"); + + // Pass the original values to the WHERE clause parameters. + SqlParameter parameter = adapter.UpdateCommand.Parameters.Add( + "@oldCustomerID", SqlDbType.NChar, 5, "CustomerID"); + parameter.SourceVersion = DataRowVersion.Original; + parameter = adapter.UpdateCommand.Parameters.Add( + "@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName"); + parameter.SourceVersion = DataRowVersion.Original; + + // Add the RowUpdated event handler. + adapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated); + + DataSet dataSet = new DataSet(); + adapter.Fill(dataSet, "Customers"); + + // Modify the DataSet contents. + adapter.Update(dataSet, "Customers"); + + foreach (DataRow dataRow in dataSet.Tables["Customers"].Rows) + { + if (dataRow.HasErrors) + Console.WriteLine(dataRow[0] + "\n" + dataRow.RowError); + } + } + } + + protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args) + { + if (args.RecordsAffected == 0) + { + args.Row.RowError = "Optimistic Concurrency Violation Encountered"; + args.Status = UpdateStatus.SkipCurrentRow; + } + } +} +// diff --git a/doc/samples/SqlDataAdapter_Events.cs b/doc/samples/SqlDataAdapter_Events.cs new file mode 100644 index 0000000000..deacdeb298 --- /dev/null +++ b/doc/samples/SqlDataAdapter_Events.cs @@ -0,0 +1,95 @@ +using System; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace DataAdapterTest +{ + class Program + { + static void Main() + { + } + + // + static DataSet DataAdapterEventsDemo(SqlConnection connection, DataSet custDS) + { + // Assumes that connection is a valid SqlConnection object + // and custDS includes the Customers table. + SqlDataAdapter custAdapter = new SqlDataAdapter( + "SELECT CustomerID, CompanyName FROM Customers", connection); + + // Add handlers. + custAdapter.RowUpdating += new SqlRowUpdatingEventHandler(OnRowUpdating); + custAdapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated); + + // Set DataAdapter command properties, fill DataSet, modify DataSet. + custAdapter.Update(custDS, "Customers"); + + // Remove handlers. + custAdapter.RowUpdating -= new SqlRowUpdatingEventHandler(OnRowUpdating); + custAdapter.RowUpdated -= new SqlRowUpdatedEventHandler(OnRowUpdated); + + return custDS; + } + + protected static void OnRowUpdating(object sender, SqlRowUpdatingEventArgs args) + { + if (args.StatementType == StatementType.Delete) + { + // Saves the removing rows with additional information in a file. + System.IO.TextWriter tw = System.IO.File.AppendText("Deletes.log"); + tw.WriteLine( + "{0}: Customer {1} Deleted.", DateTime.Now, + args.Row["CustomerID", DataRowVersion.Original]); + tw.Close(); + } + } + + protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args) + { + if (args.Status == UpdateStatus.ErrorsOccurred) + { + // Adds the error message to the row and skips from it. + args.Row.RowError = args.Errors.Message; + args.Status = UpdateStatus.SkipCurrentRow; + } + } + // + + // + static DataSet DataAdapterFillAndError(SqlDataAdapter adapter) + { + // Assuemes adapter is a valid SqlDataAdapter object. + adapter.FillError += new FillErrorEventHandler(FillError); + + DataSet dataSet = new DataSet(); + adapter.Fill(dataSet); + return dataSet; + } + + protected static void FillError(object sender, FillErrorEventArgs args) + { + if (args.Errors.GetType() == typeof(System.OverflowException)) + { + // Code to handle precision loss. + // Add a row to table using the values from the first two columns. + DataRow myRow = args.DataTable.Rows.Add(new object[] + {args.Values[0], args.Values[1], DBNull.Value}); + //Set the RowError containing the value for the third column. + myRow.RowError = + "OverflowException Encountered. Value from data source: " + + args.Values[2]; + args.Continue = true; + } + } + // + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + } + } +} diff --git a/doc/samples/SqlDataAdapter_FillDataSet.cs b/doc/samples/SqlDataAdapter_FillDataSet.cs new file mode 100644 index 0000000000..0365bedc40 --- /dev/null +++ b/doc/samples/SqlDataAdapter_FillDataSet.cs @@ -0,0 +1,114 @@ +using System; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace DataAdapterTest +{ + class Program + { + static void Main() + { + string s = GetConnectionString(); + SqlConnection c = new SqlConnection(s); + GetCustomers(c); + PrintCustomersOrders(c, c); + CustomerFillSchema1(c); + CustomerFillSchema2(c); + Console.ReadLine(); + } + + static DataSet GetCustomers(SqlConnection connection) + { + using (connection) + { + // + // Assumes that connection is a valid SqlConnection object. + string queryString = + "SELECT CustomerID, CompanyName FROM dbo.Customers"; + SqlDataAdapter adapter = new SqlDataAdapter(queryString, connection); + + DataSet customers = new DataSet(); + adapter.Fill(customers, "Customers"); + // + return customers; + } + } + + static void PrintCustomersOrders(SqlConnection customerConnection, SqlConnection orderConnection) + { + using (customerConnection) + using (orderConnection) + { + // + // Assumes that customerConnection and orderConnection are valid SqlConnection objects. + SqlDataAdapter custAdapter = new SqlDataAdapter( + "SELECT * FROM dbo.Customers", customerConnection); + SqlDataAdapter ordAdapter = new SqlDataAdapter( + "SELECT * FROM Orders", orderConnection); + + DataSet customerOrders = new DataSet(); + + custAdapter.Fill(customerOrders, "Customers"); + ordAdapter.Fill(customerOrders, "Orders"); + + DataRelation relation = customerOrders.Relations.Add("CustOrders", + customerOrders.Tables["Customers"].Columns["CustomerID"], + customerOrders.Tables["Orders"].Columns["CustomerID"]); + + foreach (DataRow pRow in customerOrders.Tables["Customers"].Rows) + { + Console.WriteLine(pRow["CustomerID"]); + foreach (DataRow cRow in pRow.GetChildRows(relation)) + Console.WriteLine("\t" + cRow["OrderID"]); + } + // + } + } + + static DataSet CustomerFillSchema1(SqlConnection connection) + { + using (connection) + { + // + // Assumes that connection is a valid SqlConnection object. + DataSet custDataSet = new DataSet(); + + SqlDataAdapter custAdapter = new SqlDataAdapter( + "SELECT * FROM dbo.Customers", connection); + + custAdapter.FillSchema(custDataSet, SchemaType.Source, "Customers"); + custAdapter.Fill(custDataSet, "Customers"); + // + + return custDataSet; + } + } + + static DataSet CustomerFillSchema2(SqlConnection connection) + { + using (connection) + { + // + // Assumes that connection is a valid SqlConnection object. + DataSet custDataSet = new DataSet(); + + SqlDataAdapter custAdapter = new SqlDataAdapter( + "SELECT * FROM dbo.Customers", connection); + + custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; + custAdapter.Fill(custDataSet, "Customers"); + // + + return custDataSet; + } + } + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + } + } +} diff --git a/doc/samples/SqlDataAdapter_MergeIdentity.cs b/doc/samples/SqlDataAdapter_MergeIdentity.cs new file mode 100644 index 0000000000..010beda2c7 --- /dev/null +++ b/doc/samples/SqlDataAdapter_MergeIdentity.cs @@ -0,0 +1,97 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + MergeIdentityColumns(connectionString); + Console.ReadLine(); + } + + // + private static void MergeIdentityColumns(string connectionString) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // Create the DataAdapter + SqlDataAdapter adapter = new SqlDataAdapter( + "SELECT ShipperID, CompanyName FROM dbo.Shippers", + connection); + + //Add the InsertCommand to retrieve new identity value. + adapter.InsertCommand = new SqlCommand( + "INSERT INTO dbo.Shippers (CompanyName) " + + "VALUES (@CompanyName); " + + "SELECT ShipperID, CompanyName FROM dbo.Shippers " + + "WHERE ShipperID = SCOPE_IDENTITY();", connection); + + // Add the parameter for the inserted value. + adapter.InsertCommand.Parameters.Add( + new SqlParameter("@CompanyName", SqlDbType.NVarChar, 40, + "CompanyName")); + adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both; + + // MissingSchemaAction adds any missing schema to + // the DataTable, including identity columns + adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; + + // Fill the DataTable. + DataTable shipper = new DataTable(); + adapter.Fill(shipper); + + // Add a new shipper. + DataRow newRow = shipper.NewRow(); + newRow["CompanyName"] = "New Shipper"; + shipper.Rows.Add(newRow); + + // Add changed rows to a new DataTable. This + // DataTable will be used by the DataAdapter. + DataTable dataChanges = shipper.GetChanges(); + + // Add the event handler. + adapter.RowUpdated += + new SqlRowUpdatedEventHandler(OnRowUpdated); + + adapter.Update(dataChanges); + connection.Close(); + + // Merge the updates. + shipper.Merge(dataChanges); + + // Commit the changes. + shipper.AcceptChanges(); + + Console.WriteLine("Rows after merge."); + foreach (DataRow row in shipper.Rows) + { + { + Console.WriteLine("{0}: {1}", row[0], row[1]); + } + } + } + } + // + + // + protected static void OnRowUpdated( + object sender, SqlRowUpdatedEventArgs e) + { + // If this is an insert, then skip this row. + if (e.StatementType == StatementType.Insert) + { + e.Status = UpdateStatus.SkipCurrentRow; + } + } + // + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=true"; + } +} diff --git a/doc/samples/SqlDataAdapter_Paging.cs b/doc/samples/SqlDataAdapter_Paging.cs new file mode 100644 index 0000000000..511c05ba16 --- /dev/null +++ b/doc/samples/SqlDataAdapter_Paging.cs @@ -0,0 +1,91 @@ +using System; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace DataAdapterTest +{ + class Program + { + static void Main() + { + string s = GetConnectionString(); + SqlConnection c = new SqlConnection(s); + GetOrders_Fill(c); + GetOrders_Select(c); + Console.ReadLine(); + } + + static DataSet GetOrders_Fill(SqlConnection connection) + { + using (connection) + { + // + int currentIndex = 0; + int pageSize = 5; + + string orderSQL = "SELECT * FROM Orders ORDER BY OrderID"; + // Assumes that connection is a valid SqlConnection object. + SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection); + + DataSet dataSet = new DataSet(); + adapter.Fill(dataSet, currentIndex, pageSize, "Orders"); + // + + // Retrieve the next page. + // + currentIndex += pageSize; + + // Assumes that dataset and adapter are valid objects. + dataSet.Tables["Orders"].Rows.Clear(); + adapter.Fill(dataSet, currentIndex, pageSize, "Orders"); + // + + return dataSet; + } + } + + static DataSet GetOrders_Select(SqlConnection connection) + { + using (connection) + { + // + int pageSize = 5; + + string orderSQL = "SELECT TOP " + pageSize + + " * FROM Orders ORDER BY OrderID"; + + // Assumes that connection is a valid SqlConnection object. + SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection); + + DataSet dataSet = new DataSet(); + adapter.Fill(dataSet, "Orders"); + // + + // + string lastRecord = + dataSet.Tables["Orders"].Rows[pageSize - 1]["OrderID"].ToString(); + // + + // + orderSQL = "SELECT TOP " + pageSize + + " * FROM Orders WHERE OrderID > " + lastRecord + " ORDER BY OrderID"; + adapter.SelectCommand.CommandText = orderSQL; + + dataSet.Tables["Orders"].Rows.Clear(); + + adapter.Fill(dataSet, "Orders"); + // + + return dataSet; + } + } + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + } + } +} diff --git a/doc/samples/SqlDataAdapter_Properties.cs b/doc/samples/SqlDataAdapter_Properties.cs new file mode 100644 index 0000000000..d66385c5d8 --- /dev/null +++ b/doc/samples/SqlDataAdapter_Properties.cs @@ -0,0 +1,206 @@ +// +using System; +using System.Data; +using System.Data.Common; +using Microsoft.Data.SqlClient; +using System.Linq; +using CSDataAdapterOperations.Properties; + +class Program +{ + static void Main(string[] args) + { + Settings settings = new Settings(); + + // Copy the data from the database. Get the table Department and Course from the database. + String selectString = @"SELECT [DepartmentID],[Name],[Budget],[StartDate],[Administrator] + FROM [MySchool].[dbo].[Department]; + + SELECT [CourseID],@Year as [Year],Max([Title]) as [Title], + Max([Credits]) as [Credits],Max([DepartmentID]) as [DepartmentID] + FROM [MySchool].[dbo].[Course] + Group by [CourseID]"; + + DataSet mySchool = new DataSet(); + + SqlCommand selectCommand = new SqlCommand(selectString); + SqlParameter parameter = selectCommand.Parameters.Add("@Year", SqlDbType.SmallInt, 2); + parameter.Value = new Random(DateTime.Now.Millisecond).Next(9999); + + // Use DataTableMapping to map the source tables and the destination tables. + DataTableMapping[] tableMappings = { new DataTableMapping("Table", "Department"), new DataTableMapping("Table1", "Course") }; + CopyData(mySchool, settings.MySchoolConnectionString, selectCommand, tableMappings); + + Console.WriteLine("The following tables are from the database."); + foreach (DataTable table in mySchool.Tables) + { + Console.WriteLine(table.TableName); + ShowDataTable(table); + } + + // Roll back the changes + DataTable department = mySchool.Tables["Department"]; + DataTable course = mySchool.Tables["Course"]; + + department.Rows[0]["Name"] = "New" + department.Rows[0][1]; + course.Rows[0]["Title"] = "New" + course.Rows[0]["Title"]; + course.Rows[0]["Credits"] = 10; + + Console.WriteLine("After we changed the tables:"); + foreach (DataTable table in mySchool.Tables) + { + Console.WriteLine(table.TableName); + ShowDataTable(table); + } + + department.RejectChanges(); + Console.WriteLine("After use the RejectChanges method in Department table to roll back the changes:"); + ShowDataTable(department); + + DataColumn[] primaryColumns = { course.Columns["CourseID"] }; + DataColumn[] resetColumns = { course.Columns["Title"] }; + ResetCourse(course, settings.MySchoolConnectionString, primaryColumns, resetColumns); + Console.WriteLine("After use the ResetCourse method in Course table to roll back the changes:"); + ShowDataTable(course); + + // Batch update the table. + String insertString = @"Insert into [MySchool].[dbo].[Course]([CourseID],[Year],[Title], + [Credits],[DepartmentID]) + values (@CourseID,@Year,@Title,@Credits,@DepartmentID)"; + SqlCommand insertCommand = new SqlCommand(insertString); + insertCommand.Parameters.Add("@CourseID", SqlDbType.NVarChar, 10, "CourseID"); + insertCommand.Parameters.Add("@Year", SqlDbType.SmallInt, 2, "Year"); + insertCommand.Parameters.Add("@Title", SqlDbType.NVarChar, 100, "Title"); + insertCommand.Parameters.Add("@Credits", SqlDbType.Int, 4, "Credits"); + insertCommand.Parameters.Add("@DepartmentID", SqlDbType.Int, 4, "DepartmentID"); + + const Int32 batchSize = 10; + BatchInsertUpdate(course, settings.MySchoolConnectionString, insertCommand, batchSize); + } + + private static void CopyData(DataSet dataSet, String connectionString, SqlCommand selectCommand, DataTableMapping[] tableMappings) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + selectCommand.Connection = connection; + + connection.Open(); + + using (SqlDataAdapter adapter = new SqlDataAdapter(selectCommand)) + { + adapter.TableMappings.AddRange(tableMappings); + // If set the AcceptChangesDuringFill as the false, AcceptChanges will not be called on a + // DataRow after it is added to the DataTable during any of the Fill operations. + adapter.AcceptChangesDuringFill = false; + + adapter.Fill(dataSet); + } + } + } + + // Roll back only one column or several columns data of the Course table by call ResetDataTable method. + private static void ResetCourse(DataTable table, String connectionString, + DataColumn[] primaryColumns, DataColumn[] resetColumns) + { + table.PrimaryKey = primaryColumns; + + // Build the query string + String primaryCols = String.Join(",", primaryColumns.Select(col => col.ColumnName)); + String resetCols = String.Join(",", resetColumns.Select(col => $"Max({col.ColumnName}) as {col.ColumnName}")); + + String selectString = $"Select {primaryCols},{resetCols} from Course Group by {primaryCols}"; + + SqlCommand selectCommand = new SqlCommand(selectString); + + ResetDataTable(table, connectionString, selectCommand); + } + + // RejectChanges will roll back all changes made to the table since it was loaded, or the last time AcceptChanges + // was called. When you copy from the database, you can lose all the data after calling RejectChanges + // The ResetDataTable method rolls back one or more columns of data. + private static void ResetDataTable(DataTable table, String connectionString, + SqlCommand selectCommand) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + selectCommand.Connection = connection; + + connection.Open(); + + using (SqlDataAdapter adapter = new SqlDataAdapter(selectCommand)) + { + // The incoming values for this row will be written to the current version of each + // column. The original version of each column's data will not be changed. + adapter.FillLoadOption = LoadOption.Upsert; + + adapter.Fill(table); + } + } + } + + private static void BatchInsertUpdate(DataTable table, String connectionString, + SqlCommand insertCommand, Int32 batchSize) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + insertCommand.Connection = connection; + // When setting UpdateBatchSize to a value other than 1, all the commands + // associated with the SqlDataAdapter have to have their UpdatedRowSource + // property set to None or OutputParameters. An exception is thrown otherwise. + insertCommand.UpdatedRowSource = UpdateRowSource.None; + + connection.Open(); + + using (SqlDataAdapter adapter = new SqlDataAdapter()) + { + adapter.InsertCommand = insertCommand; + // Gets or sets the number of rows that are processed in each round-trip to the server. + // Setting it to 1 disables batch updates, as rows are sent one at a time. + adapter.UpdateBatchSize = batchSize; + + adapter.Update(table); + + Console.WriteLine("Successfully to update the table."); + } + } + } + + private static void ShowDataTable(DataTable table) + { + foreach (DataColumn col in table.Columns) + { + Console.Write("{0,-14}", col.ColumnName); + } + Console.WriteLine("{0,-14}", "RowState"); + + foreach (DataRow row in table.Rows) + { + foreach (DataColumn col in table.Columns) + { + if (col.DataType.Equals(typeof(DateTime))) + Console.Write("{0,-14:d}", row[col]); + else if (col.DataType.Equals(typeof(Decimal))) + Console.Write("{0,-14:C}", row[col]); + else + Console.Write("{0,-14}", row[col]); + } + Console.WriteLine("{0,-14}", row.RowState); + } + } +} + +namespace CSDataAdapterOperations.Properties +{ + internal sealed partial class Settings : System.Configuration.ApplicationSettingsBase + { + private static readonly Settings defaultInstance = + ((Settings)(System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default => defaultInstance; + + [System.Configuration.ApplicationScopedSetting()] + [System.Configuration.DefaultSettingValue("Data Source=(local);Initial Catalog=MySchool;Integrated Security=True")] + public string MySchoolConnectionString => ((string)(this["MySchoolConnectionString"])); + } +} +// diff --git a/doc/samples/SqlDataAdapter_RetrieveIdentityStoredProcedure.cs b/doc/samples/SqlDataAdapter_RetrieveIdentityStoredProcedure.cs new file mode 100644 index 0000000000..976de4e3b1 --- /dev/null +++ b/doc/samples/SqlDataAdapter_RetrieveIdentityStoredProcedure.cs @@ -0,0 +1,71 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + RetrieveIdentity(connectionString); + Console.ReadLine(); + } + + // + private static void RetrieveIdentity(string connectionString) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // Create a SqlDataAdapter based on a SELECT query. + SqlDataAdapter adapter = new SqlDataAdapter( + "SELECT CategoryID, CategoryName FROM dbo.Categories", + connection); + + //Create the SqlCommand to execute the stored procedure. + adapter.InsertCommand = new SqlCommand("dbo.InsertCategory", + connection); + adapter.InsertCommand.CommandType = CommandType.StoredProcedure; + + // Add the parameter for the CategoryName. Specifying the + // ParameterDirection for an input parameter is not required. + adapter.InsertCommand.Parameters.Add( + new SqlParameter("@CategoryName", SqlDbType.NVarChar, 15, + "CategoryName")); + + // Add the SqlParameter to retrieve the new identity value. + // Specify the ParameterDirection as Output. + SqlParameter parameter = + adapter.InsertCommand.Parameters.Add( + "@Identity", SqlDbType.Int, 0, "CategoryID"); + parameter.Direction = ParameterDirection.Output; + + // Create a DataTable and fill it. + DataTable categories = new DataTable(); + adapter.Fill(categories); + + // Add a new row. + DataRow newRow = categories.NewRow(); + newRow["CategoryName"] = "New Category"; + categories.Rows.Add(newRow); + + adapter.Update(categories); + + Console.WriteLine("List All Rows:"); + foreach (DataRow row in categories.Rows) + { + { + Console.WriteLine("{0}: {1}", row[0], row[1]); + } + } + } + } + // + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=true"; + } +} diff --git a/doc/samples/SqlDataAdapter_SPIdentityReturn.cs b/doc/samples/SqlDataAdapter_SPIdentityReturn.cs new file mode 100644 index 0000000000..41628b5478 --- /dev/null +++ b/doc/samples/SqlDataAdapter_SPIdentityReturn.cs @@ -0,0 +1,70 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + ReturnIdentity(connectionString); + // Console.ReadLine(); + } + + private static void ReturnIdentity(string connectionString) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + // Create a SqlDataAdapter based on a SELECT query. + SqlDataAdapter adapter = new SqlDataAdapter( + "SELECT CategoryID, CategoryName FROM dbo.Categories", connection); + + // Create a SqlCommand to execute the stored procedure. + adapter.InsertCommand = new SqlCommand("InsertCategory", connection); + adapter.InsertCommand.CommandType = CommandType.StoredProcedure; + + // Create a parameter for the ReturnValue. + SqlParameter parameter = adapter.InsertCommand.Parameters.Add("@RowCount", SqlDbType.Int); + parameter.Direction = ParameterDirection.ReturnValue; + + // Create an input parameter for the CategoryName. + // You do not need to specify direction for input parameters. + adapter.InsertCommand.Parameters.Add("@CategoryName", SqlDbType.NChar, 15, "CategoryName"); + + // Create an output parameter for the new identity value. + parameter = adapter.InsertCommand.Parameters.Add("@Identity", SqlDbType.Int, 0, "CategoryID"); + parameter.Direction = ParameterDirection.Output; + + // Create a DataTable and fill it. + DataTable categories = new DataTable(); + adapter.Fill(categories); + + // Add a new row. + DataRow categoryRow = categories.NewRow(); + categoryRow["CategoryName"] = "New Beverages"; + categories.Rows.Add(categoryRow); + + // Update the database. + adapter.Update(categories); + + // Retrieve the ReturnValue. + Int rowCount = (Int)adapter.InsertCommand.Parameters["@RowCount"].Value; + + Console.WriteLine("ReturnValue: {0}", rowCount.ToString()); + Console.WriteLine("All Rows:"); + foreach (DataRow row in categories.Rows) + { + Console.WriteLine(" {0}: {1}", row[0], row[1]); + } + } + } + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;Integrated Security=true"; + } +} +// diff --git a/doc/samples/SqlDataAdapter_SqlDataAdapter.cs b/doc/samples/SqlDataAdapter_SqlDataAdapter.cs index beb2486cbd..f48edacc8a 100644 --- a/doc/samples/SqlDataAdapter_SqlDataAdapter.cs +++ b/doc/samples/SqlDataAdapter_SqlDataAdapter.cs @@ -1,6 +1,5 @@ using System; using System.Data; -// using Microsoft.Data.SqlClient; class Program @@ -8,8 +7,10 @@ class Program static void Main() { } + // public static SqlDataAdapter CreateSqlDataAdapter(SqlConnection connection) { + // Assumes that connection is a valid SqlConnection object SqlDataAdapter adapter = new SqlDataAdapter(); adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; @@ -45,5 +46,19 @@ public static SqlDataAdapter CreateSqlDataAdapter(SqlConnection connection) return adapter; } + // + + public static SqlDataAdapter CustomerUpdateCommand(SqlDataAdapter adapter) + { + // + // Assumes that connection is a valid SqlAdapter object + adapter.UpdateCommand.Parameters.Add("@CompanyName", + SqlDbType.VarChar, 15, "CompanyName"); + SqlParameter parameter = adapter.UpdateCommand.Parameters.Add("@CustomerID", + SqlDbType.Char, 5, "CustomerID"); + parameter.SourceVersion = DataRowVersion.Original; + // + return adapter; + } + } -// diff --git a/doc/samples/SqlDataAdapter_TableMappings.cs b/doc/samples/SqlDataAdapter_TableMappings.cs new file mode 100644 index 0000000000..4f2fc112e3 --- /dev/null +++ b/doc/samples/SqlDataAdapter_TableMappings.cs @@ -0,0 +1,98 @@ +using System; +using Microsoft.Data.SqlClient; +using System.Data; +using System.Data.Common; + +namespace DataAdapterTest +{ + class Program + { + static void Main() + { + string s = GetConnectionString(); + SqlConnection c = new SqlConnection(s); + CustomerTableMapping(c); + BizTableMapping(c); + GetCustomersOrders(c); + Console.ReadLine(); + } + + static DataSet CustomerTableMapping(SqlConnection connection) + { + using (connection) + { + // + // Assumes that connection is a valid SqlConnection object. + DataSet custDataSet = new DataSet(); + + SqlDataAdapter custAdapter = new SqlDataAdapter( + "SELECT * FROM dbo.Customers", connection); + + DataTableMapping mapping = + custAdapter.TableMappings.Add("Table", "NorthwindCustomers"); + mapping.ColumnMappings.Add("CompanyName", "Company"); + mapping.ColumnMappings.Add("ContactName", "Contact"); + mapping.ColumnMappings.Add("PostalCode", "ZIPCode"); + + custAdapter.Fill(custDataSet); + // + + return custDataSet; + } + } + + static DataSet BizTableMapping(SqlConnection connection) + { + using (connection) + { + // + // Assumes that connection is a valid SqlConnection object. + DataSet custDataSet = new DataSet(); + + SqlDataAdapter custAdapter = new SqlDataAdapter( + "SELECT * FROM dbo.Customers", connection); + + // The DataTableMapping is implemented ITableMapping. + ITableMapping mapping = + custAdapter.TableMappings.Add("Table", "BizTalkSchema"); + mapping.ColumnMappings.Add("CustomerID", "ClientID"); + mapping.ColumnMappings.Add("CompanyName", "ClientName"); + mapping.ColumnMappings.Add("ContactName", "Contact"); + mapping.ColumnMappings.Add("PostalCode", "ZIP"); + + custAdapter.Fill(custDataSet); + // + + return custDataSet; + } + } + + static DataSet GetCustomersOrders(SqlConnection connection) + { + using (connection) + { + // + // Assumes that connection is a valid SqlConnection object. + string queryString = + "SELECT * FROM dbo.Customers; SELECT * FROM dbo.Orders;"; + SqlDataAdapter adapter = new SqlDataAdapter(queryString, connection); + + DataSet customersDataSet = new DataSet(); + + adapter.TableMappings.Add("Customers1", "Orders"); + adapter.Fill(customersDataSet, "Customers"); + // + + return customersDataSet; + } + } + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + } + } +} diff --git a/doc/samples/SqlDataAdapter_Update.cs b/doc/samples/SqlDataAdapter_Update.cs new file mode 100644 index 0000000000..9b1e194bd1 --- /dev/null +++ b/doc/samples/SqlDataAdapter_Update.cs @@ -0,0 +1,79 @@ +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + AdapterUpdate(connectionString); + Console.ReadLine(); + } + // + private static void AdapterUpdate(string connectionString) + { + using (SqlConnection connection = + new SqlConnection(connectionString)) + { + SqlDataAdapter dataAdpater = new SqlDataAdapter( + "SELECT CategoryID, CategoryName FROM Categories", + connection); + + dataAdpater.UpdateCommand = new SqlCommand( + "UPDATE Categories SET CategoryName = @CategoryName " + + "WHERE CategoryID = @CategoryID", connection); + + dataAdpater.UpdateCommand.Parameters.Add( + "@CategoryName", SqlDbType.NVarChar, 15, "CategoryName"); + + SqlParameter parameter = dataAdpater.UpdateCommand.Parameters.Add( + "@CategoryID", SqlDbType.Int); + parameter.SourceColumn = "CategoryID"; + parameter.SourceVersion = DataRowVersion.Original; + + DataTable categoryTable = new DataTable(); + dataAdpater.Fill(categoryTable); + + DataRow categoryRow = categoryTable.Rows[0]; + categoryRow["CategoryName"] = "New Beverages"; + + dataAdpater.Update(categoryTable); + + Console.WriteLine("Rows after update."); + foreach (DataRow row in categoryTable.Rows) + { + { + Console.WriteLine("{0}: {1}", row[0], row[1]); + } + } + } + } + // + + static void UpdateCustomers(DataSet dataSet, SqlDataAdapter adapter) + { + // + // Assumes that dataSet and adapter are valid objects. + DataTable table = dataSet.Tables["Customers"]; + + // First process deletes. + adapter.Update(table.Select(null, null, DataViewRowState.Deleted)); + + // Next process updates. + adapter.Update(table.Select(null, null, + DataViewRowState.ModifiedCurrent)); + + // Finally, process inserts. + adapter.Update(table.Select(null, null, DataViewRowState.Added)); + // + } + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=true"; + } +} diff --git a/doc/samples/SqlDataReader_DataDiscoveryAndClassification.cs b/doc/samples/SqlDataReader_DataDiscoveryAndClassification.cs index d9dfcee124..352cb20fe9 100644 --- a/doc/samples/SqlDataReader_DataDiscoveryAndClassification.cs +++ b/doc/samples/SqlDataReader_DataDiscoveryAndClassification.cs @@ -24,8 +24,9 @@ public static void Main() if (DataClassificationSupported(connection)) { // Create the temporary table and retrieve its Data Discovery and Classification information. - CreateTable(connection); - RunTests(connection); + // Set rankEnabled to be true if testing with rank information. + CreateTable(connection, rankEnabled : true); + RunTests(connection, rankEnabled : true); } } finally @@ -65,7 +66,8 @@ public static bool DataClassificationSupported(SqlConnection connection) /// Creates a temporary table for this sample program and sets tags for Sensitivity Classification. /// /// The SqlConnection to work with. - private static void CreateTable(SqlConnection connection) + /// True if rank information is enabled and false otherwise + private static void CreateTable(SqlConnection connection, bool rankEnabled = false) { SqlCommand command = new SqlCommand(null, connection); @@ -81,35 +83,58 @@ private static void CreateTable(SqlConnection connection) + "[Fax] [nvarchar](30) MASKED WITH (FUNCTION = 'default()') NULL)"; command.ExecuteNonQuery(); - // Set Sensitivity Classification tags for table columns. - command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" - + ".CompanyName WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Company Name', INFORMATION_TYPE_ID='COMPANY')"; - command.ExecuteNonQuery(); + if (rankEnabled) + { + // Set Sensitivity Classification tags for table columns with rank information + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".CompanyName WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Company Name', INFORMATION_TYPE_ID='COMPANY', RANK=LOW)"; + command.ExecuteNonQuery(); - command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" - + ".ContactName WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Person Name', INFORMATION_TYPE_ID='NAME')"; - command.ExecuteNonQuery(); + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".ContactName WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Person Name', INFORMATION_TYPE_ID='NAME', RANK=LOW)"; + command.ExecuteNonQuery(); - command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" - + ".Phone WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Contact Information', INFORMATION_TYPE_ID='CONTACT')"; - command.ExecuteNonQuery(); + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".Phone WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Contact Information', INFORMATION_TYPE_ID='CONTACT', RANK=MEDIUM)"; + command.ExecuteNonQuery(); - command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" - + ".Fax WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Contact Information', INFORMATION_TYPE_ID='CONTACT')"; - command.ExecuteNonQuery(); + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".Fax WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Contact Information', INFORMATION_TYPE_ID='CONTACT', RANK=MEDIUM)"; + command.ExecuteNonQuery(); + } + else + { + // Set Sensitivity Classification tags for table columns without rank information + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".CompanyName WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Company Name', INFORMATION_TYPE_ID='COMPANY')"; + command.ExecuteNonQuery(); + + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".ContactName WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Person Name', INFORMATION_TYPE_ID='NAME')"; + command.ExecuteNonQuery(); + + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".Phone WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Contact Information', INFORMATION_TYPE_ID='CONTACT')"; + command.ExecuteNonQuery(); + + command.CommandText = $"ADD SENSITIVITY CLASSIFICATION TO {tableName}" + + ".Fax WITH (LABEL='PII', LABEL_ID='L1', INFORMATION_TYPE='Contact Information', INFORMATION_TYPE_ID='CONTACT')"; + command.ExecuteNonQuery(); + } } /// /// Run query to fetch result set from target table. /// /// The SqlConnection to work with. - private static void RunTests(SqlConnection connection) + /// True if rank information is enabled and false otherwise + private static void RunTests(SqlConnection connection, bool rankEnabled = false) { SqlCommand command = new SqlCommand(null, connection); command.CommandText = $"SELECT * FROM {tableName}"; using (SqlDataReader reader = command.ExecuteReader()) { - PrintSensitivityClassification(reader); + PrintSensitivityClassification(reader, rankEnabled); } } @@ -117,7 +142,8 @@ private static void RunTests(SqlConnection connection) /// Prints Sensitivity Classification data as received in the result set. /// /// The SqlDataReader to work with. - private static void PrintSensitivityClassification(SqlDataReader reader) + /// True if rank information is enabled and false otherwise + private static void PrintSensitivityClassification(SqlDataReader reader, bool rankEnabled = false) { if (reader.SensitivityClassification != null) { @@ -140,8 +166,11 @@ private static void PrintSensitivityClassification(SqlDataReader reader) Console.WriteLine($"Information Type: {sp.InformationType.Name}"); Console.WriteLine(); } + + Console.WriteLine($"Sensitivity Rank: {sp.SensitivityRank.ToString()}"); } } + Console.Writeline($"reader.SensitivityClassification.SensitivityRank : {reader.SensitivityClassification.SensitivityRank.ToString()}"); } } diff --git a/doc/samples/SqlDataReader_GetSchemaTable.cs b/doc/samples/SqlDataReader_GetSchemaTable.cs new file mode 100644 index 0000000000..5f540ae5d9 --- /dev/null +++ b/doc/samples/SqlDataReader_GetSchemaTable.cs @@ -0,0 +1,54 @@ +using System; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace NextResultCS +{ + class Program + { + static void Main() + { + string s = GetConnectionString(); + SqlConnection c = new SqlConnection(s); + GetSchemaInfo(c); + Console.ReadLine(); + } + // + static void GetSchemaInfo(SqlConnection connection) + { + using (connection) + { + SqlCommand command = new SqlCommand( + "SELECT CategoryID, CategoryName FROM Categories;", + connection); + connection.Open(); + + SqlDataReader reader = command.ExecuteReader(); + + // Retrieve schema information about the current result-set. + DataTable schemaTable = reader.GetSchemaTable(); + + foreach (DataRow row in schemaTable.Rows) + { + foreach (DataColumn column in schemaTable.Columns) + { + Console.WriteLine(String.Format("{0} = {1}", + column.ColumnName, row[column])); + } + } + + // Always call the Close method when you have finished using the DataReader object. + reader.Close(); + } + } + // + + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + } + } +} diff --git a/doc/samples/SqlDataReader_HasRows.cs b/doc/samples/SqlDataReader_HasRows.cs new file mode 100644 index 0000000000..caeb01f1d6 --- /dev/null +++ b/doc/samples/SqlDataReader_HasRows.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace NextResultCS +{ + class Program + { + static void Main() + { + string s = GetConnectionString(); + SqlConnection c = new SqlConnection(s); + HasRows(c); + Console.ReadLine(); + } + + // + static void HasRows(SqlConnection connection) + { + using (connection) + { + SqlCommand command = new SqlCommand( + "SELECT CategoryID, CategoryName FROM Categories;", + connection); + connection.Open(); + + SqlDataReader reader = command.ExecuteReader(); + + // Check if the DataReader has any row. + if (reader.HasRows) + { + // Obtain a row from the query result. + while (reader.Read()) + { + Console.WriteLine("{0}\t{1}", reader.GetInt32(0), + reader.GetString(1)); + } + } + else + { + Console.WriteLine("No rows found."); + } + // Always call the Close method when you have finished using the DataReader object. + reader.Close(); + } + } + + // + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + } + } +} diff --git a/doc/samples/SqlDataReader_NextResult.cs b/doc/samples/SqlDataReader_NextResult.cs new file mode 100644 index 0000000000..4dccf5a55f --- /dev/null +++ b/doc/samples/SqlDataReader_NextResult.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Data.SqlClient; +using System.Data; + +namespace NextResultCS +{ + class Program + { + static void Main() + { + string s = GetConnectionString(); + SqlConnection c = new SqlConnection(s); + RetrieveMultipleResults(c); + Console.ReadLine(); + } + + // + static void RetrieveMultipleResults(SqlConnection connection) + { + using (connection) + { + SqlCommand command = new SqlCommand( + "SELECT CategoryID, CategoryName FROM dbo.Categories;" + + "SELECT EmployeeID, LastName FROM dbo.Employees", + connection); + connection.Open(); + + SqlDataReader reader = command.ExecuteReader(); + + // Check if the DataReader has any row. + while (reader.HasRows) + { + Console.WriteLine("\t{0}\t{1}", reader.GetName(0), + reader.GetName(1)); + + // Obtain a row from the query result. + while (reader.Read()) + { + Console.WriteLine("\t{0}\t{1}", reader.GetInt32(0), + reader.GetString(1)); + } + + // Hop to the next result-set. + reader.NextResult(); + } + // Always call the Close method when you have finished using the DataReader object. + reader.Close(); + } + } + + // + static private string GetConnectionString() + { + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + return "Data Source=(local);Initial Catalog=Northwind;" + + "Integrated Security=SSPI"; + } + } +} diff --git a/doc/samples/SqlDataSourceEnumeratorExample.cs b/doc/samples/SqlDataSourceEnumeratorExample.cs new file mode 100644 index 0000000000..279881c672 --- /dev/null +++ b/doc/samples/SqlDataSourceEnumeratorExample.cs @@ -0,0 +1,33 @@ +using System; +// +using Microsoft.Data.Sql; + +class Program +{ + static void Main() + { + // Retrieve the enumerator instance and then the data. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Display the contents of the table. + DisplayData(table); + + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +} +// diff --git a/doc/samples/SqlDataSourceEnumeratorVersionExample.cs b/doc/samples/SqlDataSourceEnumeratorVersionExample.cs new file mode 100644 index 0000000000..73eefc1235 --- /dev/null +++ b/doc/samples/SqlDataSourceEnumeratorVersionExample.cs @@ -0,0 +1,25 @@ +using System; +// +using Microsoft.Data.Sql; + +class Program +{ + static void Main() + { + // Retrieve the enumerator instance, and + // then retrieve the data sources. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Filter the sources to just show SQL Server 2012 instances. + System.Data.DataRow[] rows = table.Select("Version LIKE '11%'"); + foreach (System.Data.DataRow row in rows) + { + Console.WriteLine(row["ServerName"]); + } + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } +} +// diff --git a/doc/samples/SqlFunctionAttribute.cs b/doc/samples/SqlFunctionAttribute.cs index 6f20986cf6..28a027caad 100644 --- a/doc/samples/SqlFunctionAttribute.cs +++ b/doc/samples/SqlFunctionAttribute.cs @@ -2,7 +2,7 @@ using System.IO; using System.Collections; // -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; public class Class1 { diff --git a/doc/samples/SqlTransactionLocal.cs b/doc/samples/SqlTransactionLocal.cs new file mode 100644 index 0000000000..29d29526ab --- /dev/null +++ b/doc/samples/SqlTransactionLocal.cs @@ -0,0 +1,57 @@ +// +using System; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main(string[] args) + { + string connectionString = "Data Source = localhost; Integrated Security = true; Initial Catalog = AdventureWorks"; + + using (SqlConnection connection = new SqlConnection(connectionString)) + { + connection.Open(); + + // Start a local transaction. + SqlTransaction sqlTran = connection.BeginTransaction(); + + // Enlist a command in the current transaction. + SqlCommand command = connection.CreateCommand(); + command.Transaction = sqlTran; + + try + { + // Execute two separate commands. + command.CommandText = + "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')"; + command.ExecuteNonQuery(); + command.CommandText = + "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')"; + command.ExecuteNonQuery(); + + // Commit the transaction. + sqlTran.Commit(); + Console.WriteLine("Both records were written to database."); + } + catch (Exception ex) + { + // Handle the exception if the transaction fails to commit. + Console.WriteLine(ex.Message); + + try + { + // Attempt to roll back the transaction. + sqlTran.Rollback(); + } + catch (Exception exRollback) + { + // Throws an InvalidOperationException if the connection + // is closed or the transaction has already been rolled + // back on the server. + Console.WriteLine(exRollback.Message); + } + } + } + } +} +// diff --git a/doc/samples/SqlTransactionScope.cs b/doc/samples/SqlTransactionScope.cs new file mode 100644 index 0000000000..77f5d75694 --- /dev/null +++ b/doc/samples/SqlTransactionScope.cs @@ -0,0 +1,99 @@ +// +using System; +using System.Transactions; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main(string[] args) + { + string connectionString = "Data Source = localhost; Integrated Security = true; Initial Catalog = AdventureWorks"; + + string commandText1 = "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')"; + string commandText2 = "INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')"; + + int result = CreateTransactionScope(connectionString, connectionString, commandText1, commandText2); + + Console.WriteLine("result = " + result); + } + + static public int CreateTransactionScope(string connectString1, string connectString2, + string commandText1, string commandText2) + { + // Initialize the return value to zero and create a StringWriter to display results. + int returnValue = 0; + System.IO.StringWriter writer = new System.IO.StringWriter(); + + // Create the TransactionScope in which to execute the commands, guaranteeing + // that both commands will commit or roll back as a single unit of work. + using (TransactionScope scope = new TransactionScope()) + { + using (SqlConnection connection1 = new SqlConnection(connectString1)) + { + try + { + // Opening the connection automatically enlists it in the + // TransactionScope as a lightweight transaction. + connection1.Open(); + + // Create the SqlCommand object and execute the first command. + SqlCommand command1 = new SqlCommand(commandText1, connection1); + returnValue = command1.ExecuteNonQuery(); + writer.WriteLine("Rows to be affected by command1: {0}", returnValue); + + // if you get here, this means that command1 succeeded. By nesting + // the using block for connection2 inside that of connection1, you + // conserve server and network resources by opening connection2 + // only when there is a chance that the transaction can commit. + using (SqlConnection connection2 = new SqlConnection(connectString2)) + try + { + // The transaction is promoted to a full distributed + // transaction when connection2 is opened. + connection2.Open(); + + // Execute the second command in the second database. + returnValue = 0; + SqlCommand command2 = new SqlCommand(commandText2, connection2); + returnValue = command2.ExecuteNonQuery(); + writer.WriteLine("Rows to be affected by command2: {0}", returnValue); + } + catch (Exception ex) + { + // Display information that command2 failed. + writer.WriteLine("returnValue for command2: {0}", returnValue); + writer.WriteLine("Exception Message2: {0}", ex.Message); + } + } + catch (Exception ex) + { + // Display information that command1 failed. + writer.WriteLine("returnValue for command1: {0}", returnValue); + writer.WriteLine("Exception Message1: {0}", ex.Message); + } + } + + // If an exception has been thrown, Complete will not + // be called and the transaction is rolled back. + scope.Complete(); + } + + // The returnValue is greater than 0 if the transaction committed. + if (returnValue > 0) + { + writer.WriteLine("Transaction was committed."); + } + else + { + // You could write additional business logic here, notify the caller by + // throwing a TransactionAbortedException, or log the failure. + writer.WriteLine("Transaction rolled back."); + } + + // Display messages. + Console.WriteLine(writer.ToString()); + + return returnValue; + } +} +// diff --git a/doc/samples/SqlUserDefinedAggregate.cs b/doc/samples/SqlUserDefinedAggregate.cs index 45a45dff7c..7f3792c133 100644 --- a/doc/samples/SqlUserDefinedAggregate.cs +++ b/doc/samples/SqlUserDefinedAggregate.cs @@ -1,20 +1,20 @@ using System; // -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; using System.IO; using System.Data.Sql; using System.Data.SqlTypes; using System.Text; [Serializable] -[Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregate( - Microsoft.Data.SqlClient.Server.Format.UserDefined, +[Microsoft.SqlServer.Server.SqlUserDefinedAggregate( + Microsoft.SqlServer.Server.Format.UserDefined, IsInvariantToNulls = true, IsInvariantToDuplicates = false, IsInvariantToOrder = false, MaxByteSize = 8000) ] -public class Concatenate : Microsoft.Data.SqlClient.Server.IBinarySerialize +public class Concatenate : Microsoft.SqlServer.Server.IBinarySerialize { public void Read(BinaryReader r) diff --git a/doc/samples/SqlUserDefinedType1.cs b/doc/samples/SqlUserDefinedType1.cs index 5601a016b1..8f440648c2 100644 --- a/doc/samples/SqlUserDefinedType1.cs +++ b/doc/samples/SqlUserDefinedType1.cs @@ -2,7 +2,7 @@ // using System; using System.Data.SqlTypes; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [Serializable()] [SqlUserDefinedType(Format.Native)] @@ -133,7 +133,7 @@ public SqlString Quadrant() //----------------------------------------------------------------------------- // -// using Microsoft.Data.SqlClient.Server; +// using Microsoft.SqlServer.Server; [SqlUserDefinedType(Format.Native, MaxByteSize = 8000)] public class SampleType diff --git a/doc/samples/TransactionIsolationLevels.cs b/doc/samples/TransactionIsolationLevels.cs index dc85d9447b..6abd99e15d 100644 --- a/doc/samples/TransactionIsolationLevels.cs +++ b/doc/samples/TransactionIsolationLevels.cs @@ -487,7 +487,7 @@ class TransactionIsolationLevelsProgram { internal static void Main(string[] args) { - String connString = "Data Source=(local);Initial Catalog=master;Integrated Security=True;Asynchronous Processing=true;"; + String connString = "Data Source=(local);Initial Catalog=master;Integrated Security=True;"; OperateDatabase.CreateDatabase(connString); Console.WriteLine(); diff --git a/doc/snippets/Microsoft.Data.Sql/SqlDataSourceEnumerator.xml b/doc/snippets/Microsoft.Data.Sql/SqlDataSourceEnumerator.xml new file mode 100644 index 0000000000..55e1f2c057 --- /dev/null +++ b/doc/snippets/Microsoft.Data.Sql/SqlDataSourceEnumerator.xml @@ -0,0 +1,65 @@ + + + + + Provides a mechanism for enumerating all available instances of SQL Server within the local network. + + class exposes this information to the application developer, providing a containing information about all the available servers. This returned table contains a list of server instances that matches the list provided when a user attempts to create a new connection, and on the `Connection Properties` dialog box, expands the drop-down list containing all the available servers. + + ]]> + + Enumerating Instances of SQL Server + + + Retrieves a containing information about all visible SQL Server instances. + A containing information about the visible SQL Server instances. + +
10.0.xx for SQL Server 2008
10.50.x for SQL Server 2008 R2
11.0.xx for SQL Server 2012
12.0.xx for SQL Server 2014
13.0.xx for SQL Server 2016
14.0.xx for SQL Server 2017| + +> [!NOTE] +> Due to the nature of the mechanism used by to locate data sources on a network, the method will not always return a complete list of the available servers, and the list might not be the same on every call. If you plan to use this function to let users select a server from a list, make sure that you always also supply an option to type in a name that is not in the list, in case the server enumeration does not return all the available servers. In addition, this method may take a significant amount of time to execute, so be careful about calling it when performance is critical. + +## Examples + The following console application retrieves information about all the visible SQL Server instances and displays the information in the console window. + +[!code-csharp[SqlDataSourceEnumerator.Example#1](~/../sqlclient/doc/samples/SqlDataSourceEnumeratorExample.cs#1)] + + ]]>
+
+ Enumerating Instances of SQL Server +
+ + Gets an instance of the , which can be used to retrieve information about available SQL Server instances. + An instance of the used to retrieve information about available SQL Server instances. + + class does not provide a constructor. Use the property to retrieve an instance of the class instead. + +[!code-csharp[SqlDataSourceEnumeratorExample#1](~/../sqlclient/doc/samples/SqlDataSourceEnumeratorExample.cs#1)] + +## Examples +The following console application displays a list of all the available SQL Server 2005 instances within the local network. This code uses the method to filter the rows in the table returned by the method. + [!code-csharp[SqlDataSourceEnumeratorVersionExample#1](~/../sqlclient/doc/samples/SqlDataSourceEnumeratorVersionExample.cs#1)] + + ]]> + + Enumerating Instances of SQL Server + + +
+
diff --git a/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml b/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml index 4c3f6bd5f9..8a60e5c16c 100644 --- a/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml +++ b/doc/snippets/Microsoft.Data.Sql/SqlNotificationRequest.xml @@ -11,7 +11,7 @@ This class provides low-level access to the query notification services exposed ]]> - Using Query Notifications + Using Query Notifications Creates a new instance of the class with default values. @@ -23,7 +23,7 @@ If the parameterless constructor is used to create a - Using Query Notifications + Using Query Notifications A string that contains an application-specific identifier for this notification. It is not used by the notifications infrastructure, but it allows you to associate notifications with the application state. The value indicated in this parameter is included in the Service Broker queue message. @@ -40,7 +40,7 @@ This constructor allows you to initialize a new The value of the parameter is NULL. The or parameter is longer than or the value in the parameter is less than zero. - Using Query Notifications + Using Query Notifications Gets or sets the SQL Server Service Broker service name where notification messages are posted. @@ -64,7 +64,7 @@ The SQL Server Service Broker service must be previously configured on the serve The value is NULL. The value is longer than . - Using Query Notifications + Using Query Notifications Gets or sets a value that specifies how long SQL Server waits for a change to occur before the operation times out. @@ -78,7 +78,7 @@ After the time-out period expires, the notification is sent even if no change ta ]]> The value is less than zero. - Using Query Notifications + Using Query Notifications Gets or sets an application-specific identifier for this notification. @@ -92,7 +92,7 @@ This value is not used by the notifications infrastructure. Instead, it is a mec ]]> The value is longer than . - Using Query Notifications + Using Query Notifications diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/DataAccessKind.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/DataAccessKind.xml deleted file mode 100644 index d429e11dd9..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/DataAccessKind.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Describes the type of access to user data for a user-defined method or function. - - and to indicate whether the method or function uses ADO.NET to connect back to the database using the "context connection." - - Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function perform read-only data-access operations, such as executing SELECT statements). - - ]]> - - - - The method or function does not access user data. - - - The method or function reads user data. - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/IBinarySerialize.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/IBinarySerialize.xml deleted file mode 100644 index a84e479859..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/IBinarySerialize.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - Provides custom implementation for user-defined type (UDT) and user-defined aggregate serialization and deserialization. - - .`Native` or .`UserDefined`. - - .`Native` allows SQL Server to handle serialization and deserialization automatically, but the format has restrictions on the kind of types it can handle. .`UserDefined` allows user-defined types and aggregates to handle their own serialization. User-defined types and aggregates must be marked with .`UserDefined` in the `SqlUserDefinedType` or `SqlUserDefinedAggregate` attribute, and must implement the interface. - - Note that even with custom serialization, the total size of each instance must be under the maximum allowed limit, currently 8000 bytes. - - ]]> - - - - The stream from which the object is deserialized. - Generates a user-defined type (UDT) or user-defined aggregate from its binary form. - - method must reconstitute your object using the information written by the method. - -## Examples - The following example shows the implementation of the method of a UDT, which uses a to de-serialize a previously persisted UDT. This example assumes that the UDT has two data properties: `StringValue` and `DoubleValue`. - - [!code-csharp[IBinarySerialize Samples#1](~/../sqlclient/doc/samples/IBinarySerialize.cs#1)] - - ]]> - - - - The stream to which the UDT or user-defined aggregate is serialized. - Converts a user-defined type (UDT) or user-defined aggregate into its binary format so that it may be persisted. - - method to reconstitute your UDT or user-defined aggregate. - -## Examples - The following example shows the implementation of the method of a UDT, which uses a to serialize the UDT in the user-defined binary format. The purpose of the null character padding is to ensure that the string value is completely separated from the double value, so that one UDT is compared to another in Transact-SQL code, string bytes are compared to string bytes and double bytes are compared to double bytes. - - [!code-csharp[IBinarySerialize Samples#2](~/../sqlclient/doc/samples/IBinarySerialize.cs#2)] - - ]]> - - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/InvalidUdtException.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/InvalidUdtException.xml deleted file mode 100644 index 70bfd160f4..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/InvalidUdtException.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Thrown when SQL Server or the ADO.NET provider detects an invalid user-defined type (UDT). - To be added. - - - The object. - The object. - Streams all the properties into the class for the given . - - class to make the class serializable. - - ]]> - - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml index cb89fe35a0..a2481e9363 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml +++ b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlDataRecord.xml @@ -2,7 +2,7 @@ - Represents a single row of data and its metadata. This class cannot be inherited. + Represents a single row of data and its metadata. @@ -236,7 +236,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -255,7 +255,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -321,7 +321,7 @@ The following are the default values assigned to `dbType`, depending on the `Sql @@ -341,7 +341,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -361,7 +361,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -384,7 +384,7 @@ For more information, see [Table-Valued Parameters](/dotnet/framework/data/adone @@ -692,7 +692,7 @@ The default is `FALSE`. This property can only be set in one of the constructors. -For more information, see [Table-Valued Parameters](/dotnet/framework/data/adonet/sql/table-valued-parameters). +For more information, see [Table-Valued Parameters](/sql/connect/ado-net/sql/table-valued-parameters). ]]> @@ -777,7 +777,7 @@ Returns 0 if the scale of the underlying column type is not defined. ## Remarks This property can only be set in one of the constructors. -For more information, see [Table-Valued Parameters](/dotnet/framework/data/adonet/sql/table-valued-parameters). +For more information, see [Table-Valued Parameters](/sql/connect/ado-net/sql/table-valued-parameters). ]]> @@ -793,7 +793,7 @@ The default is 1. This property can only be set in one of the constructors. -For more information, see [Table-Valued Parameters](/dotnet/framework/data/adonet/sql/table-valued-parameters). +For more information, see [Table-Valued Parameters](/sql/connect/ado-net/sql/table-valued-parameters). ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedTypeAttribute.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedTypeAttribute.xml deleted file mode 100644 index 14bc23a2bd..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedTypeAttribute.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Used to mark a type definition in an assembly as a user-defined type (UDT) in SQL Server. The properties on the attribute reflect the physical characteristics used when the type is registered with SQL Server. This class cannot be inherited. - - custom attribute. Every UDT must be annotated with this attribute. See [CLR User-Defined Types](https://go.microsoft.com/fwlink/?LinkId=128028) for more information about UDTs, including an example of a UDT. - -## Examples -The following example shows the `UserDefinedType` attribute of the Point UDT. The UDT is byte-ordered, is named "Point", has a validation method named "ValidatePoint", and uses the native serialization format. - -[!code-csharp[SqlUserDefinedType Example#1](~/../sqlclient/doc/samples/SqlUserDefinedType.cs#1)] - -]]> - - - - One of the values representing the serialization format of the type. - A required attribute on a user-defined type (UDT), used to confirm that the given type is a UDT and to indicate the storage format of the UDT. - - - - - - The serialization format as a . - A value representing the serialization format. - - - Indicates whether the user-defined type is byte ordered. - - if the user-defined type is byte ordered; otherwise . - - property in effect guarantees that the serialized binary data can be used for semantic ordering of the information. Thus, each instance of a byte-ordered UDT object can only have one serialized representation. When a comparison operation is performed in SQL Server on the serialized bytes, its results should be the same as if the same comparison operation had taken place in managed code. - -The following features are supported when is set to `true`: - -- The ability to create indexes on columns of this type. - -- The ability to create primary and foreign keys as well as CHECK and UNIQUE constraints on columns of this type. - -- The ability to use Transact-SQL ORDER BY, GROUP BY, and PARTITION BY clauses. In these cases, the binary representation of the type is used to determine the order. - -- The ability to use comparison operators in Transact-SQL statements. - -- The ability to persist computed columns of this type. - -Note that both the `Native` and `UserDefined` serialization formats support the following comparison operators when is set to `true`: - -- Equal to (=) - -- Not equal to (!=) - -- Greater than (>) - -- Less than (\<) - -- Greater than or equal to (>=) - -- Less than or equal to (<=) - -]]> - - - - Indicates whether all instances of this user-defined type are the same length. - - if all instances of this type are the same length; otherwise . - - - . This attribute is only relevant for UDTs with `UserDefined` serialization . - -]]> - - - - The maximum size of the instance, in bytes. - An value representing the maximum size of the instance. - - property with the `UserDefined` serialization . - -When connecting to SQL Server 2005 or earlier, must be between 1 and 8000. - -When connecting to SQL Server 2008 or later, set between 1 and 8000, for a type whose instances are always 8,000 bytes or less. For types that can have instances larger than 8000, specify -1. - -For a UDT with user-defined serialization specified, refers to the total size of the UDT in its serialized form as defined by the user. Consider a UDT with a property of a string of 10 characters (). When the UDT is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized UDT must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. - -This property should not be used with `Native` serialization . - -]]> - - - - The SQL Server name of the user-defined type. - A value representing the SQL Server name of the user-defined type. - - property is not used within SQL Server, but is used by the Microsoft Visual Studio .NET Integrated Development Environment (IDE). - -]]> - - - - The name of the method used to validate instances of the user-defined type. - A representing the name of the method used to validate instances of the user-defined type. - - - - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SystemDataAccessKind.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SystemDataAccessKind.xml deleted file mode 100644 index e2209278d7..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SystemDataAccessKind.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Describes the type of access to system data for a user-defined method or function. - - and to indicate what type of access to system data the method or function has. - - Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function performs read-only data-access operations, such as executing SELECT statements). - - ]]> - - - - The method or function does not access system data. - - - The method or function reads system data. - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml index 081e22dcd6..5a69be7478 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml @@ -95,13 +95,16 @@ The following example demonstrates providing a custom device flow callback to Sq are: + The supported authentication modes with are: - Active Directory Password - Active Directory Integrated - Active Directory Interactive - Active Directory Service Principal - Active Directory Device Code Flow +- Active Directory Managed Identity +- Active Directory MSI +- Active Directory Default ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml index 19927484fd..a52a2ec41a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml @@ -34,12 +34,16 @@ 6 - The authentication method uses Active Directory Managed Identity. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the object ID of the user identity. + The authentication method uses Active Directory Managed Identity. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the "client ID" of the user identity. 7 - Alias for "Active Directory Managed Identity" authentication method. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the object ID of the user identity. + Alias for "Active Directory Managed Identity" authentication method. Use System Assigned or User Assigned Managed Identity to connect to SQL Database from Azure client environments that have enabled support for Managed Identity. For User Assigned Managed Identity, 'User Id' or 'UID' is required to be set to the "client ID" of the user identity. 8 + + The authentication method uses Active Directory Default. Use this mode to connect to a SQL Database using multiple non-interactive authentication methods tried sequentially to acquire an access token. This method does not fallback to the "Active Directory Interactive" authentication method. + 9 + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml index 520882eb93..51fec0ae1f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml @@ -12,7 +12,8 @@ The user login name/ID. The user password. The connection ID. - Initializes a new instance of the class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password and connection ID. + The connection timeout value in seconds. + Initializes a new instance of the class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password, connection ID and connection timeout value. Gets the authentication method. @@ -46,5 +47,9 @@ Gets the database name. The database name. + + Gets the connection timeout value. + The connection timeout value to be passed to Cancellation Token Source. + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml index 6247136669..d7299c5e80 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml @@ -8,14 +8,14 @@ class lets you write managed code solutions that provide similar functionality. There are other ways to load data into a SQL Server table (INSERT statements, for example), but offers a significant performance advantage over them. The class can be used to write data only to SQL Server tables. However, the data source is not limited to SQL Server; any data source can be used, as long as the data can be loaded to a instance or read with a instance. will fail when bulk loading a column of type into a SQL Server column whose type is one of the date/time types added in SQL Server 2008. +Microsoft SQL Server includes a popular command-prompt utility named **bcp** for moving data from one table to another, whether on a single server or between servers. The class lets you write managed code solutions that provide similar functionality. There are other ways to load data into a SQL Server table (INSERT statements, for example), but offers a significant performance advantage over them. The class can be used to write data only to SQL Server tables. However, the data source is not limited to SQL Server; any data source can be used, as long as the data can be loaded to a instance or read with a instance. will fail when bulk loading a column of type into a SQL Server column whose type is one of the date/time types added in SQL Server 2008. ## Examples -The following console application demonstrates how to load data using the class. -In this example, a is used to copy data from the **Production.Product** table in the SQL Server **AdventureWorks** database to a similar table in the same database. +The following console application demonstrates how to load data using the class. +In this example, a is used to copy data from the **Production.Product** table in the SQL Server **AdventureWorks** database to a similar table in the same database. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -55,7 +55,7 @@ Note that the source data does not have to be located on SQL Server; you can use . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -102,14 +102,14 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. ## Remarks If options include `UseInternalTransaction` and the `externalTransaction` argument is not null, an **InvalidArgumentException** is thrown. -For examples demonstrating how to use `SqlBulkCopy` in a transaction, see [Transaction and Bulk Copy Operations](/dotnet/framework/data/adonet/sql/transaction-and-bulk-copy-operations). +For examples demonstrating how to use `SqlBulkCopy` in a transaction, see [Transaction and Bulk Copy Operations](/sql/connect/ado-net/sql/transaction-bulk-copy-operations). ]]> Performing Bulk Copy Operations - - ADO.NET Overview + + Overview of the SqlClient driver @@ -152,7 +152,7 @@ In this example, the source data is first read from a SQL Server table to a or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -216,7 +216,7 @@ Then run the sample again without emptying the table. An exception is thrown and added because of primary key constraint violations. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -252,10 +252,10 @@ operations on the same instance use ## Examples The following console application demonstrates how to bulk load data in batches of 50 rows. For an example illustrating how -works with a transaction, see [Transaction and Bulk Copy Operations](/dotnet/framework/data/adonet/sql/transaction-and-bulk-copy-operations). +works with a transaction, see [Transaction and Bulk Copy Operations](/sql/connect/ado-net/sql/transaction-bulk-copy-operations). > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -284,7 +284,7 @@ In this example, the source data is first read from a SQL Server table to a or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -307,7 +307,7 @@ Note that open instances are closed The following example uses the same instance to add sales orders and their associated details to two destination tables. Because the **AdventureWorks** sales order tables are large, the sample reads only orders placed by a certain account number and bulk copies those orders and details to the destination tables. The method is used only after both bulk copy operations are complete. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.OrdersDetails#1](~/../sqlclient/doc/samples/SqlBulkCopy_OrdersDetails.cs#1)] ]]> @@ -335,8 +335,12 @@ The following example uses the same is `true`, reads from an object using , -optimizing memory usage by using the streaming capabilities. When it's set to false, the class loads all the data returned by the - object into memory before sending it to SQL Server or SQL Azure. +optimizing memory usage by using the streaming capabilities. Streaming is only applicable to max data types (i.e. +VARBINARY(MAX), VARCHAR(MAX), NVARCHAR(MAX), and XML). When is set to false, +the class loads all the data returned by the object into memory before sending it to the server. + +> [!NOTE] +> The main advantage of enabling streaming is reducing memory usage during bulk copy of max data types. ]]> @@ -406,7 +410,7 @@ In this example, the connection is first used to read data from a SQL Server tab be located on SQL Server; you can use any data source that can be read to an or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.Single#1](~/../sqlclient/doc/samples/SqlBulkCopy_Single.cs#1)] @@ -441,7 +445,7 @@ In this example, the connection is first used to read data from a SQL Server tab Note that the source data does not have to be located on SQL Server; you can use any data source that can be read to an or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -466,7 +470,7 @@ Note that the settings of ) or SqlConnection.Close () from this event. Doing this will cause an being thrown, and the object state will not change. If the user wants to cancel the operation from the event, the property of the can be used. -(See [Transaction and Bulk Copy Operations](/dotnet/framework/data/adonet/sql/transaction-and-bulk-copy-operations) for examples that use the +(See [Transaction and Bulk Copy Operations](/sql/connect/ado-net/sql/transaction-bulk-copy-operations) for examples that use the property.) No action, such as transaction activity, is supported in the connection during the execution of the bulk copy operation, and it is recommended that you not use the same connection used @@ -481,7 +485,7 @@ In this example, the connection is first used to read data from a SQL Server tab SQL Server; you can use any data source that can be read to an or loaded to a . > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -504,7 +508,7 @@ and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. ## Remarks This value is incremented during the event and does not imply that this number of rows has been sent to the server or committed. -During the execution of a bulk copy operation, this value can be accessed, but it cannot be changed. Any attempt to change it will throw an . +This value can be accessed during or after the execution of a bulk copy operation. ]]> @@ -588,7 +592,7 @@ The collection maps f The following console application demonstrates how to bulk load data from a . The destination table is a table in the **AdventureWorks** database. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -632,7 +636,7 @@ The following Console application demonstrates how to bulk load data from a is created at run time and is the source of the `SqlBulkCopy` operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.DataTable#1](~/../sqlclient/doc/samples/SqlBulkCopy_DataTable.cs#1)] @@ -646,8 +650,8 @@ This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. Performing Bulk Copy Operations - - ADO.NET Overview + + Overview of the SqlClient driver @@ -692,7 +696,7 @@ In this example, a is created at run time and three The method is called with a `DataRowState.Unchanged` `rowState` argument, so only the two unchanged rows are bulk copied to the destination. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.DataRowState#1](~/../sqlclient/doc/samples/SqlBulkCopy_DataRowState.cs#1)] ]]> @@ -720,7 +724,7 @@ The following console application demonstrates how to bulk load data from a is created at run time. A single row is selected from the to copy to the destination table. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.RowArray#1](~/../sqlclient/doc/samples/SqlBulkCopy_RowArray.cs#1)] @@ -755,7 +759,7 @@ In this example, a is created at run time. A single @@ -780,8 +784,6 @@ For more information about asynchronous programming in the .NET Framework Data P Returned in the task object, the object is closed before method execution. - - is specified in the connection string. A @@ -822,7 +824,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -850,9 +852,6 @@ For more information about asynchronous programming in the .NET Framework Data P object is closed before method execution. - - is specified in the connection string. - A did not specify a valid destination column name. @@ -883,7 +882,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -921,8 +920,6 @@ For more information about asynchronous programming in the .NET Framework Data P 's associated connection was closed before the completed returned. - - is specified in the connection string. A @@ -987,7 +984,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1026,8 +1023,6 @@ For more information about asynchronous programming in the .NET Framework Data P 's associated connection was closed before the completed returned. - - is specified in the connection string. A @@ -1068,7 +1063,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1106,8 +1101,6 @@ For more information about asynchronous programming in the .NET Framework Data P 's associated connection was closed before the completed returned. - - is specified in the connection string. A @@ -1140,7 +1133,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1167,8 +1160,6 @@ For more information about asynchronous programming in the .NET Framework Data P Returned in the task object, the object is closed before method execution. - - is specified in the connection string. A @@ -1209,7 +1200,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1236,8 +1227,6 @@ For more information about asynchronous programming in the .NET Framework Data P Returned in the task object, the object is closed before method execution. - - is specified in the connection string. A @@ -1271,7 +1260,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1298,8 +1287,6 @@ For more information about asynchronous programming in the .NET Framework Data P Returned in the task object, the object is closed before method execution. - - is specified in the connection string. A @@ -1345,7 +1332,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -1372,8 +1359,6 @@ For more information about asynchronous programming in the .NET Framework Data P Returned in the task object, the object is closed before method execution. - - is specified in the connection string. A diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml index 51925e7dfa..f8d258ff32 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMapping.xml @@ -26,7 +26,7 @@ destination matches the number of columns in the source, and each destination co objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -54,7 +54,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -81,7 +81,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy based on the ordinal positions of the columns. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -108,7 +108,7 @@ the destination matches the number of columns in the source, the column names an column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -135,7 +135,7 @@ the destination matches the number of columns in the source, the column names an a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). This code is +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -162,7 +162,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -192,7 +192,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -222,7 +222,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -252,7 +252,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -282,7 +282,7 @@ matches the number of columns in the source, the column names and ordinal positi bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml index 145e8407a0..5c8ddd2ef3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnMappingCollection.xml @@ -23,7 +23,7 @@ Although the number of columns in the destination matches the number of columns object to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -45,7 +45,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -73,7 +73,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy using the ordinal position of the source and destination columns. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -100,7 +100,7 @@ destination matches the number of columns in the source, the column names and or column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -127,7 +127,7 @@ Although the number of columns in the destination matches the number of columns objects are used to create a column map for the bulk copy. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -154,7 +154,7 @@ Although the number of columns in the destination matches the number of columns object by specifying the column names. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -182,7 +182,7 @@ Although not strictly necessary in this example (because the ordinal positions o The method must be used after the first bulk copy is performed and before the next bulk copy's column mappings are defined. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -249,7 +249,7 @@ Both bulk copies include a mapping for the **SalesOrderID**, so rather than clea mapping and then adds the appropriate mappings for the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -281,7 +281,7 @@ Both bulk copies include a mapping for the **SalesOrderID**, so rather than clea **SalesOrderID** mapping and then adds the appropriate mappings for the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index ff7a7130f4..d3e846546c 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -31,7 +31,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -58,7 +58,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -88,7 +88,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -118,7 +118,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 662ad0e652..1f6912efa5 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -29,7 +29,7 @@ The following example bulk copies data from a source table in the **AdventureWor object to specify order hints for the bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -51,7 +51,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is used to define the sort order for the **ProductNumber** destination column. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -78,7 +78,7 @@ The following example bulk copies data from a source table in the **AdventureWor A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -107,7 +107,7 @@ The example defines a column order hint for each bulk copy operation. The method must be used after the first bulk copy is performed and before the next bulk copy's order hint is defined. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -176,7 +176,7 @@ The following example performs two bulk copy operations. The first operation cop The example defines a column order hint for the **OrderDate** column in the first bulk copy operation. The hint is removed before the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. @@ -208,7 +208,7 @@ The following example performs two bulk copy operations. The first operation cop The example defines a column order hint for the **OrderDate** column in the first bulk copy operation. The hint is removed before the second bulk copy operation. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml index f1433ee4ff..152fcbde42 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyOptions.xml @@ -18,7 +18,7 @@ Next, run the sample again without emptying the table. An exception is thrown, a primary key violations. > [!IMPORTANT] -> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/dotnet/framework/data/adonet/sql/bulk-copy-example-setup). +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](/sql/connect/ado-net/sql/bulk-copy-example-setup). This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml index e013016ee9..01d30ee8d3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml @@ -60,5 +60,9 @@ A constant for use with the **GetSchema** method that represents the **ColumnSetColumns** collection. To be added. + + A constant for use with the **GetSchema** method that represents the **StructuredTypeMembers** collection. + To be added. + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCertificateStoreProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCertificateStoreProvider.xml index 6c70855915..697e9b8539 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCertificateStoreProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCertificateStoreProvider.xml @@ -26,7 +26,7 @@ The master key path. The encryption algorithm. Currently, the only valid value is: RSA_OAEP - The encrypted column encryption key. + The plaintext column encryption key. Encrypts a column encryption key using the certificate with the specified key path and using the specified algorithm. The format of the key path should be "Local Machine/My/<certificate_thumbprint>" or "Current User/My/<certificate_thumbprint>". diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCngProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCngProvider.xml index 3c2f528166..30d0640781 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCngProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCngProvider.xml @@ -30,7 +30,7 @@ The master key path. The encryption algorithm. - The encrypted column encryption key. + The plaintext column encryption key. Encrypts the given plain text column encryption key using an asymmetric key specified by the key path and the specified algorithm. The key path will be in the format of [ProviderName]/KeyIdentifier and should be an asymmetric key stored in the specified CNG key store provider. The valid algorithm used to encrypt/decrypt the CEK is 'RSA_OAEP'. The encrypted column encryption key. To be added. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCspProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCspProvider.xml index 26b62bb5f8..9e360781be 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCspProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionCspProvider.xml @@ -28,7 +28,7 @@ Enables storing Always Encrypted column master key keys in a store, such as a ha The master key path. The encryption algorithm. - The encrypted column encryption key. + The plaintext column encryption key. Encrypts the given plain text column encryption key using an asymmetric key specified by the key path and the specified algorithm. The key path will be in the format of [ProviderName]/KeyIdentifier and should be an asymmetric key stored in the specified CSP provider. The valid algorithm used to encrypt/decrypt the CEK is 'RSA_OAEP'. The encrypted column encryption key. To be added. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml index 02cadd325f..7c2dc715fd 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlColumnEncryptionKeyStoreProvider.xml @@ -1,9 +1,12 @@ - Base class for all key store providers. A custom provider must derive from this class and override its member functions and then register it using SqlConnection.RegisterColumnEncryptionKeyStoreProviders(). For details see, Always Encrypted. + Base class for all key store providers. A custom provider must derive from this class and override its member functions and then register it using + , + or + . + For details see, Always Encrypted. - To be added. Initializes a new instance of the SqlColumnEncryptionKeyStoreProviderClass. @@ -21,7 +24,7 @@ The master key path. The encryption algorithm. - The encrypted column encryption key. + The plaintext column encryption key. Encrypts a column encryption key using the column master key with the specified key path and using the specified algorithm. Returns . The encrypted column encryption key. To be added. @@ -55,5 +58,17 @@ The When implemented in a derived class, the method is expected to return true if the specified signature is valid, or false if the specified signature is not valid. The default implementation throws NotImplementedException. To be added. + + Gets or sets the lifespan of the decrypted column encryption key in the cache. Once the timespan has elapsed, the decrypted column encryption key is discarded and must be revalidated. + + . Any caching implementation should reference the value of this property before caching a column encryption key and not cache it if the value is zero. This will avoid duplicate caching and possible user confusion when they are trying to configure key caching. + ]]> + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 4dbe1f6511..6aac0a9d42 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -254,7 +254,7 @@ The following console application creates updates data within the **AdventureWor was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -281,19 +281,18 @@ The following console application creates updates data within the **AdventureWor -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. + An error occurred in a @@ -301,7 +300,7 @@ The following console application creates updates data within the **AdventureWor or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -310,7 +309,7 @@ The following console application creates updates data within the **AdventureWor or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -373,7 +372,7 @@ To set up this example, create a new Windows application. Put a was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -400,19 +399,18 @@ To set up this example, create a new Windows application. Put a - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. + @@ -457,7 +455,7 @@ The following console application starts the process of retrieving a data reader was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -484,19 +482,18 @@ The following console application starts the process of retrieving a data reader -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. + An error occurred in a @@ -504,7 +501,7 @@ The following console application starts the process of retrieving a data reader or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -513,7 +510,7 @@ The following console application starts the process of retrieving a data reader or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -570,7 +567,7 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -597,19 +594,18 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. + An error occurred in a @@ -617,7 +613,7 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -626,7 +622,7 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -694,7 +690,7 @@ To set up this example, create a new Windows application. Put a was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -721,19 +717,18 @@ To set up this example, create a new Windows application. Put a - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. + An error occurred in a @@ -741,7 +736,7 @@ To set up this example, create a new Windows application. Put a or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -750,7 +745,7 @@ To set up this example, create a new Windows application. Put a or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -829,7 +824,7 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -856,19 +851,18 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. + An error occurred in a @@ -876,7 +870,7 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -885,7 +879,7 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -943,7 +937,7 @@ The following console application starts the process of retrieving XML data asyn was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -970,18 +964,17 @@ The following console application starts the process of retrieving XML data asyn -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. An error occurred in a @@ -990,7 +983,7 @@ The following console application starts the process of retrieving XML data asyn or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -999,7 +992,7 @@ The following console application starts the process of retrieving XML data asyn or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1077,7 +1070,7 @@ To set up this example, create a new Windows application. Put a was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1104,19 +1097,18 @@ To set up this example, create a new Windows application. Put a - The name/value pair "Asynchronous Processing=true" was not included within the connection string defining the connection for this - - . - - -or- - The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). + + - or - + + + is set to true and a parameter with direction Output or InputOutput has been added to the collection. + An error occurred in a @@ -1124,7 +1116,7 @@ To set up this example, create a new Windows application. Put a or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1133,7 +1125,7 @@ To set up this example, create a new Windows application. Put a or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1177,14 +1169,11 @@ The following example demonstrates the use of the - Gets or sets the column encryption setting for this command. + Gets the column encryption setting for this command. The column encryption setting for this command. - - To be added. - @@ -1205,7 +1194,7 @@ The Microsoft .NET Framework Data Provider for SQL Server does not support the q SELECT * FROM dbo.Customers WHERE CustomerID = @CustomerID ``` -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ## Examples The following example creates a and sets some of its properties. @@ -1230,8 +1219,6 @@ A value of 0 indicates no limit (an attempt to execute a command will wait indef > [!NOTE] > The property will be ignored during old-style asynchronous method calls such as . It will be honored by the newer async methods such as . - has no effect when the command is executed against a context connection (a opened with "context connection=true" in the connection string). - > [!NOTE] > This property is the cumulative time-out (for all network packets that are read during the invocation of a method) for all network reads during command execution or processing of the results. A time-out can still occur after the first row is returned, and does not include user processing time, only network read time. @@ -1265,7 +1252,7 @@ The Microsoft .NET Framework Data Provider for SQL Server does not support the q SELECT * FROM Customers WHERE CustomerID = @CustomerID -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ## Examples The following example creates a and sets some of its properties. @@ -1396,6 +1383,33 @@ The method is a st To be added. + + + Gets or sets a value indicating whether the command object should optimize parameter performance by disabling Output and InputOutput directions when submitting the command to the SQL Server. + + + A value indicating whether the command object should optimize parameter performance by disabling Output and InputOuput parameter directions when submitting the command to the SQL Server. + The default is . + + + + [!NOTE] +If the option is enabled and a parameter with Direction Output or InputOutput is present in the Parameters collection an InvalidOperationException will be thrown when the command is executed. + +]]> + + + The @@ -1614,7 +1628,7 @@ The following example creates a and t was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1641,7 +1655,7 @@ The following example creates a and t -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1650,12 +1664,12 @@ The following example creates a and t or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1664,7 +1678,7 @@ The following example creates a and t or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1683,7 +1697,7 @@ The following example creates a and t @@ -1693,7 +1707,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1724,18 +1738,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1744,7 +1754,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1753,7 +1763,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1798,7 +1808,7 @@ The following example creates a , and was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1825,7 +1835,7 @@ The following example creates a , and -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The current state of the connection is closed. @@ -1838,7 +1848,7 @@ The following example creates a , and The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1847,7 +1857,7 @@ The following example creates a , and or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1856,7 +1866,7 @@ The following example creates a , and or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1907,7 +1917,7 @@ The following example creates a , and was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -1930,7 +1940,7 @@ The following example creates a , and . - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -1939,12 +1949,12 @@ The following example creates a , and or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -1953,7 +1963,7 @@ The following example creates a , and or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -1975,7 +1985,7 @@ The following example creates a , and @@ -1985,7 +1995,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2021,18 +2031,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2041,7 +2047,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2050,7 +2056,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2079,7 +2085,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -2089,7 +2095,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2123,18 +2129,14 @@ For more information about asynchronous programming in the .NET Framework Data P -or- - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2143,7 +2145,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2152,7 +2154,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2179,7 +2181,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -2190,7 +2192,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2226,18 +2228,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2246,7 +2244,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2255,7 +2253,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2287,7 +2285,7 @@ For more information about asynchronous programming in the .NET Framework Data P @@ -2297,7 +2295,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2333,18 +2331,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2353,7 +2347,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2362,7 +2356,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2400,7 +2394,7 @@ The following example creates a and t was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2427,12 +2421,12 @@ The following example creates a and t -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2441,7 +2435,7 @@ The following example creates a and t or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2450,7 +2444,7 @@ The following example creates a and t or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2471,7 +2465,7 @@ The following example creates a and t @@ -2481,7 +2475,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2512,18 +2506,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2532,7 +2522,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2541,7 +2531,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2591,7 +2581,7 @@ The following example creates a and t was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2618,12 +2608,12 @@ The following example creates a and t -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2632,7 +2622,7 @@ The following example creates a and t or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2641,7 +2631,7 @@ The following example creates a and t or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2666,7 +2656,7 @@ The following example creates a and t ## Remarks The **XmlReader** returned by this method does not support asynchronous operations. -For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). +For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> @@ -2676,7 +2666,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2707,18 +2697,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2727,7 +2713,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2736,7 +2722,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2764,7 +2750,7 @@ For more information about asynchronous programming in the .NET Framework Data P ## Remarks The **XmlReader** returned by this method does not support asynchronous operations. -For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). +For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> @@ -2774,7 +2760,7 @@ For more information about asynchronous programming in the .NET Framework Data P was set to - . For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + . For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). -or- @@ -2805,18 +2791,14 @@ For more information about asynchronous programming in the .NET Framework Data P The - closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). - - -or- - - is specified in the connection string. + closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). SQL Server returned an error while executing the command text. -or- - A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + A timeout occurred during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). An error occurred in a @@ -2825,7 +2807,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). The @@ -2834,7 +2816,7 @@ For more information about asynchronous programming in the .NET Framework Data P or - object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + object was closed during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -2854,6 +2836,105 @@ You must set the value for this property before the command is executed for it t ]]> + + Dictionary of custom column encryption key providers + Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the or + methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + + A null dictionary was provided. + + -or- + + A string key in the dictionary was null or empty. + + -or- + + A value in the dictionary was null. + + + A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. + + + + + + + + Gets or sets a value that specifies the + + object bound to this command. + + + When set to null (default), the default non-retriable provider will be used. + + + type. +2. Create a by using one of the following static methods of the class: + - + - + - + - +3. Assign the object to the `RetryLogicProvider` property. + +> [!NOTE] +> Detecting retriable exceptions is a vital part of the retry pattern. Before applying retry logic, it is important to investigate exceptions and choose a retry provider that best fits your scenario. First, log your exceptions and find transient faults. + +> [!NOTE] +> The command **timeout** restarts for each execution of a command within the retry logic and after applying the retry time delay. There is no timing overlap between these two actions. + +> [!NOTE] +> The default retry logic provider is not enabled unless it is configured in an application configuration file. For more information, see [Configurable retry logic configuration file](/sql/connect/ado-net/configurable-retry-logic-config-file-sqlclient). + +> [!CAUTION] +> A command with isn't compatible with the built-in retry logic. The underlying connection is immediately closed after the first execution attempt and is no longer available for subsequent retries. + +## Example +The following sample creates a database and establishes an active connection to it. While the database has an active connection, it tries to drop it with a new and a that uses a . You should kill the active connection through the database to unblock the second command before exceeding the number of retries. +The blocking connection simulates a situation like a command still running in the database and unlikely to finish. + +[!code-csharp[SqlConfigurableRetryLogic_SqlCommand#1](~/../sqlclient/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs#1)] + +### How to use with synchronous commands + +[!code-csharp[SqlConfigurableRetryLogic_SqlCommand#2](~/../sqlclient/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs#2)] + +### How to use with asynchoronous commands + +[!code-csharp[SqlConfigurableRetryLogic_SqlCommand#3](~/../sqlclient/doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs#3)] + +### 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)] + +> [!NOTE] +> The Asynchronous Programming Model (APM) is a legacy pattern that uses a pair of methods starting with `Begin` and `End`, and an interface called `IAsyncResult`. It's not recommended to use this pattern in new applications. These methods are for backwards compatibility. + +]]> + + Gets or sets a value indicating whether the application should automatically receive query notifications from a common @@ -2898,7 +2979,7 @@ SELECT * FROM Customers WHERE CustomerID = @CustomerID > [!NOTE] > If the parameters in the collection do not match the requirements of the query to be executed, an error may result. -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ## Examples The following example demonstrates how to create a and add parameters to the . @@ -3001,7 +3082,7 @@ You cannot set the pro ## Remarks The default value is **Both** unless the command is automatically generated (as in the case of the ), in which case the default is **None**. -For more information about using the **UpdatedRowSource** property, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters). +For more information about using the **UpdatedRowSource** property, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters). ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml index e242e17c2c..87ddd2c8b8 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommandBuilder.xml @@ -142,7 +142,7 @@ When you create a new instance of with arbitrary Transact-SQL statements, such as a parameterized SELECT statement. -For more information, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). +For more information, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). ]]> @@ -165,7 +165,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -183,7 +183,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -210,7 +210,7 @@ The default behavior, when generating parameter names, is to use `@p1`, `@p2`, a - A returned from the **GetSchema** method call and found in the collection is specified. -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -227,7 +227,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -245,7 +245,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -272,7 +272,7 @@ The default behavior, when generating parameter names, is to use `@p1`, `@p2`, a - A returned from the **GetSchema** method call and found in the collection is specified. -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -313,7 +313,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -331,7 +331,7 @@ You can also use if it changes the statement in any way. Otherwise, the will still be using information from the previous statement, which might not be correct. The Transact-SQL statements are first generated when the application calls either or . -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -358,7 +358,7 @@ The default behavior, when generating parameter names, is to use `@p1`, `@p2`, a - A returned from the **GetSchema** method call and found in the collection is specified. -For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). +For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). ]]> @@ -431,9 +431,9 @@ Generally, database servers indicate the schema for a identifier by separating t Given a quoted identifier, returns the correct unquoted form of that identifier. This includes correctly unescaping any embedded quotes in the identifier. The unquoted identifier, with embedded quotes properly unescaped. To be added. - Connecting and Retrieving Data in ADO.NET - Using the .NET Framework Data Provider for SQL Server - ADO.NET Overview + Connecting and Retrieving Data in ADO.NET + Using the .NET Framework Data Provider for SQL Server + Overview of the SqlClient driver diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml new file mode 100644 index 0000000000..cbeb9de0b1 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConfigurableRetryFactory.xml @@ -0,0 +1,116 @@ + + + + + Provides different retry logic providers with a common list of transient errors. + + + + + + + An object of containing the configuration for the object. + Provides an exponential time interval retry logic provider. + A object. + + [!NOTE] +> The inner enumerator includes randomization to prevent multiple instances of the client from performing subsequent retry attempts at the same time. + +]]> + + If the `retryLogicOption` parameter was null. + If at least one of the following conditions occurs: +- `NumberOfTries` is less than 1 or bigger than 60. +- `DeltaTime` is bigger than 120 seconds. +- `MinTimeInterval` is bigger than 120 seconds. +- `MaxTimeInterval` is bigger than 120 seconds. +- `MinTimeInterval` is not less than `MaxTimeInterval`. + + + + An object of containing the configuration for the object. + Provides an incremental time interval retry logic provider. + A object. + + [!NOTE] +> The inner enumerator includes randomization to prevent multiple instances of the client from performing subsequent retry attempts at the same time. + +]]> + + If the `retryLogicOption` parameter was null. + If at least one of the following conditions occurs: +- `NumberOfTries` is less than 1 or bigger than 60. +- `DeltaTime` is bigger than 120 seconds. +- `MinTimeInterval` is bigger than 120 seconds. +- `MaxTimeInterval` is bigger than 120 seconds. +- `MinTimeInterval` is not less than `MaxTimeInterval`. + + + + An object of containing the configuration for the object. + Provides a fixed interval time retry logic provider. + A object. + + [!NOTE] +> The inner enumerator includes randomization to prevent multiple instances of the client from performing subsequent retry attempts at the same time. + +]]> + + If the `retryLogicOption` parameter was null. + If at least one of the following conditions occurs: +- `NumberOfTries` is less than 1 or bigger than 60. +- `DeltaTime` is bigger than 120 seconds. +- `MinTimeInterval` is bigger than 120 seconds. +- `MaxTimeInterval` is bigger than 120 seconds. +- `MinTimeInterval` is not less than `MaxTimeInterval`. + + + + Provides a non-retriable provider with a that returns . + A object. + + [!NOTE] +> The returned provider of this function performs a single execution without any retry logic. + +]]> + + + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index 24aac534e4..bfc53dddac 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -16,7 +16,7 @@ If the goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling `Close` or `Dispose`. `Close` and `Dispose` are functionally equivalent. If the connection pooling value `Pooling` is set to `true` or `yes`, the underlying connection is returned back to the connection pool. On the other hand, if `Pooling` is set to `false` or `no`, the underlying connection to the server is actually closed. > [!NOTE] -> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). +> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). To ensure that connections are always closed, open the connection inside of a `using` block, as shown in the following code fragment. Doing so ensures that the connection is automatically closed when the code exits the block. @@ -37,13 +37,13 @@ using (SqlConnection connection = new SqlConnection(connectionString)) ``` > [!NOTE] -> To deploy high-performance applications, you must use connection pooling. When you use the .NET Framework Data Provider for SQL Server, you do not have to enable connection pooling because the provider manages this automatically, although you can modify some settings. For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). +> To deploy high-performance applications, you must use connection pooling. When you use the .NET Framework Data Provider for SQL Server, you do not have to enable connection pooling because the provider manages this automatically, although you can modify some settings. For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). If a is generated by the method executing a , the remains open when the severity level is 19 or less. When the severity level is 20 or greater, the server ordinarily closes the . However, the user can reopen the connection and continue. An application that creates an instance of the object can require all direct and indirect callers to have sufficient permission to the code by setting declarative or imperative security demands. makes security demands using the object. Users can verify that their code has sufficient permissions by using the object. Users and administrators can also use the [Caspol.exe (Code Access Security Policy Tool)](/dotnet/framework/tools/caspol-exe-code-access-security-policy-tool) to modify security policy at the machine, user, and enterprise levels. For more information, see [Security in .NET](/dotnet/standard/security/). For an example demonstrating how to use security demands, see [Code Access Security and ADO.NET](/dotnet/framework/data/adonet/code-access-security). - For more information about handling warning and informational messages from the server, see [Connection Events](/dotnet/framework/data/adonet/connection-events). For more information about SQL Server engine errors and error messages, see [Database Engine Events and Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). + For more information about handling warning and informational messages from the server, see [Connection Events](/sql/connect/ado-net/connection-events). For more information about SQL Server engine errors and error messages, see [Database Engine Events and Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). > [!CAUTION] > You can force TCP instead of shared memory. You can do that by prefixing tcp: to the server name in the connection string or you can use localhost. @@ -362,9 +362,6 @@ End Module The connection string contains any combination of , , or . --or- - -The connection string contains . -or- @@ -432,7 +429,7 @@ The connection string contains . If the goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling `Close` or `Dispose`. `Close` and `Dispose` are functionally equivalent. If the connection pooling value `Pooling` is set to `true` or `yes`, the underlying connection is returned back to the connection pool. On the other hand, if `Pooling` is set to `false` or `no`, the underlying connection to the server is closed. > [!NOTE] -> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). +> Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool, because the connection is not actually closed when it is returned to the connection pool. For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). > [!CAUTION] > Do not call `Close` or `Dispose` on a Connection, a DataReader, or any other managed object in the `Finalize` method of your class. In a finalizer, you should only release unmanaged resources that your class owns directly. If your class does not own any unmanaged resources, do not include a `Finalize` method in your class definition. For more information, see [Garbage Collection](/dotnet/standard/garbage-collection/). @@ -472,23 +469,23 @@ The connection string contains . The list of trusted master key paths for the column encryption. To be added. - - - Gets the default wait time (in seconds) before terminating the attempt to execute a command and generating an error. The default is 30 seconds. - - - The time in seconds to wait for the command to execute. The default is 30 seconds. - - - + + Gets the default wait time (in seconds) before terminating the attempt to execute a command and generating an error. The default is 30 seconds. + + + The time in seconds to wait for the command to execute. The default is 30 seconds. + + + - - + + Gets or sets the string used to open a SQL Server database. The connection string that includes the source database name, and other parameters needed to establish the initial connection. The default value is an empty string. @@ -504,7 +501,7 @@ The connection string contains . "Persist Security Info=False;Integrated Security=true;Initial Catalog=Northwind;server=(local)" ``` - Use the new to construct valid connection strings at run time. For more information, see [Connection String Builders](/dotnet/framework/data/adonet/connection-string-builders). + Use the new to construct valid connection strings at run time. For more information, see [Connection String Builders](/sql/connect/ado-net/connection-string-builders). The property can be set only when the connection is closed. Many of the connection string values have corresponding read-only properties. When the connection string is set, these properties are updated, except when an error is detected. In this case, none of the properties are updated. properties return only those settings that are contained in the . @@ -527,47 +524,50 @@ The connection string contains . |Addr|N/A|Synonym of **Data Source**.| |Address|N/A|Synonym of **Data Source**.| |App|N/A|Synonym of **Application Name**.| -|Application Name|N/A|The name of the application, or '.NET SQLClient Data Provider' if no application name is provided.

An application name can be 128 characters or less.| -|Application Intent

-or-

ApplicationIntent|ReadWrite|Declares the application workload type when connecting to a server. Possible values are `ReadOnly` and `ReadWrite`. For example:

`ApplicationIntent=ReadOnly`

For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery).| -|Asynchronous Processing

-or-

Async|'false'|When `true`, enables asynchronous operation support. Recognized values are `true`, `false`, `yes`, and `no`.

This property is ignored beginning in .NET Framework 4.5. For more information about SqlClient support for asynchronous programming, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming).| -|Attestation Protocol|N/A|Gets or sets the value of Attestation Protocol.

Valid values are:
`AAS`
`HGS`| +|Application Intent

-or-

ApplicationIntent|ReadWrite|Declares the application workload type when connecting to a server. Possible values are `ReadOnly` and `ReadWrite`. For example:

`ApplicationIntent=ReadOnly`

For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery).| +|Application Name|N/A|The name of the application. If no application name is provided, 'Framework Microsoft SqlClient Data Provider' when running on .NET Framework and 'Core Microsoft SqlClient Data Provider' otherwise.

An application name can be 128 characters or less.| |AttachDBFilename

-or-

Extended Properties

-or-

Initial File Name|N/A|The name of the primary database file, including the full path name of an attachable database. AttachDBFilename is only supported for primary data files with an .mdf extension.

If the value of the AttachDBFileName key is specified in the connection string, the database is attached and becomes the default database for the connection.

If this key is not specified and if the database was previously attached, the database will not be reattached. The previously attached database will be used as the default database for the connection.

If this key is specified together with the AttachDBFileName key, the value of this key will be used as the alias. However, if the name is already used in another attached database, the connection will fail.

The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. **Note:** Remote server, HTTP, and UNC path names are not supported.

The database name must be specified with the keyword 'database' (or one of its aliases) as in the following:

"AttachDbFileName=|DataDirectory|\data\YourDB.mdf;integrated security=true;database=YourDatabase"

An error will be generated if a log file exists in the same directory as the data file and the 'database' keyword is used when attaching the primary data file. In this case, remove the log file. Once the database is attached, a new log file will be automatically generated based on the physical path.| -|Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Sql Password`. Currently `Active Directory Integrated` and `Active Directory Interactive` modes of authentication are supported only for .NET Framework. | -|Column Encryption Setting|N/A|Enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-2017) functionality for the connection.| +|Attestation Protocol|NotSpecified|Gets or sets the value of Attestation Protocol.

When no value is specified, secure enclaves are disabled on the connection.

Valid values are:
`AAS`
`HGS`
`None` (Only valid in v3.1 and v4.1+))| +|Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Active Directory Service Principal`, `Active Directory Device Code Flow`, `Active Directory Managed Identity`, `Active Directory MSI`, `Active Directory Default`, `Sql Password`.

For additional information see [Using Azure Active Directory authentication with SqlClient](https://docs.microsoft.com/sql/connect/ado-net/sql/azure-active-directory-authentication?view=sql-server-ver15).| +|Column Encryption Setting|disabled|Enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine) functionality for the connection. Supported values are: `enabled` and `disabled`| |Command Timeout|30|The default wait time (in seconds) before terminating the attempt to execute a command and generating an error.

Valid values are greater than or equal to 0 and less than or equal to 2147483647.| +|Connect Retry Count

-or-

ConnectRetryCount|1|Controls the number of reconnection attempts after the client identifies an idle connection failure. Valid values are 0 to 255. The default is 1. 0 means do not attempt to reconnect (disable connection resiliency).

For additional information about idle connection resiliency, see[.NET SqlConnection parameters for connection retry](https://learn.microsoft.com/azure/azure-sql/database/troubleshoot-common-connectivity-issues?view=azuresql#net-sqlconnection-parameters-for-connection-retry) and [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).| +|Connect Retry Interval

-or-

ConnectRetryInterval|10|Specifies the time between each connection retry attempt (`ConnectRetryCount`). Valid values are 1 to 60 seconds (default=10), applied after the first reconnection attempt. When a broken connection is detected, the client immediately attempts to reconnect; this is the first reconnection attempt and only occurs if `ConnectRetryCount` is greater than 0. If the first reconnection attempt fails and `ConnectRetryCount` is greater than 1, the client waits `ConnectRetryInterval` to try the second and subsequent reconnection attempts.

For additional information about idle connection resiliency, see[.NET SqlConnection parameters for connection retry](https://learn.microsoft.com/azure/azure-sql/database/troubleshoot-common-connectivity-issues?view=azuresql#net-sqlconnection-parameters-for-connection-retry) and [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).| |Connect Timeout

-or-

Connection Timeout

-or-

Timeout|15|The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.

Valid values are greater than or equal to 0 and less than or equal to 2147483647.

When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds.| -|Connection Lifetime

-or-

Load Balance Timeout|0|When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by `Connection Lifetime`. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.

A value of zero (0) causes pooled connections to have the maximum connection timeout.| -|Connect Retry Count

-or-

ConnectRetryCount|1|Controls the number of reconnection attempts after the client identifies an idle connection failure. Valid values are 0 to 255. The default is 1. 0 means do not attempt to reconnect (disable connection resiliency).

For additional information about idle connection resiliency, see [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).| -|Connect Retry Interval

-or-

ConnectRetryInterval|10|Specifies the time between each connection retry attempt (ConnectRetryCount). Valid values are 1 to 60 seconds (default=10), applied after the first reconnection attempt. When a broken connection is detected, the client immediately attempts to reconnect; this is the first reconnection attempt and only occurs if ConnectRetryCount is greater than 0. If the first reconnection attempt fails and ConnectRetryCount is greater than 1, the client waits ConnectRetryInterval to try the second and subsequent reconnection attempts.

For additional information about idle connection resiliency, see [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).| -|Context Connection|'false'|`true` if an in-process connection to SQL Server should be made.| |Current Language

-or-

Language|N/A|Sets the language used for database server warning or error messages.

The language name can be 128 characters or less.| -|Data Source

-or-

Server

-or-

Address

-or-

Addr

-or-

Network Address|N/A|The name or network address of the instance of SQL Server to which to connect. The port number can be specified after the server name:

`server=tcp:servername, portnumber`

When specifying a local instance, always use (local). To force a protocol, add one of the following prefixes:

`np:(local), tcp:(local), lpc:(local)`

Beginning in .NET Framework 4.5, you can also connect to a LocalDB database as follows:

`server=(localdb)\\myInstance`

For more information about LocalDB, see [SqlClient Support for LocalDB](/dotnet/framework/data/adonet/sql/sqlclient-support-for-localdb).

**Data Source** must use the TCP format or the Named Pipes format.

TCP format is as follows:

- tcp:\\\
- tcp:\,\

The TCP format must start with the prefix "tcp:" and is followed by the database instance, as specified by a host name and an instance name. This format is not applicable when connecting to Azure SQL Database. TCP is automatically selected for connections to Azure SQL Database when no protocol is specified.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The instance name is used to resolve to a particular TCP/IP port number on which a database instance is hosted. Alternatively, specifying a TCP/IP port number directly is also allowed. If both instance name and port number are not present, the default database instance is used.

The Named Pipes format is as follows:

- np:\\\\\pipe\\

The Named Pipes format MUST start with the prefix "np:" and is followed by a named pipe name.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The pipe name is used to identify the database instance to which the .NET Framework application will be connected.

If the value of the **Network** key is specified, the prefixes "tcp:" and "np:" should not be specified. **Note:** You can force the use of TCP instead of shared memory, either by prefixing **tcp:** to the server name in the connection string, or by using **localhost**.| -|Enclave Attestation Url|N/A|Gets or sets the enclave attestation Url to be used with enclave based Always Encrypted.| -|Encrypt|'false'|When `true`, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/dotnet/framework/data/adonet/connection-string-syntax).

Beginning in .NET Framework 4.5, when `TrustServerCertificate` is false and `Encrypt` is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see [Accepted wildcards used by server certificates for server authentication](https://support.microsoft.com/kb/258858).| +|Data Source

-or-

Server

-or-

Address

-or-

Addr

-or-

Network Address|N/A|The name or network address of the instance of SQL Server to which to connect. The port number can be specified after the server name:

`server=tcp:servername, portnumber`

When specifying a local instance, always use (local). To force a protocol, add one of the following prefixes:

`np:(local), tcp:(local), lpc:(local)`

You can also connect to a LocalDB database as follows:

`server=(localdb)\\myInstance`

For more information about LocalDB, see [SqlClient Support for LocalDB](/sql/connect/ado-net/sql/sqlclient-support-localdb).

**Data Source** must use the TCP format or the Named Pipes format.

TCP format is as follows:

- tcp:\\\
- tcp:\,\

The TCP format must start with the prefix "tcp:" and is followed by the database instance, as specified by a host name and an instance name. This format is not applicable when connecting to Azure SQL Database. TCP is automatically selected for connections to Azure SQL Database when no protocol is specified.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The instance name is used to resolve to a particular TCP/IP port number on which a database instance is hosted. Alternatively, specifying a TCP/IP port number directly is also allowed. If both instance name and port number are not present, the default database instance is used.

The Named Pipes format is as follows:

- np:\\\\\pipe\\

The Named Pipes format MUST start with the prefix "np:" and is followed by a named pipe name.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The pipe name is used to identify the database instance to which the .NET application will connect.

If the value of the **Network** key is specified, the prefixes "tcp:" and "np:" should not be specified. **Note:** You can force the use of TCP instead of shared memory, either by prefixing **tcp:** to the server name in the connection string, or by using **localhost**.| +|Enclave Attestation Url|N/A|Gets or sets the enclave attestation URL to be used with enclave based Always Encrypted.| +|Encrypt|'true' in 4.0 and above

'false' in 3.x and below|Recognized values are:
versions 1 - 4: `true`/`yes` and `false`/`no`
versions 5+: `true`/`yes`/`mandatory`, `false`/`no`/`optional` and `strict`. When `true`, TLS encryption is used for all data sent between the client and server if the server has a certificate installed. When `strict`, TDS 8.0 TLS encryption is used and the `TrustServerCertificate` setting is ignored and treated as false. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).

When `Encrypt` is `mandatory` or `strict` and `TrustServerCertificate` is `false`, the server name (or IP address) in a server's certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. | |Enlist|'true'|`true` indicates that the SQL Server connection pooler automatically enlists the connection in the creation thread's current transaction context.| |Failover Partner|N/A|The name of the failover partner server where database mirroring is configured.

If the value of this key is "", then **Initial Catalog** must be present, and its value must not be "".

The server name can be 128 characters or less.

If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.| +|Failover Partner SPN

-or-

FailoverPartnerSPN|N/A|The SPN for the failover partner. The default value is an empty string, which causes SqlClient to use the default, driver-generated SPN.

(Only available in v5.0+)| +|Host Name In Certificate

-or-

HostNameInCertificate|N/A|The host name to use when validating the server certificate. When not specified, the server name from the Data Source is used for certificate validation.

(Only available in v5.0+)| +|Server Certificate

-or-

ServerCertificate|N/A|The path to a certificate file to match against the SQL Server TLS/SSL certificate. The accepted certificate formats are PEM, DER, and CER. If specified, the SQL Server certificate is checked by verifying if the ServerCertificate provided is an exact match.

(Only available in v5.1+)| |Initial Catalog

-or-

Database|N/A|The name of the database.

The database name can be 128 characters or less.| |Integrated Security

-or-

Trusted_Connection|'false'|When `false`, User ID and Password are specified in the connection. When `true`, the current Windows account credentials are used for authentication.

Recognized values are `true`, `false`, `yes`, `no`, and `sspi` (strongly recommended), which is equivalent to `true`.

If User ID and Password are specified and Integrated Security is set to true, the User ID and Password will be ignored and Integrated Security will be used.

is a more secure way to specify credentials for a connection that uses SQL Server Authentication (`Integrated Security=false`).| +|IP Address Preference

-or-

IPAddressPreference|IPv4First|The IP address family preference when establishing TCP connections. If `Transparent Network IP Resolution` (in .NET Framework) or `Multi Subnet Failover` is set to true, this setting has no effect. Supported values include:

`IPAddressPreference=IPv4First`

`IPAddressPreference=IPv6First`

`IPAddressPreference=UsePlatformDefault`| +|Load Balance Timeout

-or-

Connection Lifetime|0|When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by `Connection Lifetime`. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.

A value of zero (0) causes pooled connections to have the maximum connection timeout.| |Max Pool Size|100|The maximum number of connections that are allowed in the pool.

Valid values are greater than or equal to 1. Values that are less than **Min Pool Size** generate an error.| |Min Pool Size|0|The minimum number of connections that are allowed in the pool.

Valid values are greater than or equal to 0. Zero (0) in this field means no minimum connections are initially opened.

Values that are greater than **Max Pool Size** generate an error.| -|Multiple Active Result Sets

-or-

MultipleActiveResultSets|false|When `true`, an application can maintain multiple active result sets (MARS). When `false`, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

Recognized values are `true` and `false`.

For more information, see [Multiple Active Result Sets (MARS)](/dotnet/framework/data/adonet/sql/multiple-active-result-sets-mars).| -|Multi Subnet Failover

-or-

MultiSubnetFailover|false|Always specify `multiSubnetFailover=True` when connecting to the availability group listener of a SQL Server 2012 (or later) availability group or a SQL Server 2012 (or later) Failover Cluster Instance. `multiSubnetFailover=True` configures SqlClient to provide faster detection of and connection to the (currently) active server. Possible values are `Yes` and `No`, `True` and `False` or `1` and `0`. For example:

`MultiSubnetFailover=True`

The default is `False`. For more information about SqlClient's support for Always On AGs, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery).| +|Multiple Active Result Sets

-or-

MultipleActiveResultSets|false|When `true`, an application can maintain multiple active result sets (MARS). When `false`, an application must process or cancel all result sets from one batch before it can execute any other batch on that connection.

Recognized values are `true` and `false`.

For more information, see [Multiple Active Result Sets (MARS)](/sql/connect/ado-net/sql/multiple-active-result-sets-mars).| +|Multi Subnet Failover

-or-

MultiSubnetFailover|false|Always specify `multiSubnetFailover=True` when connecting to the availability group listener of a SQL Server 2012 (or later) availability group or a SQL Server 2012 (or later) Failover Cluster Instance. `multiSubnetFailover=True` configures SqlClient to provide faster detection of and connection to the (currently) active server. Possible values are `Yes` and `No`, `True` and `False` or `1` and `0`. For example:

`MultiSubnetFailover=True`

The default is `False`. For more information about SqlClient's support for Always On AGs, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery).| |Network Library

-or-

Network

-or-

Net|N/A|The network library used to establish a connection to an instance of SQL Server. Supported values include:

dbnmpntw (Named Pipes)

dbmsrpcn (Multiprotocol, Windows RPC)

dbmsadsn (Apple Talk)

dbmsgnet (VIA)

dbmslpcn (Shared Memory)

dbmsspxn (IPX/SPX)

dbmssocn (TCP/IP)

Dbmsvinn (Banyan Vines)

The corresponding network DLL must be installed on the system to which you connect. If you do not specify a network and you use a local server (for example, "." or "(local)"), shared memory is used. In this example, the network library is Win32 Winsock TCP/IP (dbmssocn), and 1433 is the port being used.

`Network Library=dbmssocn;Data Source=000.000.000.000,1433;`| |Packet Size|8000|Size in bytes of the network packets used to communicate with an instance of SQL Server.

The packet size can be greater than or equal to 512 and less than or equal to 32768.| |Password

-or-

PWD|N/A|The password for the SQL Server account logging on. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keyword instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The password must be 128 characters or less.| -|Persist Security Info

-or-

PersistSecurityInfo|'false'|When set to `false` or `no` (strongly recommended), security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state. Resetting the connection string resets all connection string values including the password. Recognized values are `true`, `false`, `yes`, and `no`.| +|Persist Security Info

-or-

PersistSecurityInfo|'false'|When set to `false` or `no` (strongly recommended), security-sensitive information, such as the password or access token, is not returned as part of the connection if the connection is open or has ever been in an open state. This property should only be set to `true` if your application has a specific need to read the password out of an already-opened database connection. The default value of `false` is the more secure setting; using `true` for this property opens your application to security risks such as accidentally logging or tracing the database password.

Resetting the connection string resets all connection string values including the password. Recognized values are `true`, `false`, `yes`, and `no`.| |Pool Blocking Period

-or-

PoolBlockingPeriod|Auto|Sets the blocking period behavior for a connection pool. See property for details.| |Pooling|'true'|When the value of this key is set to true, any newly created connection will be added to the pool when closed by the application. In a next attempt to open the same connection, that connection will be drawn from the pool.

Connections are considered the same if they have the same connection string. Different connections have different connection strings.

The value of this key can be "true", "false", "yes", or "no".| |Replication|'false'|`true` if replication is supported using the connection.| +|Server SPN

-or-

ServerSPN|N/A|The SPN for the data source. The default value is an empty string, which causes SqlClient to use the default, driver-generated SPN.

(Only available in v5.0+)| |Transaction Binding|Implicit Unbind|Controls connection association with an enlisted `System.Transactions` transaction.

Possible values are:

`Transaction Binding=Implicit Unbind;`

`Transaction Binding=Explicit Unbind;`

Implicit Unbind causes the connection to detach from the transaction when it ends. After detaching, additional requests on the connection are performed in autocommit mode. The `System.Transactions.Transaction.Current` property is not checked when executing requests while the transaction is active. After the transaction has ended, additional requests are performed in autocommit mode.

If the system ends the transaction (in the scope of a using block) before the last command completes, it will throw .

Explicit Unbind causes the connection to remain attached to the transaction until the connection is closed or an explicit `SqlConnection.TransactionEnlist(null)` is called. Beginning in .NET Framework 4.0, changes to Implicit Unbind make Explicit Unbind obsolete. An `InvalidOperationException` is thrown if `Transaction.Current` is not the enlisted transaction or if the enlisted transaction is not active.| |Transparent Network IP Resolution

-or-

TransparentNetworkIPResolution|See description.|When the value of this key is set to `true`, the application is required to retrieve all IP addresses for a particular DNS entry and attempt to connect with the first one in the list. If the connection is not established within 0.5 seconds, the application will try to connect to all others in parallel. When the first answers, the application will establish the connection with the respondent IP address.

If the `MultiSubnetFailover` key is set to `true`, `TransparentNetworkIPResolution` is ignored.

If the `Failover Partner` key is set, `TransparentNetworkIPResolution` is ignored.

The value of this key must be `true`, `false`, `yes`, or `no`.

A value of `yes` is treated the same as a value of `true`.

A value of `no` is treated the same as a value of `false`.

The default values are as follows:

  • `false` when:

    • Connecting to Azure SQL Database where the data source ends with:

      • .database.chinacloudapi.cn
      • .database.usgovcloudapi.net
      • .database.cloudapi.de
      • .database.windows.net
    • `Authentication` is 'Active Directory Password' or 'Active Directory Integrated'
  • `true` in all other cases.
| -|Trust Server Certificate

-or-

TrustServerCertificate|'false'|When set to `true`, SSL is used to encrypt the channel when bypassing walking the certificate chain to validate trust. If TrustServerCertificate is set to `true` and Encrypt is set to `false`, the channel is not encrypted. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/dotnet/framework/data/adonet/connection-string-syntax).| +|Trust Server Certificate

-or-

TrustServerCertificate|'false'|When set to `true`, TLS is used to encrypt the channel when bypassing walking the certificate chain to validate trust. If TrustServerCertificate is set to `true` and Encrypt is set to `false`, the channel is not encrypted. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).| |Type System Version|N/A|A string value that indicates the type system the application expects. The functionality available to a client application is dependent on the version of SQL Server and the compatibility level of the database. Explicitly setting the type system version that the client application was written for avoids potential problems that could cause an application to break if a different version of SQL Server is used. **Note:** The type system version cannot be set for common language runtime (CLR) code executing in-process in SQL Server. For more information, see [SQL Server Common Language Runtime Integration](/dotnet/framework/data/adonet/sql/sql-server-common-language-runtime-integration).

Possible values are:

`Type System Version=SQL Server 2012;`

`Type System Version=SQL Server 2008;`

`Type System Version=SQL Server 2005;`

`Type System Version=Latest;`

`Type System Version=SQL Server 2012;` specifies that the application will require version 11.0.0.0 of Microsoft.SqlServer.Types.dll. The other `Type System Version` settings will require version 10.0.0.0 of Microsoft.SqlServer.Types.dll.

`Latest` is obsolete and should not be used. `Latest` is equivalent to `Type System Version=SQL Server 2008;`.| -|User ID

-or-

UID

-or-|N/A|The SQL Server login account. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keywords instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The user ID must be 128 characters or less.| +|User ID

-or-

UID

-or-

User|N/A|The SQL Server login account. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keywords instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The user ID must be 128 characters or less.| |User Instance|'false'|A value that indicates whether to redirect the connection from the default SQL Server Express instance to a runtime-initiated instance running under the account of the caller.| |Workstation ID

-or-

WSID|The local computer name|The name of the workstation connecting to SQL Server.

The ID must be 128 characters or less.| - The following list contains the valid names for connection pooling values within the . For more information, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). + The following list contains the valid names for connection pooling values within the . For more information, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). - Connection Lifetime (or Load Balance Timeout) @@ -587,7 +587,7 @@ The connection string contains . > Universal data link (UDL) files are not supported for the .NET Framework Data Provider for SQL Server. > [!CAUTION] -> In this release, the application should use caution when constructing a connection string based on user input (for example when retrieving user ID and password information from a dialog box, and appending it to the connection string). The application should make sure that a user cannot embed additional connection string parameters in these values (for example, entering a password as "validpassword;database=somedb" in an attempt to attach to a different database). If you need to construct connection strings based on user input, use the new , which validates the connection string and helps to eliminate this problem. See [Connection String Builders](/dotnet/framework/data/adonet/connection-string-builders) for more information. +> In this release, the application should use caution when constructing a connection string based on user input (for example when retrieving user ID and password information from a dialog box, and appending it to the connection string). The application should make sure that a user cannot embed additional connection string parameters in these values (for example, entering a password as "validpassword;database=somedb" in an attempt to attach to a different database). If you need to construct connection strings based on user input, use the new , which validates the connection string and helps to eliminate this problem. See [Connection String Builders](/sql/connect/ado-net/connection-string-builders) for more information. @@ -652,8 +652,6 @@ The connection string contains . - If is set on an open connection. -- If is set when `Context Connection=true`. - - If is set when `Integrated Security = true`. - If is set when the connection string uses `Password`. @@ -692,14 +690,7 @@ The connection string contains . The name of the instance of SQL Server to which to connect. The default value is an empty string. [!NOTE] -> The property returns `null` if the connection string for the is "context connection=true". - - - + ## Examples The following example creates a and displays some of its read-only properties. @@ -725,7 +716,7 @@ The connection string contains . method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction** for this purpose. For more information, see [Distributed Transactions](/dotnet/framework/data/adonet/distributed-transactions). + You can use the method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction** for this purpose. For more information, see [Distributed Transactions](/sql/connect/ado-net/distributed-transactions). You can continue to enlist in an existing distributed transaction using the **EnlistDistributedTransaction** method if auto-enlistment is disabled. Enlisting in an existing distributed transaction makes sure that, if the transaction is committed or rolled back, modifications made by the code at the data source are also committed or rolled back. @@ -741,7 +732,7 @@ The connection string contains . method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction**, which uses a **System.EnterpriseServices.ITransaction** object. It also has slightly different semantics: once a connection is explicitly enlisted on a transaction, it cannot be unenlisted or enlisted in another transaction until the first transaction finishes. For more information about distributed transactions, see [Distributed Transactions](/dotnet/framework/data/adonet/distributed-transactions). + You can use the method to enlist in a distributed transaction. Because it enlists a connection in a instance, **EnlistTransaction** takes advantage of functionality available in the namespace for managing distributed transactions, making it preferable to **EnlistDistributedTransaction**, which uses a **System.EnterpriseServices.ITransaction** object. It also has slightly different semantics: once a connection is explicitly enlisted on a transaction, it cannot be unenlisted or enlisted in another transaction until the first transaction finishes. For more information about distributed transactions, see [Distributed Transactions](/sql/connect/ado-net/distributed-transactions). ]]> @@ -759,7 +750,7 @@ The connection string contains . > [!NOTE] > An error with a severity level of 17 or above that causes the server to stop processing the command needs to be handled as an exception. In this case, an exception is thrown regardless of how the error is handled in the event. - For more information on working with events, see [Connection Events](/dotnet/framework/data/adonet/connection-events). For more information on errors generated by the SQL Server engine, see [Database Engine Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). + For more information on working with events, see [Connection Events](/sql/connect/ado-net/connection-events). For more information on errors generated by the SQL Server engine, see [Database Engine Errors](/sql/relational-databases/errors-events/database-engine-events-and-errors). ]]> @@ -776,7 +767,7 @@ The connection string contains . - Returns schema information for the data source of this . For more information about scheme, see [SQL Server Schema Collections](/dotnet/framework/data/adonet/sql-server-schema-collections). + Returns schema information for the data source of this . For more information about scheme, see [SQL Server Schema Collections](/sql/connect/ado-net/sql-server-schema-collections). A that contains schema information. To be added. @@ -903,7 +894,7 @@ GO The event occurs when a message with a severity of 10 or less is returned by SQL Server. Messages that have a severity between 11 and 20 raise an error and messages that have a severity over 20 causes the connection to close. For more information on SQL Server error levels, see [Database Engine Error Severities](/sql/relational-databases/errors-events/database-engine-error-severities). - For more information and an example, see [Connection Events](/dotnet/framework/data/adonet/connection-events). + For more information and an example, see [Connection Events](/sql/connect/ado-net/connection-events). ]]> @@ -1001,14 +992,12 @@ GO A call to will attempt to cancel or close the corresponding call. - For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). + For more information about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> Calling more than once for the same instance before task completion. - is specified in the connection string. - A connection was not available from the connection pool before the connection time out elapsed. Any error returned by SQL Server that occurred while opening the connection. @@ -1036,8 +1025,12 @@ GO - Custom column encryption key provider dictionary - Registers the column encryption key store providers. This function should only be called once in an app. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + Dictionary of custom column encryption key store providers + + Registers the column encryption key store providers. This function should only be called once in an app. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + + The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. + + + A null dictionary was provided. + + -or- + + A string key in the dictionary was null or empty. + + -or- + + A value in the dictionary was null. + + + A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. + + + This function was called more than once. + + + Dictionary of custom column encryption key providers + Registers the encryption key store providers on the instance. If this function has been called, any providers registered using the static methods will be ignored. This function can be called more than once. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set. + + A null dictionary was provided. + + -or- + + A string key in the dictionary was null or empty. + + -or- + + A value in the dictionary was null. + + + A string key in the dictionary started with "MSSQL_". This prefix is reserved for system providers. + + + + + + + Gets or sets a value that specifies the + + object bound to this command. + + + When set to null (default), the default non-retriable provider will be applied. + + + type. +2. Create a by using one of the following static methods of the class: + - + - + - + - +3. Assign the object to the `RetryLogicProvider` property. + +> [!NOTE] +> Detecting retriable exceptions is a vital part of the retry pattern. Before applying retry logic, it is important to investigate exceptions and choose a retry provider that best fits your scenario. First, log your exceptions and find transient faults. + +> [!NOTE] +> The connection **timeout** restarts for each execution of a connection open. There is no timing overlap between these two actions. + +> [!NOTE] +> The default retry logic provider is not enabled unless it is configured in an application configuration file. For more information, see [Configurable retry logic and configuration file](/sql/connect/ado-net/configurable-retry-logic-config-file-sqlclient). + +## Example +The following sample tries to open a connection to an invalid database to simulate a condition that the database service is temporarily unavailable . You should manually create the database while the tries to establish the connection. + +[!code-csharp[SqlConfigurableRetryLogic_OpenConnection#1](~/../sqlclient/doc/samples/SqlConfigurableRetryLogic_OpenConnection.cs#1)] + +]]> + + If statistics gathering is enabled, all values are reset to zero. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml index d5f32ef2d3..db98ad4fdf 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml @@ -13,10 +13,10 @@ Attestation portocol for Azure Attestation Service 1 - - Attestation protocol for Simulator + + Attestation protocol for no attestation. Only compatible with Virtualization-based security (VBS) enclaves. An Enclave Attestation Url is not required when using this protocol. 2 - + Attestation protocol for Host Guardian Service 3 diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml new file mode 100644 index 0000000000..35b8d24ab6 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml @@ -0,0 +1,75 @@ + + + + + These options are used to control encryption behavior of the communication between the server and the client. + + property. When converting from a boolean, a value of `true` converts to and a value of `false` converts to . When converting to a boolean, , , and `null` convert to `true` and converts `false`. + + ]]> + + + + + Converts the specified string representation of a logical value to its equivalent. + + A string containing the value to convert. + + An object that is equivalent to contained in . + + + Throws exception if provided is not convertible to type. + + + + + Converts the specified string representation of a logical value to its equivalent and returns a value that indicates whether the conversion succeeded. + + A string containing the value to convert. + + An object that is equivalent to contained in . if conversion fails. + + + if the parameter was converted successfully; otherwise, . + + This method does not throw an exception if conversion fails. + + + Specifies that TLS encryption is optional when connecting to the server. If the server requires encryption, encryption will be negotiated. + + + Specifies that TLS encryption is required when connecting to the server. If the server doesn't support encryption, the connection will fail. + + + Enables and requires TDS 8.0, TLS encryption to the server. If the server doesn't support TDS 8.0, TLS encryption, the connection will fail. + + + The boolean value to be used for implicit comparison. + + Enables implicit converstion of a boolean to a . A value of converts to . A value of converts to . + + + + The value to be used for implicit comparison. + + Enables implicit converstion of a to a boolean. and convert to . converts to . + + + + Returns the string value of . + + + + Compares the representation of to another . + + + + + Returns the hash code of the value. + + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionIPAddressPreference.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionIPAddressPreference.xml new file mode 100644 index 0000000000..e713cb776b --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionIPAddressPreference.xml @@ -0,0 +1,40 @@ + + + + + Specifies a value for IP address preference during a TCP connection. + + + + + + + + Specifies a value for IP address preference during a TCP connection. + + + + + + + Connects using IPv4 address(es) first. If the connection fails, try IPv6 address(es), if provided. This is the default value. + 0 + + + Connect using IPv6 address(es) first. If the connection fails, try IPv4 address(es), if available. + 1 + + + Connects with IP addresses in the order the underlying platform or operating system provides them. + 2 + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml index 98d4d5cc70..82f7aa8ec9 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml @@ -86,20 +86,30 @@ Integrated Security=True The supplied is not valid. - Declares the application workload type when connecting to a database in an SQL Server Availability Group. You can set the value of this property with . For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery). - Returns the current value of the property (a value of type ). - To be added. - ADO.NET Overview + Declares the application workload type when connecting to a database in an SQL Server Availability Group. You can set the value of this property with . For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery). + Returns the current value of the property. + + + + Overview of the SqlClient driver Gets or sets the name of the application associated with the connection string. - The name of the application, or ".NET SqlClient Data Provider" if no name has been supplied. + The name of the application. If no name has been supplied, "Framework Microsoft SqlClient Data Provider" when running on .NET Framework and "Core Microsoft SqlClient Data Provider" otherwise. To set the value to null, use . - - Gets or sets a Boolean value that indicates whether asynchronous processing is allowed by the connection created by using this connection string. - The value of the property, or if no value has been supplied. - - object, this key/value pair must be included within the connection string of the associated object. - - - -## Examples - The following example retrieves a connection string and verifies that the connection string is configured to allow for asynchronous processing. (In this case, the string comes from a procedure within the application, but in a production application, the connection string might come from a configuration file, or some other source.) Then, the example performs an asynchronous operation, updating values within a sample database on a background thread. - - [!code-csharp[SqlConnectionStringBuilder_AsynchronousProcessing#1](~/../sqlclient/doc/samples/SqlConnectionStringBuilder_AsynchronousProcessing.cs#1)] - - ]]> - - - - - Gets or sets a string that contains the name of the primary data file. This includes the full path name of an attachable database. The value of the property, or if no value has been supplied. @@ -151,9 +139,20 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security This property corresponds to the "AttachDBFilename", "extended properties", and "initial file name" keys within the connection string. `AttachDBFilename` is only supported for primary data files with an .mdf extension. - + + If the value of the AttachDBFileName key is specified in the connection string, the database is attached and becomes the default database for the connection. + + If this key is not specified and if the database was previously attached, the database will not be reattached. The previously attached database will be used as the default database for the connection. + + If this key is specified together with the AttachDBFileName key, the value of this key will be used as the alias. However, if the name is already used in another attached database, the connection will fail. + + The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. **Note:** Remote server, HTTP, and UNC path names are not supported. + + The database name must be specified with the keyword 'database' (or one of its aliases) as in the following: + + `"AttachDbFileName=|DataDirectory|\data\YourDB.mdf;integrated security=true;database=YourDatabase"` + An error will be generated if a log file exists in the same directory as the data file and the 'database' keyword is used when attaching the primary data file. In this case, remove the log file. Once the database is attached, a new log file will be automatically generated based on the physical path. - ## Examples @@ -165,12 +164,26 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security To set the value to null, use . Working with Connection Strings - ADO.NET Overview + Overview of the SqlClient driver + + Gets or sets the value of Attestation Protocol. + The attestation protocol. + - Gets the authentication of the connection string. - The authentication of the connection string. - To be added. + Gets or sets the authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities). + The authentication method of the connection string. + + + Clears the contents of the instance. @@ -179,7 +192,6 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security ## Remarks The method removes all key/value pairs from the , and resets all corresponding properties. This includes setting the property to 0, and setting the property to an empty string. - ## Examples @@ -192,8 +204,7 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security Gets or sets the column encryption settings for the connection string builder. - The column encryption settings for the connection string builder. - To be added. + The column encryption settings for the connection string builder.This property enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine) functionality for the connection. @@ -207,6 +218,8 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security ## Remarks This property corresponds to the "Command Timeout" key within the connection string. + + Valid values are greater than or equal to 0 and less than or equal to 2147483647. ]]> @@ -239,8 +252,9 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security - Amount of time (in seconds) between each reconnection attempt after identifying that there was an idle connection failure. This must be an integer between 1 and 60. The default is 10 seconds. An will be thrown if set to a value outside of the allowed range. + Amount of time (in seconds) between each reconnection attempt after identifying that there was an idle connection failure. This must be an integer between 1 and 60. The default is 10 seconds. Amount of time (in seconds) between each reconnection attempt after identifying that there was an idle connection failure. + Value is outside of the allowed range. connection string. +This value is applied after the first reconnection attempt. When a broken connection is detected, the client immediately attempts to reconnect; this is the first reconnection attempt and only occurs if `ConnectRetryCount` is greater than 0. If the first reconnection attempt fails and `ConnectRetryCount` is greater than 1, the client waits `ConnectRetryInterval` to try the second and subsequent reconnection attempts. + ]]> @@ -263,6 +279,7 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security When opening a connection to a Azure SQL Database, set the connection timeout to 30 seconds. + Valid values are greater than or equal to 0 and less than or equal to 2147483647. ## Examples @@ -306,7 +323,7 @@ False is null ( in Visual Basic) - Gets or sets a value that indicates whether a client/server or in-process connection to SQL Server should be made. + Obsolete. Gets or sets a value that indicates whether a client/server or in-process connection to SQL Server should be made. The value of the property, or if none has been supplied. - Gets or sets the SQL Server Language record name. + Gets or sets the language used for database server warning or error messages.. The value of the property, or if no value has been supplied. @@ -342,7 +361,40 @@ False ## Remarks This property corresponds to the "Data Source", "server", "address", "addr", and "network address" keys within the connection string. Regardless of which of these values has been supplied within the supplied connection string, the connection string created by the `SqlConnectionStringBuilder` will use the well-known "Data Source" key. - +The port number can be specified after the server name: `server=tcp:servername, portnumber`. + +When specifying a local instance, always use (local). To force a protocol, add one of the following prefixes:`np:(local), tcp:(local), lpc:(local)`. + +You can also connect to a LocalDB database as follows: `server=(localdb)\\myInstance`. For more information about LocalDB, see [SqlClient Support for LocalDB](/sql/connect/ado-net/sql/sqlclient-support-localdb). +**Data Source** must use the TCP format or the Named Pipes format. TCP format is as follows: + +- tcp:\\\ +- tcp:\,\ + +The TCP format must start with the prefix "tcp:" and is followed by the database instance, as specified by a host name and an instance name. This format is not applicable when connecting to Azure SQL Database. TCP is automatically selected for connections to Azure SQL Database when no protocol is specified. + +The host name MUST be specified in one of the following ways: +- NetBIOSName +- IPv4Address +- IPv6Address + +The instance name is used to resolve to a particular TCP/IP port number on which a database instance is hosted. Alternatively, specifying a TCP/IP port number directly is also allowed. If both instance name and port number are not present, the default database instance is used. + +The Named Pipes format is as follows: +- np:\\\\\pipe\\ + +The Named Pipes format MUST start with the prefix "np:" and is followed by a named pipe name. + +The host name MUST be specified in one of the following ways: + +- NetBIOSName +- IPv4Address +- IPv6Address + +The pipe name is used to identify the database instance to which the .NET application will connect. + +If the value of the **Network** key is specified, the prefixes "tcp:" and "np:" should not be specified. **Note:** You can force the use of TCP instead of shared memory, either by prefixing **tcp:** to the server name in the connection string, or by using **localhost**. + ## Examples The following example demonstrates that the class converts synonyms for the "Data Source" connection string key into the well-known key: @@ -354,27 +406,30 @@ False To set the value to null, use . - Gets or sets the enclave attestation Url to be used with enclave based Always Encrypted. - The enclave attestation Url. - To be added. + Gets or sets the enclave attestation URL to be used with enclave based Always Encrypted. + The enclave attestation URL. - - Set/Get the value of Attestation Protocol. - Returns Attestation Protocol. - - Gets or sets a Boolean value that indicates whether SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. - The value of the property, or if none has been supplied. + Gets or sets a value since version 5.0 or a value for the earlier versions that indicates whether TLS encryption is required for all data sent between the client and server. + The value of the property. , or `true`, the server name (or IP address) in a server's TLS certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see [Enable encrypted connections to the Database Engine](/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine#certificate-requirements). + +> [!NOTE] +> Starting from **version 4.0**, the default value of the property `Encrypt` is set to `true` while it is `false` for earlier versions. + +> [!NOTE] +> Starting from **version 5.0**, the data type is updated to , and the default value of the `Encrypt` property is set to . + ]]> Working with Connection Strings - ADO.NET Overview + Overview of the SqlClient driver Gets or sets a Boolean value that indicates whether the SQL Server connection pooler automatically enlists the connection in the creation thread's current transaction context. @@ -391,9 +446,60 @@ False Gets or sets the name or address of the partner server to connect to if the primary server is down. The value of the property, or if none has been supplied. - To be added. To set the value to null, use . + + + + + Gets or sets the service principal name (SPN) of the failover partner for the connection. + + The value of the property, or if none has been supplied. + + + + [!NOTE] +> This property only applies when using Integrated Security mode, otherwise it is ignored. + + ]]> + + + + + Gets or sets the host name to use when validating the server certificate for the connection. When not specified, the server name from the `Data Source` is used for certificate validation. (Only available in v5.0+) + + The value of the property, or if none has been supplied. + + + + [!NOTE] +> This property only applies when using `Encrypt` in or mode, otherwise it is ignored. + + ]]> + + + To be added. To be added. @@ -408,7 +514,7 @@ False ## Remarks This property corresponds to the "Initial Catalog" and "database" keys within the connection string. - + The database name can be 128 characters or less. ## Examples The following example creates a simple connection string and then uses the class to add the name of the database to the connection string. The code displays the contents of the property, just to verify that the class was able to convert from the synonym ("Database") to the appropriate property value. @@ -428,6 +534,9 @@ False ## Remarks This property corresponds to the "Integrated Security" and "trusted_connection" keys within the connection string. + If User ID and Password are specified and Integrated Security is set to true, the User ID and Password will be ignored and Integrated Security will be used. + + is a more secure way to specify credentials for a connection that uses SQL Server Authentication (`Integrated Security=false`). ## Examples @@ -441,13 +550,25 @@ False ]]> + + Gets or sets the IP address family preference when establishing TCP connections. + Returns the IP address preference. + + + + Gets a value that indicates whether the has a fixed size. in every case, because the supplies a fixed-size collection of key/value pairs. To be added. Working with Connection Strings - ADO.NET Overview + Overview of the SqlClient driver The key of the item to get or set. @@ -503,6 +624,10 @@ False ## Remarks This property corresponds to the "Load Balance Timeout" and "connection lifetime" keys within the connection string. +When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by `Connection Lifetime`. This is useful in clustered configurations to force load balancing between a running server and a server just brought online. + +A value of zero (0) causes pooled connections to have the maximum connection timeout. + ]]> @@ -542,7 +667,7 @@ False ## Examples - The following example explicitly disables the Multiple Active Result Sets feature. + The following example explicitly enables the Multiple Active Result Sets feature. [!code-csharp[SqlConnectionStringBuilder_MultipleActiveResultSets.MARS#1](~/../sqlclient/doc/samples/SqlConnectionStringBuilder_MultipleActiveResultSets.cs#1)] @@ -550,9 +675,16 @@ False - If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover=true provides faster detection of and connection to the (currently) active server. For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/dotnet/framework/data/adonet/sql/sqlclient-support-for-high-availability-disaster-recovery). + If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover=true provides faster detection of and connection to the (currently) active server. For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery). Returns indicating the current value of the property. - To be added. + + + Gets or sets a string that contains the name of the network library used to establish a connection to the SQL Server. @@ -578,6 +710,8 @@ False ## Remarks This property corresponds to the "Packet Size" key within the connection string. +The packet size can be greater than or equal to 512 and less than or equal to 32768. + ]]> @@ -589,9 +723,12 @@ False ## Remarks This property corresponds to the "Password" and "pwd" keys within the connection string. + +Setting this property is not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keyword instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication. If has not been set and you retrieve the value, the return value is . To reset the password for the connection string, pass null to the Item property. +The password must be 128 characters or less. ## Examples @@ -604,7 +741,7 @@ False The password was incorrectly set to null. See code sample below. - Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state. + Gets or sets a Boolean value indicating if security-sensitive information, such as the password or access token, should be returned as part of the connection string on a connection created with this after that connection has ever been in an open state. This property should only be set to if your application has a specific need to read the password out of an already-opened database connection. The default value of is the more secure setting; using for this property opens your application to security risks such as accidentally logging or tracing the database password. The value of the property, or if none has been supplied. @@ -641,6 +780,8 @@ False ## Remarks This property corresponds to the "Pooling" key within the connection string. +Connections are considered the same if they have the same connection string. Different connections have different connection strings. + ]]> @@ -659,14 +800,13 @@ False |Key|Default value| |---------|-------------------| -|Application Name|".Net SqlClient Data Provider"| -|Asynchronous Processing|False| +|Application Name|"Framework Microsoft SqlClient Data Provider" when running on .NET Framework. "Core Microsoft SqlClient Data Provider" otherwise.| |AttachDBFilename|Empty string| |Connection Timeout|15| -|Context Connection|False| +|Context Connection(Obsolete)|False| |Current Language|Empty string| |Data Source|Empty string| -|Encrypt|False| +|Encrypt|False in versions prior to 4.0, True in versions 4.0 and up| |Enlist|True| |Failover Partner|Empty string| |Initial Catalog|Empty string| @@ -721,6 +861,44 @@ Database = AdventureWorks ]]> + + Gets or sets the path to a certificate file to match against the SQL Server TLS/SSL certificate for the connection. The accepted certificate formats are PEM, DER, and CER. If specified, the SQL Server certificate is checked by verifying if the `ServerCertificate` provided is an exact match. (Only available in v5.1+) + + The value of the property, or if none has been supplied. + + + + [!NOTE] +> This property only applies when using `Encrypt` in or mode, otherwise it is ignored. + + ]]> + + + + + Gets or sets the service principal name (SPN) of the data source. + + The value of the property, or if none has been supplied. + + + + [!NOTE] +> This property only applies when using Integrated Security mode, otherwise it is ignored. + + ]]> + + + The key to locate in the . Indicates whether the specified key exists in this instance. @@ -737,7 +915,7 @@ Database = AdventureWorks Gets or sets a string value that indicates how the connection maintains its association with an enlisted transaction. - The value of the property, or if none has been supplied. + The value of the property, or `Implicit Unbind` if none has been supplied. Gets or sets a value that indicates whether the channel will be encrypted while bypassing walking the certificate chain to validate trust. - A . Recognized values are , , , and . + A boolean. The default is `false`. is a more secure way to specify credentials for a connection that uses SQL Server Authentication. + + The user ID must be 128 characters or less. + ]]> To set the value to null, use . @@ -888,7 +1072,7 @@ Unable to retrieve value for null key. This property corresponds to the "User Instance" key within the connection string. > [!NOTE] -> This feature is available only with the SQL Server Express Edition. For more information on user instances, see [SQL Server Express User Instances](/dotnet/framework/data/adonet/sql/sql-server-express-user-instances). +> This feature is available only with the SQL Server Express Edition. For more information on user instances, see [SQL Server Express User Instances](/sql/connect/ado-net/sql/sql-server-express-user-instances). ]]> @@ -922,7 +1106,9 @@ Unable to retrieve value for null key. ## Remarks This property corresponds to the "Workstation ID" and "wsid" keys within the connection string. - + + The ID must be 128 characters or less. + ]]> To set the value to null, use . diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml index 41ff58e6a5..41caafa87f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCredential.xml @@ -14,7 +14,7 @@ to get or set a connection's object. Use to change the password for a object. For information on how a object affects connection pool behavior, see [SQL Server Connection Pooling (ADO.NET)](/dotnet/framework/data/adonet/sql-server-connection-pooling). + Use to get or set a connection's object. Use to change the password for a object. For information on how a object affects connection pool behavior, see [SQL Server Connection Pooling (ADO.NET)](/sql/connect/ado-net/sql-server-connection-pooling). An exception will be raised if a non-null object is used in a connection with any of the following connection string keywords: @@ -24,8 +24,6 @@ - `User ID` -- `Context Connection = true` - The following sample connects to a SQL Server database using : ``` @@ -70,19 +68,19 @@ using (SqlConnection conn = new SqlConnection(connString.ConnectionString)) ]]> - ADO.NET Overview + Overview of the SqlClient driver Gets the password component of the object. The password component of the object. To be added. - ADO.NET Overview + Overview of the SqlClient driver Gets the user ID component of the object. The user ID component of the object. To be added. - ADO.NET Overview + Overview of the SqlClient driver diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml index 6d4ace23d3..6fb16341ef 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDataAdapter.xml @@ -7,7 +7,7 @@ , serves as a bridge between a and SQL Server for retrieving and saving data. The provides this bridge by mapping , which changes the data in the to match the data in the data source, and , which changes the data in the data source to match the data in the , using the appropriate Transact-SQL statements against the data source. The update is performed on a by-row basis. For every inserted, modified, and deleted row, the method determines the type of change that has been performed on it (`Insert`, `Update`, or `Delete`). Depending on the type of change, the `Insert`, `Update`, or `Delete` command template executes to propagate the modified row to the data source. When the fills a , it creates the necessary tables and columns for the returned data if they do not already exist. However, primary key information is not included in the implicitly created schema unless the property is set to . You may also have the create the schema of the , including primary key information, before filling it with data using `FillSchema`. For more information, see [Adding Existing Constraints to a DataSet](/dotnet/framework/data/adonet/adding-existing-constraints-to-a-dataset). + The , serves as a bridge between a and SQL Server for retrieving and saving data. The provides this bridge by mapping , which changes the data in the to match the data in the data source, and , which changes the data in the data source to match the data in the , using the appropriate Transact-SQL statements against the data source. The update is performed on a by-row basis. For every inserted, modified, and deleted row, the method determines the type of change that has been performed on it (`Insert`, `Update`, or `Delete`). Depending on the type of change, the `Insert`, `Update`, or `Delete` command template executes to propagate the modified row to the data source. When the fills a , it creates the necessary tables and columns for the returned data if they do not already exist. However, primary key information is not included in the implicitly created schema unless the property is set to . You may also have the create the schema of the , including primary key information, before filling it with data using `FillSchema`. For more information, see [Adding Existing Constraints to a DataSet](/sql/connect/ado-net/add-existing-constraints-to-dataset). is used in conjunction with and to increase performance when connecting to a SQL Server database. @@ -186,7 +186,7 @@ , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). + During , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). When is assigned to a previously created , the is not cloned. The maintains a reference to the previously created object. @@ -238,7 +238,7 @@ , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). + During , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). When is assigned to a previously created , the is not cloned. The maintains a reference to the previously created object. @@ -476,7 +476,7 @@ , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/dotnet/framework/data/adonet/generating-commands-with-commandbuilders). + During , if this property is not set and primary key information is present in the , the can be generated automatically if you set the property and use the . Then, any additional commands that you do not set are generated by the . This generation logic requires key column information to be present in the . For more information, see [Generating Commands with CommandBuilders](/sql/connect/ado-net/generate-commands-with-commandbuilders). When is assigned to a previously created , the is not cloned. The maintains a reference to the previously created object. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml index 9f61a25a03..b0ce159fbf 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml @@ -319,22 +319,24 @@ Synchronously gets the value of the specified column as a type. is the asynchronous version of this method. The returned type object. - + .||| +|Boolean|Byte|Char|DateOnly (.NET 6 or later)| +|DateTime|DateTimeOffset|Decimal|Double| +|Float|Guid|Int16|Int32| +|Int64|SqlBoolean|SqlByte|SqlDateTime| +|SqlDecimal|SqlDouble|SqlGuid|SqlInt16| +|SqlInt32|SqlInt64|SqlMoney|SqlSingle| +|SqlString|Stream|String|TextReader| +|TimeOnly (.NET 6 or later)|XmlReader||UDT, which can be any CLR type marked with .| - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -359,22 +361,24 @@ Asynchronously gets the value of the specified column as a type. is the synchronous version of this method. The returned type object. - + .||| +|Boolean|Byte|Char|DateOnly (.NET 6 or later)| +|DateTime|DateTimeOffset|Decimal|Double| +|Float|Guid|Int16|Int32| +|Int64|SqlBoolean|SqlByte|SqlDateTime| +|SqlDecimal|SqlDouble|SqlGuid|SqlInt16| +|SqlInt32|SqlInt64|SqlMoney|SqlSingle| +|SqlString|Stream|String|TextReader| +|TimeOnly (.NET 6 or later)|XmlReader||UDT, which can be any CLR type marked with .| - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -386,9 +390,7 @@ 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. - - is specified in the connection string. + 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. The value of the column was null ( == ), retrieving a non-SQL type. @@ -530,7 +532,7 @@ method returns metadata about each column in the following order: + The method returns the following metadata about each column: |DataReader column|Description| |-----------------------|-----------------| @@ -835,9 +837,7 @@ - WriteTimeout - When the connection property `ContextConnection=true`, only supports synchronous data retrieval for both sequential () and non-sequential () access. - - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -891,9 +891,7 @@ will raise an exception when used on an object returned by when is in effect. - When the connection property `ContextConnection=true`, only supports synchronous data retrieval for both sequential () and non-sequential () access. - - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -988,7 +986,7 @@ will raise an exception when used on an object returned by when is in effect. - For more information, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For more information, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -1065,7 +1063,7 @@ @@ -1077,9 +1075,8 @@ 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. - - is specified in the connection string. + 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. @@ -1133,13 +1130,11 @@ - Calling more than once for the same instance before task completion. - - is specified in the connection string. + Calling more than once for the same instance before task completion. SQL Server returned an error while executing the command text. @@ -1177,13 +1172,11 @@ ## Remarks If the `behavior` parameter of is set to `Default`, reads the entire row before returning the Task. - For more information, including code samples, about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/dotnet/framework/data/adonet/asynchronous-programming). + For more information, including code samples, about asynchronous programming in the .NET Framework Data Provider for SQL Server, see [Asynchronous Programming](/sql/connect/ado-net/asynchronous-programming). ]]> - Calling more than once for the same instance before task completion. - - is specified in the connection string. + Calling more than once for the same instance before task completion. SQL Server returned an error while executing the command text. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml index d4ecfc52c0..379fb5c930 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlDependency.xml @@ -14,7 +14,7 @@ > [!NOTE] > was designed to be used in ASP.NET or middle-tier services where there is a relatively small number of servers having dependencies active against the database. It was not designed for use in client applications, where hundreds or thousands of client computers would have objects set up for a single database server. If you are developing an application where you need reliable sub-second notifications when data changes, review the sections [Planning an Efficient Query Notifications Strategy](https://docs.microsoft.com/previous-versions/sql/sql-server-2008-r2/ms187528(v=sql.105)#planning-an-efficient-query-notifications-strategy) and [Alternatives to Query Notifications](https://docs.microsoft.com/previous-versions/sql/sql-server-2008-r2/ms187528(v=sql.105)#alternatives-to-query-notifications) in the [Planning for Notifications](https://docs.microsoft.com/previous-versions/sql/sql-server-2008-r2/ms187528(v%3dsql.105)) article. - For more information, see [Query Notifications in SQL Server](/dotnet/framework/data/adonet/sql/query-notifications-in-sql-server) and [Building Notification Solutions](https://docs.microsoft.com/previous-versions/sql/sql-server-2005/ms171065(v%3dsql.90)). + For more information, see [Query Notifications in SQL Server](/sql/connect/ado-net/sql/query-notifications-sql-server) and [Building Notification Solutions](https://docs.microsoft.com/previous-versions/sql/sql-server-2005/ms171065(v%3dsql.90)). > [!NOTE] > The event may be generated on a different thread from the thread that initiated command execution. @@ -206,7 +206,7 @@ - Stops a listener for a connection specified in a previous call. + Stops a listener for a connection specified in a previous call. Connection string for the instance of SQL Server that was used in a previous call. - Stops a listener for a connection specified in a previous call. + Stops a listener for a connection specified in a previous call. if the listener was completely stopped; if the was unbound from the listener, but there are is at least one other using the same listener. @@ -237,7 +237,7 @@ Connection string for the instance of SQL Server that was used in a previous call. The SQL Server Service Broker queue that was used in a previous call. - Stops a listener for a connection specified in a previous call. + Stops a listener for a connection specified in a previous call. if the listener was completely stopped; if the was unbound from the listener, but there is at least one other using the same listener. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml index c5eafa53b1..9bb1ef18f3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlException.xml @@ -105,7 +105,7 @@ catch (Exception ex) { - Represents the client connection ID. For more information, see Data Tracing in ADO.NET. + Represents the client connection ID. For more information, see Data Tracing in ADO.NET. The client connection ID. - Represents a parameter to a and optionally its mapping to columns. This class cannot be inherited. For more information on parameters, see [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). + Represents a parameter to a and optionally its mapping to columns. This class cannot be inherited. For more information on parameters, see [Configuring parameters](/sql/connect/ado-net/configure-parameters). [!NOTE] > Nameless, also called ordinal, parameters are not supported by the .NET Framework Data Provider for SQL Server. - For more information, along with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/dotnet/framework/data/adonet/commands-and-parameters). + For more information, along with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/sql/connect/ado-net/commands-parameters). ## Examples - The following example creates multiple instances of through the collection within the . These parameters are used to select data from the data source and put the data in the . This example assumes that a and a have already been created by using the appropriate schema, commands, and connection. For more information and additional examples on using parameters, see [Retrieving and Modifying Data in ADO.NET](/dotnet/framework/data/adonet/retrieving-and-modifying-data) and [Configuring Parameters and Parameter Data Types](/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types). + The following example creates multiple instances of through the collection within the . These parameters are used to select data from the data source and put the data in the . This example assumes that a and a have already been created by using the appropriate schema, commands, and connection. For more information and additional examples on using parameters, see [Retrieving and Modifying Data in ADO.NET](/sql/connect/ado-net/retrieving-modifying-data) and [Configuring parameters](/sql/connect/ado-net/configure-parameters). [!code-csharp[SqlParameterCollection_Add6](~/../sqlclient/doc/samples/SqlParameterCollection_Add6.cs#1)] @@ -203,7 +203,7 @@ If you do not perform this conversion, the compiler assumes that you are trying ## Remarks The and are linked. Therefore, setting the changes the to a supporting . - For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters). + For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters). @@ -231,11 +231,11 @@ If you do not perform this conversion, the compiler assumes that you are trying ## Examples The following example creates a and sets some of its properties. - [Commands and Parameters](/dotnet/framework/data/adonet/commands-and-parameters) + [Commands and Parameters](/sql/connect/ado-net/commands-parameters) - [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters) + [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters) - [SQL Server and ADO.NET](/dotnet/framework/data/adonet/sql/) + [SQL Server and ADO.NET](/sql/connect/ado-net/sql/) ]]> @@ -428,7 +428,7 @@ static void CreateSqlParameterLocaleId(){ For fixed length data types, the value of is ignored. It can be retrieved for informational purposes, and returns the maximum amount of bytes the provider uses when transmitting the value of the parameter to the server. - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). @@ -449,7 +449,7 @@ static void CreateSqlParameterLocaleId(){ ## Remarks When is set to anything other than an empty string, the value of the parameter is retrieved from the column with the name. If is set to `Input`, the value is taken from the . If is set to `Output`, the value is taken from the data source. A of `InputOutput` is a combination of both. - For more information about how to use the property, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters) and [Updating Data Sources with DataAdapters](/dotnet/framework/data/adonet/updating-data-sources-with-dataadapters). + For more information about how to use the property, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters) and [Updating Data Sources with DataAdapters](/sql/connect/ado-net/update-data-sources-with-dataadapters). @@ -517,9 +517,9 @@ FieldName = @OriginalFieldName ## Remarks The and are linked. Therefore, setting the changes the to a supporting . - For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/dotnet/framework/data/adonet/dataadapter-parameters). + For a list of the supported data types, see the appropriate member. For more information, see [DataAdapter Parameters](/sql/connect/ado-net/dataadapter-parameters). - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -543,7 +543,7 @@ FieldName = @OriginalFieldName Use the property to return parameter values as common language runtime (CLR) types. - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). ]]> @@ -601,7 +601,7 @@ FieldName = @OriginalFieldName The property is overwritten by `SqlDataAdapter.UpdateCommand`. - For information about streaming, see [SqlClient Streaming Support](/dotnet/framework/data/adonet/sqlclient-streaming-support). + For information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support). diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml index 1141a8ec68..c75791c6bc 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlParameterCollection.xml @@ -9,7 +9,7 @@ ## Remarks If the command contains an ad hoc SQL statement, as opposed to a stored-procedure name, the number of the parameters in the collection must be equal to the number of parameter placeholders within the command text, or SQL Server raises an error. With a stored procedure, all the parameters declared in the stored procedure without a default value must be provided. Parameters declared with a default value are optional. This lets you specify a value other than the default. -For more information with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/dotnet/framework/data/adonet/commands-and-parameters). +For more information with additional sample code demonstrating how to use parameters, see [Commands and Parameters](/sql/connect/ado-net/commands-parameters). diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml new file mode 100644 index 0000000000..8856f2fe22 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryIntervalBaseEnumerator.xml @@ -0,0 +1,55 @@ + + + + + Generates a sequence of time intervals. + + + Initializes a new instance of the class with a default value of zero for the gap time, minimum, and maximum interval time. + + + The gap time used to calculate the time delay before each attempt. + The maximum time allowed as a gap time. + The minimum time allowed as a gap time. + Initializes a new instance of the class. + The supplied arguments failed validation. + + + The default gap time of each interval. + + + The minimum allowed time interval value. + + + The maximum allowed time interval value. + + + Gets the element in the collection at the current position of the enumerator. + + + Sets the enumerator to its initial position, which is before the first element in the collection. + + + The gap time of each interval. Must be between 0 and 120 seconds. + Maximum time interval value. Must be between 0 and 120 seconds. + Minimum time interval value. Must be less than maximum time interval and between 0 and 120 seconds. + Validate the enumeration parameters. + The supplied arguments failed validation. + + + Calculates the next interval time. + Returns the next gap time interval. + + + Advances the enumerator to the next element of the collection. + Returns , if the enumerator was successfully advanced to the next element; if the enumerator has passed the end of the collection. + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + + Creates a new object that is a copy of the current instance. + A new object that is a copy of this instance. + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBase.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBase.xml new file mode 100644 index 0000000000..3766fa205e --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBase.xml @@ -0,0 +1,57 @@ + + + + + Retrieves the next time interval with respect to the number of retries if a transient condition occurs. + + + Maximum number of retries. + + that returns the maximum number of retry execution attempts that will be attempted after the first failure. + + + Current retry number starting from zero. + + that returns the number of retry execution attempts after the first failure. + + + The timer interval enumerator. + + value that indicates an enumerator to generate a sequence of time intervals. + + + Delegate to a transient condition predicate. The function that this delegate points to must return a true value when an expected transient exception happens. + + value that delegates to a function that receives a input parameter. + + + The sender object. + Pre-retry validation for the sender state. + Returns if the sender is authorized to retry the operation. + + [IMPORTANT!] +> Operations that are part of a **Transaction** are not safe to retry without specific knowledge of business logic. Due to this complexity, retry logic should be managed at the application level. + +> [!NOTE] +> The `RetryCondition` is an extra condition that checks before executing the `TransientPredicate` and the default condition always returns **true**. + +]]> + + + + The interval time that is generated by the `RetryIntervalEnumerator`. + Try to get the next interval time by using the enumerator if the counter does not exceed the number of retries. + Returns if the number of retry attempts has not been exceeded; otherwise . + + + Set the counters and enumerator to default values for next use. + + + Creates a new object that is a copy of the current instance. + When implemented in a derived class, the method is expected to return a new object of the current instance. The default implementation throws NotImplementedException. + In all cases. + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBaseProvider.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBaseProvider.xml new file mode 100644 index 0000000000..031c2b1ff7 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicBaseProvider.xml @@ -0,0 +1,90 @@ + + + + + Applies retry logic on an operation through the `Execute` or `ExecuteAsync` function. + + + Occurs before applying the calculated delay time and executing the function on a next attempt. + + with event argument of object can be subscribed. + + [IMPORTANT!] +> Don't block execution with a time consuming action when an event occurs. For instance, if you log data to a file, run it in a new thread to avoid blocking the main execution thread. + +]]> + + + + + Defines the retry logic used to decide when to retry based on the encountered exception. + + [!NOTE] +> The `RetryLogic` property is assigned at `SqlRetryLogicBaseProvider` creation and its value is used as a template internally. Don't use it to monitor the status of the retry logic during and after execution. Instead, use the event to collect data about retry executions. + +]]> + + + + + The object that the `function` returns when executed. + The source of the event. + The operation to re-execute if a transient condition occurs. + Executes a function and applies retry logic, if enabled. **Note:** Exceptions will be reported via an aggregate exception if the execution isn't successful via retry attempts. + + The return value of the `function` if it runs without exception. + + [!NOTE] +> The type of exception depends on the `function`'s internal implementation. But if the exception is due to all retry attempts failing, it will be an that consists of all exceptions that happened during the failed attempts. + +]]> + + + The `function` parameter can't be `null`. + The collection of exceptions after all retry attempts have failed. + + + The object that the `function` returns in a Task when executed. + The source of the event. + The operation to re-execute if a transient condition occurs. + The cancellation instruction. + Executes a function and applies retry logic, if enabled. The cancellation token can be used to request that the operation be abandoned before the execution attempts are exceeded. **Note:** Exceptions will be reported via the returned Task object, which will contain an aggregate exception if execution fails for all retry attempts. + A task representing the asynchronous operation. The results of the task will be the return value of the `function`, if it runs without exception. + + [!NOTE] +> If the exception comes from all retry attempts failing, it will be an that consists of all exceptions from the failed attempts. + +]]> + + + The `function` parameter can't be `null`. + The collection of exceptions after failed retry attempts. + + + The source of the event. + The operation to re-execute if a transient condition occurs. + The cancellation instruction. + Executes a function and applies retry logic, if enabled. The cancellation token can be used to request that the operation be abandoned before the execution attempts are exceeded. **Note:** Exceptions will be reported via the returned Task object, which will contain an aggregate exception if execution fails for all retry attempts. + A Task or an exception. + + [!NOTE] +> If the exception comes from all retry attempts failing, it will be an that consists of all exceptions from the failed attempts. + +]]> + + + The `function` parameter can't be `null`. + The collection of exceptions after failed retry attempts. + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicOption.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicOption.xml new file mode 100644 index 0000000000..8ba200753b --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryLogicOption.xml @@ -0,0 +1,47 @@ + + + + + Provides the retry logic parameters to create an instance of the class by using methods. + + + object that is configured to apply retry logic for the error number **102** for a maximum of **5** times and **3** to **60** seconds gap time between each run. It will only work for the `Select` SQL statements assigned to the . + +[!code-csharp[SqlConfigurableRetryLogic_SqlRetryLogicOptions#1](~/../sqlclient/doc/samples/SqlConfigurableRetryLogic_SqlRetryLogicOptions.cs#1)] + +]]> + + + Sets the number of times to try and execute the function. + + between 1 and 60; 1 means to execute one time and if an error is encountered, don't retry. + + + Sets the gap time interval as a object. + + The upcoming gap time before the next execution attempt; must be between 0 and 120 seconds. + + + Sets the minimum allowed gap time interval as a object. + + The minimum upcoming gap time before the next execution attempt; the default value is **zero** and must be between 0 and 120 seconds. + + + Sets the allowed maximum gap time interval as a object. + + The maximum upcoming gap time interval before the next execution attempt; must be between 0 and 120 seconds. + + + Sets the list of transient error numbers on which to retry when they occur. + List of ; Set to to use the internal list of exceptions from the object. + + + Sets a pre-retry validation function on the to only include specific SQL statements. + + The pre-retry validation delegate function; if the `CommandText` is authorized to retry the operation. + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlRetryingEventArgs.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryingEventArgs.xml new file mode 100644 index 0000000000..f7911a4160 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlRetryingEventArgs.xml @@ -0,0 +1,32 @@ + + + + + Represents the set of arguments passed to the event. + + + The current retry attempt count. + The delay that indicates how long the current thread will be suspended before the next iteration is invoked. + The list of exceptions since the first retry that caused the retry logic to re-execute the function. + Initializes a new instance of the class. + + + Retry-attempt-number, after the first exception occurrence. + + that returns the number of retry execution attempts; starting from 1. + + + Gets or sets a value that indicates whether the retry logic should be canceled. + If set to , the execution attempt will be interrupted immediately. + + + Gets the current waiting time as a object. + + The upcoming gap time before the next execution attempt. + + + Gets the list of exceptions since the first attempt failure. + List of occurred exceptions. + + + diff --git a/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml b/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml index 1330327723..2397ba11a3 100644 --- a/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml +++ b/doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml @@ -15,7 +15,7 @@ Specifying the FILESTREAM attribute on a `varbinary(max)` column causes SQL Serv The class is derived from the class, which represents an abstraction of a sequence of bytes from some arbitrary data source such as a file or a block of memory. You can read from a FILESTREAM by transferring data from a stream into a data structure such as an array of bytes. You can write to a FILESTREAM by transferring the data from a data structure into a stream. You can also seek within the stream, which allows you to query and modify data at the current position within the stream. -For conceptual documentation and code examples, see [FILESTREAM Data](/dotnet/framework/data/adonet/sql/filestream-data). +For conceptual documentation and code examples, see [FILESTREAM Data](/sql/connect/ado-net/sql/filestream-data). For documentation about setting up and configuring FILESTREAM data on SQL Server, see [Designing and Implementing FILESTREAM Storage](https://go.microsoft.com/fwlink/?LinkId=121499) in SQL Server 2008 Books Online. diff --git a/doc/snippets/Microsoft.SqlServer.Server/DataAccessKind.xml b/doc/snippets/Microsoft.SqlServer.Server/DataAccessKind.xml new file mode 100644 index 0000000000..1590300ea4 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/DataAccessKind.xml @@ -0,0 +1,26 @@ + + + + + Describes the type of access to user data for a user-defined method or function. + + and to indicate whether the method or function uses ADO.NET to connect back to the database using the "context connection." + +Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function perform read-only data-access operations, such as executing SELECT statements). + + ]]> + + + + The method or function does not access user data. + + + The method or function reads user data. + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml b/doc/snippets/Microsoft.SqlServer.Server/Format.xml similarity index 67% rename from doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml rename to doc/snippets/Microsoft.SqlServer.Server/Format.xml index b15cd6eadb..31b5776207 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/Format.xml @@ -2,39 +2,44 @@ - Used by and to indicate the serialization format of a user-defined type (UDT) or aggregate. + Used by and to indicate the serialization format of a user-defined type (UDT) or aggregate. and to indicate the serialization format of a user-defined type (UDT) or aggregate. Use of the `Native` and `UserDefined` enumeration members has special requirements. + +## Remarks + +This enumeration is used by and to indicate the serialization format of a user-defined type (UDT) or aggregate. Use of the `Native` and `UserDefined` enumeration members has special requirements. + - `Format.Native` The requirements for the `Format.Native` format are: - + - The with a property value of must be applied to the aggregate or UDT if it is defined in a class and not a structure. This controls the physical layout of the data fields and is used to force the members to be laid out sequentially in the order they appear. SQL Server uses this attribute to determine the field order for UDTs with multiple fields. - + - The type must contain at least one member (serialized values cannot be zero bytes in size). - + - All the fields of the aggregate must be *blittable*; that is, they must have a common representation in both managed and unmanaged memory and not require special handling by the interop marshaler. - + - All the fields of the UDT should be of one of the following types that can be serialized: `bool`, `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`, , , , , , , , , or other value types defined by the user that contain fields of one of these types. - The aggregate must not specify a value for `MaxByteSize`. - + - The aggregate must not have any [NonSerialized] fields. - + - Fields must not be marked as an explicit layout (with a of ). - `Format.UserDefined` The requirements for the `Format.UserDefined` format are: - The aggregate must specify a value for `MaxByteSize`. - - - Specify the attribute property. The default value is `false`. - - - If you omit any field in the or methods, the state of that field is not serialized. + + - Specify the attribute property. The default value is `false`. + + - If you omit any field in the or methods, the state of that field is not serialized. + ## Examples + The following example shows the `UserDefinedType` attribute of the Point UDT. The UDT is byte-ordered, is named "Point", has a validation method named "ValidatePoint", and uses the native serialization format. - -[!code-csharp[SqlUserDefinedType Example#1](~/../sqlclient/doc/samples/SqlUserDefinedType.cs#1)] - + +[!code-csharp[SqlUserDefinedTypeAttribute Example#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs#1)] +[!code-vb[SqlUserDefinedTypeAttribute Example#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb#1)] + ]]> @@ -45,7 +50,7 @@ The following example shows the `UserDefinedType` attribute of the Point UDT. T The serialization format is unknown. - This serialization format gives the developer full control over the binary format through the and methods. + This serialization format gives the developer full control over the binary format through the and methods. diff --git a/doc/snippets/Microsoft.SqlServer.Server/IBinarySerialize.xml b/doc/snippets/Microsoft.SqlServer.Server/IBinarySerialize.xml new file mode 100644 index 0000000000..79128e5655 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/IBinarySerialize.xml @@ -0,0 +1,56 @@ + + + + + Provides custom implementation for user-defined type (UDT) and user-defined aggregate serialization and deserialization. + + .`Native` or .`UserDefined`. + +.`Native` allows SQL Server to handle serialization and deserialization automatically, but the format has restrictions on the kind of types it can handle. .`UserDefined` allows user-defined types and aggregates to handle their own serialization. User-defined types and aggregates must be marked with .`UserDefined` in the `SqlUserDefinedType` or `SqlUserDefinedAggregate` attribute, and must implement the interface. + +Note that even with custom serialization, the total size of each instance must be under the maximum allowed limit, currently 8000 bytes. + + ]]> + + + + The stream from which the object is deserialized. + Generates a user-defined type (UDT) or user-defined aggregate from its binary form. + + method must reconstitute your object using the information written by the method. + +## Examples +The following example shows the implementation of the method of a UDT, which uses a to de-serialize a previously persisted UDT. This example assumes that the UDT has two data properties: `StringValue` and `DoubleValue`. + +[!code-csharp[IBinarySerialize Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs#1)] +[!code-vb[IBinarySerialize Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb#1)] + + ]]> + + + + The stream to which the UDT or user-defined aggregate is serialized. + Converts a user-defined type (UDT) or user-defined aggregate into its binary format so that it may be persisted. + + method to reconstitute your UDT or user-defined aggregate. + +## Examples +The following example shows the implementation of the method of a UDT, which uses a to serialize the UDT in the user-defined binary format. The purpose of the null character padding is to ensure that the string value is completely separated from the double value, so that one UDT is compared to another in Transact-SQL code, string bytes are compared to string bytes and double bytes are compared to double bytes. + +[!code-csharp[IBinarySerialize Samples#2](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs#2)] +[!code-vb[IBinarySerialize Samples#2](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb#2)] + + ]]> + + + + diff --git a/doc/snippets/Microsoft.SqlServer.Server/InvalidUdtException.xml b/doc/snippets/Microsoft.SqlServer.Server/InvalidUdtException.xml new file mode 100644 index 0000000000..9ec4002c76 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/InvalidUdtException.xml @@ -0,0 +1,39 @@ + + + + + Thrown when SQL Server or the ADO.NET provider detects an invalid user-defined type (UDT). + To be added. + + + The object. + The object. + Streams all the properties into the class for the given . + + class to make the class serializable. + + ]]> + + + + The object. + The object that represents a string in string resources. The default value is `SqlUdtReason_NoUdtAttribute` which looks up a localized string similar to "no UDT attribute". + Creates a new object. + A new object. + + [!IMPORTANT] +> This function is exposed for backward compatibility and should be used with the default value for the `resourceReason` parameter. + + ]]> + + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlFacetAttribute.xml similarity index 56% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlFacetAttribute.xml index bbe76cdd22..cae95f8032 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlFacetAttribute.xml @@ -7,14 +7,14 @@ may only be specified on non-void return values. - - is used only to derive information about the return type, and is not intended to be a constraint specification on what can be stored in the type. Thus, if a field has a indicating its size to be 2 characters, then the SQL Server type of the field access expression is of size 2, but assignments into the field are not restricted by this facet. - - The table below captures the matrix of valid values for the various properties for specific field types. In this table, "Y" indicates that the property is valid, and "N" indicates that the property is not valid. - - The specified must be compatible with the field type. If the property is not valid, type registration will report an error if the user specifies a non-default value for the property. The maximum values for and properties are 38. For the property, the value should be in the range of 1-8000 for binary and non-Unicode data, 1-4000 for Unicode data, or -1. All other values are not valid. - + may only be specified on non-void return values. + + is used only to derive information about the return type, and is not intended to be a constraint specification on what can be stored in the type. Thus, if a field has a indicating its size to be 2 characters, then the SQL Server type of the field access expression is of size 2, but assignments into the field are not restricted by this facet. + +The table below captures the matrix of valid values for the various properties for specific field types. In this table, "Y" indicates that the property is valid, and "N" indicates that the property is not valid. + +The specified must be compatible with the field type. If the property is not valid, type registration will report an error if the user specifies a non-default value for the property. The maximum values for and properties are 38. For the property, the value should be in the range of 1-8000 for binary and non-Unicode data, 1-4000 for Unicode data, or -1. All other values are not valid. + |Type|IsFixedLength|MaxSize|Precision|Scale|IsNullable| |----------|-------------------|-------------|---------------|-----------|----------------| ||N|N|N|N|Y| @@ -39,9 +39,9 @@ |Char[]|Y|Y|N|N|Y| ||N|N|N|Y1|N| ||N|N|Y|Y|Y| - - (1) Specifying the scale on a DateTime type will cause the value to be returned to Transact-SQL as a DateTime2 type with the specified scale. - + +(1) Specifying the scale on a DateTime type will cause the value to be returned to Transact-SQL as a DateTime2 type with the specified scale. + ]]> @@ -56,7 +56,7 @@ property is set to 1. +This property must be set to `false` if the property is set to 1. The default value is `false`. @@ -71,8 +71,8 @@ The default value is `false`. @@ -83,12 +83,12 @@ The default value is `false`. @@ -99,10 +99,10 @@ The default value is `false`. property is valid only for numeric types. The property must also be specified when setting the property. - - The maximum value of the property is 38; the default value is 38. - +The property is valid only for numeric types. The property must also be specified when setting the property. + +The maximum value of the property is 38; the default value is 38. + ]]> @@ -113,10 +113,10 @@ The default value is `false`. property is valid only for decimal types. The property must also be specified when setting the property. - - The maximum value of the property is 38; the default value is 0. - +The property is valid only for decimal types. The property must also be specified when setting the property. + +The maximum value of the property is 38; the default value is 0. + ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFunctionAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlFunctionAttribute.xml similarity index 62% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlFunctionAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlFunctionAttribute.xml index 7221363628..ddf4c76e4b 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFunctionAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlFunctionAttribute.xml @@ -9,26 +9,27 @@ ## Examples The following example shows an aggregate function that returns a list of files in the specified directory path. -[!code-csharp[SqlFunctionAttribute Sample#1](~/../sqlclient/doc/samples/SqlFunctionAttribute.cs#1)] +[!code-csharp[SqlFunctionAttribute Sample#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs#1)] +[!code-vb[SqlFunctionAttribute Sample#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb#1)] ]]> - An optional attribute on a user-defined aggregate, used to indicate that the method should be registered in SQL Server as a function. Also used to set the , , , , , , and properties of the function attribute. + An optional attribute on a user-defined aggregate, used to indicate that the method should be registered in SQL Server as a function. Also used to set the , , , , , , and properties of the function attribute. To be added. Indicates whether the function involves access to user data stored in the local instance of SQL Server. - .: Does not access data. .: Only reads user data. + .: Does not access data. .: Only reads user data. . is also required when connecting to remote servers if transactions integration is required (the default). +The default is . is also required when connecting to remote servers if transactions integration is required (the default). -If a Transact-SQL query is executed from inside a table-valued function (TVF), the property should be set. +If a Transact-SQL query is executed from inside a table-valued function (TVF), the property should be set. ]]> @@ -47,11 +48,11 @@ If a Transact-SQL query is executed from inside a table-valued function (TVF), t ## Remarks A user-defined function is said to be deterministic if it always produces the same output values given the same input values and the same database state. -The property is also useful for indexing the result of the function in the form of indexed computed columns and indexed views. If this property is not specified, the function is assumed to be non-deterministic. +The property is also useful for indexing the result of the function in the form of indexed computed columns and indexed views. If this property is not specified, the function is assumed to be non-deterministic. -Functions that access local data can be deterministic. The data access characteristic is captured separately by the and properties. +Functions that access local data can be deterministic. The data access characteristic is captured separately by the and properties. -Note that data access to remote servers (for example, using a to connect to another SQL Server instance) is available in user-defined functions. However, you must still honor the declaration. If the common language runtime (CLR) function is marked as deterministic, it should not cause side-effects in the remote server. While side-effects against the context connection are restricted, SQL Server will not enforce the restriction for side-effects over remote connections. +Note that data access to remote servers (for example, using a to connect to another SQL Server instance) is available in user-defined functions. However, you must still honor the declaration. If the common language runtime (CLR) function is marked as deterministic, it should not cause side-effects in the remote server. While side-effects against the context connection are restricted, SQL Server will not enforce the restriction for side-effects over remote connections. The default value of this attribute is `false`. @@ -84,22 +85,24 @@ The default value of this attribute is `false`. ## Remarks This attribute is used only by Microsoft Visual Studio to automatically register the specified method as a user-defined function. It is not used by SQL Server. -Thee following example specifies that the user-defined function is referenced using the name `sp_scalarFunc`. +The following example specifies that the user-defined function is referenced using the name `sp_scalarFunc`. ## Examples -[!code-csharp[SqlFunction#10](~/../sqlclient/doc/samples/SqlFunction.cs#10)] +[!code-csharp[SqlFunctionAttribute Samples#10](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs#10)] +[!code-vb[SqlFunctionAttribute Samples#10](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb#10)] + ]]> Indicates whether the function requires access to data stored in the system catalogs or virtual system tables of SQL Server. - .: Does not access system data. .: Only reads system data. + .: Does not access system data. .: Only reads system data. . +The default is . ]]> @@ -116,7 +119,9 @@ This attribute is used only by Microsoft Visual Studio to automatically register The following example specifies that the user-defined function is referenced using the name `sp_tableFunc`. The `TableDefinition` property has the value `letter nchar(1)`. ## Examples -[!code-csharp[SqlFunction#11](~/../sqlclient/doc/samples/SqlFunction.cs#11)] + +[!code-csharp[SqlFunctionAttribute Samples#11](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs#11)] +[!code-vb[SqlFunctionAttribute Samples#11](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb#11)] ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlMethodAttribute.xml similarity index 63% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlMethodAttribute.xml index 95f73cda49..55a9858ded 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlMethodAttribute.xml @@ -7,14 +7,14 @@ should be used on the setter or the getter directly. +For a property, the should be used on the setter or the getter directly. - inherits from a , so inherits the `FillRowMethodName` and `TableDefinition` fields from . Note that it is not possible to write a table-valued method, although the names of these fields might suggest that it is possible. + inherits from a , so inherits the `FillRowMethodName` and `TableDefinition` fields from . Note that it is not possible to write a table-valued method, although the names of these fields might suggest that it is possible. ## Examples The following example shows a UDT method that is attributed to indicate that the method will not be invoked on null instances of the type, that the method will not change the state of the type, and that the method will not be called when `null` parameters are supplied to the method invocation. -[!code-csharp[SqlMethod Sample#1](~/../sqlclient/doc/samples/SqlMethod.cs#1)] +[!code-csharp[SqlMethodAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs#1)] ]]> @@ -43,11 +43,11 @@ The default value of the `InvokeIfReceiverIsNull` property is `false`. That is, property is set to `true` and the return type of the method is `void`, SQL Server marks the method as a mutator. A mutator method is one that causes a state change in the UDT instance. Mutator methods can be called in assignment statements or data modification statements, but cannot be used in queries. If a method is marked as a mutator but does not return void, then CREATE TYPE does not fail with an error. Even though a returned value other than `void` does not raise an error, the returned value is not accessible and cannot be used. +If the property is set to `true` and the return type of the method is `void`, SQL Server marks the method as a mutator. A mutator method is one that causes a state change in the UDT instance. Mutator methods can be called in assignment statements or data modification statements, but cannot be used in queries. If a method is marked as a mutator but does not return void, then CREATE TYPE does not fail with an error. Even though a returned value other than `void` does not raise an error, the returned value is not accessible and cannot be used. -The default value of the property is `false`. +The default value of the property is `false`. -A property can be a mutator if is used on the setter and is set to `true`. However, a property setter is implicitly treated as a mutator, so it is not necessary to set the property of the to `true`. +A property can be a mutator if is used on the setter and is set to `true`. However, a property setter is implicitly treated as a mutator, so it is not necessary to set the property of the to `true`. ]]> @@ -59,7 +59,7 @@ A property can be a mutator if property is `true`. +The default value of the property is `true`. ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.xml similarity index 70% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.xml index 5822bac69d..a0a5eafe1b 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.xml @@ -7,30 +7,32 @@ custom attribute. Every user-defined aggregate must be annotated with this attribute. +SQL Server creates a user-defined aggregate that is bound to the class definition that has the custom attribute. Every user-defined aggregate must be annotated with this attribute. See "CLR User-Defined Aggregates" in SQL Server 2005 Books Online for more information on user-defined aggregates and examples. ## Examples -The following example shows the attribute for a user-defined aggregate. The aggregate uses custom serialization, has a maximum size of 8000 bytes when serialized, and is invariant to nulls, duplicates, and order. +The following example shows the attribute for a user-defined aggregate. The aggregate uses custom serialization, has a maximum size of 8000 bytes when serialized, and is invariant to nulls, duplicates, and order. -[!code-csharp[SqlUserDefinedAggregate Sample#1](~/../sqlclient/doc/samples/SqlUserDefinedAggregate.cs#1)] +[!code-csharp[SqlUserDefinedAggregateAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs#1)] +[!code-vb[SqlUserDefinedAggregateAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb#1)] ]]> - One of the values representing the serialization format of the aggregate. + One of the values representing the serialization format of the aggregate. A required attribute on a user-defined aggregate, used to indicate that the given type is a user-defined aggregate and the storage format of the user-defined aggregate. - The serialization format as a . - A representing the serialization format. + The serialization format as a . + A representing the serialization format. @@ -102,13 +104,13 @@ Incorrectly setting this property can result in incorrect query results. This pr ## Remarks This property does not have to be specified for Native format serialization. -You must specify the property with the UserDefined serialization . +You must specify the property with the UserDefined serialization . -The maximum allowed value for this property is specified by the field. +The maximum allowed value for this property is specified by the field. The maximum size allowed is 2 gigabytes (GB). You can specify a number from 1 to 8000 bytes, or -1 to represent a value larger than 8000 bytes, up to 2 gigabytes. -For an aggregate with user-defined serialization specified, refers to the total size of the serialized data. Consider an aggregate serializing a string of 10 characters (). When the string is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized data must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. +For an aggregate with user-defined serialization specified, refers to the total size of the serialized data. Consider an aggregate serializing a string of 10 characters (). When the string is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized data must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. ]]> diff --git a/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.xml new file mode 100644 index 0000000000..ffd70496cc --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.xml @@ -0,0 +1,139 @@ + + + + + Used to mark a type definition in an assembly as a user-defined type (UDT) in SQL Server. The properties on the attribute reflect the physical characteristics used when the type is registered with SQL Server. This class cannot be inherited. + + custom attribute. Every UDT must be annotated with this attribute. See [CLR User-Defined Types](https://go.microsoft.com/fwlink/?LinkId=128028) for more information about UDTs, including an example of a UDT. + +## Examples +The following example shows the `UserDefinedType` attribute of the Point UDT. The UDT is byte-ordered, is named "Point", has a validation method named "ValidatePoint", and uses the native serialization format. + +[!code-csharp[SqlUserDefinedTypeAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs#1)] +[!code-vb[SqlUserDefinedTypeAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb#1)] + +]]> + + + + One of the values representing the serialization format of the type. + A required attribute on a user-defined type (UDT), used to confirm that the given type is a UDT and to indicate the storage format of the UDT. + + + + + + The serialization format as a . + A value representing the serialization format. + + + Indicates whether the user-defined type is byte ordered. + + if the user-defined type is byte ordered; otherwise . + + property in effect guarantees that the serialized binary data can be used for semantic ordering of the information. Thus, each instance of a byte-ordered UDT object can only have one serialized representation. When a comparison operation is performed in SQL Server on the serialized bytes, its results should be the same as if the same comparison operation had taken place in managed code. + +The following features are supported when is set to `true`: + +- The ability to create indexes on columns of this type. + +- The ability to create primary and foreign keys as well as CHECK and UNIQUE constraints on columns of this type. + +- The ability to use Transact-SQL ORDER BY, GROUP BY, and PARTITION BY clauses. In these cases, the binary representation of the type is used to determine the order. + +- The ability to use comparison operators in Transact-SQL statements. + +- The ability to persist computed columns of this type. + +Note that both the `Native` and `UserDefined` serialization formats support the following comparison operators when is set to `true`: + +- Equal to (=) + +- Not equal to (!=) + +- Greater than (>) + +- Less than (\<) + +- Greater than or equal to (>=) + +- Less than or equal to (<=) + +]]> + + + + Indicates whether all instances of this user-defined type are the same length. + + if all instances of this type are the same length; otherwise . + + + . This attribute is only relevant for UDTs with `UserDefined` serialization . + +]]> + + + + The maximum size of the instance, in bytes. + An value representing the maximum size of the instance. + + property with the `UserDefined` serialization . + +When connecting to SQL Server 2005 or earlier, must be between 1 and 8000. + +When connecting to SQL Server 2008 or later, set between 1 and 8000, for a type whose instances are always 8,000 bytes or less. For types that can have instances larger than 8000, specify -1. + +For a UDT with user-defined serialization specified, refers to the total size of the UDT in its serialized form as defined by the user. Consider a UDT with a property of a string of 10 characters (). When the UDT is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized UDT must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. + +This property should not be used with `Native` serialization . + +]]> + + + + The SQL Server name of the user-defined type. + A value representing the SQL Server name of the user-defined type. + + property is not used within SQL Server, but is used by the Microsoft Visual Studio .NET Integrated Development Environment (IDE). + +]]> + + + + The name of the method used to validate instances of the user-defined type. + A representing the name of the method used to validate instances of the user-defined type. + + + + + + diff --git a/doc/snippets/Microsoft.SqlServer.Server/SystemDataAccessKind.xml b/doc/snippets/Microsoft.SqlServer.Server/SystemDataAccessKind.xml new file mode 100644 index 0000000000..93f80783d0 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/SystemDataAccessKind.xml @@ -0,0 +1,26 @@ + + + + + Describes the type of access to system data for a user-defined method or function. + + and to indicate what type of access to system data the method or function has. + +Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function performs read-only data-access operations, such as executing SELECT statements). + + ]]> + + + + The method or function does not access system data. + + + The method or function reads system data. + + + diff --git a/porting-cheat-sheet.md b/porting-cheat-sheet.md index e6216863cd..d09c16c77e 100644 --- a/porting-cheat-sheet.md +++ b/porting-cheat-sheet.md @@ -4,6 +4,20 @@ This guide is meant to cover all namespace changes needed in client applications ## Namespace Changes needed +### Microsoft.Data.SqlClient v5.0 and newer + +| Namespace Change | Applicability | +|--|--| +| `using System.Data.SqlClient;`
`using Microsoft.Data.SqlClient;` | Applicable to all classes, enums and delegates. | +| `using Microsoft.SqlServer.Server;`
`using Microsoft.Data.SqlClient.Server;` | Applicable Classes:
`SqlDataRecord`
`SqlMetaData`

1 _All remaining types continue to be referenced from Microsoft.SqlServer.Server namespace._| +| `using System.Data.SqlTypes;`
`using Microsoft.Data.SqlTypes;` | Applicable Classes:
`SqlFileStream`| +| `using System.Data.Sql;`
`using Microsoft.Data.Sql;` | Applicable Classes:
`SqlNotificationRequest`
| +| `using System.Data;`
`using Microsoft.Data;` | Applicable Classes:
`OperationAbortedException`| + +1 Breaking change for User-Defined types and Microsoft.SqlServer.Types support over _Microsoft.Data.SqlClient v3.0.0_. + +### Microsoft.Data.SqlClient v4.0 and older + | Namespace Change | Applicability | |--|--| | `using System.Data.SqlClient;`
`using Microsoft.Data.SqlClient;` | Applicable to all classes, enums and delegates. | @@ -12,6 +26,33 @@ This guide is meant to cover all namespace changes needed in client applications | `using System.Data.Sql;`
`using Microsoft.Data.Sql;` | Applicable Classes:
`SqlNotificationRequest`
| | `using System.Data;`
`using Microsoft.Data;` | Applicable Classes:
`OperationAbortedException`| +## Configuration + +For .NET Framework projects it may be necessary to include the following in your App.config or Web.config file: + +``` xml + + ... + + + + + + ... + +``` + +## Functionality Changes + +| System.Data.SqlClient | Microsoft.Data.SqlClient | +|--|--| +| Can use DateTime object as value for SqlParameter with type `DbType.Time`. | Must use TimeSpan object as value for SqlParameter with type `DbType.Time`. | +| Using DateTime object as value for SqlParameter with type `DbType.Date` would send date and time to SQL Server. | DateTime object's time components will be truncated when sent to SQL Server using `DbType.Date`. | +| `Encrypt` defaults to `false`. | Starting in v4.0, default encryption settings were made more secure, requiring opt-in to non-encrypted connections. `Encrypt` defaults to `true` and the driver will always validate the server certificate based on `TrustServerCertificate`. (Previously, server certificates would only be validated if `Encrypt` was also `true`.)

If you need to turn off encryption, you must specify `Encrypt=false`. If you use encryption with a self-signed certificate on the server, you must specify `TrustServerCertificate=true`.

In v5.0, `SqlConnectionStringBuilder.Encrypt` is no longer a `bool`. It's a `SqlConnectionEncryptOption` with multiple values to support `Strict` encryption mode (TDS 8.0). It uses implicit conversion operators to remain code-backwards compatible, but it was a binary breaking change, requiring a recompile of applications. | + ## Contribute to this Cheat Sheet We would love the SqlClient community to help enhance this cheat sheet by contributing experiences and challenges faced when porting their applications. diff --git a/release-notes/1.1/1.1.4.md b/release-notes/1.1/1.1.4.md new file mode 100644 index 0000000000..a89b6473d0 --- /dev/null +++ b/release-notes/1.1/1.1.4.md @@ -0,0 +1,62 @@ +# Release Notes + +## General Availability of Microsoft.Data.SqlClient 1.1.4 released 10 March 2021 + +This update brings the below changes over the previous release: + +### Fixed +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#950](https://github.com/dotnet/SqlClient/pull/950) +- Fixed MARS header contains errors issue against .NET Framework 4.8+ [#959](https://github.com/dotnet/SqlClient/pull/959) + + +## Target Platform Support + +- .NET Framework 4.6+ +- .NET Core 2.1+ (Windows x86, Windows x64, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- System.Data.Common 4.3.0 +- Microsoft.Data.SqlClient.SNI [1.1.0,1.2.0) +- Microsoft.Identity.Client 3.0.8 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.5.0 +- Microsoft.IdentityModel.JsonWebTokens 5.5.0 + +#### .NET Core + +- Microsoft.Win32.Registry 4.5.0 +- runtime.native.System.Data.SqlClient.sni 4.4.0 +- System.Security.Principal.Windows 4.5.0 +- System.Text.Encoding.CodePages 4.5.0 +- System.Configuration.ConfigurationManager 4.5.0 +- Microsoft.Identity.Client 3.0.8 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.5.0 +- Microsoft.IdentityModel.JsonWebTokens 5.5.0 + +#### .NET Standard + +- Microsoft.Win32.Registry 4.5.0 +- runtime.native.System.Data.SqlClient.sni 4.4.0 +- System.Buffers 4.4.0 +- System.Diagnostics.DiagnosticSource 4.5.0 +- System.Memory 4.5.1 +- System.Security.Principal.Windows 4.5.0 +- System.Text.Encoding.CodePages 4.5.0 +- System.Configuration.ConfigurationManager 4.5.0 +- Microsoft.Identity.Client 3.0.8 + +### Always Encrypted with secure enclaves + +In general, existing documentation that uses System.Data.SqlClient on .NET Framework should now work with .NET Core, too. + +- [Develop using Always Encrypted with .NET Framework Data Provider](https://docs.microsoft.com/sql/relational-databases/security/encryption/develop-using-always-encrypted-with-net-framework-data-provider) +- [Always Encrypted: Protect sensitive data and store encryption keys in the Windows certificate store](https://docs.microsoft.com/azure/sql-database/sql-database-always-encrypted) + +In order to use the enclave feature, the connection string should include the required attestation protocol and attestation URL. + +Example: + +- `Attestation Protocol=HGS;Enclave Attestation Url=` diff --git a/release-notes/1.1/1.1.md b/release-notes/1.1/1.1.md index 1c2df922b9..5ad6dca184 100644 --- a/release-notes/1.1/1.1.md +++ b/release-notes/1.1/1.1.md @@ -4,6 +4,7 @@ The following Microsoft.Data.SqlClient 1.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/03/10 | 1.1.4 | [release notes](1.1.4.md) | | 2020/05/15 | 1.1.3 | [release notes](1.1.3.md) | | 2020/04/15 | 1.1.2 | [release notes](1.1.2.md) | | 2020/02/14 | 1.1.1 | [release notes](1.1.1.md) | diff --git a/release-notes/1.1/README.md b/release-notes/1.1/README.md index 1c2df922b9..5ad6dca184 100644 --- a/release-notes/1.1/README.md +++ b/release-notes/1.1/README.md @@ -4,6 +4,7 @@ The following Microsoft.Data.SqlClient 1.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2021/03/10 | 1.1.4 | [release notes](1.1.4.md) | | 2020/05/15 | 1.1.3 | [release notes](1.1.3.md) | | 2020/04/15 | 1.1.2 | [release notes](1.1.2.md) | | 2020/02/14 | 1.1.1 | [release notes](1.1.1.md) | diff --git a/release-notes/2.1/2.1.1.md b/release-notes/2.1/2.1.1.md new file mode 100644 index 0000000000..35de2ed583 --- /dev/null +++ b/release-notes/2.1/2.1.1.md @@ -0,0 +1,75 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.1.1 released 18 December 2020 + +This update brings the below changes over the previous stable release: + +### Fixed +- Fixed issue with System-Assigned Managed Identity in Azure Functions [#841](https://github.com/dotnet/SqlClient/pull/841) +- Fixed issue with Kerberos Authentication for .NET Core in Unix environments [#848](https://github.com/dotnet/SqlClient/pull/848) +- Fixed issue with TCP Keep Alive for .NET Core in Unix environments [#855](https://github.com/dotnet/SqlClient/pull/855) + +### Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/2.1/2.1.2.md b/release-notes/2.1/2.1.2.md new file mode 100644 index 0000000000..151607c779 --- /dev/null +++ b/release-notes/2.1/2.1.2.md @@ -0,0 +1,84 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.1.2 released 3 March 2021 + +This update brings the below changes over the previous stable release: + +### Fixed +- Fixed issue connecting with instance name from a Linux/macOS environment [#874](https://github.com/dotnet/SqlClient/pull/874) +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#929](https://github.com/dotnet/SqlClient/pull/929) +- Fixed a vulnerability by prohibiting `DtdProcessing` on `XmlTextReader` instances in .NET Core [#885](https://github.com/dotnet/SqlClient/pull/885) +- Fixed Kerberos authentication when an SPN does not contain the port [#935](https://github.com/dotnet/SqlClient/pull/935) +- Fixed missing error messages in Managed SNI [#883](https://github.com/dotnet/SqlClient/pull/883) +- Fixed missing `System.Runtime.Caching` dependency for .NET Standard assemblies [#878](https://github.com/dotnet/SqlClient/pull/878) +- Fixed event source tracing issues [#941](https://github.com/dotnet/SqlClient/pull/941) +- Fixed MARS header contains errors issue against .NET Framework 4.8.1 [#928](https://github.com/dotnet/SqlClient/pull/928) + + + +### Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/2.1/2.1.3.md b/release-notes/2.1/2.1.3.md new file mode 100644 index 0000000000..5034cfa306 --- /dev/null +++ b/release-notes/2.1/2.1.3.md @@ -0,0 +1,77 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.1.3 released 21 May 2021 + +This update brings the below changes over the previous stable release: + +### Fixed + +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1051](https://github.com/dotnet/SqlClient/pull/1051) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1049](https://github.com/dotnet/SqlClient/pull/1049) + +### Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/2.1/2.1.4.md b/release-notes/2.1/2.1.4.md new file mode 100644 index 0000000000..77c3901166 --- /dev/null +++ b/release-notes/2.1/2.1.4.md @@ -0,0 +1,81 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.1.4 released 20 September 2021 + +This update brings the below changes over the previous stable release: + +### Fixed + +- Fixed issue with connection encryption to ensure connections fail when encryption is required. [#1232](https://github.com/dotnet/SqlClient/pull/1232) [Read more](#ensure-connections-fail-when-encryption-is-required) +- Fixed issue where connection goes to unusable state. [#1239](https://github.com/dotnet/SqlClient/pull/1239) + +### Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Ensure connections fail when encryption is required + +In scenarios where client encryption libraries were disabled or unavailable, it was possible for unencrypted connections to be made when Encrypt was set to true or the server required encryption. + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/2.1/2.1.5.md b/release-notes/2.1/2.1.5.md new file mode 100644 index 0000000000..1617db1db9 --- /dev/null +++ b/release-notes/2.1/2.1.5.md @@ -0,0 +1,82 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.1.5 released 30 August 2022 + +This update brings the below changes over the previous stable release: + +### Fixed + +- Added CommandText length validation when using stored procedure command types. [#1726](https://github.com/dotnet/SqlClient/pull/1726) +- Fixed Kerberos authentication failure when using .NET 6. [#1727](https://github.com/dotnet/SqlClient/pull/1727) +- Removed union overlay design and used reflection in `SqlTypeWorkarounds`. [#1729](https://github.com/dotnet/SqlClient/pull/1729) + +### Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Ensure connections fail when encryption is required + +In scenarios where client encryption libraries were disabled or unavailable, it was possible for unencrypted connections to be made when Encrypt was set to true or the server required encryption. + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/2.1/2.1.md b/release-notes/2.1/2.1.md index 1be1be4fb0..2c149e041b 100644 --- a/release-notes/2.1/2.1.md +++ b/release-notes/2.1/2.1.md @@ -4,6 +4,11 @@ The following Microsoft.Data.SqlClient 2.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2022/08/30 | 2.1.5 | [release notes](2.1.5.md) | +| 2021/09/20 | 2.1.4 | [release notes](2.1.4.md) | +| 2021/05/21 | 2.1.3 | [release notes](2.1.3.md) | +| 2021/03/03 | 2.1.2 | [release notes](2.1.2.md) | +| 2020/12/18 | 2.1.1 | [release notes](2.1.1.md) | | 2020/11/19 | 2.1.0 | [release notes](2.1.0.md) | The following Microsoft.Data.SqlClient 2.1 preview releases have been shipped: diff --git a/release-notes/2.1/README.md b/release-notes/2.1/README.md index 1be1be4fb0..2c149e041b 100644 --- a/release-notes/2.1/README.md +++ b/release-notes/2.1/README.md @@ -4,6 +4,11 @@ The following Microsoft.Data.SqlClient 2.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2022/08/30 | 2.1.5 | [release notes](2.1.5.md) | +| 2021/09/20 | 2.1.4 | [release notes](2.1.4.md) | +| 2021/05/21 | 2.1.3 | [release notes](2.1.3.md) | +| 2021/03/03 | 2.1.2 | [release notes](2.1.2.md) | +| 2020/12/18 | 2.1.1 | [release notes](2.1.1.md) | | 2020/11/19 | 2.1.0 | [release notes](2.1.0.md) | The following Microsoft.Data.SqlClient 2.1 preview releases have been shipped: diff --git a/release-notes/3.0/3.0.0-preview1.md b/release-notes/3.0/3.0.0-preview1.md new file mode 100644 index 0000000000..39737afecf --- /dev/null +++ b/release-notes/3.0/3.0.0-preview1.md @@ -0,0 +1,202 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.0-preview1.21075.2 released 15 March 2021 + +This update brings the below changes over the previous release: + +### Breaking Changes over stable release v2.1 +- The minimum supported .NET Framework version has been increased to v4.6.1. .NET Framework v4.6.0 is no longer supported. [#899](https://github.com/dotnet/SqlClient/pull/899) + +### Added +- Added support for Configurable Retry Logic [#693](https://github.com/dotnet/SqlClient/pull/693) [#966](https://github.com/dotnet/SqlClient/pull/966) [Read more](#configurable-retry-logic) +- Added support for Event counters in .NET Core 3.1+ and .NET Standard 2.1+ [#719](https://github.com/dotnet/SqlClient/pull/719) [Read more](#event-counters) +- Added support for Assembly Context Unloading in .NET Core [#913](https://github.com/dotnet/SqlClient/pull/913) +- Added missing `System.Runtime.Caching` dependency for .NET Standard assemblies [#877](https://github.com/dotnet/SqlClient/pull/877) + +### Fixed +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#906](https://github.com/dotnet/SqlClient/pull/906) +- Fixed Kerberos authentication issues when configured Server Principal Name (SPN) didn't contain default port [#930](https://github.com/dotnet/SqlClient/pull/930) +- Fixed MARS header errors when `MakeReadAsyncBlocking` App Context switch is set to `false` [#910](https://github.com/dotnet/SqlClient/pull/910) [#922](https://github.com/dotnet/SqlClient/pull/922) +- Fixed unwanted exceptions being thrown from `SqlDataReader.Dispose` [#920](https://github.com/dotnet/SqlClient/pull/920) +- Fixed issues connecting to SQL Server instance with instance name specified from Unix environment [#870](https://github.com/dotnet/SqlClient/pull/870) +- Fixed TCP Keep Alive issues in .NET Core [#854](https://github.com/dotnet/SqlClient/pull/854) +- Fixed Kerberos Authentication issues caused due to regression [#845](https://github.com/dotnet/SqlClient/pull/845) +- Fixed issues with System-Assigned Managed Identity in Azure Functions [#829](https://github.com/dotnet/SqlClient/pull/829) +- Fixed missing error messages in Managed SNI [#882](https://github.com/dotnet/SqlClient/pull/882) +- Fixed event source trace string issue [#940](https://github.com/dotnet/SqlClient/pull/940) + +### Changes +- Changed App Context switch `MakeReadAsyncBlocking` default to `false` [#937](https://github.com/dotnet/SqlClient/pull/937) +- Replaced usage of `BinaryFormatter` with `DataContractSerializer` [#869](https://github.com/dotnet/SqlClient/pull/869) +- Prohibited `DtdProcessing` on `XmlTextReader` instance in .NET Core [#884](https://github.com/dotnet/SqlClient/pull/884) +- Improved performance by reducing memory allocations in `SerializeEncodingChar`/`WriteEncodingChar` and some options boxing [#785](https://github.com/dotnet/SqlClient/pull/785) +- Improved performance by preventing orphaned active packets being GC'ed without clear [#888](https://github.com/dotnet/SqlClient/pull/888) +- Various performance improvements [#889](https://github.com/dotnet/SqlClient/pull/889) [#900](https://github.com/dotnet/SqlClient/pull/900) +- Partial event source tracing improvements in .NET Core [#867](https://github.com/dotnet/SqlClient/pull/867) [#897](https://github.com/dotnet/SqlClient/pull/897) +- Changes to share common files between NetFx and NetCore source code [#827](https://github.com/dotnet/SqlClient/pull/827) [#835](https://github.com/dotnet/SqlClient/pull/835) [#838](https://github.com/dotnet/SqlClient/pull/838) [#881](https://github.com/dotnet/SqlClient/pull/881) + + +## New features over stable release v2.1 + +### Configurable Retry Logic + +This new feature introduces configurable support for client applications to retry on "transient" or "retriable" errors. Configuration can be done through code or app config files and retry operations can be applied to opening a connection or executing a command. This feature is disabled by default and is currently in preview. To enable this support, client applications must turn on the following safety switch: + +`AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.EnableRetryLogic", true);` + +Once the .NET AppContext switch is enabled, a retry logic policy can be defined for `SqlConnection` and `SqlCommand` independently, or together using various customization options. + +New public APIs are introduced in `SqlConnection` and `SqlCommand` for registering a custom `SqlRetryLogicBaseProvider` implementation: + +```cs +public SqlConnection +{ + public SqlRetryLogicBaseProvider RetryLogicProvider; +} + +public SqlCommand +{ + public SqlRetryLogicBaseProvider RetryLogicProvider; +} + +``` + +API Usage examples can be found here: +[SqlConnection retry sample](..\..\doc\samples\SqlConfigurableRetryLogic_OpenConnection.cs) +[SqlCommand retry sample](..\..\doc\samples\SqlConfigurableRetryLogic_SqlCommand.cs) +[Sample for retry logic options](..\..\doc\samples\SqlConfigurableRetryLogic_SqlRetryLogicOptions.cs) + +New configuration sections have also been introduced to do the same registration from configuration files, without having to modify existing code: + +```xml +
+ +
+``` + +A simple example of using the new configuration sections in configuration files is below: + +```xml + + + +
+
+ +
+ + + + + + + + + + + +``` + +Alternatively, applications can implement their own provider of the `SqlRetryLogicBaseProvider` base class, and register it with `SqlConnection`/`SqlCommand`. + +### Event Counters + +The following counters are now available for applications targeting .NET Core 3.1+ and .NET Standard 2.1+: + +- **HardConnects**: The number of physical connections that are being made. +- **HardDisconnects**: The number of actual disconnects that are being made. +- **ActiveHardConnections**: The number of active physical connections that are being made. +- **SoftConnects**: The number of connections acquired from the pool. +- **SoftDisconnects**: The number of connections returned to the pool. +- **ActiveSoftConnections**: The total number of active pooled connections. +- **NumberOfNonPooledConnections**: The total number of non-pooled connections. +- **NumberOfPooledConnections**: The total number of pooled connections. +- **NumberOfActiveConnectionPoolGroups**: The number of unique connection pool groups. +- **NumberOfInactiveConnectionPoolGroups**: The number of unique connection pool groups to be pruned. +- **NumberOfActiveConnectionPools**: The number of active connection pools. +- **NumberOfInactiveConnectionPools**: The number of inactive connection pools. +- **NumberOfActiveConnections**: The number of active connections currently in-use. +- **NumberOfFreeConnections**: The number of free connections available for use. +- **NumberOfStasisConnections**: The number of active free connections with open transactions. +- **NumberOfReclaimedConnections**: The number of connections reclaimed from GC'd external connections. + +These counters can be used with .NET Core global CLI tools: `dotnet-counters` and `dotnet-trace` in Windows or Linux and PerfView in Windows, using `Microsoft.Data.SqlClient.EventSource` as the provider name. + +```cmd +dotnet-counters monitor Microsoft.Data.SqlClient.EventSource -p +PerfView /onlyProviders=*Microsoft.Data.SqlClient.EventSource:EventCounterIntervalSec=1 collect +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework 4.6.1 + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/3.0/3.0.0-preview2.md b/release-notes/3.0/3.0.0-preview2.md new file mode 100644 index 0000000000..df52852bd7 --- /dev/null +++ b/release-notes/3.0/3.0.0-preview2.md @@ -0,0 +1,116 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.0-preview2.21106.5 released 16 April 2021 + +This update brings the below changes over the previous release: + +### Breaking Changes over preview release V3.0.0-preview1 +- `User Id` connection property now requires `Client Id` instead of `Object Id` for **User-Assigned Managed Identity** [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- `SqlDataReader` now returns a `DBNull` value instead of an empty `byte[]`. Legacy behavior can be enabled by setting `AppContext` switch **Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior** [#998](https://github.com/dotnet/SqlClient/pull/998) [Read more](#enabling-row-version-null-behavior) + +### Added +**Microsoft.Data.SqlClient** now depends on **Azure.Identity** library to acquire a token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- Upgraded Native SNI dependency to **v3.0.0-preview1** along with enhanced event tracing support [#1006](https://github.com/dotnet/SqlClient/pull/1006) [Read more](#event-tracing-improvements-in-sni.dll) + +### Fixed +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1023](https://github.com/dotnet/SqlClient/pull/1023) +- Fixed derived parameters containing incorrect typename [#1020](https://github.com/dotnet/SqlClient/pull/1020) +- Fixed server connection leak possibilities when an exception occurs in pooling layer [#890](https://github.com/dotnet/SqlClient/pull/890) +- Fixed IP connection resolving logic in .NET Core [#1016](https://github.com/dotnet/SqlClient/pull/1016) [#1031](https://github.com/dotnet/SqlClient/pull/1031) + +### Changed +- Performance improvements in `SqlDateTime` to `DateTime` internal conversion method [#912](https://github.com/dotnet/SqlClient/pull/912) +- Improved memory allocation by avoiding unnecessary context switching [1008](https://github.com/dotnet/SqlClient/pull/1008) +- Updated `Microsoft.Identity.Client` version from **4.21.1** to **4.22.0** [#1036](https://github.com/dotnet/SqlClient/pull/1036) +- Various performance improvements [#963](https://github.com/dotnet/SqlClient/pull/963) [#996](https://github.com/dotnet/SqlClient/pull/996) [#1004](https://github.com/dotnet/SqlClient/pull/1004) [#1012](https://github.com/dotnet/SqlClient/pull/1012) [#1017](https://github.com/dotnet/SqlClient/pull/1017) +- Event source tracing improvements [#1018](https://github.com/dotnet/SqlClient/pull/1018) +- Changes to share common files between NetFx and NetCore source code [#871](https://github.com/dotnet/SqlClient/pull/871) [#887](https://github.com/dotnet/SqlClient/pull/887) + +### Azure Identity dependency introduction +**Microsoft.Data.SqlClient** now depends on the **Azure.Identity** library to acquire tokens for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. This change brings the following changes to the public surface area: + +- **Breaking Change** + The "User Id" connection property now requires "Client Id" instead of "Object Id" for "User-Assigned Managed Identity". +- **Public API** + New read-only public property: `SqlAuthenticationParameters.ConnectionTimeout` +- **Dependency** + Azure.Identity v1.3.0 + +### Event tracing improvements in SNI.dll +`Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) versions have been updated to `v3.0.0-preview1.21104.2`. Event tracing in SNI.dll will no longer be enabled through a client application. Subscribing a session to the **Microsoft.Data.SqlClient.EventSource** provider through tools like xperf or perfview will be sufficient. + +### Enabling row version null behavior +`SqlDataReader` returns a `DBNull` value instead of an empty `byte[]`. To enable the legacy behavior, you must enable the following AppContext switch on application startup: +**"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"** + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework 4.6.1 + +- Microsoft.Data.SqlClient.SNI 3.0.0-preview1.21104.2 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/3.0/3.0.0-preview3.md b/release-notes/3.0/3.0.0-preview3.md new file mode 100644 index 0000000000..6c213d0c3f --- /dev/null +++ b/release-notes/3.0/3.0.0-preview3.md @@ -0,0 +1,176 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.0-preview3.21140.5 released 20 May 2021 + +This update brings the below changes over the previous release: + +### Added + +- Added support for "Active Directory Default" authentication mode [#1043](https://github.com/dotnet/SqlClient/pull/1043) [Read more](#active-directory-default-authentication-support) +- Added support for connection-level and command-level registration of custom key store providers to enable multi-tenant applications to control key store access [#1045](https://github.com/dotnet/SqlClient/pull/1045) [#1056](https://github.com/dotnet/SqlClient/pull/1056) [#1078](https://github.com/dotnet/SqlClient/pull/1078) [Read more](#custom-master-key-store-provider-registration-enhancements) +- Added IP address preference support for TCP connections [#1015](https://github.com/dotnet/SqlClient/pull/1015) [Read more](#ip-address-preference) + +### Fixed + +- Fixed corrupted connection issue when an exception occurs during RPC execution with TVP types [#1068](https://github.com/dotnet/SqlClient/pull/1068) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1042](https://github.com/dotnet/SqlClient/pull/1042) + +### Changed + +- Updated error messages for enclave exceptions to include a link to a troubleshooting guide. [#994](https://github.com/dotnet/SqlClient/pull/994) +- Changes to share common files between projects [#1022](https://github.com/dotnet/SqlClient/pull/1022) [#1038](https://github.com/dotnet/SqlClient/pull/1038) [#1040](https://github.com/dotnet/SqlClient/pull/1040) [#1033](https://github.com/dotnet/SqlClient/pull/1033) [#1028](https://github.com/dotnet/SqlClient/pull/1028) [#1039](https://github.com/dotnet/SqlClient/pull/1039) + +### Active Directory Default authentication support + +This PR introduces a new SQL Authentication method, **Active Directory Default**. This authentication mode widens the possibilities of user authentication, extending login solutions to the client environment, Visual Studio Code, Visual Studio, Azure CLI etc. + +With this authentication mode, the driver acquires a token by passing "[DefaultAzureCredential](https://docs.microsoft.com/dotnet/api/azure.identity.defaultazurecredential)" from the Azure Identity library to acquire an access token. This mode attempts to use these credential types to acquire an access token in the following order: + +- **EnvironmentCredential** + - Enables authentication to Azure Active Directory using client and secret, or username and password, details configured in the following environment variables: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_CLIENT_CERTIFICATE_PATH, AZURE_USERNAME, AZURE_PASSWORD ([More details](https://docs.microsoft.com/dotnet/api/azure.identity.environmentcredential)) +- **ManagedIdentityCredential** + - Attempts authentication to Azure Active Directory using a managed identity that has been assigned to the deployment environment. **"Client Id" of "User Assigned Managed Identity"** is read from the **"User Id" connection property**. +- **SharedTokenCacheCredential** + - Authenticates using tokens in the local cache shared between Microsoft applications. +- **VisualStudioCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio +- **VisualStudioCodeCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio Code. +- **AzureCliCredential** + - Enables authentication to Azure Active Directory using Azure CLI to obtain an access token. + +> InteractiveBrowserCredential is disabled in the driver implementation of "Active Directory Default", and "Active Directory Interactive" is the only option available to acquire a token using MFA/Interactive authentication.* + +> Further customization options are not available at the moment. + +### Custom master key store provider registration enhancements + +Microsoft.Data.SqlClient now offers more control of where master key store providers are accessible in an application in order to better support multi-tenant applications and their use of column encryption/decryption. The following APIs are introduced to allow registration of custom master key store providers on instances of `SqlConnection` and `SqlCommand`: + +```cs +public class SqlConnection +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) +} +public class SqlCommand +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(IDictionary customProviders) +} +``` + +The static API on `SqlConnection`, i.e. `SqlConnection.RegisterColumnEncryptionKeyStoreProviders` to register custom master key store providers globally continues to be supported. The column encryption key cache maintained globally only applies to globally registered providers. + +#### Column master key store provider registration precedence + +The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed. + +Custom master key store providers can be registered with the driver at three different layers. The global level is as it currently is. The new per-connection and per-command level registrations will be empty initially and can be set more than once. + +The precedence of the three registrations are as follows: + +- The per-command registration will be checked if it is not empty. +- If the per-command registration is empty, the per-connection registration will be checked if it is not empty. +- If the per-connection registration is empty, the global registration will be checked. + +Once any key store provider is found at a registration level, the driver will **NOT** fall back to the other registrations to search for a provider. If providers are registered but the proper provider is not found at a level, an exception will be thrown containing only the registered providers in the registration that was checked. + +#### Column encryption key cache precedence + +The column encryption keys (CEKs) for custom key store providers registered using the new instance-level APIs will not be cached by the driver. The key store providers need to implement their own cache to gain performance. This local cache of column encryption keys implemented by custom key store providers will be disabled by the driver if the key store provider instance is registered in the driver at the global level. + +A new API has also been introduced on the `SqlColumnEncryptionKeyStoreProvider` base class to set the cache time to live: + +```cs +public abstract class SqlColumnEncryptionKeyStoreProvider +{ + // The default value of Column Encryption Key Cache Time to Live is 0. + // Provider's local cache is disabled for globally registered providers. + // Custom key store provider implementation must include column encryption key cache to provide caching support to locally registered providers. + public virtual TimeSpan? ColumnEncryptionKeyCacheTtl { get; set; } = new TimeSpan(0); +} +``` + +### IP Address preference + +A new connection property `IPAddressPreference` is introduced to specify the IP address family preference to the driver when establishing TCP connections. If `Transparent Network IP Resolution` (in .NET Framework) or `Multi Subnet Failover` is set to `true`, this setting has no effect. Below are the three accepted values for this property: + +- **IPv4First** + - This is the default preference value. The driver will use resolved IPv4 addresses first. If none of them can be connected to successfully, it will try resolved IPv6 addresses. + +- **IPv6First** + - The driver will use resolved IPv6 addresses first. If none of them can be connected to successfully, it will try resolved IPv4 addresses. + +- **UsePlatformDefault** + - The driver will try IP addresses in the order received from the DNS resolution response. + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework 4.6.1 + +- Microsoft.Data.SqlClient.SNI 3.0.0-preview1.21104.2 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0-preview1.21104.2 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/3.0/3.0.0.md b/release-notes/3.0/3.0.0.md new file mode 100644 index 0000000000..ef090c2f3c --- /dev/null +++ b/release-notes/3.0/3.0.0.md @@ -0,0 +1,346 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.0 released 09 June 2021 + +This update brings the below changes over the previous preview release: + +### Added + +- Added support for column encryption key caching when the server supports retrying queries that require enclave computations [#1062](https://github.com/dotnet/SqlClient/pull/1062) +- Added support for configurable retry logic configuration file in .NET Standard [#1090](https://github.com/dotnet/SqlClient/pull/1090) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v3.0.0` [#1102](https://github.com/dotnet/SqlClient/pull/1102) +- Improved event counter display information [#1091](https://github.com/dotnet/SqlClient/pull/1091) + +### Breaking Changes + +- Modified column encryption key store provider registrations to give built-in system providers precedence over providers registered on connection and command instances. [#1101](https://github.com/dotnet/SqlClient/pull/1101) + +## Summary of changes in 3.0 + +All changes in Microsoft.Data.SqlClient v3.0 over v2.1: + +### New Additions + +- Added support for Configurable Retry Logic [#693](https://github.com/dotnet/SqlClient/pull/693) [#966](https://github.com/dotnet/SqlClient/pull/966) [Read more](#configurable-retry-logic) +- Added support for Event counters in .NET Core 3.1+ and .NET Standard 2.1+ [#719](https://github.com/dotnet/SqlClient/pull/719) [Read more](#event-counters) +- Added support for Assembly Context Unloading in .NET Core [#913](https://github.com/dotnet/SqlClient/pull/913) +- Added missing `System.Runtime.Caching` dependency for .NET Standard assemblies [#877](https://github.com/dotnet/SqlClient/pull/877) +- **Microsoft.Data.SqlClient** now depends on **Azure.Identity** library to acquire a token for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- Upgraded Native SNI dependency to **v3.0.0-preview1** along with enhanced event tracing support [#1006](https://github.com/dotnet/SqlClient/pull/1006) [Read more](#event-tracing-improvements-in-sni.dll) +- Added support for "Active Directory Default" authentication mode [#1043](https://github.com/dotnet/SqlClient/pull/1043) [Read more](#active-directory-default-authentication-support) +- Added support for connection-level and command-level registration of custom key store providers to enable multi-tenant applications to control key store access [#1045](https://github.com/dotnet/SqlClient/pull/1045) [#1056](https://github.com/dotnet/SqlClient/pull/1056) [#1078](https://github.com/dotnet/SqlClient/pull/1078) [Read more](#custom-master-key-store-provider-registration-enhancements) +- Added IP address preference support for TCP connections [#1015](https://github.com/dotnet/SqlClient/pull/1015) [Read more](#ip-address-preference) + +### Bug Fixes + +- Fixed wrong results issues by changing the timeout timer to ensure a correct execution state [#906](https://github.com/dotnet/SqlClient/pull/906) +- Fixed Kerberos authentication issues when configured Server Principal Name (SPN) didn't contain default port [#930](https://github.com/dotnet/SqlClient/pull/930) +- Fixed MARS header errors when `MakeReadAsyncBlocking` App Context switch is set to `false` [#910](https://github.com/dotnet/SqlClient/pull/910) [#922](https://github.com/dotnet/SqlClient/pull/922) +- Fixed unwanted exceptions being thrown from `SqlDataReader.Dispose` [#920](https://github.com/dotnet/SqlClient/pull/920) +- Fixed issues connecting to SQL Server instance with instance name specified from Unix environment [#870](https://github.com/dotnet/SqlClient/pull/870) +- Fixed TCP Keep Alive issues in .NET Core [#854](https://github.com/dotnet/SqlClient/pull/854) +- Fixed Kerberos Authentication issues caused due to regression [#845](https://github.com/dotnet/SqlClient/pull/845) +- Fixed issues with System-Assigned Managed Identity in Azure Functions [#829](https://github.com/dotnet/SqlClient/pull/829) +- Fixed missing error messages in Managed SNI [#882](https://github.com/dotnet/SqlClient/pull/882) +- Fixed event source trace string issue [#940](https://github.com/dotnet/SqlClient/pull/940) +- Fixed wrong data blended with transactions in .NET Core by marking a connection as doomed if the transaction completes or aborts while there is an open result set [#1023](https://github.com/dotnet/SqlClient/pull/1023) +- Fixed derived parameters containing incorrect typename [#1020](https://github.com/dotnet/SqlClient/pull/1020) +- Fixed server connection leak possibilities when an exception occurs in pooling layer [#890](https://github.com/dotnet/SqlClient/pull/890) +- Fixed IP connection resolving logic in .NET Core [#1016](https://github.com/dotnet/SqlClient/pull/1016) [#1031](https://github.com/dotnet/SqlClient/pull/1031) +- Fixed corrupted connection issue when an exception occurs during RPC execution with TVP types [#1068](https://github.com/dotnet/SqlClient/pull/1068) +- Fixed race condition issues between SinglePhaseCommit and TransactionEnded events [#1042](https://github.com/dotnet/SqlClient/pull/1042) + +### Improvements and Changes + +- Changed App Context switch `MakeReadAsyncBlocking` default to `false` [#937](https://github.com/dotnet/SqlClient/pull/937) +- Replaced usage of `BinaryFormatter` with `DataContractSerializer` [#869](https://github.com/dotnet/SqlClient/pull/869) +- Prohibited `DtdProcessing` on `XmlTextReader` instance in .NET Core [#884](https://github.com/dotnet/SqlClient/pull/884) +- Improved performance by reducing memory allocations in `SerializeEncodingChar`/`WriteEncodingChar` and some options boxing [#785](https://github.com/dotnet/SqlClient/pull/785) +- Improved performance by preventing orphaned active packets being GC'ed without clear [#888](https://github.com/dotnet/SqlClient/pull/888) +- Various performance improvements [#889](https://github.com/dotnet/SqlClient/pull/889) [#900](https://github.com/dotnet/SqlClient/pull/900) +- Partial event source tracing improvements in .NET Core [#867](https://github.com/dotnet/SqlClient/pull/867) [#897](https://github.com/dotnet/SqlClient/pull/897) +- Changes to share common files between NetFx and NetCore source code [#827](https://github.com/dotnet/SqlClient/pull/827) [#835](https://github.com/dotnet/SqlClient/pull/835) [#838](https://github.com/dotnet/SqlClient/pull/838) [#881](https://github.com/dotnet/SqlClient/pull/881) +- Performance improvements in `SqlDateTime` to `DateTime` internal conversion method [#912](https://github.com/dotnet/SqlClient/pull/912) +- Improved memory allocation by avoiding unnecessary context switching [1008](https://github.com/dotnet/SqlClient/pull/1008) +- Updated `Microsoft.Identity.Client` version from **4.21.1** to **4.22.0** [#1036](https://github.com/dotnet/SqlClient/pull/1036) +- Various performance improvements [#963](https://github.com/dotnet/SqlClient/pull/963) [#996](https://github.com/dotnet/SqlClient/pull/996) [#1004](https://github.com/dotnet/SqlClient/pull/1004) [#1012](https://github.com/dotnet/SqlClient/pull/1012) [#1017](https://github.com/dotnet/SqlClient/pull/1017) +- Event source tracing improvements [#1018](https://github.com/dotnet/SqlClient/pull/1018) +- Changes to share common files between NetFx and NetCore source code [#871](https://github.com/dotnet/SqlClient/pull/871) [#887](https://github.com/dotnet/SqlClient/pull/887) +- Updated error messages for enclave exceptions to include a link to a troubleshooting guide. [#994](https://github.com/dotnet/SqlClient/pull/994) +- Changes to share common files between projects [#1022](https://github.com/dotnet/SqlClient/pull/1022) [#1038](https://github.com/dotnet/SqlClient/pull/1038) [#1040](https://github.com/dotnet/SqlClient/pull/1040) [#1033](https://github.com/dotnet/SqlClient/pull/1033) [#1028](https://github.com/dotnet/SqlClient/pull/1028) [#1039](https://github.com/dotnet/SqlClient/pull/1039) + +### Breaking Changes + +- The minimum supported .NET Framework version has been increased to v4.6.1. .NET Framework v4.6.0 is no longer supported. [#899](https://github.com/dotnet/SqlClient/pull/899) +- `User Id` connection property now requires `Client Id` instead of `Object Id` for **User-Assigned Managed Identity** [#1010](https://github.com/dotnet/SqlClient/pull/1010) [Read more](#azure-identity-dependency-introduction) +- `SqlDataReader` now returns a `DBNull` value instead of an empty `byte[]`. Legacy behavior can be enabled by setting `AppContext` switch **Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior** [#998](https://github.com/dotnet/SqlClient/pull/998) [Read more](#enabling-row-version-null-behavior) + +### Configurable Retry Logic + +This new feature introduces configurable support for client applications to retry on "transient" or "retriable" errors. Configuration can be done through code or app config files and retry operations can be applied to opening a connection or executing a command. This feature is disabled by default and is currently in preview. To enable this support, client applications must turn on the following safety switch: + +`AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.EnableRetryLogic", true);` + +Once the .NET AppContext switch is enabled, a retry logic policy can be defined for `SqlConnection` and `SqlCommand` independently, or together using various customization options. + +New public APIs are introduced in `SqlConnection` and `SqlCommand` for registering a custom `SqlRetryLogicBaseProvider` implementation: + +```cs +public SqlConnection +{ + public SqlRetryLogicBaseProvider RetryLogicProvider; +} + +public SqlCommand +{ + public SqlRetryLogicBaseProvider RetryLogicProvider; +} + +``` + +API Usage examples can be found here: + +[SqlConnection retry sample](../../doc/samples/SqlConfigurableRetryLogic_OpenConnection.cs) + +[SqlCommand retry sample](../../doc/samples/SqlConfigurableRetryLogic_SqlCommand.cs) + +[Sample for retry logic options](../../doc/samples/SqlConfigurableRetryLogic_SqlRetryLogicOptions.cs) + +New configuration sections have also been introduced to do the same registration from configuration files, without having to modify existing code: + +```xml +
+ +
+``` + +A simple example of using the new configuration sections in configuration files is below: + +```xml + + + +
+
+ +
+ + + + + + + + + + + +``` + +Alternatively, applications can implement their own provider of the `SqlRetryLogicBaseProvider` base class, and register it with `SqlConnection`/`SqlCommand`. + +### Event Counters + +The following counters are now available for applications targeting .NET Core 3.1+ and .NET Standard 2.1+: + +|Name|Display name|Description| +|-------------------------|-----------------|-----------------| +|**active-hard-connections**|Actual active connections currently made to servers|The number of connections that are currently open to database servers.| +|**hard-connects**|Actual connection rate to servers|The number of connections per second that are being opened to database servers.| +|**hard-disconnects**|Actual disconnection rate from servers|The number of disconnects per second that are being made to database servers.| +|**active-soft-connects**|Active connections retrieved from the connection pool|The number of already-open connections being consumed from the connection pool.| +|**soft-connects**|Rate of connections retrieved from the connection pool|The number of connections per second that are being consumed from the connection pool.| +|**soft-disconnects**|Rate of connections returned to the connection pool|The number of connections per second that are being returned to the connection pool.| +|**number-of-non-pooled-connections**|Number of connections not using connection pooling|The number of active connections that aren't pooled.| +|**number-of-pooled-connections**|Number of connections managed by the connection pool|The number of active connections that are being managed by the connection pooling infrastructure.| +|**number-of-active-connection-pool-groups**|Number of active unique connection strings|The number of unique connection pool groups that are active. This counter is controlled by the number of unique connection strings that are found in the AppDomain.| +|**number-of-inactive-connection-pool-groups**|Number of unique connection strings waiting for pruning|The number of unique connection pool groups that are marked for pruning. This counter is controlled by the number of unique connection strings that are found in the AppDomain.| +|**number-of-active-connection-pools**|Number of active connection pools|The total number of connection pools.| +|**number-of-inactive-connection-pools**|Number of inactive connection pools|The number of inactive connection pools that haven't had any recent activity and are waiting to be disposed.| +|**number-of-active-connections**|Number of active connections|The number of active connections that are currently in use.| +|**number-of-free-connections**|Number of ready connections in the connection pool|The number of open connections available for use in the connection pools.| +|**number-of-stasis-connections**|Number of connections currently waiting to be ready|The number of connections currently awaiting completion of an action and which are unavailable for use by the application.| +|**number-of-reclaimed-connections**|Number of reclaimed connections from GC|The number of connections that have been reclaimed through garbage collection where `Close` or `Dispose` wasn't called by the application. **Note** Not explicitly closing or disposing connections hurts performance.| + +These counters can be used with .NET Core global CLI tools: `dotnet-counters` and `dotnet-trace` in Windows or Linux and PerfView in Windows, using `Microsoft.Data.SqlClient.EventSource` as the provider name. For more information, see [Retrieve event counter values](https://docs.microsoft.com/en-us/sql/connect/ado-net/event-counters#retrieve-event-counter-values). + +```cmd +dotnet-counters monitor Microsoft.Data.SqlClient.EventSource -p +PerfView /onlyProviders=*Microsoft.Data.SqlClient.EventSource:EventCounterIntervalSec=1 collect +``` + +### Azure Identity dependency introduction +**Microsoft.Data.SqlClient** now depends on the **Azure.Identity** library to acquire tokens for "Active Directory Managed Identity/MSI" and "Active Directory Service Principal" authentication modes. This change brings the following changes to the public surface area: + +- **Breaking Change** + The "User Id" connection property now requires "Client Id" instead of "Object Id" for "User-Assigned Managed Identity". +- **Public API** + New read-only public property: `SqlAuthenticationParameters.ConnectionTimeout` +- **Dependency** + Azure.Identity v1.3.0 + +### Event tracing improvements in SNI.dll +`Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) versions have been updated to `v3.0.0-preview1.21104.2`. Event tracing in SNI.dll will no longer be enabled through a client application. Subscribing a session to the **Microsoft.Data.SqlClient.EventSource** provider through tools like xperf or perfview will be sufficient. For more information, see [Event tracing support in Native SNI](https://docs.microsoft.com/en-us/sql/connect/ado-net/enable-eventsource-tracing#event-tracing-support-in-native-sni). + +### Enabling row version null behavior +`SqlDataReader` returns a `DBNull` value instead of an empty `byte[]`. To enable the legacy behavior, you must enable the following AppContext switch on application startup: +**"Switch.Microsoft.Data.SqlClient.LegacyRowVersionNullBehavior"** + +### Active Directory Default authentication support + +This PR introduces a new SQL Authentication method, **Active Directory Default**. This authentication mode widens the possibilities of user authentication, extending login solutions to the client environment, Visual Studio Code, Visual Studio, Azure CLI etc. + +With this authentication mode, the driver acquires a token by passing "[DefaultAzureCredential](https://docs.microsoft.com/dotnet/api/azure.identity.defaultazurecredential)" from the Azure Identity library to acquire an access token. This mode attempts to use these credential types to acquire an access token in the following order: + +- **EnvironmentCredential** + - Enables authentication to Azure Active Directory using client and secret, or username and password, details configured in the following environment variables: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_CLIENT_CERTIFICATE_PATH, AZURE_USERNAME, AZURE_PASSWORD ([More details](https://docs.microsoft.com/dotnet/api/azure.identity.environmentcredential)) +- **ManagedIdentityCredential** + - Attempts authentication to Azure Active Directory using a managed identity that has been assigned to the deployment environment. **"Client Id" of "User Assigned Managed Identity"** is read from the **"User Id" connection property**. +- **SharedTokenCacheCredential** + - Authenticates using tokens in the local cache shared between Microsoft applications. +- **VisualStudioCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio +- **VisualStudioCodeCredential** + - Enables authentication to Azure Active Directory using data from Visual Studio Code. +- **AzureCliCredential** + - Enables authentication to Azure Active Directory using Azure CLI to obtain an access token. + +> InteractiveBrowserCredential is disabled in the driver implementation of "Active Directory Default", and "Active Directory Interactive" is the only option available to acquire a token using MFA/Interactive authentication.* + +> Further customization options are not available at the moment. + +### Custom master key store provider registration enhancements + +Microsoft.Data.SqlClient now offers more control of where master key store providers are accessible in an application in order to better support multi-tenant applications and their use of column encryption/decryption. The following APIs are introduced to allow registration of custom master key store providers on instances of `SqlConnection` and `SqlCommand`: + +```cs +public class SqlConnection +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) +} +public class SqlCommand +{ + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(IDictionary customProviders) +} +``` + +The static API on `SqlConnection`, i.e. `SqlConnection.RegisterColumnEncryptionKeyStoreProviders` to register custom master key store providers globally continues to be supported. The column encryption key cache maintained globally only applies to globally registered providers. + +#### Column master key store provider registration precedence + +The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed. + +Custom master key store providers can be registered with the driver at three different layers. The global level is as it currently is. The new per-connection and per-command level registrations will be empty initially and can be set more than once. + +The precedence of the three registrations are as follows: + +- The per-command registration will be checked if it is not empty. +- If the per-command registration is empty, the per-connection registration will be checked if it is not empty. +- If the per-connection registration is empty, the global registration will be checked. + +Once any key store provider is found at a registration level, the driver will **NOT** fall back to the other registrations to search for a provider. If providers are registered but the proper provider is not found at a level, an exception will be thrown containing only the registered providers in the registration that was checked. + +#### Column encryption key cache precedence + +The column encryption keys (CEKs) for custom key store providers registered using the new instance-level APIs will not be cached by the driver. The key store providers need to implement their own cache to gain performance. This local cache of column encryption keys implemented by custom key store providers will be disabled by the driver if the key store provider instance is registered in the driver at the global level. + +A new API has also been introduced on the `SqlColumnEncryptionKeyStoreProvider` base class to set the cache time to live: + +```cs +public abstract class SqlColumnEncryptionKeyStoreProvider +{ + // The default value of Column Encryption Key Cache Time to Live is 0. + // Provider's local cache is disabled for globally registered providers. + // Custom key store provider implementation must include column encryption key cache to provide caching support to locally registered providers. + public virtual TimeSpan? ColumnEncryptionKeyCacheTtl { get; set; } = new TimeSpan(0); +} +``` + +### IP Address preference + +A new connection property `IPAddressPreference` is introduced to specify the IP address family preference to the driver when establishing TCP connections. If `Transparent Network IP Resolution` (in .NET Framework) or `Multi Subnet Failover` is set to `true`, this setting has no effect. Below are the three accepted values for this property: + +- **IPv4First** + - This is the default preference value. The driver will use resolved IPv4 addresses first. If none of them can be connected to successfully, it will try resolved IPv6 addresses. + +- **IPv6First** + - The driver will use resolved IPv6 addresses first. If none of them can be connected to successfully, it will try resolved IPv4 addresses. + +- **UsePlatformDefault** + - The driver will try IP addresses in the order received from the DNS resolution response. + + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 3.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Loader 4.3.0 diff --git a/release-notes/3.0/3.0.1.md b/release-notes/3.0/3.0.1.md new file mode 100644 index 0000000000..177ec32e25 --- /dev/null +++ b/release-notes/3.0/3.0.1.md @@ -0,0 +1,87 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.0.1 released 24 September 2021 + +This update brings the below changes over the previous stable release: + +### Fixed + +- Fixed async thread blocking issues on `SqlConnection.Open()` for active directory authentication modes. [#1270](https://github.com/dotnet/SqlClient/pull/1270) +- Fixed unknown transaction state issues when promoting delegated transaction. [1247](https://github.com/dotnet/SqlClient/pull/1247) +- Fixed issue with connection encryption to ensure connections fail when encryption is required. [#1233](https://github.com/dotnet/SqlClient/pull/1233) [Read more](#ensure-connections-fail-when-encryption-is-required) +- Fixed bug with `LegacyRowVersionNullBehavior` App Context switch. [#1246](https://github.com/dotnet/SqlClient/pull/1246) +- Fixed recursive calls to `RetryLogicProvider` when calling `SqlCommand.ExecuteScalarAsync`. [#1245](https://github.com/dotnet/SqlClient/pull/1245) +- Fixed async deadlock scenarios in web contexts with configurable retry logic provider. [#1245](https://github.com/dotnet/SqlClient/pull/1245) +- Fixed deadlock in transaction using .NET Framework. [#1243](https://github.com/dotnet/SqlClient/pull/1243) +- Fixed issue where connection goes to unusable state. [#1238](https://github.com/dotnet/SqlClient/pull/1238) + +### Ensure connections fail when encryption is required + +In scenarios where client encryption libraries were disabled or unavailable, it was possible for unencrypted connections to be made when Encrypt was set to true or the server required encryption. + +### Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/3.0/3.0.md b/release-notes/3.0/3.0.md new file mode 100644 index 0000000000..e35ae11698 --- /dev/null +++ b/release-notes/3.0/3.0.md @@ -0,0 +1,16 @@ +# Microsoft.Data.SqlClient 3.0 Releases + +The following Microsoft.Data.SqlClient 3.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2021/09/24 | 3.0.1 | [release notes](3.0.1.md) | +| 2021/06/09 | 3.0.0 | [release notes](3.0.0.md) | + +The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2021/05/20 | 3.0.0-preview3.21140.5 | [release notes](3.0.0-preview3.md) | +| 2021/04/15 | 3.0.0-preview2.21106.5 | [release notes](3.0.0-preview2.md) | +| 2021/03/15 | 3.0.0-preview1.21075.2 | [release notes](3.0.0-preview1.md) | diff --git a/release-notes/3.0/README.md b/release-notes/3.0/README.md new file mode 100644 index 0000000000..0d7f9e97da --- /dev/null +++ b/release-notes/3.0/README.md @@ -0,0 +1,16 @@ +# Microsoft.Data.SqlClient 3.0 Releases + +The following Microsoft.Data.SqlClient 3.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2021/09/24 | 3.0.1 | [Release notes](3.0.1.md) | +| 2021/06/09 | 3.0.0 | [release notes](3.0.0.md) | + +The following Microsoft.Data.SqlClient 3.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2021/05/20 | 3.0.0-preview3.21140.5 | [release notes](3.0.0-preview3.md) | +| 2021/04/15 | 3.0.0-preview2.21106.5 | [release notes](3.0.0-preview2.md) | +| 2021/03/15 | 3.0.0-preview1.21075.2 | [release notes](3.0.0-preview1.md) | diff --git a/release-notes/3.1/3.1.0.md b/release-notes/3.1/3.1.0.md new file mode 100644 index 0000000000..eba2ad30d0 --- /dev/null +++ b/release-notes/3.1/3.1.0.md @@ -0,0 +1,75 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.1.0 released 30 March 2022 + +This update includes the following changes over the 3.0 release: + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1539](https://github.com/dotnet/SqlClient/pull/1539) [Read more](#introduce-attestation-protocol-none) +- Included `42108` and `42109` error codes to retriable transient errors list. [#1560](https://github.com/dotnet/SqlClient/pull/1560) + +### Fixed + +- Changed EnclaveDelegate.Crypto GetEnclaveProvider to use a thread safe concurrent dictionary. [#1564](https://github.com/dotnet/SqlClient/pull/1564) + +### Introduce Attestation Protocol None + +A new attestation protocol called `None` will be allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 3.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Loader 4.3.0 diff --git a/release-notes/3.1/3.1.1.md b/release-notes/3.1/3.1.1.md new file mode 100644 index 0000000000..d7cb669712 --- /dev/null +++ b/release-notes/3.1/3.1.1.md @@ -0,0 +1,77 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.1.1 released 12 August 2022 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1700](https://github.com/dotnet/SqlClient/pull/1700) +- Fixed Kerberos authentication failure when using .NET 6. [#1696](https://github.com/dotnet/SqlClient/pull/1696) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1695](https://github.com/dotnet/SqlClient/pull/1695) +- Removed union overlay design and use reflection in `SqlTypeWorkarounds`. [#1699](https://github.com/dotnet/SqlClient/pull/1699) + +## Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework 4.61 + +- Microsoft.Data.SqlClient.SNI 3.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Loader 4.3.0 diff --git a/release-notes/3.1/3.1.md b/release-notes/3.1/3.1.md new file mode 100644 index 0000000000..6e4d68e534 --- /dev/null +++ b/release-notes/3.1/3.1.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 3.1 Releases + +The following Microsoft.Data.SqlClient 3.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/08/12 | 3.1.1 | [release notes](3.1.1.md) | +| 2022/03/30 | 3.1.0 | [release notes](3.1.0.md) | diff --git a/release-notes/3.1/README.md b/release-notes/3.1/README.md new file mode 100644 index 0000000000..6e4d68e534 --- /dev/null +++ b/release-notes/3.1/README.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 3.1 Releases + +The following Microsoft.Data.SqlClient 3.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/08/12 | 3.1.1 | [release notes](3.1.1.md) | +| 2022/03/30 | 3.1.0 | [release notes](3.1.0.md) | diff --git a/release-notes/4.0/4.0.0-preview1.md b/release-notes/4.0/4.0.0-preview1.md new file mode 100644 index 0000000000..459462ca48 --- /dev/null +++ b/release-notes/4.0/4.0.0-preview1.md @@ -0,0 +1,113 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.0-preview1.21237.2 released 25 August 2021 + +This update brings the below changes over the previous release: + +### Breaking changes over stable release 3.0.0 +- Changed `Encrypt` connection string property to be `true` by default. [#1210](https://github.com/dotnet/SqlClient/pull/1210) [Read more](#encrypt-default-value-set-to-true) +- The driver now throws `SqlException` replacing `AggregateException` for active directory authentication modes. [#1213](https://github.com/dotnet/SqlClient/pull/1213) +- Dropped obsolete `Asynchronous Processing` connection property from .NET Framework. [#1148](https://github.com/dotnet/SqlClient/pull/1148) + +### Added +- Added `SqlCommand.EnableOptimizedParameterBinding` property that when enabled increases performance for commands with very large numbers of parameters. [#1041](https://github.com/dotnet/SqlClient/pull/1041) [Read more](#enable-optimized-parameter-binding) +- Included `42108` and `42109` error codes to retriable transient errors list. [#1215](https://github.com/dotnet/SqlClient/pull/1215) +- Added new App Context switch to use OS enabled client protocols only. [#1168](https://github.com/dotnet/SqlClient/pull/1168) [Read more](#app-context-switch-for-using-system-default-protocols) +- Added `PoolBlockingPeriod` connection property support in .NET Standard. [#1181](https://github.com/dotnet/SqlClient/pull/1181) +- Added support for `SqlDataReader.GetColumnSchema()` in .NET Standard. [#1181](https://github.com/dotnet/SqlClient/pull/1181) +- Added PropertyGrid support with component model annotations to `SqlConnectionStringBuilder` properties for .NET Core. [#1152](https://github.com/dotnet/SqlClient/pull/1152) + +### Fixed +- Fixed issue with connectivity when TLS 1.3 is enabled on client and server. [#1168](https://github.com/dotnet/SqlClient/pull/1168) +- Fixed issue with connection encryption to ensure connections fail when encryption is required. [#1210](https://github.com/dotnet/SqlClient/pull/1210) [Read more](#ensure-connections-fail-when-encryption-is-required) +- Fixed issue where connection goes to unusable state. [#1128](https://github.com/dotnet/SqlClient/pull/1128) +- Fixed recursive calls to `RetryLogicProvider` when calling `SqlCommand.ExecuteScalarAsync`. [#1220](https://github.com/dotnet/SqlClient/pull/1220) +- Fixed async deadlock scenarios in web contexts with configurable retry logic provider. [#1220](https://github.com/dotnet/SqlClient/pull/1220) +- Fixed `EntryPointNotFoundException` in `InOutOfProcHelper` constructor. [#1120](https://github.com/dotnet/SqlClient/pull/1120) +- Fixed async thread blocking issues on `SqlConnection.Open()` for active directory authentication modes. [#1213](https://github.com/dotnet/SqlClient/pull/1213) +- Fixed driver behavior for Always Encrypted with secure enclaves to not fail when no user parameters have been provided. [#1115](https://github.com/dotnet/SqlClient/pull/1115) +- Fixed bug with `LegacyRowVersionNullBehavior` App Context switch. [#1182](https://github.com/dotnet/SqlClient/pull/1182) +- Fixed issues in Strings.resx file containing error messages. [#1136](https://github.com/dotnet/SqlClient/pull/1136) [#1178](https://github.com/dotnet/SqlClient/pull/1178) + +### Changed +- Updated error code to match with Windows when certificate validation fails in non-Windows client environments. [#1130](https://github.com/dotnet/SqlClient/pull/1130) +- Removed designer attributes from `SqlCommand` and `SqlDataAdapter`. [#1132](https://github.com/dotnet/SqlClient/pull/1132) +- Updated configurable retry logic default retriable error list. [#1125](https://github.com/dotnet/SqlClient/pull/1125) +- Improved performance by changing `SqlParameter` bool fields to flags. [#1064](https://github.com/dotnet/SqlClient/pull/1064) +- Improved performance by implementing static delegates. [#1060](https://github.com/dotnet/SqlClient/pull/1060) +- Optimized async method allocations in .NET Framework by porting changes from .NET Core. [#1084](https://github.com/dotnet/SqlClient/pull/1084) +- Various code improvements [#902](https://github.com/dotnet/SqlClient/pull/902) [#925](https://github.com/dotnet/SqlClient/pull/925) [#933](https://github.com/dotnet/SqlClient/pull/933) [#934](https://github.com/dotnet/SqlClient/pull/934) [#1024](https://github.com/dotnet/SqlClient/pull/1024) [#1057](https://github.com/dotnet/SqlClient/pull/1057) [#1122](https://github.com/dotnet/SqlClient/pull/1122) [#1133](https://github.com/dotnet/SqlClient/pull/1133) [#1134](https://github.com/dotnet/SqlClient/pull/1134) [#1141](https://github.com/dotnet/SqlClient/pull/1141) [#1187](https://github.com/dotnet/SqlClient/pull/1187) [#1188](https://github.com/dotnet/SqlClient/pull/1188) [#1223](https://github.com/dotnet/SqlClient/pull/1223) [#1225](https://github.com/dotnet/SqlClient/pull/1225) [#1226](https://github.com/dotnet/SqlClient/pull/1226) + +## New features over stable release v3.0 + +### Encrypt default value set to true +The default value of the `Encrypt` connection setting has been changed from `false` to `true`. With the growing use of cloud databases and the need to ensure those connections are secure, it's time for this backwards-compatibility-breaking change. + +### Ensure connections fail when encryption is required +In scenarios where client encryption libraries were disabled or unavailable, it was possible for unencrypted connections to be made when Encrypt was set to true or the server required encryption. + +### App Context Switch for using System default protocols +TLS 1.3 is not supported by the driver; therefore, it has been removed from the supported protocols list by default. Users can switch back to forcing use of Operating System's client protocols, by enabling the App Context switch below: + + `Switch.Microsoft.Data.SqlClient.UseSystemDefaultSecureProtocols` + +### Enable optimized parameter binding +Microsoft.Data.SqlClient introduces new `SqlCommand` API, `EnableOptimizedParameterBinding` to improve performance of queries with large number of parameters. This property is disabled by default. When set to `true`, parameter names will not be sent to the SQL server when the command is executed. + +```cs +public class SqlCommand +{ + public bool EnableOptimizedParameterBinding { get; set; } +} +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0-preview1.21232.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0-preview1.21232.1 +- Microsoft.Win32.Registry 5.0.0 +- System.Security.Principal.Windows 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Runtime.Caching 5.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0-preview1.21232.1 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Caching 5.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Runtime.Loader 4.3.0 diff --git a/release-notes/4.0/4.0.0-preview2.md b/release-notes/4.0/4.0.0-preview2.md new file mode 100644 index 0000000000..db321cc79f --- /dev/null +++ b/release-notes/4.0/4.0.0-preview2.md @@ -0,0 +1,100 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.0-preview2.21264.2 released 21 September 2021 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v4.0.0-preview1 + +- Removed `Configurable Retry Logic` safety switch. [#1254](https://github.com/dotnet/SqlClient/pull/1254) [Read more](#remove-configurable-retry-logic-safety-switch) + +### Added + +- Added support for `SqlFileStream` on Windows using .NET Standard 2.0 and above. [#1240](https://github.com/dotnet/SqlClient/pull/1240) +- Added support for **localdb** `shared instance` using managed SNI. [#1237](https://github.com/dotnet/SqlClient/pull/1237) [Read more](#sqllocaldb-shared-instance-support) + +### Fixed + +- Fixed `.NET decimal` conversion from `SqlDecimal`. [#1179](https://github.com/dotnet/SqlClient/pull/1179) +- Fixed `Event Source` changes on **TryBeginExecuteEvent** and **WriteEndExecuteEvent** to address the failure on other MS products such as OpenTelemetry and Application Insight. [#1258](https://github.com/dotnet/SqlClient/pull/1258) +- Fixed command's async cancellation. [#956](https://github.com/dotnet/SqlClient/pull/956) +- Fixed deadlock in transaction using .NET Framework. [#1242](https://github.com/dotnet/SqlClient/pull/1242) +- Fixed unknown transaction state issues when prompting delegated transaction. [1216](https://github.com/dotnet/SqlClient/pull/1216) + +### Changed + +- Various code improvements [#1155](https://github.com/dotnet/SqlClient/pull/1155) [#1236](https://github.com/dotnet/SqlClient/pull/1236) [#1251](https://github.com/dotnet/SqlClient/pull/1251) [#1266](https://github.com/dotnet/SqlClient/pull/1266) + +### Remove configurable retry logic safety switch + +The App Context switch "Switch.Microsoft.Data.SqlClient.EnableRetryLogic" will no longer be required to use the configurable retry logic feature. The feature is now supported in production. The default behavior of the feature will continue to be a non-retry policy, which will need to be overridden by client applications to enable retries. + +### SqlLocalDb shared instance support + +SqlLocalDb shared instances are now supported when using Managed SNI. + +- Possible scenarios: + - `(localdb)\.` (connects to default instance of SqlLocalDb) + - `(localdb)\` + - `(localdb)\.\` (*newly added support) + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0-preview1.21232.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0-preview1.21232.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0-preview1.21232.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/4.0/4.0.0-preview3.md b/release-notes/4.0/4.0.0-preview3.md new file mode 100644 index 0000000000..005ae6db0c --- /dev/null +++ b/release-notes/4.0/4.0.0-preview3.md @@ -0,0 +1,118 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.0-preview3.21293.2 released 20 October 2021 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v4.0.0-preview2 + +- Dropped support for .NET Core 2.1 [#1272](https://github.com/dotnet/SqlClient/pull/1272) +- [.NET Framework] Exception will not be thrown if a User ID is provided in the connection string when using `Active Directory Integrated` authentication [#1359](https://github.com/dotnet/SqlClient/pull/1359) + +### Added + +- Add `GetFieldValueAsync` and `GetFieldValue` support for `XmlReader`, `TextReader`, `Stream` [#1019](https://github.com/dotnet/SqlClient/pull/1019). [Read more](#getfieldvalueasynct-and-getfieldvaluet-support-for-xmlreader-textreader-stream-types) + +### Fixed + +- Fixed `FormatException` when opening a connection with event tracing enabled [#1291](https://github.com/dotnet/SqlClient/pull/1291) +- Fixed improper initialization of `ActiveDirectoryAuthenticationProvider` [#1328](https://github.com/dotnet/SqlClient/pull/1328) +- Fixed `MissingMethodException` when accessing `SqlAuthenticationParameters.ConnectionTimeout` [#1336](https://github.com/dotnet/SqlClient/pull/1336) +- Fixed data corruption issues by reverting changes to async cancellations [#1352](https://github.com/dotnet/SqlClient/pull/1352) +- Fixed performance degradation by reverting changes to MARS state machine [#1357](https://github.com/dotnet/SqlClient/pull/1357) +- Fixed bug where environment variables are ignored when using `Active Directory Default` authentication [#1360](https://github.com/dotnet/SqlClient/pull/1360) + +### Changed + +- Removed attributes for classes used in Microsoft.VSDesigner due to lack of support for Microsoft.Data.SqlClient [#1296](https://github.com/dotnet/SqlClient/pull/1296) +- Disable encryption when connecting to SQL LocalDB [#1312](https://github.com/dotnet/SqlClient/pull/1312) +- Various code health and performance improvements. See [milestone](https://github.com/dotnet/SqlClient/milestone/31?closed=1) for more info. + +## New features over preview release v4.0.0-preview2 + +### `GetFieldValueAsync` and `GetFieldValue` support for `XmlReader`, `TextReader`, `Stream` types + +`XmlReader`, `TextReader`, `Stream` types are now supported when using `GetFieldValueAsync` and `GetFieldValue`. + +Example usage: + +```cs +using (SqlConnection connection = new SqlConnection(connectionString)) +{ + using (SqlCommand command = new SqlCommand(query, connection)) + { + connection.Open(); + using (SqlDataReader reader = await command.ExecuteReaderAsync()) + { + if (await reader.ReadAsync()) + { + using (Stream stream = await reader.GetFieldValueAsync(1)) + { + // Continue to read from stream + } + } + } + } +} +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0-preview1.21232.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0-preview1.21232.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0-preview1.21232.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/4.0/4.0.0.md b/release-notes/4.0/4.0.0.md new file mode 100644 index 0000000000..6059c27983 --- /dev/null +++ b/release-notes/4.0/4.0.0.md @@ -0,0 +1,196 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.0 released 18 November 2021 + +This update brings the below changes over the previous preview release: + +### Added + +- Added missing `SqlClientLogger` class to .NET Core refs and missing `SqlClientLogger.LogWarning` method in .NET Framework refs [#1392](https://github.com/dotnet/SqlClient/pull/1392) + +### Changed + +- Avoid throwing unnecessary exception when an invalid `SqlNotificationInfo` value is received from SQL Server [#1378](https://github.com/dotnet/SqlClient/pull/1378) +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.0` [#1391](https://github.com/dotnet/SqlClient/pull/1391) + +## Summary of changes in 4.0 + +All changes in Microsoft.Data.SqlClient v4.0 over v3.0: + +### New Additions + +- Added `SqlCommand.EnableOptimizedParameterBinding` property that when enabled increases performance for commands with very large numbers of parameters. [#1041](https://github.com/dotnet/SqlClient/pull/1041) [Read more](#enable-optimized-parameter-binding) +- Included `42108` and `42109` error codes to retriable transient errors list. [#1215](https://github.com/dotnet/SqlClient/pull/1215) +- Added new App Context switch to use OS enabled client protocols only. [#1168](https://github.com/dotnet/SqlClient/pull/1168). [Read more](#app-context-switch-for-using-system-default-protocols) +- Added `PoolBlockingPeriod` connection property support in .NET Standard. [#1181](https://github.com/dotnet/SqlClient/pull/1181) +- Added support for `SqlDataReader.GetColumnSchema()` in .NET Standard. [#1181](https://github.com/dotnet/SqlClient/pull/1181) +- Added PropertyGrid support with component model annotations to `SqlConnectionStringBuilder` properties for .NET Core. [#1152](https://github.com/dotnet/SqlClient/pull/1152) +- Added support for `SqlFileStream` on Windows using .NET Standard 2.0 and above. [#1240](https://github.com/dotnet/SqlClient/pull/1240) +- Added support for **localdb** `shared instance` using managed SNI. [#1237](https://github.com/dotnet/SqlClient/pull/1237). [Read more](#sqllocaldb-shared-instance-support) +- Added `GetFieldValueAsync` and `GetFieldValue` support for `XmlReader`, `TextReader`, `Stream` [#1019](https://github.com/dotnet/SqlClient/pull/1019). [Read more](#getfieldvalueasynct-and-getfieldvaluet-support-for-xmlreader-textreader-stream-types) +- Added missing `SqlClientLogger` class to .NET Core refs and missing 'SqlClientLogger.LogWarning' method in .NET Framework refs [#1392](https://github.com/dotnet/SqlClient/pull/1392) + +### Bug Fixes + +- Fixed issue with connectivity when TLS 1.3 is enabled on client and server. [#1168](https://github.com/dotnet/SqlClient/pull/1168) +- Fixed issue with connection encryption to ensure connections fail when encryption is required. [#1210](https://github.com/dotnet/SqlClient/pull/1210) [Read more](#ensure-connections-fail-when-encryption-is-required) +- Fixed issue where connection goes to unusable state. [#1128](https://github.com/dotnet/SqlClient/pull/1128) +- Fixed recursive calls to `RetryLogicProvider` when calling `SqlCommand.ExecuteScalarAsync`. [#1220](https://github.com/dotnet/SqlClient/pull/1220) +- Fixed async deadlock scenarios in web contexts with configurable retry logic provider. [#1220](https://github.com/dotnet/SqlClient/pull/1220) +- Fixed `EntryPointNotFoundException` in `InOutOfProcHelper` constructor. [#1120](https://github.com/dotnet/SqlClient/pull/1120) +- Fixed async thread blocking issues on `SqlConnection.Open()` for active directory authentication modes. [#1213](https://github.com/dotnet/SqlClient/pull/1213) +- Fixed driver behavior for Always Encrypted with secure enclaves to not fail when no user parameters have been provided. [#1115](https://github.com/dotnet/SqlClient/pull/1115) +- Fixed bug with `LegacyRowVersionNullBehavior` App Context switch. [#1182](https://github.com/dotnet/SqlClient/pull/1182) +- Fixed issues in Strings.resx file containing error messages. [#1136](https://github.com/dotnet/SqlClient/pull/1136) [#1178](https://github.com/dotnet/SqlClient/pull/1178) +- Fixed `.NET decimal` conversion from `SqlDecimal`. [#1179](https://github.com/dotnet/SqlClient/pull/1179) +- Fixed `Event Source` changes on **TryBeginExecuteEvent** and **WriteEndExecuteEvent** to address the failure on other MS products such as OpenTelemetry and Application Insight. [#1258](https://github.com/dotnet/SqlClient/pull/1258) +- Fixed deadlock in transaction using .NET Framework. [#1242](https://github.com/dotnet/SqlClient/pull/1242) +- Fixed unknown transaction state issues when prompting delegated transaction. [1216](https://github.com/dotnet/SqlClient/pull/1216) +- Fixed `FormatException` when opening a connection with event tracing enabled [#1291](https://github.com/dotnet/SqlClient/pull/1291) +- Fixed improper initialization of `ActiveDirectoryAuthenticationProvider` [#1328](https://github.com/dotnet/SqlClient/pull/1328) +- Fixed `MissingMethodException` when accessing `SqlAuthenticationParameters.ConnectionTimeout` [#1336](https://github.com/dotnet/SqlClient/pull/1336) +- Fixed bug where environment variables are ignored when using `Active Directory Default` authentication [#1360](https://github.com/dotnet/SqlClient/pull/1360) + +### Improvements and Changes + +- Updated error code to match with Windows when certificate validation fails in non-Windows client environments. [#1130](https://github.com/dotnet/SqlClient/pull/1130) +- Removed designer attributes from `SqlCommand` and `SqlDataAdapter`. [#1132](https://github.com/dotnet/SqlClient/pull/1132) +- Updated configurable retry logic default retriable error list. [#1125](https://github.com/dotnet/SqlClient/pull/1125) +- Improved performance by changing `SqlParameter` bool fields to flags. [#1064](https://github.com/dotnet/SqlClient/pull/1064) +- Improved performance by implementing static delegates. [#1060](https://github.com/dotnet/SqlClient/pull/1060) +- Optimized async method allocations in .NET Framework by porting changes from .NET Core. [#1084](https://github.com/dotnet/SqlClient/pull/1084) +- Various code improvements [#902](https://github.com/dotnet/SqlClient/pull/902) [#925](https://github.com/dotnet/SqlClient/pull/925) [#933](https://github.com/dotnet/SqlClient/pull/933) [#934](https://github.com/dotnet/SqlClient/pull/934) [#1024](https://github.com/dotnet/SqlClient/pull/1024) [#1057](https://github.com/dotnet/SqlClient/pull/1057) [#1122](https://github.com/dotnet/SqlClient/pull/1122) [#1133](https://github.com/dotnet/SqlClient/pull/1133) [#1134](https://github.com/dotnet/SqlClient/pull/1134) [#1141](https://github.com/dotnet/SqlClient/pull/1141) [#1155](https://github.com/dotnet/SqlClient/pull/1155) [#1187](https://github.com/dotnet/SqlClient/pull/1187) [#1188](https://github.com/dotnet/SqlClient/pull/1188) [#1223](https://github.com/dotnet/SqlClient/pull/1223) [#1225](https://github.com/dotnet/SqlClient/pull/1225) [#1226](https://github.com/dotnet/SqlClient/pull/1226) [#1236](https://github.com/dotnet/SqlClient/pull/1236) [#1251](https://github.com/dotnet/SqlClient/pull/1251) [#1266](https://github.com/dotnet/SqlClient/pull/1266) +- Removed attributes for classes used in Microsoft.VSDesigner due to lack of support for Microsoft.Data.SqlClient [#1296](https://github.com/dotnet/SqlClient/pull/1296) +- Disable encryption when connecting to SQL LocalDB [#1312](https://github.com/dotnet/SqlClient/pull/1312) +- Avoid throwing unnecessary exception when an invalid SqlNotificationInfo value is received from SQL Server [#1378](https://github.com/dotnet/SqlClient/pull/1378) +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.0` [#1391](https://github.com/dotnet/SqlClient/pull/1391) +- Various code health and performance improvements. See [milestone](https://github.com/dotnet/SqlClient/milestone/31?closed=1) for more info. + +### Breaking Changes +- Changed `Encrypt` connection string property to be `true` by default. [#1210](https://github.com/dotnet/SqlClient/pull/1210) [Read more](#encrypt-default-value-set-to-true) +- The driver now throws `SqlException` replacing `AggregateException` for active directory authentication modes. [#1213](https://github.com/dotnet/SqlClient/pull/1213) +- Dropped obsolete `Asynchronous Processing` connection property from .NET Framework. [#1148](https://github.com/dotnet/SqlClient/pull/1148) +- Removed `Configurable Retry Logic` safety switch. [#1254](https://github.com/dotnet/SqlClient/pull/1254) [Read more](#remove-configurable-retry-logic-safety-switch) +- Dropped support for .NET Core 2.1 [#1272](https://github.com/dotnet/SqlClient/pull/1272) +- [.NET Framework] Exception will not be thrown if a User ID is provided in the connection string when using `Active Directory Integrated` authentication [#1359](https://github.com/dotnet/SqlClient/pull/1359) + +### Encrypt default value set to true +The default value of the `Encrypt` connection setting has been changed from `false` to `true`. With the growing use of cloud databases and the need to ensure those connections are secure, it's time for this backwards-compatibility-breaking change. + +### Ensure connections fail when encryption is required +In scenarios where client encryption libraries were disabled or unavailable, it was possible for unencrypted connections to be made when Encrypt was set to true or the server required encryption. + +### App Context Switch for using System default protocols +TLS 1.3 is not supported by the driver; therefore, it has been removed from the supported protocols list by default. Users can switch back to forcing use of the Operating System's client protocols, by enabling the App Context switch below: + + `Switch.Microsoft.Data.SqlClient.UseSystemDefaultSecureProtocols` + +### Enable optimized parameter binding +Microsoft.Data.SqlClient introduces a new `SqlCommand` API, `EnableOptimizedParameterBinding` to improve performance of queries with a large number of parameters. This property is disabled by default. When set to `true`, parameter names will not be sent to the SQL server when the command is executed. + +```cs +public class SqlCommand +{ + public bool EnableOptimizedParameterBinding { get; set; } +} +``` + +### Remove configurable retry logic safety switch + +The App Context switch "Switch.Microsoft.Data.SqlClient.EnableRetryLogic" will no longer be required to use the configurable retry logic feature. The feature is now supported in production. The default behavior of the feature will continue to be a non-retry policy, which will need to be overridden by client applications to enable retries. + +### SqlLocalDb shared instance support + +SqlLocalDb shared instances are now supported when using Managed SNI. + +- Possible scenarios: + - `(localdb)\.` (connects to default instance of SqlLocalDb) + - `(localdb)\` + - `(localdb)\.\` (*newly added support) + +### `GetFieldValueAsync` and `GetFieldValue` support for `XmlReader`, `TextReader`, `Stream` types + +`XmlReader`, `TextReader`, `Stream` types are now supported when using `GetFieldValueAsync` and `GetFieldValue`. + +Example usage: + +```cs +using (SqlConnection connection = new SqlConnection(connectionString)) +{ + using (SqlCommand command = new SqlCommand(query, connection)) + { + connection.Open(); + using (SqlDataReader reader = await command.ExecuteReaderAsync()) + { + if (await reader.ReadAsync()) + { + using (Stream stream = await reader.GetFieldValueAsync(1)) + { + // Continue to read from stream + } + } + } + } +} +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/4.0/4.0.1.md b/release-notes/4.0/4.0.1.md new file mode 100644 index 0000000000..7e43a9bcc8 --- /dev/null +++ b/release-notes/4.0/4.0.1.md @@ -0,0 +1,83 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.1 released 17 January 2022 + +This update brings the below changes over the previous preview release: + +### Added + +- Added AppContext switch `SuppressInsecureTLSWarning` to allow suppression of TLS security warning when using `Encrypt=false` in the connection string. [#1457](https://github.com/dotnet/SqlClient/pull/1457) [Read more](#suppress-tls-security-warnings) + +### Fixed + +- Fixed Kerberos authentication failure when using .NET 6. [#1411](https://github.com/dotnet/SqlClient/pull/1411) +- Fixed connection failure when using `SqlLocalDB` instance pipe name. [#1433](https://github.com/dotnet/SqlClient/pull/1433) +- Fixed a failure when executing concurrent queries requiring enclaves. [#1451](https://github.com/dotnet/SqlClient/pull/1451) +- Updated obsolete API calls targeting .NET 6. [#1401](https://github.com/dotnet/SqlClient/pull/1401) + +### Suppress TLS security warnings + +When connecting to a SQL Server, if a protocol lower than TLS 1.2 is negotiated, a security warning is printed out to the console. This warning can be suppressed by enabling the following `AppContext` switch on the application startup while `Encrypt` is set to `false` on connection string. + +`Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/4.0/4.0.2.md b/release-notes/4.0/4.0.2.md new file mode 100644 index 0000000000..96c485a613 --- /dev/null +++ b/release-notes/4.0/4.0.2.md @@ -0,0 +1,80 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.2 released 13 September 2022 + +This update brings the below changes over the previous preview release: + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1718](https://github.com/dotnet/SqlClient/pull/1718) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1720](https://github.com/dotnet/SqlClient/pull/1720), [#1747](https://github.com/dotnet/SqlClient/pull/1747) +- Added CommandText length validation when using stored procedure command types. [#1721](https://github.com/dotnet/SqlClient/pull/1721) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1722](https://github.com/dotnet/SqlClient/pull/1722) +- Fixed null SqlBinary as rowversion. [#1724](https://github.com/dotnet/SqlClient/pull/1724) +- Fixed table's collation overriding with default UTF8 collation. [#1750](https://github.com/dotnet/SqlClient/pull/1750) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1754](https://github.com/dotnet/SqlClient/pull/1754), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1723](https://github.com/dotnet/SqlClient/pull/1723) + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/4.0/4.0.md b/release-notes/4.0/4.0.md new file mode 100644 index 0000000000..8d0ffeceb8 --- /dev/null +++ b/release-notes/4.0/4.0.md @@ -0,0 +1,17 @@ +# Microsoft.Data.SqlClient 4.0 Releases + +The following Microsoft.Data.SqlClient 4.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/09/13 | 4.0.2 | [release notes](4.0.2.md) | +| 2022/01/17 | 4.0.1 | [release notes](4.0.1.md) | +| 2021/11/18 | 4.0.0 | [release notes](4.0.0.md) | + +The following Microsoft.Data.SqlClient 4.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2021/10/20 | 4.0.0-preview3.21293.2 | [release notes](4.0.0-preview3.md) | +| 2021/09/21 | 4.0.0-preview2.21264.2 | [release notes](4.0.0-preview2.md) | +| 2021/08/25 | 4.0.0-preview1.21237.2 | [release notes](4.0.0-preview1.md) | diff --git a/release-notes/4.0/README.md b/release-notes/4.0/README.md new file mode 100644 index 0000000000..8d0ffeceb8 --- /dev/null +++ b/release-notes/4.0/README.md @@ -0,0 +1,17 @@ +# Microsoft.Data.SqlClient 4.0 Releases + +The following Microsoft.Data.SqlClient 4.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/09/13 | 4.0.2 | [release notes](4.0.2.md) | +| 2022/01/17 | 4.0.1 | [release notes](4.0.1.md) | +| 2021/11/18 | 4.0.0 | [release notes](4.0.0.md) | + +The following Microsoft.Data.SqlClient 4.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2021/10/20 | 4.0.0-preview3.21293.2 | [release notes](4.0.0-preview3.md) | +| 2021/09/21 | 4.0.0-preview2.21264.2 | [release notes](4.0.0-preview2.md) | +| 2021/08/25 | 4.0.0-preview1.21237.2 | [release notes](4.0.0-preview1.md) | diff --git a/release-notes/4.1/4.1.0.md b/release-notes/4.1/4.1.0.md new file mode 100644 index 0000000000..c8fdd801da --- /dev/null +++ b/release-notes/4.1/4.1.0.md @@ -0,0 +1,82 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.1.0 released 31 January 2022 + +This update brings the below changes over the previous preview release: + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1419](https://github.com/dotnet/SqlClient/pull/1419) [#1425](https://github.com/dotnet/SqlClient/pull/1425) [Read more](#introduce-attestation-protocol-none) + +### Introduce Attestation Protocol None + +A new attestation protocol called `None` will be allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" + +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/4.1/4.1.1.md b/release-notes/4.1/4.1.1.md new file mode 100644 index 0000000000..f6fbc70f19 --- /dev/null +++ b/release-notes/4.1/4.1.1.md @@ -0,0 +1,80 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.1.1 released 13 September 2022 + +This update brings the below changes over the previous preview release: + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1706](https://github.com/dotnet/SqlClient/pull/1706) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1708](https://github.com/dotnet/SqlClient/pull/1708), [#1746](https://github.com/dotnet/SqlClient/pull/1746) +- Added CommandText length validation when using stored procedure command types. [#1709](https://github.com/dotnet/SqlClient/pull/1709) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1710](https://github.com/dotnet/SqlClient/pull/1710) +- Fixed null SqlBinary as rowversion. [#1712](https://github.com/dotnet/SqlClient/pull/1712) +- Fixed table's collation overriding with default UTF8 collation. [#1749](https://github.com/dotnet/SqlClient/pull/1749) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1755](https://github.com/dotnet/SqlClient/pull/1755), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1711](https://github.com/dotnet/SqlClient/pull/1711) + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/4.1/4.1.md b/release-notes/4.1/4.1.md new file mode 100644 index 0000000000..a310caa5f7 --- /dev/null +++ b/release-notes/4.1/4.1.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 4.1 Releases + +The following Microsoft.Data.SqlClient 4.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/09/13 | 4.1.1 | [release notes](4.1.1.md) | +| 2022/01/31 | 4.1.0 | [release notes](4.1.0.md) | diff --git a/release-notes/4.1/README.md b/release-notes/4.1/README.md new file mode 100644 index 0000000000..a310caa5f7 --- /dev/null +++ b/release-notes/4.1/README.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 4.1 Releases + +The following Microsoft.Data.SqlClient 4.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/09/13 | 4.1.1 | [release notes](4.1.1.md) | +| 2022/01/31 | 4.1.0 | [release notes](4.1.0.md) | diff --git a/release-notes/5.0/5.0.0-preview1.md b/release-notes/5.0/5.0.0-preview1.md new file mode 100644 index 0000000000..55faad3c70 --- /dev/null +++ b/release-notes/5.0/5.0.0-preview1.md @@ -0,0 +1,136 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0-preview1.22069.1 released 9 March 2022 + +This update brings the below changes over the previous release: + +### Added + +- Added SqlDataSourceEnumerator. [#1430](https://github.com/dotnet/SqlClient/pull/1430), [Read more](#sql-data-source-enumerator-support) +- Added new attestation protocol `None` option to forgo enclave attestation when using VBS enclaves. [#1425](https://github.com/dotnet/SqlClient/pull/1425) and [#1419](https://github.com/dotnet/SqlClient/pull/1419), [Read more](#new-attestation-protocol-none) +- Added a new AppContext switch to suppress insecure TLS warnings. [#1457](https://github.com/dotnet/SqlClient/pull/1457), [Read more](#suppress-insecure-tls-warnings) + +### Fixed + +- Fixed all documentation paths to Unix format path. [#1442](https://github.com/dotnet/SqlClient/pull/1442) +- Fixed thread safety issue for `GetEnclaveProvider` by converting dictionary to concurrent dictionary. [#1451](https://github.com/dotnet/SqlClient/pull/1451) + +### Changed +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v5.0.0-preview1.22062.1`. [#1537](https://github.com/dotnet/SqlClient/pull/1537) +- Modernized style in ValueUtilSmi. [#1351](https://github.com/dotnet/SqlClient/pull/1351) +- Changed SQL server codenames to version names. [#1439](https://github.com/dotnet/SqlClient/pull/1439) +- Prevented subtype generation in project files. [#1452](https://github.com/dotnet/SqlClient/pull/1452) +- Changed `Array.Copy` to `Buffer.BlockCopy` for byte arrays. [#1366](https://github.com/dotnet/SqlClient/pull/1366) +- Changed files in csproj to be alphabetically sorted in netfx and netcore. [#1364](https://github.com/dotnet/SqlClient/pull/1364) +- Sqlstream, SqlInternalTransaction and MetaDataUtilsSmi are moved to shared folder. [#1337](https://github.com/dotnet/SqlClient/pull/1337), [#1346](https://github.com/dotnet/SqlClient/pull/1346) and [#1339](https://github.com/dotnet/SqlClient/pull/1339) +- Various code improvements: [#1197](https://github.com/dotnet/SqlClient/pull/1197), [#1313](https://github.com/dotnet/SqlClient/pull/1313),[#1330](https://github.com/dotnet/SqlClient/pull/1330),[#1366](https://github.com/dotnet/SqlClient/pull/1366), [#1435](https://github.com/dotnet/SqlClient/pull/1435),[#1478](https://github.com/dotnet/SqlClient/pull/1478) + +### SQL Data Source Enumerator support +Provides a mechanism for enumerating all available instances of SQL Server within the local network. +```cs +using Microsoft.Data.Sql; + +static void Main() + { + // Retrieve the enumerator instance and then the data. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Display the contents of the table. + DisplayData(table); + + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +``` + +### New Attestation protocol `None` + new attestation protocol called `None` will be allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" + +``` + +### Suppress insecure TLS warnings +A security warning is ouptput on the console if the TLS version less than 1.2 is used to negotiate with the server. This warning could be suppressed on SQL connection while `Encrypt = false` by enabling the following AppContext switch on the application startup: +```cs +Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.0.0.preview1.22062.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0.preview1.22062.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0.preview1.22062.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/5.0/5.0.0-preview2.md b/release-notes/5.0/5.0.0-preview2.md new file mode 100644 index 0000000000..16158a05fb --- /dev/null +++ b/release-notes/5.0/5.0.0-preview2.md @@ -0,0 +1,82 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0-preview2.22096.2 released 6 April 2022 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview1 + +- Dropped support for .NET Framework 4.6.1 [#1574](https://github.com/dotnet/SqlClient/pull/1574) + +### Fixed + +- Fixed connection failure by skipping Certificate Revocation List (CRL) check during authentication [#1559](https://github.com/dotnet/SqlClient/pull/1559) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0-preview2.22084.1`. [#1563](https://github.com/dotnet/SqlClient/pull/1563) +- Updated `Azure.Identity` version to `1.5.0` and `Microsoft.Identity.Client` version to `4.30.1` [#1462](https://github.com/dotnet/SqlClient/pull/1462) +- Replaced AlwaysEncryptedAttestationException with SqlException [#1515](https://github.com/dotnet/SqlClient/pull/1515) +- Improved error message when adding wrong type to SqlParameterCollection [#1547](https://github.com/dotnet/SqlClient/pull/1547) +- Code health improvements [#1343](https://github.com/dotnet/SqlClient/pull/1343) [#1370](https://github.com/dotnet/SqlClient/pull/1370) [#1371](https://github.com/dotnet/SqlClient/pull/1371) [#1438](https://github.com/dotnet/SqlClient/pull/1438) [#1483](https://github.com/dotnet/SqlClient/pull/1483) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.0.0-preview2.22084.1 +- Azure.Identity 1.5.0 +- Microsoft.Identity.Client 4.30.1 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview2.22084.1 +- Azure.Identity 1.5.0 +- Microsoft.Identity.Client 4.30.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview2.22084.1 +- Azure.Identity 1.5.0 +- Microsoft.Identity.Client 4.30.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/5.0/5.0.0-preview3.md b/release-notes/5.0/5.0.0-preview3.md new file mode 100644 index 0000000000..19819018bd --- /dev/null +++ b/release-notes/5.0/5.0.0-preview3.md @@ -0,0 +1,128 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0-preview3 released 16 June 2022 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview2 + +- Added a dependency on the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package. This new dependency may cause namespace conflicts if your application references that namespace and still has package references (direct or indirect) to System.Data.SqlClient from .NET Core. +- Dropped classes from the `Microsoft.Data.SqlClient.Server` namespace and replaced them with supported types from the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package.[#1585](https://github.com/dotnet/SqlClient/pull/1585) The affected classes and enums are: + - Microsoft.Data.SqlClient.Server.IBinarySerialize -> Microsoft.SqlServer.Server.IBinarySerialize + - Microsoft.Data.SqlClient.Server.InvalidUdtException -> Microsoft.SqlServer.Server.InvalidUdtException + - Microsoft.Data.SqlClient.Server.SqlFacetAttribute -> Microsoft.SqlServer.Server.SqlFacetAttribute + - Microsoft.Data.SqlClient.Server.SqlFunctionAttribute -> Microsoft.SqlServer.Server.SqlFunctionAttribute + - Microsoft.Data.SqlClient.Server.SqlMethodAttribute -> Microsoft.SqlServer.Server.SqlMethodAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregateAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute + - (enum) Microsoft.Data.SqlClient.Server.DataAccessKind -> Microsoft.SqlServer.Server.DataAccessKind + - (enum) Microsoft.Data.SqlClient.Server.Format -> Microsoft.SqlServer.Server.Format + - (enum) Microsoft.Data.SqlClient.Server.SystemDataAccessKind -> Microsoft.SqlServer.Server.SystemDataAccessKind + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify Encrypt=Strict in the connection string. [#1608](https://github.com/dotnet/SqlClient/pull/1608) [Read more](#tds-8-enhanced-security) +- Added support for specifying Server SPN and Failover Server SPN on the connection. [#1607](https://github.com/dotnet/SqlClient/pull/1607) [Read more](#server-spn) +- Added support for aliases when targeting .NET Core on Windows. [#1588](https://github.com/dotnet/SqlClient/pull/1588) [Read more](#support-for-aliases) + +### Fixed + +- Fixed naming, order, and formatting for `SqlDiagnosticsListener` on .NET Core and .NET. [#1637] (https://github.com/dotnet/SqlClient/pull/1637) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1625] (https://github.com/dotnet/SqlClient/pull/1625) +- Added CommandText length validation when using stored procedure command types. [#1484](https://github.com/dotnet/SqlClient/pull/1484) +- Fixed `GetSchema("StructuredTypeMembers")` to return correct schema information. [#1500] (https://github.com/dotnet/SqlClient/pull/1500), [#1639](https://github.com/dotnet/SqlClient/pull/1639) +- Fixed NullReferenceException when using `SqlDependency.Start` against an Azure SQL Database.[#1294] (https://github.com/dotnet/SqlClient/pull/1294) +- Send the correct retained transaction descriptor in the MARS TDS Header when there is no current transaction on .NET 5+ and .NET Core. [#1624] (https://github.com/dotnet/SqlClient/pull/1624) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1578] (https://github.com/dotnet/SqlClient/pull/1578) +- Adjust the default ConnectRetryCount against Azure Synapse OnDemand endpoints [#1626] (https://github.com/dotnet/SqlClient/pull/1626) + +### Changed + +- Code health improvements [#1353](https://github.com/dotnet/SqlClient/pull/1353) [#1354](https://github.com/dotnet/SqlClient/pull/1354) [#1525](https://github.com/dotnet/SqlClient/pull/1525) [#1186](https://github.com/dotnet/SqlClient/pull/1186) +- Update Azure Identity dependency from 1.5.0 to 1.6.0.[#1611](https://github.com/dotnet/SqlClient/pull/1611) +- Improved Regex for SqlCommandSet [#1548] (https://github.com/dotnet/SqlClient/pull/1548) +- Rework on `TdsParserStateObjectManaged` with nullable annotations. [#1555] (https://github.com/dotnet/SqlClient/pull/1555) + +### TDS 8 Enhanced Security + +To use TDS 8, specify Encrypt=Strict in the connection string. Strict mode disables TrustServerCertificate (always treated as False in Strict mode). HostNameInCertificate has been added to help some Strict mode scenarios. TDS 8 begins and continues all server communication inside a secure, encrypted TLS connection. + +New Encrypt values have been added to clarify connection encryption behavior. Encrypt=Mandatory is equavalent to Encrypt=True and encrypts connections during the TDS connection negotiation. Encrypt=Optional is equivalent to Encrypt=False and only encrypts the connection if the server tells the client that encryption is required during the TDS connection negotiation. + +HostNameInCertificate can be specified in the connection string when using aliases to connect with encryption to a server that has a server certificate with a different name or alternate subject name than the name used by the client to identify the server (DNS aliases, for example). Example usage: HostNameInCertificate=MyDnsAliasName + +### Server SPN + +When connecting in an environment that has unique domain/forest topography, the ServerSPN/Server SPN and FailoverServerSPN/Failover Server SPN connection string settings can be used to override the auto-generated server SPNs used in the library when authenticating with integrated authentication in a domain environment. + +### Support for Aliases + +Users can configure Aliases by using the SQL Server Configuration Manager. These are stored in the Windows registry and are already supported when targeting .NET Framework. This release brings support for aliases when targeting .NET or .NET Core on Windows. + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview3.22165.1 +- Azure.Identity 1.6.0.0 +- Microsoft.Identity.Client 4.43.2.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0.0 +- System.Buffers 4.0.3.0 +- System.Configuration 4.0.0.0 +- System.Data 4.0.0.0 +- System.EnterpriseServices 4.0.0.0 +- System.IdentityModel.Tokens.Jwt 6.8.0.0 +- System.Runtime.Caching 4.0.0.0 +- System.Runtime.InteropServices.RuntimeInformation 4.0.2.0 +- System.Runtime.Serialization 4.0.0.0 +- System.Transactions 4.0.0.0 +- System.Xml 4.0.0.0 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview3.22165.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.43.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview3.22165.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.43.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 +- System.Security.Permissions 5.0.0 +- NetStandard.Library 2.0.3 diff --git a/release-notes/5.0/5.0.0.md b/release-notes/5.0/5.0.0.md new file mode 100644 index 0000000000..1bf0f071c5 --- /dev/null +++ b/release-notes/5.0/5.0.0.md @@ -0,0 +1,196 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0 released 5 August 2022 + +This update includes the following changes over the 4.1 release: + +### Breaking changes + +- As part of the [`TDS 8` feature](#tds-8-enhanced-security), the `SqlConnectionStringBuilder.Encrypt` property has changed from a `bool` to a `SqlConnectionEncryptOption`. `SqlConnectionEncryptOption` has implicit conversion rules to convert to/from a `bool` so that existing code remains backwards compatible, however this is a binary-breaking change and a recompile is required against this version. +- Added a dependency on the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package. This new dependency may cause namespace conflicts if your application references that namespace and still has package references (direct or indirect) to System.Data.SqlClient from .NET Core. +- Dropped classes from the `Microsoft.Data.SqlClient.Server` namespace and replaced them with supported types from the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package.[#1585](https://github.com/dotnet/SqlClient/pull/1585) The affected classes and enums are: + - Microsoft.Data.SqlClient.Server.IBinarySerialize -> Microsoft.SqlServer.Server.IBinarySerialize + - Microsoft.Data.SqlClient.Server.InvalidUdtException -> Microsoft.SqlServer.Server.InvalidUdtException + - Microsoft.Data.SqlClient.Server.SqlFacetAttribute -> Microsoft.SqlServer.Server.SqlFacetAttribute + - Microsoft.Data.SqlClient.Server.SqlFunctionAttribute -> Microsoft.SqlServer.Server.SqlFunctionAttribute + - Microsoft.Data.SqlClient.Server.SqlMethodAttribute -> Microsoft.SqlServer.Server.SqlMethodAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregateAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute + - (enum) Microsoft.Data.SqlClient.Server.DataAccessKind -> Microsoft.SqlServer.Server.DataAccessKind + - (enum) Microsoft.Data.SqlClient.Server.Format -> Microsoft.SqlServer.Server.Format + - (enum) Microsoft.Data.SqlClient.Server.SystemDataAccessKind -> Microsoft.SqlServer.Server.SystemDataAccessKind +- Dropped support for .NET Framework 4.6.1 [#1574](https://github.com/dotnet/SqlClient/pull/1574) + + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify Encrypt=Strict in the connection string. [#1608](https://github.com/dotnet/SqlClient/pull/1608) [Read more](#tds-8-enhanced-security) +- Added `TDS 8` version for TDSLogin. [#1657](https://github.com/dotnet/SqlClient/pull/1657) +- Added support for specifying Server SPN and Failover Server SPN on the connection. [#1607](https://github.com/dotnet/SqlClient/pull/1607) [Read more](#server-spn) +- Added support for aliases when targeting .NET Core on Windows. [#1588](https://github.com/dotnet/SqlClient/pull/1588) [Read more](#support-for-aliases) +- Added support for `SqlDataSourceEnumerator` on Windows. [#1430](https://github.com/dotnet/SqlClient/pull/1430), [Read more](#sql-data-source-enumerator-support) +- Added new attestation protocol `None` option to forgo enclave attestation when using VBS enclaves. [#1425](https://github.com/dotnet/SqlClient/pull/1425) and [#1419](https://github.com/dotnet/SqlClient/pull/1419), [Read more](#new-attestation-protocol-none) +- Added a new AppContext switch to suppress insecure TLS warnings. [#1457](https://github.com/dotnet/SqlClient/pull/1457), [Read more](#suppress-insecure-tls-warnings) + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1688](https://github.com/dotnet/SqlClient/pull/1688) +- Fixed **KeyNotFoundException** for the `FailoverPartner` key on SQL servers with availability group configured. [#1614](https://github.com/dotnet/SqlClient/pull/1614) +- Fixed naming, order, and formatting for `SqlDiagnosticsListener` on .NET Core and .NET. [#1637](https://github.com/dotnet/SqlClient/pull/1637) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1625](https://github.com/dotnet/SqlClient/pull/1625) +- Added CommandText length validation when using stored procedure command types. [#1484](https://github.com/dotnet/SqlClient/pull/1484) +- Fixed `GetSchema("StructuredTypeMembers")` to return correct schema information. [#1500](https://github.com/dotnet/SqlClient/pull/1500), [#1639](https://github.com/dotnet/SqlClient/pull/1639) +- Fixed **NullReferenceException** when using `SqlDependency.Start` against an Azure SQL Database. [#1294](https://github.com/dotnet/SqlClient/pull/1294) +- Fixed transaction descriptor in the MARS TDS Header when there is no current transaction on .NET 5+ and .NET Core. [#1624](https://github.com/dotnet/SqlClient/pull/1624) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1578](https://github.com/dotnet/SqlClient/pull/1578) +- Fixed connection failure by skipping Certificate Revocation List (CRL) check during authentication. [#1559](https://github.com/dotnet/SqlClient/pull/1559) +- Fixed thread safety issue for `GetEnclaveProvider` by converting dictionary to concurrent dictionary. [#1451](https://github.com/dotnet/SqlClient/pull/1451) + +### Changed + +- Updated `AuthProviderInfo` struct to be matched the changes in native SNI for `TDS 8` server certificate validation. [#1680](https://github.com/dotnet/SqlClient/pull/1680) +- Updated default system protocol for `TDS 8` on managed code. [#1678](https://github.com/dotnet/SqlClient/pull/1678) +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0`. [#1608](https://github.com/dotnet/SqlClient/pull/1608) +- Changed encoding UTF-7 to ASCII for SSRP Broadcast. [#1671](https://github.com/dotnet/SqlClient/pull/1671) +- Updated `IdentityModel` dependency from 6.8.0 to 6.21.0 and `IdentityClient` from 4.32.2 to 4.45.0. [#1646](https://github.com/dotnet/SqlClient/pull/1646) +- Updated **Azure Identity** dependency from 1.5.0 to 1.6.0. [#1611](https://github.com/dotnet/SqlClient/pull/1611) +- Improved Regex for `SqlCommandSet`. [#1548](https://github.com/dotnet/SqlClient/pull/1548) +- Adjust the default **ConnectRetryCount** against Azure Synapse OnDemand endpoints. [#1626](https://github.com/dotnet/SqlClient/pull/1626) +- Updated `Azure.Identity` version to `1.5.0` and `Microsoft.Identity.Client` version to `4.30.1`. [#1462](https://github.com/dotnet/SqlClient/pull/1462) +- Replaced `AlwaysEncryptedAttestationException` with `SqlException`. [#1515](https://github.com/dotnet/SqlClient/pull/1515) +- Improved error message when adding wrong type to `SqlParameterCollection`. [#1547](https://github.com/dotnet/SqlClient/pull/1547) +- Changed SQL server codenames to version names in the code. [#1439](https://github.com/dotnet/SqlClient/pull/1439) +- Changed `Array.Copy` to `Buffer.BlockCopy` for byte arrays. [#1366](https://github.com/dotnet/SqlClient/pull/1366) +- Various code improvements: [#1197](https://github.com/dotnet/SqlClient/pull/1197), [#1313](https://github.com/dotnet/SqlClient/pull/1313), [#1330](https://github.com/dotnet/SqlClient/pull/1330), [#1366](https://github.com/dotnet/SqlClient/pull/1366), [#1435](https://github.com/dotnet/SqlClient/pull/1435), [#1478](https://github.com/dotnet/SqlClient/pull/1478), [#1353](https://github.com/dotnet/SqlClient/pull/1353), [#1354](https://github.com/dotnet/SqlClient/pull/1354), [#1525](https://github.com/dotnet/SqlClient/pull/1525), [#1186](https://github.com/dotnet/SqlClient/pull/1186), [#1343](https://github.com/dotnet/SqlClient/pull/1343), [#1370](https://github.com/dotnet/SqlClient/pull/1370), [#1371](https://github.com/dotnet/SqlClient/pull/1371), [#1438](https://github.com/dotnet/SqlClient/pull/1438), [#1483](https://github.com/dotnet/SqlClient/pull/1483), [#1351](https://github.com/dotnet/SqlClient/pull/1351), [#1452](https://github.com/dotnet/SqlClient/pull/1452), [#1364](https://github.com/dotnet/SqlClient/pull/1364),[#1337](https://github.com/dotnet/SqlClient/pull/1337), [#1346](https://github.com/dotnet/SqlClient/pull/1346), [#1339](https://github.com/dotnet/SqlClient/pull/1339), [#1555](https://github.com/dotnet/SqlClient/pull/1555) + + +### TDS 8 Enhanced Security + +To use TDS 8.0, specify Encrypt=Strict in the connection string. Strict mode disables TrustServerCertificate (always treated as False in Strict mode). HostNameInCertificate has been added to help some Strict mode scenarios. TDS 8 begins and continues all server communication inside a secure, encrypted TLS connection. + +New Encrypt values have been added to clarify connection encryption behavior. Encrypt=Mandatory is equivalent to Encrypt=True and encrypts connections during the TDS connection negotiation. Encrypt=Optional is equivalent to Encrypt=False and only encrypts the connection if the server tells the client that encryption is required during the TDS connection negotiation. + +HostNameInCertificate can be specified in the connection string when using aliases to connect with encryption to a server that has a server certificate with a different name or alternate subject name than the name used by the client to identify the server (DNS aliases, for example). Example usage: HostNameInCertificate=MyDnsAliasName + +To read more about TDS 8.0 in SQL Server, see the [SQL Server online documentation](https://docs.microsoft.com/sql/relational-databases/security/networking/tds-8-and-tls-1-3). + +### Server SPN + +When connecting in an environment that has unique domain/forest topography, the ServerSPN/Server SPN and FailoverServerSPN/Failover Server SPN connection string settings can be used to override the auto-generated server SPNs used in the library when authenticating with integrated authentication in a domain environment. + +### Support for Aliases + +Users can configure Aliases by using the SQL Server Configuration Manager. These are stored in the Windows registry and are already supported when targeting .NET Framework. This release brings support for aliases when targeting .NET or .NET Core on Windows. + + +### SQL Data Source Enumerator support +Provides a mechanism for enumerating all available instances of SQL Server within the local network. +```cs +using Microsoft.Data.Sql; + +static void Main() + { + // Retrieve the enumerator instance and then the data. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Display the contents of the table. + DisplayData(table); + + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +``` + +### New Attestation protocol `None` +A new attestation protocol called `None` is allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" + +``` + +### Suppress insecure TLS warnings +A security warning is output to the console if a TLS version less than 1.2 is used to negotiate encryption with the server. This warning can be suppressed on connections where `Encrypt = false` by enabling the following AppContext switch at application startup: +```cs +Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning +``` + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.0.0 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encoding.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Loader 4.3.0 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.0/5.0.1.md b/release-notes/5.0/5.0.1.md new file mode 100644 index 0000000000..39d36cc579 --- /dev/null +++ b/release-notes/5.0/5.0.1.md @@ -0,0 +1,81 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.1 released 7 October 2022 + +This update includes the following changes over the 5.0.0 release: + +### Fixed + +- Fixed missing `HostNameInCertificate` connection string property in .NET Framework. [#1782](https://github.com/dotnet/SqlClient/pull/1782) +- Fixed async deadlock issue when sending attention fails due to network failure. [#1783](https://github.com/dotnet/SqlClient/pull/1783) +- Fixed **Null Reference Exception** on assigning `null` to `SqlConnectionStringBuilder.Encrypt`. [#1784](https://github.com/dotnet/SqlClient/pull/1784) +- Fixed `ReadAsync()` behavior to register Cancellation token action before streaming results. [#1785](https://github.com/dotnet/SqlClient/pull/1785) +- Fixed hang on infinite timeout and managed SNI. [#1798](https://github.com/dotnet/SqlClient/pull/1798) +- Fixed Default UTF8 collation conflict. [#1799](https://github.com/dotnet/SqlClient/pull/1799) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.1` [#1795](https://github.com/dotnet/SqlClient/pull/1795), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418). + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.0.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encoding.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Loader 4.3.0 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.0/5.0.md b/release-notes/5.0/5.0.md new file mode 100644 index 0000000000..ec3dbc7eb6 --- /dev/null +++ b/release-notes/5.0/5.0.md @@ -0,0 +1,16 @@ +# Microsoft.Data.SqlClient 5.0 Releases + +The following Microsoft.Data.SqlClient 5.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/10/07 | 5.0.1 | [release notes](5.0.1.md) | +| 2022/08/05 | 5.0.0 | [release notes](5.0.0.md) | + +The following Microsoft.Data.SqlClient 5.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/06/16 | 5.0.0-preview3.22168.1 | [release notes](5.0.0-preview3.md) | +| 2022/04/06 | 5.0.0-preview2.22096.2 | [release notes](5.0.0-preview2.md) | +| 2022/03/09 | 5.0.0-preview1.22069.1 | [release notes](5.0.0-preview1.md) | diff --git a/release-notes/5.0/README.md b/release-notes/5.0/README.md new file mode 100644 index 0000000000..ec3dbc7eb6 --- /dev/null +++ b/release-notes/5.0/README.md @@ -0,0 +1,16 @@ +# Microsoft.Data.SqlClient 5.0 Releases + +The following Microsoft.Data.SqlClient 5.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/10/07 | 5.0.1 | [release notes](5.0.1.md) | +| 2022/08/05 | 5.0.0 | [release notes](5.0.0.md) | + +The following Microsoft.Data.SqlClient 5.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/06/16 | 5.0.0-preview3.22168.1 | [release notes](5.0.0-preview3.md) | +| 2022/04/06 | 5.0.0-preview2.22096.2 | [release notes](5.0.0-preview2.md) | +| 2022/03/09 | 5.0.0-preview1.22069.1 | [release notes](5.0.0-preview1.md) | diff --git a/release-notes/5.1/5.1.0-preview1.md b/release-notes/5.1/5.1.0-preview1.md new file mode 100644 index 0000000000..222f10f7d5 --- /dev/null +++ b/release-notes/5.1/5.1.0-preview1.md @@ -0,0 +1,89 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.0-preview1.22279.3 released 19 October 2022 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed `ReadAsync()` behavior to register Cancellation token action before streaming results. [#1781](https://github.com/dotnet/SqlClient/pull/1781) +- Fixed `NullReferenceException` when assigning `null` to `SqlConnectionStringBuilder.Encrypt`. [#1778](https://github.com/dotnet/SqlClient/pull/1778) +- Fixed missing `HostNameInCertificate` property in .NET Framework Reference Project. [#1776](https://github.com/dotnet/SqlClient/pull/1776) +- Fixed async deadlock issue when sending attention fails due to network failure. [#1766](https://github.com/dotnet/SqlClient/pull/1766) +- Fixed failed connection requests in ConnectionPool in case of PoolBlock. [#1768](https://github.com/dotnet/SqlClient/pull/1768) +- Fixed hang on infinite timeout and managed SNI. [#1742](https://github.com/dotnet/SqlClient/pull/1742) +- Fixed Default UTF8 collation conflict. [#1739](https://github.com/dotnet/SqlClient/pull/1739) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.0-preview1.22278.1`. [#1787](https://github.com/dotnet/SqlClient/pull/1787) which includes TLS 1.3 Support and fix for AppDomain crash in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Changed the `SqlConnectionEncryptOption` string parser to public. [#1771](https://github.com/dotnet/SqlClient/pull/1771) +- Converted `ExecuteNonQueryAsync` to use async context object. [#1692](https://github.com/dotnet/SqlClient/pull/1692) +- Code health improvements [#1604](https://github.com/dotnet/SqlClient/pull/1604) [#1598](https://github.com/dotnet/SqlClient/pull/1598) [#1595](https://github.com/dotnet/SqlClient/pull/1595) [#1443](https://github.com/dotnet/SqlClient/pull/1443) + +### Known issues + +- When using `Encrypt=Strict` with TLS v1.3, the TLS handshake occurs twice on initial connection on .NET Framework due to a timeout during the TLS handshake and a retry helper re-establishes the connection; however, on .NET Core, it will throw a `System.ComponentModel.Win32Exception (258): The wait operation timed out.` and is being investigated. If you're using Microsoft.Data.SqlClient with .NET Core on Windows 11, you will need to enable the managed SNI on Windows context switch using following statement `AppContext.SetSwitch("Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows", true);` to use TLS v1.3 or disabling TLS 1.3 from the registry by assigning `0` to the following `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Client\Enabled` registry key and it'll use TLS v1.2 for the connection. This will be fixed in a future release. + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.1.0.preview1.22278.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encoding.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI 5.1.0.preview1.22278.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI 5.1.0.preview1.22278.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Loader 4.3.0 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.0-preview2.md b/release-notes/5.1/5.1.0-preview2.md new file mode 100644 index 0000000000..93b58078a0 --- /dev/null +++ b/release-notes/5.1/5.1.0-preview2.md @@ -0,0 +1,98 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.0-preview2.22314.2 released 10 November 2022 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.1.0-preview1 + +- Add support for .NET 6.0 and Dropped support for .NET Core 3.1. [#1704](https://github.com/dotnet/SqlClient/pull/1704) [#1823](https://github.com/dotnet/SqlClient/pull/1823) + +### Added + +- Added support for `DateOnly` and `TimeOnly` for `SqlParameter` value and `GetFieldValue`. [#1813](https://github.com/dotnet/SqlClient/pull/1813) +- Added support for TLS 1.3 for .NET Core and SNI Native. [#1821](https://github.com/dotnet/SqlClient/pull/1821) +- Added `ServerCertificate` support for `Encrypt=Mandatory` or `Encrypt=Strict`. [#1822](https://github.com/dotnet/SqlClient/pull/1822) [Read more](#server-certificate-support) +- Added Windows ARM64 support when targeting .NET Framework. [#1828](https://github.com/dotnet/SqlClient/pull/1828) + +### Fixed + +- Fixed memory leak regression from [#1781](https://github.com/dotnet/SqlClient/pull/1781) using a `DisposableTemporaryOnStack` struct. [#1818](https://github.com/dotnet/SqlClient/pull/1818) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.0-preview2.22311.2`. [#1831](https://github.com/dotnet/SqlClient/pull/1831) which includes the fix for the TLS 1.3 timeout and double handshake issue, removal of ARM32 binaries, and support for the `ServerCertificate` option. [#1822](https://github.com/dotnet/SqlClient/issues/1822) [Read more](#server-certificate-support) +- Reverted "Excluding unsupported TLS protocols" for issue [#1151](https://github.com/dotnet/SqlClient/issues/1151) (i.e. removed `Switch.Microsoft.Data.SqlClient.EnableSecureProtocolsByOS`) by adding support for TLS 1.3. [#1824](https://github.com/dotnet/SqlClient/issues/1824) +- Code health improvements [#1812](https://github.com/dotnet/SqlClient/pull/1812) [#1520](https://github.com/dotnet/SqlClient/pull/1520) + +## New features + +### Server Certificate Support +The default value of the `ServerCertificate` connection setting is an empty string. When `Encrypt` is set to `Mandatory` or `Strict`, `ServerCertificate` can be used to specify a path on the file system to a certificate file to match against the SQL Server's TLS/SSL certificate. For this to be valid, the certificate specified must be an exact match. The accepted certificate formats are `PEM`, `DER`, and `CER`. Here is an usage example: + + ```cs + "Data Source=...;Encrypt=Strict;ServerCertificate=C:\\certificates\\server.cer" + ``` + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET 6.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.1.0.preview2.22311.2 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encoding.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI 5.1.0.preview2.22311.2 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI 5.1.0.preview2.22311.2 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.IO 4.3.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.0.md b/release-notes/5.1/5.1.0.md new file mode 100644 index 0000000000..015b8966b9 --- /dev/null +++ b/release-notes/5.1/5.1.0.md @@ -0,0 +1,103 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.1.0 released 19 January 2023 + +This update includes the following changes over the 5.0 release: + +### Breaking changes + +- Dropped support for .NET Core 3.1. [#1704](https://github.com/dotnet/SqlClient/pull/1704) [#1823](https://github.com/dotnet/SqlClient/pull/1823) + +### Added + +- Added support for .NET 6.0. [#1704](https://github.com/dotnet/SqlClient/pull/1704) +- Added support for `DateOnly` and `TimeOnly` for `SqlParameter` value and `GetFieldValue`. [#1813](https://github.com/dotnet/SqlClient/pull/1813) +- Added support for TLS 1.3 on .NET Core and native SNI. [#1821](https://github.com/dotnet/SqlClient/pull/1821) +- Added `ServerCertificate` setting for `Encrypt=Mandatory` or `Encrypt=Strict`. [#1822](https://github.com/dotnet/SqlClient/pull/1822) [Read more](#server-certificate) +- Added Windows ARM64 support when targeting .NET Framework. [#1828](https://github.com/dotnet/SqlClient/pull/1828) + +### Fixed + +- Fixed thread safety of transient error list in configurable retry logic. [#1882](https://github.com/dotnet/SqlClient/pull/1882) +- Fixed deadlock when using SinglePhaseCommit with distributed transactions. [#1801](https://github.com/dotnet/SqlClient/pull/1801) +- Fixed Dedicated Admin Connections (DAC) to localhost in managed SNI [#1865](https://github.com/dotnet/SqlClient/pull/1865) +- Fixed memory leak regression from [#1781](https://github.com/dotnet/SqlClient/pull/1781) using a `DisposableTemporaryOnStack` struct. [#1818](https://github.com/dotnet/SqlClient/pull/1818) +- Fixed `ReadAsync()` behavior to register Cancellation token action before streaming results. [#1781](https://github.com/dotnet/SqlClient/pull/1781) +- Fixed `NullReferenceException` when assigning `null` to `SqlConnectionStringBuilder.Encrypt`. [#1778](https://github.com/dotnet/SqlClient/pull/1778) +- Fixed missing `HostNameInCertificate` property in .NET Framework Reference Project. [#1776](https://github.com/dotnet/SqlClient/pull/1776) +- Fixed async deadlock issue when sending attention fails due to network failure. [#1766](https://github.com/dotnet/SqlClient/pull/1766) +- Fixed failed connection requests in ConnectionPool in case of PoolBlock. [#1768](https://github.com/dotnet/SqlClient/pull/1768) +- Fixed hang on infinite timeout and managed SNI. [#1742](https://github.com/dotnet/SqlClient/pull/1742) +- Fixed Default UTF8 collation conflict. [#1739](https://github.com/dotnet/SqlClient/pull/1739) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.1.0`. [#1889](https://github.com/dotnet/SqlClient/pull/1889) which includes fix for AppDomain crash in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418), TLS 1.3 Support, removal of ARM32 binaries, and support for the `ServerCertificate` option. [#1822](https://github.com/dotnet/SqlClient/issues/1822) [Read more](#server-certificate) +- Reverted "Excluding unsupported TLS protocols" for issue [#1151](https://github.com/dotnet/SqlClient/issues/1151) (i.e. removed `Switch.Microsoft.Data.SqlClient.EnableSecureProtocolsByOS`) by adding support for TLS 1.3. [#1824](https://github.com/dotnet/SqlClient/issues/1824) +- Changed the `SqlConnectionEncryptOption` string parser to public. [#1771](https://github.com/dotnet/SqlClient/pull/1771) +- Converted `ExecuteNonQueryAsync` to use async context object. [#1692](https://github.com/dotnet/SqlClient/pull/1692) +- Code health improvements [#1867](https://github.com/dotnet/SqlClient/pull/1867) [#1849](https://github.com/dotnet/SqlClient/pull/1849) [#1812](https://github.com/dotnet/SqlClient/pull/1812) [#1520](https://github.com/dotnet/SqlClient/pull/1520) [#1604](https://github.com/dotnet/SqlClient/pull/1604) [#1598](https://github.com/dotnet/SqlClient/pull/1598) [#1595](https://github.com/dotnet/SqlClient/pull/1595) [#1443](https://github.com/dotnet/SqlClient/pull/1443) + +## New features + +### Server Certificate + +The default value of the `ServerCertificate` connection setting is an empty string. When `Encrypt` is set to `Mandatory` or `Strict`, `ServerCertificate` can be used to specify a path on the file system to a certificate file to match against the SQL Server's TLS/SSL certificate. For this to be valid, the certificate specified must be an exact match. The accepted certificate formats are `PEM`, `DER`, and `CER`. Here is an example: + + ```cs + "Data Source=...;Encrypt=Strict;ServerCertificate=C:\\certificates\\server.cer" + ``` + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET 6.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.1.0 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Text.Encoding.Web 6.0.0 + +#### .NET + +- Microsoft.Data.SqlClient.SNI 5.1.0 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Diagnostics.DiagnosticSource 6.0.0 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI 5.1.0 +- Azure.Identity 1.7.0 +- Microsoft.Identity.Client 4.47.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.24.0 +- Microsoft.IdentityModel.JsonWebTokens 6.24.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 6.0.1 +- System.Runtime.Caching 6.0.0 +- System.Text.Encoding.CodePages 6.0.0 +- System.Text.Encodings.Web 6.0.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.1/5.1.md b/release-notes/5.1/5.1.md new file mode 100644 index 0000000000..6f6687b5d5 --- /dev/null +++ b/release-notes/5.1/5.1.md @@ -0,0 +1,14 @@ +# Microsoft.Data.SqlClient 5.1 Releases + +The following Microsoft.Data.SqlClient 5.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2023/01/19 | 5.1.0 | [release notes](5.1.0.md) | + +The following Microsoft.Data.SqlClient 5.1 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/11/10 | 5.1.0-preview2.22314.2 | [release notes](5.1.0-preview2.md) | +| 2022/10/19 | 5.1.0-preview1.22279.3 | [release notes](5.1.0-preview1.md) | diff --git a/release-notes/5.1/README.md b/release-notes/5.1/README.md new file mode 100644 index 0000000000..6f6687b5d5 --- /dev/null +++ b/release-notes/5.1/README.md @@ -0,0 +1,14 @@ +# Microsoft.Data.SqlClient 5.1 Releases + +The following Microsoft.Data.SqlClient 5.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2023/01/19 | 5.1.0 | [release notes](5.1.0.md) | + +The following Microsoft.Data.SqlClient 5.1 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/11/10 | 5.1.0-preview2.22314.2 | [release notes](5.1.0-preview2.md) | +| 2022/10/19 | 5.1.0-preview1.22279.3 | [release notes](5.1.0-preview1.md) | diff --git a/release-notes/MSqlServerServer/1.0/1.0.0.md b/release-notes/MSqlServerServer/1.0/1.0.0.md new file mode 100644 index 0000000000..72072f174a --- /dev/null +++ b/release-notes/MSqlServerServer/1.0/1.0.0.md @@ -0,0 +1,42 @@ +# Release Notes + +## General Availability of Microsoft.SqlServer.Server + +_**1.0.0 released 28 January 2022**_ + +This is the initial public stable release of the **Microsoft.SqlServer.Server** namespace in a separate assembly. This library is a dependency for **Microsoft.Data.SqlClient** enabling cross framework support of UDT types. + +### Supported types + +#### Targeting .NET Standard 2.0 + +- Microsoft.SqlServer.Server.DataAccessKind +- Microsoft.SqlServer.Server.Format +- Microsoft.SqlServer.Server.IBinarySerialize +- Microsoft.SqlServer.Server.InvalidUdtException +- Microsoft.SqlServer.Server.SqlFacetAttribute +- Microsoft.SqlServer.Server.SqlFunctionAttribute +- Microsoft.SqlServer.Server.SqlMethodAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute +- Microsoft.SqlServer.Server.SystemDataAccessKind + +#### Targeting .NET Framework 4.6+ + +Following types forwarded to `System.Data`: + +- Microsoft.SqlServer.Server.DataAccessKind +- Microsoft.SqlServer.Server.Format +- Microsoft.SqlServer.Server.IBinarySerialize +- Microsoft.SqlServer.Server.InvalidUdtException +- Microsoft.SqlServer.Server.SqlFacetAttribute +- Microsoft.SqlServer.Server.SqlFunctionAttribute +- Microsoft.SqlServer.Server.SqlMethodAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute +- Microsoft.SqlServer.Server.SystemDataAccessKind + +## Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Standard 2.0 (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) diff --git a/release-notes/MSqlServerServer/1.0/README.md b/release-notes/MSqlServerServer/1.0/README.md new file mode 100644 index 0000000000..ffe4befce8 --- /dev/null +++ b/release-notes/MSqlServerServer/1.0/README.md @@ -0,0 +1,7 @@ +# Microsoft.SqlServer.Server 1.0 Releases + +The following Microsoft.SqlServer.Server 1.0 stable release has been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2022/01/28 | 1.0.0 | [release notes](1.0.0.md) | diff --git a/release-notes/README.md b/release-notes/README.md index fc3dcf3315..ba76fe8433 100644 --- a/release-notes/README.md +++ b/release-notes/README.md @@ -1,9 +1,15 @@ # Microsoft.Data.SqlClient Release Notes -The latest stable release is [Microsoft.Data.SqlClient 2.1](2.1). +The latest stable release is [Microsoft.Data.SqlClient 5.0](5.0). ## Release Information +- [Microsoft.Data.SqlClient 5.1](5.1) +- [Microsoft.Data.SqlClient 5.0](5.0) +- [Microsoft.Data.SqlClient 4.1](4.1) +- [Microsoft.Data.SqlClient 4.0](4.0) +- [Microsoft.Data.SqlClient 3.0](3.0) +- [Microsoft.Data.SqlClient 3.1](3.1) - [Microsoft.Data.SqlClient 2.1](2.1) - [Microsoft.Data.SqlClient 2.0](2.0) - [Microsoft.Data.SqlClient 1.1](1.1) @@ -11,8 +17,20 @@ The latest stable release is [Microsoft.Data.SqlClient 2.1](2.1). # Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider Release Notes -The latest stable release is [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.0](add-ons/AzureKeyVaultProvider/1.0). +The latest stable release is [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0](add-ons/AzureKeyVaultProvider/3.0). ## Release Information +- [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0](add-ons/AzureKeyVaultProvider/3.0) +- [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 2.0](add-ons/AzureKeyVaultProvider/2.0) +- [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.2](add-ons/AzureKeyVaultProvider/1.2) +- [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.1](add-ons/AzureKeyVaultProvider/1.1) - [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.0](add-ons/AzureKeyVaultProvider/1.0) + +# Microsoft.SqlServer.Server Release Notes + +The latest stable release is [Microsoft.SqlServer.Server 1.0](MSqlServerServer/1.0). + +## Release Information + +- [Microsoft.SqlServer.Server 1.0](MSqlServerServer/1.0) diff --git a/release-notes/add-ons/AzureKeyVaultProvider/1.1/1.1.1.md b/release-notes/add-ons/AzureKeyVaultProvider/1.1/1.1.1.md new file mode 100644 index 0000000000..6fd241bbbf --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/1.1/1.1.1.md @@ -0,0 +1,36 @@ +# Release Notes + +## General Availability of Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +_**1.1.1 released 02 March 2020**_ + +This library contains the implementation of `Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider` for accessing Azure Key Vault, and the provider class is named `SqlColumnEncryptionAzureKeyVaultProvider`. + +### Working with SQLColumnEncryptionAzureKeyVaultProvider +`SqlColumnEncryptionAzureKeyVaultProvider` is implemented against `Microsoft.Data.SqlClient` and supports .NET Framework 4.6+ and .NET Core 2.1+. The provider name identifier for this library is "**AZURE_KEY_VAULT**" and it is not registered in the driver by default. Client applications may call the `SqlConnection.RegisterColumnEncryptionKeyStoreProviders()` API once in the lifetime of the driver to register this custom provider by implementing a custom Authentication Callback mechanism. + +Once the provider is registered, it can used to perform Always Encrypted operations by creating a Column Master Key using the Azure Key Vault Key Identifier URL. + +A sample C# application to demonstrate Always Encrypted with Azure Key Vault can be download from the samples directory: [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/master/doc/samples/AzureKeyVaultProviderExample.cs) + +## Target Platform Support + +- .NET Framework 4.6+ +- .NET Core 2.1+ (Windows x86, Windows x64, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Azure.KeyVault 3.0.4 +- Microsoft.Azure.KeyVault.WebKey 3.0.4 +- Microsoft.Data.SqlClient 1.0.19269.1 +- Microsoft.Rest.ClientRuntime 2.3.20 +- Microsoft.Rest.ClientRuntime.Azure 3.3.19 + +#### .NET Core + +- Microsoft.Azure.KeyVault 3.0.4 +- Microsoft.Azure.KeyVault.WebKey 3.0.4 +- Microsoft.Data.SqlClient 1.0.19269.1 +- Microsoft.Rest.ClientRuntime 2.3.20 +- Microsoft.Rest.ClientRuntime.Azure 3.3.19 diff --git a/release-notes/add-ons/AzureKeyVaultProvider/1.1/README.md b/release-notes/add-ons/AzureKeyVaultProvider/1.1/README.md new file mode 100644 index 0000000000..eed9f0741b --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/1.1/README.md @@ -0,0 +1,7 @@ +# Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.1 Releases + +The following Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.1 stable releases have been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2020/03/02 | 1.1.1 | [release notes](1.1.1.md) | diff --git a/release-notes/add-ons/AzureKeyVaultProvider/1.2/1.2.0.md b/release-notes/add-ons/AzureKeyVaultProvider/1.2/1.2.0.md new file mode 100644 index 0000000000..a4ed97335c --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/1.2/1.2.0.md @@ -0,0 +1,51 @@ +# Release Notes + +## General Availability of Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +_**1.2.0 released 01 December 2020**_ + +This library contains the implementation of `Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider` for accessing Azure Key Vault, and the provider class is named `SqlColumnEncryptionAzureKeyVaultProvider`. + +### Added + +- Added support for .NET Standard 2.0. This requires Microsoft.Data.SqlClient v2.1.0 and above. [#823](https://github.com/dotnet/SqlClient/pull/823) +- Added new HSM endpoints. [#750](https://github.com/dotnet/SqlClient/pull/750) +- Added source linked PDBs for easier debugging of the package. [#789](https://github.com/dotnet/SqlClient/pull/789) + +### Working with SQLColumnEncryptionAzureKeyVaultProvider +`SqlColumnEncryptionAzureKeyVaultProvider` is implemented against `Microsoft.Data.SqlClient` and supports .NET Framework 4.6+, .NET Core 2.1+, and .NET Standard 2.0+. The provider name identifier for this library is "**AZURE_KEY_VAULT**" and it is not registered in the driver by default. Client applications may call the `SqlConnection.RegisterColumnEncryptionKeyStoreProviders()` API once in the lifetime of the driver to register this custom provider by implementing a custom Authentication Callback mechanism. + +Once the provider is registered, it can be used to perform Always Encrypted operations by creating a Column Master Key using the Azure Key Vault Key Identifier URL. + +A sample C# application to demonstrate Always Encrypted with Azure Key Vault can be download from the samples directory: [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/master/doc/samples/AzureKeyVaultProviderExample.cs) + +## Target Platform Support + +- .NET Framework 4.6+ +- .NET Core 2.1+ (Windows x86, Windows x64, Linux, macOS) +- .NET Standard 2.0+ + +### Dependencies + +#### .NET Framework + +- Microsoft.Azure.KeyVault 3.0.4 +- Microsoft.Azure.KeyVault.WebKey 3.0.4 +- Microsoft.Data.SqlClient 1.0.19269.1 +- Microsoft.Rest.ClientRuntime 2.3.20 +- Microsoft.Rest.ClientRuntime.Azure 3.3.19 + +#### .NET Core + +- Microsoft.Azure.KeyVault 3.0.4 +- Microsoft.Azure.KeyVault.WebKey 3.0.4 +- Microsoft.Data.SqlClient 1.0.19269.1 +- Microsoft.Rest.ClientRuntime 2.3.20 +- Microsoft.Rest.ClientRuntime.Azure 3.3.19 + +#### .NET Standard + +- Microsoft.Azure.KeyVault 3.0.4 +- Microsoft.Azure.KeyVault.WebKey 3.0.4 +- Microsoft.Data.SqlClient 2.1.0 +- Microsoft.Rest.ClientRuntime 2.3.20 +- Microsoft.Rest.ClientRuntime.Azure 3.3.19 diff --git a/release-notes/add-ons/AzureKeyVaultProvider/1.2/README.md b/release-notes/add-ons/AzureKeyVaultProvider/1.2/README.md new file mode 100644 index 0000000000..43cd6f62f3 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/1.2/README.md @@ -0,0 +1,7 @@ +# Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.2 Releases + +The following Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.2 stable releases have been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2020/12/01 | 1.2.0 | [release notes](1.2.0.md) | diff --git a/release-notes/add-ons/AzureKeyVaultProvider/2.0/2.0.0.md b/release-notes/add-ons/AzureKeyVaultProvider/2.0/2.0.0.md new file mode 100644 index 0000000000..b798c2e581 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/2.0/2.0.0.md @@ -0,0 +1,52 @@ +# Release Notes + +## General Availability of Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +_**2.0.0 released 03 March 2021**_ + +This library contains the implementation of `Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider` for accessing Azure Key Vault, and the provider class is named `SqlColumnEncryptionAzureKeyVaultProvider`. + +### Added +- Upgraded Azure Key Vault Provider to use new Azure Key Vault libraries [#630](https://github.com/dotnet/SqlClient/pull/630) + +### Breaking Changes +- Drops support for .NET Framework 4.6. The new minimum supported .NET Framework version is v4.6.1 [#630](https://github.com/dotnet/SqlClient/pull/630) +- Updated dependency of Microsoft.Data.SqlClient on .NET Framework and .NET Core to LTS stable version v1.1.3+ [#946](https://github.com/dotnet/SqlClient/pull/946) + + +### Working with SQLColumnEncryptionAzureKeyVaultProvider +`SqlColumnEncryptionAzureKeyVaultProvider` is implemented against `Microsoft.Data.SqlClient` and supports .NET Framework 4.6.1+, .NET Core 2.1+, and .NET Standard 2.0+. The provider name identifier for this library is "**AZURE_KEY_VAULT**" and it is not registered in the driver by default. Client applications may now initialize this provider by providing an instance of `Azure.Core.TokenCredential` implementation and register it with the driver. + +Once the provider is registered, it can be used to perform Always Encrypted operations by creating a Column Master Key using the Azure Key Vault Key Identifier URL. + +The linked C# samples below demonstrate using Always Encrypted with secure enclaves with Azure Key Vault: +- Legacy API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs) +- New API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderExample_2_0.cs) +- Legacy API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample.cs) +- New API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs) + + +## Target Platform Support + +- .NET Framework 4.6.1+ +- .NET Core 2.1+ (Windows x86, Windows x64, Linux, macOS) +- .NET Standard 2.0+ + +### Dependencies + +#### .NET Framework + +- Azure.Core 1.2.2 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 1.1.3 + +#### .NET Core + +- Azure.Core 1.2.2 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 1.1.3 + +#### .NET Standard + +- Azure.Core 1.2.2 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 2.1.0 diff --git a/release-notes/add-ons/AzureKeyVaultProvider/2.0/README.md b/release-notes/add-ons/AzureKeyVaultProvider/2.0/README.md new file mode 100644 index 0000000000..82bde5d29b --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/2.0/README.md @@ -0,0 +1,7 @@ +# Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 2.0 Releases + +The following Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 2.0 stable releases have been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2021/03/03 | 2.0.0 | [release notes](2.0.0.md) | diff --git a/release-notes/add-ons/AzureKeyVaultProvider/3.0/3.0.0.md b/release-notes/add-ons/AzureKeyVaultProvider/3.0/3.0.0.md new file mode 100644 index 0000000000..cc1f90eac2 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/3.0/3.0.0.md @@ -0,0 +1,69 @@ +# Release Notes + +## General Availability of Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider + +_**3.0.0 released 14 June 2021**_ + +This library contains the implementation of `Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider` for accessing Azure Key Vault, and the provider class is named `SqlColumnEncryptionAzureKeyVaultProvider`. + +### Added + +- Introduces column encryption key caching support [#1056](https://github.com/dotnet/SqlClient/pull/1056) + +### Breaking Changes + +- Microsoft.Data.SqlClient dependency version upgraded to **v3.0.0+** [#1111](https://github.com/dotnet/SqlClient/pull/1111) + +### Working with SQLColumnEncryptionAzureKeyVaultProvider + +`SqlColumnEncryptionAzureKeyVaultProvider` **v3.0** is implemented against `Microsoft.Data.SqlClient` **v3.0** and supports .NET Framework 4.6.1+, .NET Core 2.1+, and .NET Standard 2.0+. The provider name identifier for this library is "**AZURE_KEY_VAULT**" and it is not registered in the driver by default. Client applications may initialize this provider by providing an `Azure.Core.TokenCredential` and registering it with the driver using any of the below APIs: + +- [SqlConnection.RegisterColumnEncryptionKeyStoreProviders](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlconnection.registercolumnencryptionkeystoreproviders?view=sqlclient-dotnet-3.0) +- [SqlConnection.RegisterColumnEncryptionKeyStoreProvidersOnConnection](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlconnection.registercolumnencryptionkeystoreprovidersonconnection?view=sqlclient-dotnet-3.0) (Added in version 3.0.0) +- [SqlCommand.RegisterColumnEncryptionKeyStoreProvidersOnCommand](https://docs.microsoft.com/dotnet/api/microsoft.data.sqlclient.sqlcommand.registercolumnencryptionkeystoreprovidersoncommand?view=sqlclient-dotnet-3.0) (Added in version 3.0.0) + +Once the provider is registered, it can be used to perform Always Encrypted operations by creating a Column Master Key using the Azure Key Vault Key Identifier URL. + +The linked C# samples below demonstrate using Always Encrypted with secure enclaves with Azure Key Vault: + +- Legacy API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderLegacyExample_2_0.cs) +- New API support (Always Encrypted): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProviderExample_2_0.cs) +- Legacy API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample.cs) +- New API support (Always Encrypted with secure enclaves): [AzureKeyVaultProviderExample.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/doc\samples\AzureKeyVaultProviderWithEnclaveProviderExample_2_0.cs) +- Column Encryption Key cache scope example: [AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/AzureKeyVaultProvider_ColumnEncryptionKeyCacheScope.cs) +- Registering custom key store provider - Connection Precedence: [RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/RegisterCustomKeyStoreProvider_ConnectionPrecedence.cs) +- Registering custom key store provider - Command Precedence: [RegisterCustomKeyStoreProvider_CommandPrecedence.cs](https://github.com/dotnet/SqlClient/blob/main/doc/samples/RegisterCustomKeyStoreProvider_CommandPrecedence.cs) + +For further details, refer to [Using the Azure Key Vault provider](https://docs.microsoft.com/sql/connect/ado-net/sql/sqlclient-support-always-encrypted#using-the-azure-key-vault-provider) + +## Target Platform Support + +- .NET Framework 4.6.1+ +- .NET Core 2.1+ (Windows x86, Windows x64, Linux, macOS) +- .NET Standard 2.0+ + +### Dependencies + +#### .NET Framework + +- Azure.Core 1.6.0 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 3.0.0 +- System.Text.Encodings.Web 4.7.2 +- Microsoft.Extensions.Caching.Memory 5.0.0 + +#### .NET Core + +- Azure.Core 1.6.0 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 3.0.0 +- System.Text.Encodings.Web 4.7.2 +- Microsoft.Extensions.Caching.Memory 5.0.0 + +#### .NET Standard + +- Azure.Core 1.6.0 +- Azure.Security.KeyVault.Keys 4.0.3 +- Microsoft.Data.SqlClient 3.0.0 +- System.Text.Encodings.Web 4.7.2 +- Microsoft.Extensions.Caching.Memory 5.0.0 diff --git a/release-notes/add-ons/AzureKeyVaultProvider/3.0/README.md b/release-notes/add-ons/AzureKeyVaultProvider/3.0/README.md new file mode 100644 index 0000000000..d7deee4b63 --- /dev/null +++ b/release-notes/add-ons/AzureKeyVaultProvider/3.0/README.md @@ -0,0 +1,7 @@ +# Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0 Releases + +The following Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 3.0 stable releases have been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2021/06/14 | 3.0.0 | [release notes](3.0.0.md) | diff --git a/roadmap.md b/roadmap.md index a9c8f5308d..4771802b4f 100644 --- a/roadmap.md +++ b/roadmap.md @@ -11,10 +11,12 @@ The Microsoft.Data.SqlClient roadmap communicates project priorities for evolvin | Milestone | Release Date | Project Board | |---------------------------|--------------|---------------| -| Microsoft.Data.SqlClient v1.0 (servicing) | As needed (see also [1.0 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/1.0)) | Closed | | Microsoft.Data.SqlClient v1.1 (servicing) | As needed (see also [1.1 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/1.1)) | Closed | -| Microsoft.Data.SqlClient v2.0 (servicing) | As needed (see also [2.0 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/2.0)) | Closed | -| Microsoft.Data.SqlClient v2.1 | GA (General Availability) estimated for November 2020 | [SqlClient 2.1.0](https://github.com/dotnet/SqlClient/projects/6) | +| Microsoft.Data.SqlClient v2.1 (servicing) | As needed (see also [2.1 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/2.1)) | Closed | +| Microsoft.Data.SqlClient v3.0 (servicing) | As needed (see also [3.0 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/3.0)) | Closed | +| Microsoft.Data.SqlClient v4.0 (servicing) | As needed (see also [4.0 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/4.0)) | Closed | +| Microsoft.Data.SqlClient v4.1 (servicing) | As needed (see also [4.1 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/4.1)) | Closed | +| Microsoft.Data.SqlClient v5.0 | GA (General Availability) estimated for May 2022 | [SqlClient 5.0.0](https://github.com/dotnet/SqlClient/projects/9) > Note: Dates are calendar year (as opposed to fiscal year). diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b43e2df35a..afcf503118 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,7 @@ + 9.0 true false true @@ -19,16 +20,15 @@ ************** IMPORTANT NOTE BEFORE PROCEEDING WITH "PACKAGE" AND "NETSTANDARDPACKAGE" REFERENCE TYPES *************** CREATE A NUGET PACKAGE WITH BELOW COMMAND AND ADD TO LOCAL FOLDER + UPDATE NUGET CONFIG FILE TO READ FROM THAT LOCATION - > msbuild /p:configuration=Release + > msbuild -p:configuration=Release --> Project $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - + $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)')) - + @@ -39,11 +39,14 @@ $(MSBuildThisFileDirectory) $(ProjectDir)..\ $(RepoRoot)artifacts\$(ReferenceType)\ + $(Artifacts)tools\ $(ProjectDir)Microsoft.Data.SqlClient\ + $(ProjectDir)Microsoft.SqlServer.Server\ $(ManagedSourceCode)netcore\ $(ManagedSourceCode)netfx\ $(ManagedSourceCode)netfx\src\Resources\ $(ManagedSourceCode)add-ons\ + $(RepoRoot)src\Microsoft.SqlServer.Server\ $(Artifacts)obj\ $(NetCoreSource)src\Common\src $(NetCoreSource)src\Common\tests @@ -52,6 +55,7 @@ $(Artifacts)bin\AnyOS\ $(Artifacts)bin\Unix\ $(RepoRoot)tools\ + $(ToolsDir)GenAPI\ $(RepoRoot)packages\ $(RepoRoot).nuget\ $(NuGetRoot)nuget.exe diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 855baeedaa..c5b34945a0 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29521.150 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\netfx\src\Microsoft.Data.SqlClient.csproj", "{407890AC-9876-4FEF-A6F1-F36A876BAADE}" EndProject @@ -16,6 +16,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TDS", "Microsoft.Data.SqlCl EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Tests", "Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj", "{D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}" + ProjectSection(ProjectDependencies) = postProject + {37431336-5307-4184-9356-C4B7E47DC714} = {37431336-5307-4184-9356-C4B7E47DC714} + EndProjectSection 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 @@ -26,22 +29,31 @@ 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 {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {FDA6971D-9F57-4DA4-B10A-261C91684CFC} + {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.ManualTesting.Tests", "Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.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 {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {FDA6971D-9F57-4DA4-B10A-261C91684CFC} + {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 {FDA6971D-9F57-4DA4-B10A-261C91684CFC} = {FDA6971D-9F57-4DA4-B10A-261C91684CFC} + {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 @@ -52,6 +64,11 @@ 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}") = "add-ons", "add-ons", "{C9726AED-D6A3-4AAC-BA04-92DD1F079594}" EndProject @@ -103,6 +120,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient", ..\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\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 @@ -164,80 +182,47 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlTypes", " EndProject Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.DockerLinuxTest", "Microsoft.Data.SqlClient\tests\DockerLinuxTest\Microsoft.Data.SqlClient.DockerLinuxTest.csproj", "{833157E1-1E53-4908-B4CB-5C5507A44582}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.NSLibrary", "Microsoft.Data.SqlClient\tests\NSLibrary\Microsoft.Data.SqlClient.NSLibrary.csproj", "{E7336BFB-8521-423A-A140-3123F9065C5D}" + 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.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.DockerLinuxTest", "Microsoft.Data.SqlClient\tests\Docker\DockerLinuxTest\Microsoft.Data.SqlClient.DockerLinuxTest.csproj", "{B93A3149-67E8-491E-A1E5-19D65F9D9E98}" +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}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlServer.Server", "Microsoft.SqlServer.Server\Microsoft.SqlServer.Server.csproj", "{A314812A-7820-4565-A2A8-ABBE391C11E4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - net46-Debug|Any CPU = net46-Debug|Any CPU - net46-Debug|x64 = net46-Debug|x64 - net46-Debug|x86 = net46-Debug|x86 - net46-Release|Any CPU = net46-Release|Any CPU - net46-Release|x64 = net46-Release|x64 - net46-Release|x86 = net46-Release|x86 - netcoreapp2.1-Debug|Any CPU = netcoreapp2.1-Debug|Any CPU - netcoreapp2.1-Debug|x64 = netcoreapp2.1-Debug|x64 - netcoreapp2.1-Debug|x86 = netcoreapp2.1-Debug|x86 - netcoreapp2.1-Release|Any CPU = netcoreapp2.1-Release|Any CPU - netcoreapp2.1-Release|x64 = netcoreapp2.1-Release|x64 - netcoreapp2.1-Release|x86 = netcoreapp2.1-Release|x86 - netcoreapp3.1-Debug|Any CPU = netcoreapp3.1-Debug|Any CPU - netcoreapp3.1-Debug|x64 = netcoreapp3.1-Debug|x64 - netcoreapp3.1-Debug|x86 = netcoreapp3.1-Debug|x86 - netcoreapp3.1-Release|Any CPU = netcoreapp3.1-Release|Any CPU - netcoreapp3.1-Release|x64 = netcoreapp3.1-Release|x64 - netcoreapp3.1-Release|x86 = netcoreapp3.1-Release|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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Debug|x64.ActiveCfg = net46-Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Debug|x64.Build.0 = net46-Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Debug|x86.ActiveCfg = net46-Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Debug|x86.Build.0 = net46-Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Release|x64.ActiveCfg = net46-Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Release|x64.Build.0 = net46-Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Release|x86.ActiveCfg = net46-Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.net46-Release|x86.Build.0 = net46-Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Debug|x86.Build.0 = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU - {407890AC-9876-4FEF-A6F1-F36A876BAADE}.netcoreapp3.1-Release|x86.Build.0 = Release|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 @@ -248,42 +233,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Release|x64.Build.0 = net46-Release|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.net46-Release|x86.Build.0 = net46-Release|x86 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {1FF891B4-D3DE-4CCE-887C-CB48F5351A45}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -296,42 +245,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Release|x64.Build.0 = net46-Release|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.net46-Release|x86.Build.0 = net46-Release|x86 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {978063D3-FBB5-4E10-8C45-67C90BE1B931}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -344,42 +257,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Release|x64.Build.0 = net46-Release|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.net46-Release|x86.Build.0 = net46-Release|x86 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {8DC9D1A0-351B-47BC-A90F-B9DA542550E9}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -392,42 +269,6 @@ Global {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|x64.Build.0 = Debug|Any CPU {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|x86.ActiveCfg = Debug|Any CPU {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.Debug|x86.Build.0 = Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Release|x64.Build.0 = net46-Release|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.net46-Release|x86.Build.0 = net46-Release|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {D2D1E2D1-B6E0-489F-A36D-1F3047AB87B9}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|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|Any CPU @@ -440,36 +281,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.net46-Debug|x64.ActiveCfg = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.net46-Debug|x86.ActiveCfg = Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.net46-Release|x64.ActiveCfg = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.net46-Release|x86.ActiveCfg = Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {37431336-5307-4184-9356-C4B7E47DC714}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -482,42 +293,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Release|x64.Build.0 = net46-Release|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.net46-Release|x86.Build.0 = net46-Release|x86 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {D1392B54-998A-4F27-BC17-4CE149117BCC}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -530,42 +305,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Release|x64.Build.0 = net46-Release|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.net46-Release|x86.Build.0 = net46-Release|x86 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -578,42 +317,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Release|x64.Build.0 = net46-Release|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.net46-Release|x86.Build.0 = net46-Release|x86 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {6C88F00F-9597-43AD-9E5F-9B344DA3B16F}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -626,42 +329,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Release|x64.Build.0 = net46-Release|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.net46-Release|x86.Build.0 = net46-Release|x86 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {B73A7063-37C3-415D-AD53-BB3DA20ABD6E}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -674,42 +341,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Release|x64.Build.0 = net46-Release|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.net46-Release|x86.Build.0 = net46-Release|x86 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {E0A6BB21-574B-43D9-890D-6E1144F2EE9E}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -722,36 +353,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.net46-Debug|x64.ActiveCfg = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.net46-Debug|x86.ActiveCfg = Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.net46-Release|x64.ActiveCfg = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.net46-Release|x86.ActiveCfg = Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {1C9FC4B8-54BC-4B6C-BB3A-F5CD59D80A9B}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -759,39 +360,13 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Debug|x64.ActiveCfg = net46-Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Debug|x64.Build.0 = net46-Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Debug|x86.ActiveCfg = net46-Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Debug|x86.Build.0 = net46-Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Release|x64.ActiveCfg = net46-Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Release|x64.Build.0 = net46-Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Release|x86.ActiveCfg = net46-Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.net46-Release|x86.Build.0 = net46-Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp2.1-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU - {412BCCC8-19F6-489A-B594-E9A506816155}.netcoreapp3.1-Release|x86.Build.0 = Release|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 @@ -802,42 +377,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Release|x64.ActiveCfg = net46-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Release|x64.Build.0 = net46-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Release|x86.ActiveCfg = net46-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.net46-Release|x86.Build.0 = net46-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x64.ActiveCfg = netcoreapp2.1-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x64.Build.0 = netcoreapp2.1-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.ActiveCfg = netcoreapp2.1-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {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 @@ -850,42 +389,6 @@ Global {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|x64.Build.0 = Debug|Any CPU {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|x86.ActiveCfg = Debug|Any CPU {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Debug|x86.Build.0 = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Debug|Any CPU.ActiveCfg = net46-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Debug|Any CPU.Build.0 = net46-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Debug|x64.ActiveCfg = net46-Debug|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Debug|x64.Build.0 = net46-Debug|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Debug|x86.ActiveCfg = net46-Debug|x86 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Debug|x86.Build.0 = net46-Debug|x86 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Release|Any CPU.ActiveCfg = net46-Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Release|Any CPU.Build.0 = net46-Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Release|x64.ActiveCfg = net46-Release|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Release|x64.Build.0 = net46-Release|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Release|x86.ActiveCfg = net46-Release|x86 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.net46-Release|x86.Build.0 = net46-Release|x86 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Debug|Any CPU.Build.0 = netcoreapp2.1-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Release|Any CPU.ActiveCfg = netcoreapp2.1-Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Release|Any CPU.Build.0 = netcoreapp2.1-Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = netcoreapp3.1-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Debug|Any CPU.Build.0 = netcoreapp3.1-Debug|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Release|Any CPU.ActiveCfg = netcoreapp3.1-Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Release|Any CPU.Build.0 = netcoreapp3.1-Release|Any CPU - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|Any CPU.ActiveCfg = Release|Any CPU {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|Any CPU.Build.0 = Release|Any CPU {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x64.ActiveCfg = Release|Any CPU @@ -893,143 +396,21 @@ Global {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x86.ActiveCfg = Release|Any CPU {FDA6971D-9F57-4DA4-B10A-261C91684CFC}.Release|x86.Build.0 = Release|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x64.ActiveCfg = Debug|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x64.Build.0 = Debug|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x86.ActiveCfg = Debug|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Debug|x86.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|Any CPU.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x64.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x64.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x86.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Debug|x86.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|Any CPU.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x64.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x64.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x86.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.net46-Release|x86.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Debug|x86.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|Any CPU.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.netcoreapp3.1-Release|x86.Build.0 = Release|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|Any CPU.Build.0 = Release|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x64.ActiveCfg = Release|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x64.Build.0 = Release|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x86.ActiveCfg = Release|Any CPU {F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}.Release|x86.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|Any CPU.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x64.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x64.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x86.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Debug|x86.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|Any CPU.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x64.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x64.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x86.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Debug|x86.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|Any CPU.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x64.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x64.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x86.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.net46-Release|x86.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Debug|x86.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|Any CPU.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.netcoreapp3.1-Release|x86.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|Any CPU.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|Any CPU.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x64.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x64.Build.0 = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x86.ActiveCfg = Release|Any CPU - {833157E1-1E53-4908-B4CB-5C5507A44582}.Release|x86.Build.0 = Release|Any CPU {E7336BFB-8521-423A-A140-3123F9065C5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E7336BFB-8521-423A-A140-3123F9065C5D}.Debug|Any CPU.Build.0 = Debug|Any CPU {E7336BFB-8521-423A-A140-3123F9065C5D}.Debug|x64.ActiveCfg = Debug|x64 {E7336BFB-8521-423A-A140-3123F9065C5D}.Debug|x64.Build.0 = Debug|x64 {E7336BFB-8521-423A-A140-3123F9065C5D}.Debug|x86.ActiveCfg = Debug|x86 {E7336BFB-8521-423A-A140-3123F9065C5D}.Debug|x86.Build.0 = Debug|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Debug|Any CPU.Build.0 = Debug|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Debug|x64.ActiveCfg = Debug|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Debug|x64.Build.0 = Debug|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Debug|x86.ActiveCfg = Debug|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Debug|x86.Build.0 = Debug|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Release|Any CPU.Build.0 = Release|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Release|x64.ActiveCfg = Release|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Release|x64.Build.0 = Release|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Release|x86.ActiveCfg = Release|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.net46-Release|x86.Build.0 = Release|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Debug|x64.Build.0 = Debug|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Debug|x86.Build.0 = Debug|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Release|Any CPU.Build.0 = Release|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Release|x64.ActiveCfg = Release|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Release|x64.Build.0 = Release|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Release|x86.ActiveCfg = Release|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp2.1-Release|x86.Build.0 = Release|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Debug|x64.Build.0 = Debug|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Debug|x86.Build.0 = Debug|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Release|x64.ActiveCfg = Release|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Release|x64.Build.0 = Release|x64 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Release|x86.ActiveCfg = Release|x86 - {E7336BFB-8521-423A-A140-3123F9065C5D}.netcoreapp3.1-Release|x86.Build.0 = Release|x86 {E7336BFB-8521-423A-A140-3123F9065C5D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E7336BFB-8521-423A-A140-3123F9065C5D}.Release|Any CPU.Build.0 = Release|Any CPU {E7336BFB-8521-423A-A140-3123F9065C5D}.Release|x64.ActiveCfg = Release|x64 @@ -1042,42 +423,6 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Debug|Any CPU.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Debug|x64.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Debug|x64.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Debug|x86.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Debug|x86.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Release|Any CPU.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Release|x64.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Release|x64.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Release|x86.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.net46-Release|x86.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Debug|x86.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Release|Any CPU.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU - {89D6D382-9B36-43C9-A912-03802FDA8E36}.netcoreapp3.1-Release|x86.Build.0 = Release|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 @@ -1090,48 +435,60 @@ Global {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}.net46-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Debug|Any CPU.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Debug|x64.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Debug|x64.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Debug|x86.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Debug|x86.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Release|Any CPU.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Release|Any CPU.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Release|x64.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Release|x64.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Release|x86.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.net46-Release|x86.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Debug|x64.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Debug|x64.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Debug|x86.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Debug|x86.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Release|Any CPU.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Release|x64.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Release|x64.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Release|x86.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp2.1-Release|x86.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Debug|x64.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Debug|x64.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Debug|x86.ActiveCfg = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Debug|x86.Build.0 = Debug|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Release|x64.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Release|x64.Build.0 = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Release|x86.ActiveCfg = Release|Any CPU - {E4C08DCE-DC29-4FEB-B655-1E7287DB5A2B}.netcoreapp3.1-Release|x86.Build.0 = Release|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 + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Debug|x64.ActiveCfg = Debug|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Debug|x64.Build.0 = Debug|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Debug|x86.ActiveCfg = Debug|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Debug|x86.Build.0 = Debug|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Release|Any CPU.Build.0 = Release|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Release|x64.ActiveCfg = Release|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Release|x64.Build.0 = Release|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Release|x86.ActiveCfg = Release|Any CPU + {B93A3149-67E8-491E-A1E5-19D65F9D9E98}.Release|x86.Build.0 = Release|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1161,10 +518,13 @@ Global {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} - {833157E1-1E53-4908-B4CB-5C5507A44582} = {0CC4817A-12F3-4357-912C-09315FAAD008} {E7336BFB-8521-423A-A140-3123F9065C5D} = {0CC4817A-12F3-4357-912C-09315FAAD008} {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} + {B93A3149-67E8-491E-A1E5-19D65F9D9E98} = {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/AKVEventSource.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/AKVEventSource.cs new file mode 100644 index 0000000000..085232af78 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/AKVEventSource.cs @@ -0,0 +1,127 @@ +// 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.Diagnostics; +using System.Diagnostics.Tracing; +using System.Threading; + +namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +{ + [EventSource(Name = "Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.EventSource")] + internal class AKVEventSource : EventSource + { + // Defines the singleton instance for the Resources ETW provider + public static AKVEventSource Log = new(); + + private AKVEventSource() { } + + // Initialized static Scope IDs + private static long s_nextScopeId = 0; + + // Provides Correlation Manager Activity Id for current scope. + private static Guid GetCorrelationActivityId() + { + if (Trace.CorrelationManager.ActivityId == Guid.Empty) + { + Trace.CorrelationManager.ActivityId = Guid.NewGuid(); + } + return Trace.CorrelationManager.ActivityId; + } + + #region Keywords + public class Keywords + { + /// + /// Captures basic application flow trace events. + /// + internal const EventKeywords Trace = (EventKeywords)1; + + /// + /// Captures basic application scope entering and exiting events. + /// + internal const EventKeywords Scope = (EventKeywords)2; + } + #endregion + + #region Tasks + /// + /// Tasks supported by AKV Provider's EventSource implementation + /// + public class Tasks + { + /// + /// Task that tracks trace scope. + /// + public const EventTask Scope = (EventTask)1; + } + #endregion + + [NonEvent] + internal bool IsTraceEnabled() => Log.IsEnabled(EventLevel.Informational, Keywords.Trace); + + [NonEvent] + internal bool IsScopeEnabled() => Log.IsEnabled(EventLevel.Informational, Keywords.Scope); + + #region Event Methods + [NonEvent] + internal void TryTraceEvent(string message, object p1 = null, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") + { + if (IsTraceEnabled()) + { + WriteTrace(string.Format("Caller: {0}, Message: {1}", memberName, + p1 == null ? message : + string.Format(message, p1.ToString().Length > 10 ? + p1.ToString().Substring(0, 10) + "..." : p1))); + } + } + [NonEvent] + internal long TryScopeEnterEvent(string memberName) + { + if (IsScopeEnabled()) + { + return ScopeEnter(memberName); + } + return default; + } + [NonEvent] + internal void TryScopeExitEvent(long scopeId) + { + if (Log.IsScopeEnabled()) + { + ScopeExit(scopeId); + } + } + #endregion + + #region Write Events + [Event(1, Level = EventLevel.Informational, Keywords = Keywords.Trace)] + internal void WriteTrace(string message) => WriteEvent(1, message); + + [Event(2, Level = EventLevel.Informational, Task = Tasks.Scope, Opcode = EventOpcode.Start, Keywords = Keywords.Scope)] + internal long ScopeEnter(string caller) + { + SetCurrentThreadActivityId(GetCorrelationActivityId()); + long scopeId = Interlocked.Increment(ref s_nextScopeId); + WriteEvent(2, string.Format("Entered Scope: {0}, Caller: {1}", scopeId, caller)); + return scopeId; + } + + [Event(3, Level = EventLevel.Informational, Task = Tasks.Scope, Opcode = EventOpcode.Stop, Keywords = Keywords.Scope)] + internal void ScopeExit(long scopeId) => WriteEvent(3, scopeId); + #endregion + } + + internal readonly struct AKVScope : IDisposable + { + private readonly long _scopeId; + + public AKVScope(long scopeID) => _scopeId = scopeID; + public void Dispose() => + AKVEventSource.Log.TryScopeExitEvent(_scopeId); + + public static AKVScope Create([System.Runtime.CompilerServices.CallerMemberName] string memberName = "") => + new(AKVEventSource.Log.TryScopeEnterEvent(memberName)); + } +} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/AzureSqlKeyCryptographer.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/AzureSqlKeyCryptographer.cs new file mode 100644 index 0000000000..0dff6ac786 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/AzureSqlKeyCryptographer.cs @@ -0,0 +1,228 @@ +// 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 Azure.Core; +using Azure.Security.KeyVault.Keys; +using Azure.Security.KeyVault.Keys.Cryptography; +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; +using static Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm; + +namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +{ + internal class AzureSqlKeyCryptographer + { + /// + /// TokenCredential to be used with the KeyClient + /// + private TokenCredential TokenCredential { get; set; } + + /// + /// A mapping of the KeyClient objects to the corresponding Azure Key Vault URI + /// + private readonly ConcurrentDictionary _keyClientDictionary = new(); + + /// + /// Holds references to the fetch key tasks and maps them to their corresponding Azure Key Vault Key Identifier (URI). + /// These tasks will be used for returning the key in the event that the fetch task has not finished depositing the + /// key into the key dictionary. + /// + private readonly ConcurrentDictionary>> _keyFetchTaskDictionary = new(); + + /// + /// Holds references to the Azure Key Vault keys and maps them to their corresponding Azure Key Vault Key Identifier (URI). + /// + private readonly ConcurrentDictionary _keyDictionary = new(); + + /// + /// Holds references to the Azure Key Vault CryptographyClient objects and maps them to their corresponding Azure Key Vault Key Identifier (URI). + /// + private readonly ConcurrentDictionary _cryptoClientDictionary = new(); + + /// + /// Constructs a new KeyCryptographer + /// + /// + internal AzureSqlKeyCryptographer(TokenCredential tokenCredential) + { + TokenCredential = tokenCredential; + } + + /// + /// Adds the key, specified by the Key Identifier URI, to the cache. + /// + /// + internal void AddKey(string keyIdentifierUri) + { + if (TheKeyHasNotBeenCached(keyIdentifierUri)) + { + ParseAKVPath(keyIdentifierUri, out Uri vaultUri, out string keyName, out string keyVersion); + CreateKeyClient(vaultUri); + FetchKey(vaultUri, keyName, keyVersion, keyIdentifierUri); + } + + bool TheKeyHasNotBeenCached(string k) => !_keyDictionary.ContainsKey(k) && !_keyFetchTaskDictionary.ContainsKey(k); + } + + /// + /// Returns the key specified by the Key Identifier URI + /// + /// + /// + internal KeyVaultKey GetKey(string keyIdentifierUri) + { + if (_keyDictionary.TryGetValue(keyIdentifierUri, out KeyVaultKey key)) + { + AKVEventSource.Log.TryTraceEvent("Fetched master key from cache"); + return key; + } + + if (_keyFetchTaskDictionary.TryGetValue(keyIdentifierUri, out Task> task)) + { + AKVEventSource.Log.TryTraceEvent("New Master key fetched."); + return Task.Run(() => task).GetAwaiter().GetResult(); + } + + // Not a public exception - not likely to occur. + AKVEventSource.Log.TryTraceEvent("Master key not found."); + throw ADP.MasterKeyNotFound(keyIdentifierUri); + } + + /// + /// Gets the public Key size in bytes. + /// + /// The key vault key identifier URI + /// + internal int GetKeySize(string keyIdentifierUri) + { + return GetKey(keyIdentifierUri).Key.N.Length; + } + + /// + /// Generates signature based on RSA PKCS#v1.5 scheme using a specified Azure Key Vault Key URL. + /// + /// The data to sign + /// The key vault key identifier URI + /// + internal byte[] SignData(byte[] message, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + return cryptographyClient.SignData(RS256, message).Signature; + } + + internal bool VerifyData(byte[] message, byte[] signature, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + AKVEventSource.Log.TryTraceEvent("Sending request to verify data"); + return cryptographyClient.VerifyData(RS256, message, signature).IsValid; + } + + internal byte[] UnwrapKey(KeyWrapAlgorithm keyWrapAlgorithm, byte[] encryptedKey, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + AKVEventSource.Log.TryTraceEvent("Sending request to unwrap key."); + return cryptographyClient.UnwrapKey(keyWrapAlgorithm, encryptedKey).Key; + } + + internal byte[] WrapKey(KeyWrapAlgorithm keyWrapAlgorithm, byte[] key, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + AKVEventSource.Log.TryTraceEvent("Sending request to wrap key."); + return cryptographyClient.WrapKey(keyWrapAlgorithm, key).EncryptedKey; + } + + private CryptographyClient GetCryptographyClient(string keyIdentifierUri) + { + if (_cryptoClientDictionary.TryGetValue(keyIdentifierUri, out CryptographyClient client)) + { + return client; + } + + CryptographyClient cryptographyClient = new(GetKey(keyIdentifierUri).Id, TokenCredential); + _cryptoClientDictionary.TryAdd(keyIdentifierUri, cryptographyClient); + + return cryptographyClient; + } + + /// + /// + /// + /// The Azure Key Vault URI + /// The name of the Azure Key Vault key + /// The version of the Azure Key Vault key + /// The Azure Key Vault key identifier + private void FetchKey(Uri vaultUri, string keyName, string keyVersion, string keyResourceUri) + { + Task> fetchKeyTask = FetchKeyFromKeyVault(vaultUri, keyName, keyVersion); + _keyFetchTaskDictionary.AddOrUpdate(keyResourceUri, fetchKeyTask, (k, v) => fetchKeyTask); + + fetchKeyTask + .ContinueWith(k => ValidateRsaKey(k.GetAwaiter().GetResult())) + .ContinueWith(k => _keyDictionary.AddOrUpdate(keyResourceUri, k.GetAwaiter().GetResult(), (key, v) => k.GetAwaiter().GetResult())); + + Task.Run(() => fetchKeyTask); + } + + /// + /// Looks up the KeyClient object by it's URI and then fetches the key by name. + /// + /// The Azure Key Vault URI + /// Then name of the key + /// Then version of the key + /// + private Task> FetchKeyFromKeyVault(Uri vaultUri, string keyName, string keyVersion) + { + _keyClientDictionary.TryGetValue(vaultUri, out KeyClient keyClient); + AKVEventSource.Log.TryTraceEvent("Fetching requested master key: {0}", keyName); + return keyClient?.GetKeyAsync(keyName, keyVersion); + } + + /// + /// Validates that a key is of type RSA + /// + /// + /// + private KeyVaultKey ValidateRsaKey(KeyVaultKey key) + { + if (key.KeyType != KeyType.Rsa && key.KeyType != KeyType.RsaHsm) + { + AKVEventSource.Log.TryTraceEvent("Non-RSA KeyType received: {0}", key.KeyType); + throw ADP.NonRsaKeyFormat(key.KeyType.ToString()); + } + + return key; + } + + /// + /// Instantiates and adds a KeyClient to the KeyClient dictionary + /// + /// The Azure Key Vault URI + private void CreateKeyClient(Uri vaultUri) + { + if (!_keyClientDictionary.ContainsKey(vaultUri)) + { + _keyClientDictionary.TryAdd(vaultUri, new KeyClient(vaultUri, TokenCredential)); + } + } + + /// + /// Validates and parses the Azure Key Vault URI and key name. + /// + /// The Azure Key Vault key identifier + /// The Azure Key Vault URI + /// The name of the key + /// The version of the key + private void ParseAKVPath(string masterKeyPath, out Uri vaultUri, out string masterKeyName, out string masterKeyVersion) + { + Uri masterKeyPathUri = new(masterKeyPath); + vaultUri = new Uri(masterKeyPathUri.GetLeftPart(UriPartial.Authority)); + masterKeyName = masterKeyPathUri.Segments[2]; + masterKeyVersion = masterKeyPathUri.Segments.Length > 3 ? masterKeyPathUri.Segments[3] : null; + + AKVEventSource.Log.TryTraceEvent("Received Key Name: {0}", masterKeyName); + AKVEventSource.Log.TryTraceEvent("Received Key Version: {0}", masterKeyVersion); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs index a75101669f..6e26ac8539 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Constants.cs @@ -2,21 +2,10 @@ // 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.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider { internal static class Constants { - /// - /// Hashing algorithm used for signing - /// - internal const string HashingAlgorithm = @"RS256"; - /// /// Azure Key Vault Domain Name /// @@ -32,7 +21,7 @@ internal static class Constants }; /// - /// Always Encrypted Param names for exec handling + /// Always Encrypted Parameter names for exec handling /// internal const string AeParamColumnEncryptionKey = "columnEncryptionKey"; internal const string AeParamEncryptionAlgorithm = "encryptionAlgorithm"; diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/LocalCache.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/LocalCache.cs new file mode 100644 index 0000000000..3e17f5d951 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/LocalCache.cs @@ -0,0 +1,103 @@ +// 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 Microsoft.Extensions.Caching.Memory; +using System; +using static System.Math; + +namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +{ + /// + /// LocalCache is to reuse heavy objects. + /// When performing a heavy creation operation, we will save the result in our cache container. + /// The next time that we need that result, we will pull it from the cache container, instead of performing the heavy operation again. + /// It is used for decrypting CEKs and verifying CMK metadata. Encrypted CEKs and signatures are different every time, even + /// when done with the same key, and should not be cached. + /// + internal class LocalCache + { + /// + /// A simple thread-safe implementation of an in-memory Cache. + /// When the process dies, the cache dies with it. + /// + private readonly MemoryCache _cache; + + private readonly int _maxSize; + + /// + /// Sets an absolute expiration time, relative to now. + /// + internal TimeSpan? TimeToLive { get; set; } + + /// + /// Gets the count of the current entries for diagnostic purposes. + /// + internal int Count => _cache.Count; + + /// + /// Constructs a new LocalCache object. + /// + internal LocalCache(int maxSizeLimit = int.MaxValue) + { + if (maxSizeLimit <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxSizeLimit)); + } + + _maxSize = maxSizeLimit; + _cache = new MemoryCache(new MemoryCacheOptions()); + } + + /// + /// Looks for the cache entry that maps to the value. If it exists (cache hit) it will simply be + /// returned. Otherwise, the delegate function will be invoked to create the value. + /// It will then get stored it in the cache and set the time-to-live before getting returned. + /// + /// The key for the cache entry. + /// The delegate function that will create the cache entry if it does not exist. + /// The cache entry. + internal TValue GetOrCreate(TKey key, Func createItem) + { + if (TimeToLive <= TimeSpan.Zero) + { + AKVEventSource.Log.TryTraceEvent("Key caching found disabled, fetching key information."); + return createItem(); + } + + if (!_cache.TryGetValue(key, out TValue cacheEntry)) + { + AKVEventSource.Log.TryTraceEvent("Cached entry not found, creating new entry."); + if (_cache.Count == _maxSize) + { + _cache.Compact(Max(0.10, 1.0 / _maxSize)); + } + + cacheEntry = createItem(); + var cacheEntryOptions = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeToLive + }; + + _cache.Set(key, cacheEntry, cacheEntryOptions); + AKVEventSource.Log.TryTraceEvent("Entry added to local cache."); + } + else + { + AKVEventSource.Log.TryTraceEvent("Cached entry found."); + } + + return cacheEntry; + } + + /// + /// Determines whether the LocalCache contains the specified key. + /// + /// + /// + internal bool Contains(TKey key) + { + return _cache.TryGetValue(key, out _); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj index b882eb77b7..3f2267451e 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj @@ -4,9 +4,9 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider AzureKeyVaultProvider {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7} - netcoreapp - netfx - Debug;Release;net46-Release;net46-Debug;netcoreapp2.1-Debug;netcoreapp2.1-Release;netcoreapp3.1-Debug;netcoreapp3.1-Release + netcoreapp + netfx + Debug;Release; AnyCPU;x86;x64 $(ObjFolder)$(Configuration).$(Platform)\$(AddOnName) $(BinFolder)$(Configuration).$(Platform)\$(AddOnName) @@ -19,13 +19,13 @@ - + - - - - - + + + + + diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs index ef1758bf8e..2a7a4267b6 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/SqlColumnEncryptionAzureKeyVaultProvider.cs @@ -3,52 +3,36 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using Microsoft.Data.SqlClient; -using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Security.Cryptography; using System.Text; -using System.Threading.Tasks; -using Microsoft.Azure.KeyVault; -using Microsoft.Azure.KeyVault.WebKey; -using Microsoft.Azure.KeyVault.Models; +using Azure.Core; +using Azure.Security.KeyVault.Keys.Cryptography; +using static Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.Validator; namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider { /// /// Implementation of column master key store provider that allows client applications to access data when a - /// column master key is stored in Microsoft Azure Key Vault. For more information on Always Encrypted, please refer to: https://aka.ms/AlwaysEncrypted. + /// column master key is stored in Microsoft Azure Key Vault. + /// + /// For more information on Always Encrypted, please refer to: https://aka.ms/AlwaysEncrypted. /// /// A Column Encryption Key encrypted with certificate store provider should be decryptable by this provider and vice versa. /// - /// Envelope Format for the encrypted column encryption key - /// version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature + /// Envelope Format for the encrypted column encryption key : + /// version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature /// - /// version: A single byte indicating the format version. - /// keyPathLength: Length of the keyPath. - /// ciphertextLength: ciphertext length - /// keyPath: keyPath used to encrypt the column encryption key. This is only used for troubleshooting purposes and is not verified during decryption. - /// ciphertext: Encrypted column encryption key - /// signature: Signature of the entire byte array. Signature is validated before decrypting the column encryption key. + /// - version: A single byte indicating the format version. + /// - keyPathLength: Length of the keyPath. + /// - ciphertextLength: ciphertext length + /// - keyPath: keyPath used to encrypt the column encryption key. This is only used for troubleshooting purposes and is not verified during decryption. + /// - ciphertext: Encrypted column encryption key + /// - signature: Signature of the entire byte array. Signature is validated before decrypting the column encryption key. /// /// /// API only once in the lifetime of driver to register this custom provider by implementing a custom Authentication Callback mechanism. - /// - /// Once the provider is registered, it can used to perform Always Encrypted operations by creating Column Master Key using Azure Key Vault Key Identifier URL. - /// - /// ## Example - /// - /// Sample C# applications to demonstrate Always Encrypted use with Azure Key Vault are available at links below: - /// - /// - [Example: Using Azure Key Vault with Always Encrypted](~/connect/ado-net/sql/azure-key-vault-example.md) - /// - [Example: Using Azure Key Vault with Always Encrypted with enclaves enabled](~/connect/ado-net/sql/azure-key-vault-enclave-example.md) + /// For more information, see: [Using the Azure Key Vault Provider](/sql/connect/ado-net/sql/sqlclient-support-always-encrypted#using-the-azure-key-vault-provider) /// ]]> /// public class SqlColumnEncryptionAzureKeyVaultProvider : SqlColumnEncryptionKeyStoreProvider @@ -61,18 +45,16 @@ public class SqlColumnEncryptionAzureKeyVaultProvider : SqlColumnEncryptionKeySt public const string ProviderName = "AZURE_KEY_VAULT"; /// - /// Algorithm version + /// Key storage and cryptography client /// - private readonly byte[] firstVersion = new byte[] { 0x01 }; + private AzureSqlKeyCryptographer KeyCryptographer { get; set; } /// - /// Azure Key Vault Client + /// Algorithm version /// - public KeyVaultClient KeyVaultClient - { - get; - private set; - } + private readonly static byte[] s_firstVersion = new byte[] { 0x01 }; + + private readonly static KeyWrapAlgorithm s_keyWrapAlgorithm = KeyWrapAlgorithm.RsaOaep; /// /// List of Trusted Endpoints @@ -80,73 +62,93 @@ public KeyVaultClient KeyVaultClient /// public readonly string[] TrustedEndPoints; + /// + /// A cache of column encryption keys (once they are decrypted). This is useful for rapidly decrypting multiple data values. + /// + private readonly LocalCache _columnEncryptionKeyCache = new() { TimeToLive = TimeSpan.FromHours(2) }; + + /// + /// A cache for storing the results of signature verification of column master key metadata. + /// + private readonly LocalCache, bool> _columnMasterKeyMetadataSignatureVerificationCache = + new(maxSizeLimit: 2000) { TimeToLive = TimeSpan.FromDays(10) }; + + /// + /// Gets or sets the lifespan of the decrypted column encryption key in the cache. + /// Once the timespan has elapsed, the decrypted column encryption key is discarded + /// and must be revalidated. + /// + /// + /// Internally, there is a cache of column encryption keys (once they are decrypted). + /// This is useful for rapidly decrypting multiple data values. The default value is 2 hours. + /// Setting the to zero disables caching. + /// + public override TimeSpan? ColumnEncryptionKeyCacheTtl + { + get => _columnEncryptionKeyCache.TimeToLive; + set => _columnEncryptionKeyCache.TimeToLive = value; + } + #endregion + #region Constructors /// - /// Constructor that takes a callback function to authenticate to AAD. This is used by KeyVaultClient at runtime - /// to authenticate to Azure Key Vault. + /// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token. /// - /// Callback function used for authenticating to AAD. - public SqlColumnEncryptionAzureKeyVaultProvider(KeyVaultClient.AuthenticationCallback authenticationCallback) : - this(authenticationCallback, Constants.AzureKeyVaultPublicDomainNames) + /// + public SqlColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCredential) : + this(tokenCredential, Constants.AzureKeyVaultPublicDomainNames) { } /// - /// Constructor that takes a callback function to authenticate to AAD and a trusted endpoint. + /// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token and a trusted endpoint. /// - /// Callback function used for authenticating to AAD. - /// TrustedEndpoint is used to validate the master key path - public SqlColumnEncryptionAzureKeyVaultProvider(KeyVaultClient.AuthenticationCallback authenticationCallback, string trustedEndPoint) : - this(authenticationCallback, new[] { trustedEndPoint }) + /// Instance of an implementation of Token Credential that is capable of providing an OAuth Token. + /// TrustedEndpoint is used to validate the master key path. + public SqlColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCredential, string trustedEndPoint) : + this(tokenCredential, new[] { trustedEndPoint }) { } /// - /// Constructor that takes a callback function to authenticate to AAD and an array of trusted endpoints. The callback function - /// is used by KeyVaultClient at runtime to authenticate to Azure Key Vault. + /// Constructor that takes an instance of an implementation of Token Credential that is capable of providing an OAuth Token + /// and an array of trusted endpoints. /// - /// Callback function used for authenticating to AAD. - /// TrustedEndpoints are used to validate the master key path - public SqlColumnEncryptionAzureKeyVaultProvider(KeyVaultClient.AuthenticationCallback authenticationCallback, string[] trustedEndPoints) + /// Instance of an implementation of Token Credential that is capable of providing an OAuth Token + /// TrustedEndpoints are used to validate the master key path + public SqlColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCredential, string[] trustedEndpoints) { - if (authenticationCallback == null) - { - throw new ArgumentNullException("authenticationCallback"); - } - - if (trustedEndPoints == null || trustedEndPoints.Length == 0) - { - throw new ArgumentException(Strings.InvalidTrustedEndpointsList); - } - - foreach (string trustedEndPoint in trustedEndPoints) - { - if (String.IsNullOrWhiteSpace(trustedEndPoint)) - { - throw new ArgumentException(String.Format(Strings.InvalidTrustedEndpointTemplate, trustedEndPoint)); - } - } - - KeyVaultClient = new KeyVaultClient(authenticationCallback); - this.TrustedEndPoints = trustedEndPoints; + using var _ = AKVScope.Create(); + ValidateNotNull(tokenCredential, nameof(tokenCredential)); + ValidateNotNull(trustedEndpoints, nameof(trustedEndpoints)); + ValidateNotEmpty(trustedEndpoints, nameof(trustedEndpoints)); + ValidateNotNullOrWhitespaceForEach(trustedEndpoints, nameof(trustedEndpoints)); + + KeyCryptographer = new AzureSqlKeyCryptographer(tokenCredential); + TrustedEndPoints = trustedEndpoints; } + #endregion #region Public methods /// - /// Uses an asymmetric key identified by the key path to sign the masterkey metadata consisting of (masterKeyPath, allowEnclaveComputations bit, providerName). + /// Uses an asymmetric key identified by the key path to sign the master key metadata consisting of (masterKeyPath, allowEnclaveComputations bit, providerName). /// /// Complete path of an asymmetric key. Path format is specific to a key store provider. - /// Boolean indicating whether this key can be sent to trusted enclave + /// Boolean indicating whether this key can be sent to a trusted enclave /// Encrypted column encryption key public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { - var hash = ComputeMasterKeyMetadataHash(masterKeyPath, allowEnclaveComputations, isSystemOp: false); - byte[] signedHash = AzureKeyVaultSignHashedData(hash, masterKeyPath); - return signedHash; + using var _ = AKVScope.Create(); + ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: false); + + // Also validates key is of RSA type. + KeyCryptographer.AddKey(masterKeyPath); + byte[] message = CompileMasterKeyMetadata(masterKeyPath, allowEnclaveComputations); + return KeyCryptographer.SignData(message, masterKeyPath); } /// - /// Uses an asymmetric key identified by the key path to verify the masterkey metadata consisting of (masterKeyPath, allowEnclaveComputations bit, providerName). + /// Uses an asymmetric key identified by the key path to verify the master key metadata consisting of (masterKeyPath, allowEnclaveComputations bit, providerName). /// /// Complete path of an asymmetric key. Path format is specific to a key store provider. /// Boolean indicating whether this key can be sent to trusted enclave @@ -154,422 +156,252 @@ public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool al /// Boolean indicating whether the master key metadata can be verified based on the provided signature public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { - var hash = ComputeMasterKeyMetadataHash(masterKeyPath, allowEnclaveComputations, isSystemOp: true); - return AzureKeyVaultVerifySignature(hash, signature, masterKeyPath); + using var _ = AKVScope.Create(); + ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true); + + var key = Tuple.Create(masterKeyPath, allowEnclaveComputations, ToHexString(signature)); + return GetOrCreateSignatureVerificationResult(key, VerifyColumnMasterKeyMetadata); + + bool VerifyColumnMasterKeyMetadata() + { + // Also validates key is of RSA type. + KeyCryptographer.AddKey(masterKeyPath); + byte[] message = CompileMasterKeyMetadata(masterKeyPath, allowEnclaveComputations); + return KeyCryptographer.VerifyData(message, signature, masterKeyPath); + } } /// /// This function uses the asymmetric key specified by the key path /// and decrypts an encrypted CEK with RSA encryption algorithm. /// - /// Complete path of an asymmetric key in AKV + /// Complete path of an asymmetric key in Azure Key Vault /// Asymmetric Key Encryption Algorithm /// Encrypted Column Encryption Key /// Plain text column encryption key public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { + using var _ = AKVScope.Create(); // Validate the input parameters - this.ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true); + ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true); + ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true); + ValidateNotNull(encryptedColumnEncryptionKey, nameof(encryptedColumnEncryptionKey)); + ValidateNotEmpty(encryptedColumnEncryptionKey, nameof(encryptedColumnEncryptionKey)); + ValidateVersionByte(encryptedColumnEncryptionKey[0], s_firstVersion[0]); - if (null == encryptedColumnEncryptionKey) - { - throw new ArgumentNullException(Constants.AeParamEncryptedCek, Strings.NullCekvInternal); - } + return GetOrCreateColumnEncryptionKey(ToHexString(encryptedColumnEncryptionKey), DecryptEncryptionKey); - if (0 == encryptedColumnEncryptionKey.Length) + byte[] DecryptEncryptionKey() { - throw new ArgumentException(Strings.EmptyCekvInternal, Constants.AeParamEncryptedCek); - } - - // Validate encryptionAlgorithm - this.ValidateEncryptionAlgorithm(ref encryptionAlgorithm, isSystemOp: true); - - // Validate whether the key is RSA one or not and then get the key size - int keySizeInBytes = GetAKVKeySize(masterKeyPath); + // Also validates whether the key is RSA one or not and then get the key size + KeyCryptographer.AddKey(masterKeyPath); - // Validate and decrypt the EncryptedColumnEncryptionKey - // Format is - // version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature - // - // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and - // we will not validate it against the data contained in the CMK metadata (masterKeyPath). + int keySizeInBytes = KeyCryptographer.GetKeySize(masterKeyPath); - // Validate the version byte - if (encryptedColumnEncryptionKey[0] != firstVersion[0]) - { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, Strings.InvalidAlgorithmVersionTemplate, - encryptedColumnEncryptionKey[0].ToString(@"X2"), - firstVersion[0].ToString("X2")), - Constants.AeParamEncryptedCek); - } + // Get key path length + int currentIndex = s_firstVersion.Length; + ushort keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); + currentIndex += sizeof(ushort); - // Get key path length - int currentIndex = firstVersion.Length; - UInt16 keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(UInt16); + // Get ciphertext length + ushort cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); + currentIndex += sizeof(ushort); - // Get ciphertext length - UInt16 cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(UInt16); + // Skip KeyPath + // KeyPath exists only for troubleshooting purposes and doesnt need validation. + currentIndex += keyPathLength; - // Skip KeyPath - // KeyPath exists only for troubleshooting purposes and doesnt need validation. - currentIndex += keyPathLength; - - // validate the ciphertext length - if (cipherTextLength != keySizeInBytes) - { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, Strings.InvalidCiphertextLengthTemplate, - cipherTextLength, - keySizeInBytes, - masterKeyPath), - Constants.AeParamEncryptedCek); - } + // validate the ciphertext length + if (cipherTextLength != keySizeInBytes) + { + AKVEventSource.Log.TryTraceEvent("Cipher Text length: {0}", cipherTextLength); + AKVEventSource.Log.TryTraceEvent("keySizeInBytes: {0}", keySizeInBytes); + throw ADP.InvalidCipherTextLength(cipherTextLength, keySizeInBytes, masterKeyPath); + } - // Validate the signature length - int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; - if (signatureLength != keySizeInBytes) - { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, Strings.InvalidSignatureLengthTemplate, - signatureLength, - keySizeInBytes, - masterKeyPath), - Constants.AeParamEncryptedCek); - } + // Validate the signature length + int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; + if (signatureLength != keySizeInBytes) + { + AKVEventSource.Log.TryTraceEvent("Signature length: {0}", signatureLength); + AKVEventSource.Log.TryTraceEvent("keySizeInBytes: {0}", keySizeInBytes); + throw ADP.InvalidSignatureLengthTemplate(signatureLength, keySizeInBytes, masterKeyPath); + } - // Get ciphertext - byte[] cipherText = new byte[cipherTextLength]; - Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, cipherText, 0, cipherTextLength); - currentIndex += cipherTextLength; + // Get ciphertext + byte[] cipherText = encryptedColumnEncryptionKey.Skip(currentIndex).Take(cipherTextLength).ToArray(); + currentIndex += cipherTextLength; - // Get signature - byte[] signature = new byte[signatureLength]; - Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, signature, 0, signature.Length); + // Get signature + byte[] signature = encryptedColumnEncryptionKey.Skip(currentIndex).Take(signatureLength).ToArray(); - // Compute the hash to validate the signature - byte[] hash; - using (SHA256 sha256 = SHA256.Create()) - { - sha256.TransformFinalBlock(encryptedColumnEncryptionKey, 0, encryptedColumnEncryptionKey.Length - signature.Length); - hash = sha256.Hash; - } + // Compute the message to validate the signature + byte[] message = encryptedColumnEncryptionKey.Take(encryptedColumnEncryptionKey.Length - signatureLength).ToArray(); - if (null == hash) - { - throw new CryptographicException(Strings.NullHash); - } + if (null == message) + { + throw ADP.NullHashFound(); + } - // Validate the signature - if (!AzureKeyVaultVerifySignature(hash, signature, masterKeyPath)) - { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, Strings.InvalidSignatureTemplate, - masterKeyPath), - Constants.AeParamEncryptedCek); + if (!KeyCryptographer.VerifyData(message, signature, masterKeyPath)) + { + AKVEventSource.Log.TryTraceEvent("Signature could not be verified."); + throw ADP.InvalidSignatureTemplate(masterKeyPath); + } + return KeyCryptographer.UnwrapKey(s_keyWrapAlgorithm, cipherText, masterKeyPath); } - - // Decrypt the CEK - return this.AzureKeyVaultUnWrap(masterKeyPath, encryptionAlgorithm, cipherText); } /// /// This function uses the asymmetric key specified by the key path /// and encrypts CEK with RSA encryption algorithm. /// - /// Complete path of an asymmetric key in AKV + /// Complete path of an asymmetric key in Azure Key Vault /// Asymmetric Key Encryption Algorithm - /// Plain text column encryption key + /// The plaintext column encryption key. /// Encrypted column encryption key public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { + using var _ = AKVScope.Create(); // Validate the input parameters - this.ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true); - - if (null == columnEncryptionKey) - { - throw new ArgumentNullException(Constants.AeParamColumnEncryptionKey, Strings.NullCek); - } - - if (0 == columnEncryptionKey.Length) - { - throw new ArgumentException(Strings.EmptyCek, Constants.AeParamColumnEncryptionKey); - } - - // Validate encryptionAlgorithm - this.ValidateEncryptionAlgorithm(ref encryptionAlgorithm, isSystemOp: false); + ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true); + ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true); + ValidateNotNull(columnEncryptionKey, nameof(columnEncryptionKey)); + ValidateNotEmpty(columnEncryptionKey, nameof(columnEncryptionKey)); - // Validate whether the key is RSA one or not and then get the key size - int keySizeInBytes = GetAKVKeySize(masterKeyPath); + // Also validates whether the key is RSA one or not and then get the key size + KeyCryptographer.AddKey(masterKeyPath); + int keySizeInBytes = KeyCryptographer.GetKeySize(masterKeyPath); // Construct the encryptedColumnEncryptionKey // Format is - // version + keyPathLength + ciphertextLength + ciphertext + keyPath + signature - // - // We currently only support one version - byte[] version = new byte[] { firstVersion[0] }; + // s_firstVersion + keyPathLength + ciphertextLength + keyPath + ciphertext + signature // Get the Unicode encoded bytes of cultureinvariant lower case masterKeyPath byte[] masterKeyPathBytes = Encoding.Unicode.GetBytes(masterKeyPath.ToLowerInvariant()); - byte[] keyPathLength = BitConverter.GetBytes((Int16)masterKeyPathBytes.Length); + byte[] keyPathLength = BitConverter.GetBytes((short)masterKeyPathBytes.Length); // Encrypt the plain text - byte[] cipherText = this.AzureKeyVaultWrap(masterKeyPath, encryptionAlgorithm, columnEncryptionKey); - byte[] cipherTextLength = BitConverter.GetBytes((Int16)cipherText.Length); + byte[] cipherText = KeyCryptographer.WrapKey(s_keyWrapAlgorithm, columnEncryptionKey, masterKeyPath); + byte[] cipherTextLength = BitConverter.GetBytes((short)cipherText.Length); if (cipherText.Length != keySizeInBytes) { - throw new CryptographicException(Strings.CiphertextLengthMismatch); + AKVEventSource.Log.TryTraceEvent("Cipher Text length: {0}", cipherText.Length); + AKVEventSource.Log.TryTraceEvent("keySizeInBytes: {0}", keySizeInBytes); + throw ADP.CipherTextLengthMismatch(); } - // Compute hash + // Compute message // SHA-2-256(version + keyPathLength + ciphertextLength + keyPath + ciphertext) - byte[] hash; - using (SHA256 sha256 = SHA256.Create()) - { - sha256.TransformBlock(version, 0, version.Length, version, 0); - sha256.TransformBlock(keyPathLength, 0, keyPathLength.Length, keyPathLength, 0); - sha256.TransformBlock(cipherTextLength, 0, cipherTextLength.Length, cipherTextLength, 0); - sha256.TransformBlock(masterKeyPathBytes, 0, masterKeyPathBytes.Length, masterKeyPathBytes, 0); - sha256.TransformFinalBlock(cipherText, 0, cipherText.Length); - hash = sha256.Hash; - } + byte[] message = s_firstVersion.Concat(keyPathLength).Concat(cipherTextLength).Concat(masterKeyPathBytes).Concat(cipherText).ToArray(); - // Sign the hash - byte[] signedHash = AzureKeyVaultSignHashedData(hash, masterKeyPath); + // Sign the message + byte[] signature = KeyCryptographer.SignData(message, masterKeyPath); - if (signedHash.Length != keySizeInBytes) + if (signature.Length != keySizeInBytes) { - throw new CryptographicException(Strings.HashLengthMismatch); + throw ADP.HashLengthMismatch(); } - if (!this.AzureKeyVaultVerifySignature(hash, signedHash, masterKeyPath)) - { - throw new CryptographicException(Strings.InvalidSignature); - } - - // Construct the encrypted column encryption key - // EncryptedColumnEncryptionKey = version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature - int encryptedColumnEncryptionKeyLength = version.Length + cipherTextLength.Length + keyPathLength.Length + cipherText.Length + masterKeyPathBytes.Length + signedHash.Length; - byte[] encryptedColumnEncryptionKey = new byte[encryptedColumnEncryptionKeyLength]; - - // Copy version byte - int currentIndex = 0; - Buffer.BlockCopy(version, 0, encryptedColumnEncryptionKey, currentIndex, version.Length); - currentIndex += version.Length; - - // Copy key path length - Buffer.BlockCopy(keyPathLength, 0, encryptedColumnEncryptionKey, currentIndex, keyPathLength.Length); - currentIndex += keyPathLength.Length; + ValidateSignature(masterKeyPath, message, signature); - // Copy ciphertext length - Buffer.BlockCopy(cipherTextLength, 0, encryptedColumnEncryptionKey, currentIndex, cipherTextLength.Length); - currentIndex += cipherTextLength.Length; - - // Copy key path - Buffer.BlockCopy(masterKeyPathBytes, 0, encryptedColumnEncryptionKey, currentIndex, masterKeyPathBytes.Length); - currentIndex += masterKeyPathBytes.Length; - - // Copy ciphertext - Buffer.BlockCopy(cipherText, 0, encryptedColumnEncryptionKey, currentIndex, cipherText.Length); - currentIndex += cipherText.Length; - - // copy the signature - Buffer.BlockCopy(signedHash, 0, encryptedColumnEncryptionKey, currentIndex, signedHash.Length); - - return encryptedColumnEncryptionKey; + return message.Concat(signature).ToArray(); } #endregion #region Private methods - /// - /// This function validates that the encryption algorithm is RSA_OAEP and if it is not, - /// then throws an exception - /// - /// Asymmetric key encryption algorithm - /// is the operation a system operation - private void ValidateEncryptionAlgorithm(ref string encryptionAlgorithm, bool isSystemOp) - { - // This validates that the encryption algorithm is RSA_OAEP - if (null == encryptionAlgorithm) - { - if (isSystemOp) - { - throw new ArgumentNullException(Constants.AeParamEncryptionAlgorithm, Strings.NullAlgorithmInternal); - } - else - { - throw new ArgumentNullException(Constants.AeParamEncryptionAlgorithm, Strings.NullAlgorithm); - } - } - - // Transform to standard format (dash instead of underscore) to support both "RSA_OAEP" and "RSA-OAEP" - if (encryptionAlgorithm.Equals("RSA_OAEP", StringComparison.OrdinalIgnoreCase)) - { - encryptionAlgorithm = JsonWebKeyEncryptionAlgorithm.RSAOAEP; - } - - if (String.Equals(encryptionAlgorithm, JsonWebKeyEncryptionAlgorithm.RSAOAEP, StringComparison.OrdinalIgnoreCase) != true) - { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, Strings.InvalidKeyAlgorithm, - encryptionAlgorithm, "RSA_OAEP' or 'RSA-OAEP"), // For supporting both algorithm formats. - Constants.AeParamEncryptionAlgorithm); - } - } - - private byte[] ComputeMasterKeyMetadataHash(string masterKeyPath, bool allowEnclaveComputations, bool isSystemOp) - { - // Validate the input parameters - ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp); - - // Validate whether the key is RSA one or not and then get the key size - GetAKVKeySize(masterKeyPath); - - string masterkeyMetadata = ProviderName + masterKeyPath + allowEnclaveComputations; - byte[] masterkeyMetadataBytes = Encoding.Unicode.GetBytes(masterkeyMetadata.ToLowerInvariant()); - - // Compute hash - byte[] hash; - using (SHA256 sha256 = SHA256.Create()) - { - sha256.TransformFinalBlock(masterkeyMetadataBytes, 0, masterkeyMetadataBytes.Length); - hash = sha256.Hash; - } - return hash; - } - /// /// Checks if the Azure Key Vault key path is Empty or Null (and raises exception if they are). /// internal void ValidateNonEmptyAKVPath(string masterKeyPath, bool isSystemOp) { // throw appropriate error if masterKeyPath is null or empty - if (String.IsNullOrWhiteSpace(masterKeyPath)) + if (string.IsNullOrWhiteSpace(masterKeyPath)) { - string errorMessage = null == masterKeyPath - ? Strings.NullAkvPath - : String.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvPathTemplate, masterKeyPath); - - if (isSystemOp) - { - throw new ArgumentNullException(Constants.AeParamMasterKeyPath, errorMessage); - } - - throw new ArgumentException(errorMessage, Constants.AeParamMasterKeyPath); + AKVEventSource.Log.TryTraceEvent("Azure Key Vault URI found null or empty."); + throw ADP.InvalidAKVPath(masterKeyPath, isSystemOp); } - Uri parsedUri; - - if (!Uri.TryCreate(masterKeyPath, UriKind.Absolute, out parsedUri)) + if (!Uri.TryCreate(masterKeyPath, UriKind.Absolute, out Uri parsedUri) || parsedUri.Segments.Length < 3) { // Return an error indicating that the AKV url is invalid. - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvUrlTemplate, masterKeyPath), Constants.AeParamMasterKeyPath); + AKVEventSource.Log.TryTraceEvent("URI could not be created with provided master key path: {0}", masterKeyPath); + throw ADP.InvalidAKVUrl(masterKeyPath); } // A valid URI. // Check if it is pointing to trusted endpoint. - foreach (string trustedEndPoint in this.TrustedEndPoints) + foreach (string trustedEndPoint in TrustedEndPoints) { if (parsedUri.Host.EndsWith(trustedEndPoint, StringComparison.OrdinalIgnoreCase)) { + AKVEventSource.Log.TryTraceEvent("Azure Key Vault URI validated successfully."); return; } } // Return an error indicating that the AKV url is invalid. - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvKeyPathTrustedTemplate, masterKeyPath, String.Join(", ", this.TrustedEndPoints.ToArray())), Constants.AeParamMasterKeyPath); + AKVEventSource.Log.TryTraceEvent("Master Key Path could not be validated as it does not end with trusted endpoints: {0}", masterKeyPath); + throw ADP.InvalidAKVUrlTrustedEndpoints(masterKeyPath, string.Join(", ", TrustedEndPoints.ToArray())); } - /// - /// Encrypt the text using specified Azure Key Vault key. - /// - /// Azure Key Vault key url. - /// Encryption Algorithm. - /// Plain text Column Encryption Key. - /// Returns an encrypted blob or throws an exception if there are any errors. - private byte[] AzureKeyVaultWrap(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) + private void ValidateSignature(string masterKeyPath, byte[] message, byte[] signature) { - if (null == columnEncryptionKey) + if (!KeyCryptographer.VerifyData(message, signature, masterKeyPath)) { - throw new ArgumentNullException("columnEncryptionKey"); + AKVEventSource.Log.TryTraceEvent("Signature could not be verified."); + throw ADP.InvalidSignature(); } - - var wrappedKey = Task.Run(() => KeyVaultClient.WrapKeyAsync(masterKeyPath, encryptionAlgorithm, columnEncryptionKey)).Result; - return wrappedKey.Result; + AKVEventSource.Log.TryTraceEvent("Signature verified successfully."); } - /// - /// Encrypt the text using specified Azure Key Vault key. - /// - /// Azure Key Vault key url. - /// Encryption Algorithm. - /// Encrypted Column Encryption Key. - /// Returns the decrypted plaintext Column Encryption Key or throws an exception if there are any errors. - private byte[] AzureKeyVaultUnWrap(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) + private byte[] CompileMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { - if (null == encryptedColumnEncryptionKey) - { - throw new ArgumentNullException("encryptedColumnEncryptionKey"); - } - - if (0 == encryptedColumnEncryptionKey.Length) - { - throw new ArgumentException(Strings.EncryptedCekEmpty); - } - - - var unwrappedKey = Task.Run(() => KeyVaultClient.UnwrapKeyAsync(masterKeyPath, encryptionAlgorithm, encryptedColumnEncryptionKey)).Result; - - return unwrappedKey.Result; + string masterkeyMetadata = ProviderName + masterKeyPath + allowEnclaveComputations; + return Encoding.Unicode.GetBytes(masterkeyMetadata.ToLowerInvariant()); } /// - /// Generates signature based on RSA PKCS#v1.5 scheme using a specified Azure Key Vault Key URL. + /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string representation. /// - /// Text to sign. - /// Azure Key Vault key url. - /// Signature - private byte[] AzureKeyVaultSignHashedData(byte[] dataToSign, string masterKeyPath) + /// An array of bytes to convert. + /// A string of hexadecimal characters + /// + /// Produces a string of hexadecimal character pairs preceded with "0x", where each pair represents the corresponding element in value; for example, "0x7F2C4A00". + /// + private string ToHexString(byte[] source) { - Debug.Assert((dataToSign != null) && (dataToSign.Length != 0)); - - var signedData = Task.Run(() => KeyVaultClient.SignAsync(masterKeyPath, Constants.HashingAlgorithm, dataToSign)).Result; + if (source is null) + { + return null; + } - return signedData.Result; + return "0x" + BitConverter.ToString(source).Replace("-", ""); } /// - /// Verifies the given RSA PKCSv1.5 signature. + /// Returns the cached decrypted column encryption key, or unwraps the encrypted column encryption key if not present. /// - /// - /// - /// Azure Key Vault key url. - /// true if signature is valid, false if it is not valid - private bool AzureKeyVaultVerifySignature(byte[] dataToVerify, byte[] signature, string masterKeyPath) - { - Debug.Assert((dataToVerify != null) && (dataToVerify.Length != 0)); - Debug.Assert((signature != null) && (signature.Length != 0)); - - return Task.Run(() => KeyVaultClient.VerifyAsync(masterKeyPath, Constants.HashingAlgorithm, dataToVerify, signature)).Result; - } + /// Encrypted Column Encryption Key + /// The delegate function that will decrypt the encrypted column encryption key. + /// The decrypted column encryption key. + /// + /// + /// + private byte[] GetOrCreateColumnEncryptionKey(string encryptedColumnEncryptionKey, Func createItem) + => _columnEncryptionKeyCache.GetOrCreate(encryptedColumnEncryptionKey, createItem); /// - /// Gets the public Key size in bytes + /// Returns the cached signature verification result, or proceeds to verify if not present. /// - /// Azure Key Vault Key path - /// Key size in bytes - private int GetAKVKeySize(string masterKeyPath) - { - KeyBundle retrievedKey = Task.Run(() => KeyVaultClient.GetKeyAsync(masterKeyPath)).Result; - - if (!String.Equals(retrievedKey.Key.Kty, JsonWebKeyType.Rsa, StringComparison.InvariantCultureIgnoreCase) && - !String.Equals(retrievedKey.Key.Kty, JsonWebKeyType.RsaHsm, StringComparison.InvariantCultureIgnoreCase)) - { - throw new Exception(String.Format(CultureInfo.InvariantCulture, Strings.NonRsaKeyTemplate, retrievedKey.Key.Kty)); - } - - return retrievedKey.Key.N.Length; - } + /// The encryptionKeyId, allowEnclaveComputations and hexadecimal signature. + /// The delegate function that will perform the verification. + /// + private bool GetOrCreateSignatureVerificationResult(Tuple keyInformation, Func createItem) + => _columnMasterKeyMetadataSignatureVerificationCache.GetOrCreate(keyInformation, createItem); #endregion } diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs index a725197027..fc5d88930a 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.Designer.cs @@ -10,8 +10,6 @@ namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider { - using System; - /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -71,55 +69,22 @@ internal Strings() /// /// Looks up a localized string similar to CipherText length does not match the RSA key size.. /// - internal static string CiphertextLengthMismatch + internal static string CipherTextLengthMismatch { get { - return ResourceManager.GetString("CiphertextLengthMismatch", resourceCulture); + return ResourceManager.GetString("CipherTextLengthMismatch", resourceCulture); } } /// - /// Looks up a localized string similar to Empty column encryption key specified.. + /// Looks up a localized string similar to Internal error. Empty '{0}' specified.. /// - internal static string EmptyCek + internal static string EmptyArgumentInternal { get { - return ResourceManager.GetString("EmptyCek", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Empty encrypted column encryption key specified.. - /// - internal static string EmptyCekv - { - get - { - return ResourceManager.GetString("EmptyCekv", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Internal error: Empty encrypted column encryption key specified.. - /// - internal static string EmptyCekvInternal - { - get - { - return ResourceManager.GetString("EmptyCekvInternal", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to encryptedColumnEncryptionKey length should not be zero.. - /// - internal static string EncryptedCekEmpty - { - get - { - return ResourceManager.GetString("EncryptedCekEmpty", resourceCulture); + return ResourceManager.GetString("EmptyArgumentInternal", resourceCulture); } } @@ -233,25 +198,14 @@ internal static string InvalidSignatureTemplate } } - /// - /// Looks up a localized string similar to trustedEndPoints cannot be null or empty.. - /// - internal static string InvalidTrustedEndpointsList - { - get - { - return ResourceManager.GetString("InvalidTrustedEndpointsList", resourceCulture); - } - } - /// /// Looks up a localized string similar to Invalid trusted endpoint specified: '{0}'; a trusted endpoint must have a value.. /// - internal static string InvalidTrustedEndpointTemplate + internal static string NullOrWhitespaceForEach { get { - return ResourceManager.GetString("InvalidTrustedEndpointTemplate", resourceCulture); + return ResourceManager.GetString("NullOrWhitespaceForEach", resourceCulture); } } @@ -299,39 +253,6 @@ internal static string NullAlgorithmInternal } } - /// - /// Looks up a localized string similar to Column encryption key cannot be null.. - /// - internal static string NullCek - { - get - { - return ResourceManager.GetString("NullCek", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Encrypted column encryption key cannot be null.. - /// - internal static string NullCekv - { - get - { - return ResourceManager.GetString("NullCekv", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Internal error: Encrypted column encryption key cannot be null.. - /// - internal static string NullCekvInternal - { - get - { - return ResourceManager.GetString("NullCekvInternal", resourceCulture); - } - } - /// /// Looks up a localized string similar to Hash should not be null while decrypting encrypted column encryption key.. /// diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx index 7d4e2e6db3..039d1079d5 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Strings.resx @@ -117,23 +117,17 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - trustedEndPoints cannot be null or empty. + + One or more of the elements in {0} are null or empty or consist of only whitespace. - - Invalid trusted endpoint specified: '{0}'; a trusted endpoint must have a value. - - + CipherText length does not match the RSA key size. - - Empty column encryption key specified. - - - Empty encrypted column encryption key specified. + + Internal error. Empty {0} specified. - - encryptedColumnEncryptionKey length should not be zero. + + The key with identifier '{0}' was not found. Signed hash length does not match the RSA key size. @@ -174,22 +168,10 @@ Key encryption algorithm cannot be null. - - Column encryption key cannot be null. - - - Encrypted column encryption key cannot be null. - - - Hash should not be null while decrypting encrypted column encryption key. - - - Internal error. Empty encrypted column encryption key specified. - Internal error. Key encryption algorithm cannot be null. - - Internal error. Encrypted column encryption key cannot be null. + + Hash should not be null while decrypting encrypted column encryption key. diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Utils.cs b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Utils.cs new file mode 100644 index 0000000000..a5d74ed0a7 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Utils.cs @@ -0,0 +1,139 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security.Cryptography; + +namespace Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider +{ + internal static class Validator + { + internal static void ValidateNotNull(object parameter, string name) + { + if (null == parameter) + { + throw ADP.NullArgument(name); + } + } + + internal static void ValidateNotEmpty(IList parameter, string name) + { + if (parameter.Count == 0) + { + throw ADP.EmptyArgument(name); + } + } + + internal static void ValidateNotNullOrWhitespaceForEach(string[] parameters, string name) + { + if (parameters.Any(s => string.IsNullOrWhiteSpace(s))) + { + throw ADP.NullOrWhitespaceForEach(name); + } + } + + internal static void ValidateEncryptionAlgorithm(string encryptionAlgorithm, bool isSystemOp) + { + // This validates that the encryption algorithm is RSA_OAEP + if (null == encryptionAlgorithm) + { + throw ADP.NullAlgorithm(isSystemOp); + } + + if (!encryptionAlgorithm.Equals("RSA_OAEP", StringComparison.OrdinalIgnoreCase) + && !encryptionAlgorithm.Equals("RSA-OAEP", StringComparison.OrdinalIgnoreCase)) + { + throw ADP.InvalidKeyAlgorithm(encryptionAlgorithm); + } + } + + internal static void ValidateVersionByte(byte encryptedByte, byte firstVersionByte) + { + // Validate and decrypt the EncryptedColumnEncryptionKey + // Format is + // version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature + // + // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and + // we will not validate it against the data contained in the CMK metadata (masterKeyPath). + + // Validate the version byte + if (encryptedByte != firstVersionByte) + { + throw ADP.InvalidAlgorithmVersion(encryptedByte.ToString(@"X2"), firstVersionByte.ToString("X2")); + } + } + } + + internal static class ADP + { + internal static ArgumentNullException NullArgument(string name) => + new(name); + + internal static ArgumentException EmptyArgument(string name) => + new(string.Format(Strings.EmptyArgumentInternal, name)); + + internal static ArgumentException NullOrWhitespaceForEach(string name) => + new(string.Format(Strings.NullOrWhitespaceForEach, name)); + + internal static KeyNotFoundException MasterKeyNotFound(string masterKeyPath) => + new(string.Format(CultureInfo.InvariantCulture, Strings.InvalidSignatureTemplate, masterKeyPath)); + + internal static FormatException NonRsaKeyFormat(string keyType) => + new(string.Format(CultureInfo.InvariantCulture, Strings.NonRsaKeyTemplate, keyType)); + + internal static ArgumentException InvalidCipherTextLength(ushort cipherTextLength, int keySizeInBytes, string masterKeyPath) => + new(string.Format(CultureInfo.InvariantCulture, Strings.InvalidCiphertextLengthTemplate, + cipherTextLength, keySizeInBytes, masterKeyPath), Constants.AeParamEncryptedCek); + + internal static ArgumentNullException NullAlgorithm(bool isSystemOp) => + new(Constants.AeParamEncryptionAlgorithm, (isSystemOp ? Strings.NullAlgorithmInternal : Strings.NullAlgorithm)); + + internal static ArgumentException InvalidKeyAlgorithm(string encryptionAlgorithm) => + new(string.Format(CultureInfo.InvariantCulture, Strings.InvalidKeyAlgorithm, encryptionAlgorithm, + "RSA_OAEP' or 'RSA-OAEP")/* For supporting both algorithm formats.*/, Constants.AeParamEncryptionAlgorithm); + + internal static ArgumentException InvalidSignatureLengthTemplate(int signatureLength, int keySizeInBytes, string masterKeyPath) => + new(string.Format(CultureInfo.InvariantCulture, Strings.InvalidSignatureLengthTemplate, + signatureLength, keySizeInBytes, masterKeyPath), Constants.AeParamEncryptedCek); + + internal static Exception InvalidAlgorithmVersion(string encryptedBytes, string firstVersionBytes) => + new ArgumentException(string.Format(CultureInfo.InvariantCulture, Strings.InvalidAlgorithmVersionTemplate, + encryptedBytes, firstVersionBytes), Constants.AeParamEncryptedCek); + + internal static ArgumentException InvalidSignatureTemplate(string masterKeyPath) => + new ArgumentException(string.Format(CultureInfo.InvariantCulture, Strings.InvalidSignatureTemplate, masterKeyPath), + Constants.AeParamEncryptedCek); + + internal static CryptographicException InvalidSignature() => new(Strings.InvalidSignature); + + internal static CryptographicException NullHashFound() => new(Strings.NullHash); + + internal static CryptographicException CipherTextLengthMismatch() => new(Strings.CipherTextLengthMismatch); + + internal static CryptographicException HashLengthMismatch() => new(Strings.HashLengthMismatch); + + internal static ArgumentException InvalidAKVPath(string masterKeyPath, bool isSystemOp) + { + string errorMessage = null == masterKeyPath ? Strings.NullAkvPath + : string.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvPathTemplate, masterKeyPath); + if (isSystemOp) + { + return new ArgumentNullException(Constants.AeParamMasterKeyPath, errorMessage); + } + + return new ArgumentException(errorMessage, Constants.AeParamMasterKeyPath); + } + + internal static ArgumentException InvalidAKVUrl(string masterKeyPath) => + new(string.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvUrlTemplate, masterKeyPath), Constants.AeParamMasterKeyPath); + + internal static Exception InvalidAKVUrlTrustedEndpoints(string masterKeyPath, string endpoints) => + new ArgumentException(string.Format(CultureInfo.InvariantCulture, Strings.InvalidAkvKeyPathTrustedTemplate, masterKeyPath, endpoints), + Constants.AeParamMasterKeyPath); + } +} diff --git a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props index a76d932f17..762c5f9ed8 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props +++ b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props @@ -5,38 +5,39 @@ + $(OS) + true + true Project $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true true + $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)')) - + + - $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)')) + net462 + netstandard2.0 + net6.0 + - - - - netcoreapp2.1 - - - - - - netcoreapp2.1 - netcoreapp3.1 - net46 + + + $(TargetNetFxVersion);$(TargetNetCoreVersion);$(TargetNetStandardVersion) + $(TargetNetCoreVersion);$(TargetNetStandardVersion) - + - $(Configuration.Split('-')[0]) + netstandard2.0;netstandard2.1 + net6.0 + net462 diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs deleted file mode 100644 index 2b5db7d447..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.NetCoreApp.cs +++ /dev/null @@ -1,89 +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. -// ------------------------------------------------------------------------------ -// Changes to this file must follow the http://aka.ms/api-review process. -// ------------------------------------------------------------------------------ - -namespace Microsoft.Data.SqlClient -{ - public partial class SqlDataReader : System.Data.Common.IDbColumnSchemaGenerator - { - /// - public System.Collections.ObjectModel.ReadOnlyCollection GetColumnSchema() { throw null; } - } - - /// - public enum PoolBlockingPeriod - { - /// - Auto = 0, - /// - AlwaysBlock = 1, - /// - NeverBlock = 2, - } - - /// - public sealed partial class SqlConnectionStringBuilder : System.Data.Common.DbConnectionStringBuilder - { - /// - [System.ComponentModel.DisplayNameAttribute("Pool Blocking Period")] - [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] - public PoolBlockingPeriod PoolBlockingPeriod { get { throw null; } set { } } - } -} - -namespace Microsoft.Data.SqlTypes -{ - /// - public sealed partial class SqlFileStream : System.IO.Stream - { - /// - public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access) { } - /// - public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access, System.IO.FileOptions options, System.Int64 allocationSize) { } - /// - public string Name { get { throw null; } } - /// - public byte[] TransactionContext { get { throw null; } } - /// - public override bool CanRead { get { throw null; } } - /// - public override bool CanSeek { get { throw null; } } - /// - public override bool CanTimeout { get { throw null; } } - /// - public override bool CanWrite { get { throw null; } } - /// - public override long Length { get { throw null; } } - /// - public override long Position { get { throw null; } set { throw null; } } - /// - public override int ReadTimeout { get { throw null; } } - /// - public override int WriteTimeout { get { throw null; } } - /// - public override void Flush() { } - /// - public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) { throw null; } - /// - public override int EndRead(System.IAsyncResult asyncResult) { throw null; } - /// - public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, System.Object state) { throw null; } - /// - public override void EndWrite(System.IAsyncResult asyncResult) { } - /// - public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; } - /// - public override void SetLength(long value) { throw null; } - /// - public override int Read(byte[] buffer, int offset, int count) { throw null; } - /// - public override int ReadByte() { throw null; } - /// - public override void Write(byte[] buffer, int offset, int count) { throw null; } - /// - public override void WriteByte(byte value) { } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 3cf24e9810..58dfd74e1e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. +// New attributes that are designed to work with Microsoft.Data.SqlClient and are publicly documented should be included in future. [assembly: System.CLSCompliant(true)] namespace Microsoft.Data { @@ -27,6 +29,68 @@ public SqlNotificationRequest(string userData, string options, int timeout) { } /// public string UserData { get { throw null; } set { } } } + + /// + public sealed class SqlDataSourceEnumerator : System.Data.Common.DbDataSourceEnumerator + { + /// + public static SqlDataSourceEnumerator Instance { get; } + /// + public override System.Data.DataTable GetDataSources() { throw null; } + } +} +namespace Microsoft.Data.SqlTypes +{ + /// + public sealed partial class SqlFileStream : System.IO.Stream + { + /// + public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access) { } + /// + public SqlFileStream(string path, byte[] transactionContext, System.IO.FileAccess access, System.IO.FileOptions options, System.Int64 allocationSize) { } + /// + public string Name { get { throw null; } } + /// + public byte[] TransactionContext { get { throw null; } } + /// + public override bool CanRead { get { throw null; } } + /// + public override bool CanSeek { get { throw null; } } + /// + public override bool CanTimeout { get { throw null; } } + /// + public override bool CanWrite { get { throw null; } } + /// + public override long Length { get { throw null; } } + /// + public override long Position { get { throw null; } set { throw null; } } + /// + public override int ReadTimeout { get { throw null; } } + /// + public override int WriteTimeout { get { throw null; } } + /// + public override void Flush() { } + /// + public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) { throw null; } + /// + public override int EndRead(System.IAsyncResult asyncResult) { throw null; } + /// + public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, System.Object state) { throw null; } + /// + public override void EndWrite(System.IAsyncResult asyncResult) { } + /// + public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; } + /// + public override void SetLength(long value) { throw null; } + /// + public override int Read(byte[] buffer, int offset, int count) { throw null; } + /// + public override int ReadByte() { throw null; } + /// + public override void Write(byte[] buffer, int offset, int count) { throw null; } + /// + public override void WriteByte(byte value) { } + } } namespace Microsoft.Data.SqlClient { @@ -62,6 +126,16 @@ public enum ApplicationIntent /// ReadWrite = 0 } + /// + public enum PoolBlockingPeriod + { + /// + Auto = 0, + /// + AlwaysBlock = 1, + /// + NeverBlock = 2, + } /// public delegate void OnChangeEventHandler(object sender, Microsoft.Data.SqlClient.SqlNotificationEventArgs e); /// @@ -99,16 +173,18 @@ public enum SqlAuthenticationMethod ActiveDirectoryManagedIdentity = 7, /// ActiveDirectoryMSI = 8, + /// + ActiveDirectoryDefault = 9, /// NotSpecified = 0, /// SqlPassword = 1 } /// - public partial class SqlAuthenticationParameters + public class SqlAuthenticationParameters { /// - protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId) { } + protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId, int connectionTimeout) { } /// public Microsoft.Data.SqlClient.SqlAuthenticationMethod AuthenticationMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } /// @@ -125,6 +201,8 @@ protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthentication public string ServerName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } /// public string UserId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + /// + public int ConnectionTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } /// public abstract partial class SqlAuthenticationProvider @@ -305,6 +383,7 @@ public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { } /// public new void RemoveAt(int index) { } } + /// [System.FlagsAttribute] public enum SqlBulkCopyOptions @@ -345,6 +424,22 @@ internal SqlClientFactory() { } /// public override System.Data.Common.DbParameter CreateParameter() { throw null; } } + /// + public partial class SqlClientLogger + { + /// + public SqlClientLogger() { } + /// + public bool IsLoggingEnabled { get { throw null; } } + /// + public void LogWarning(string type, string method, string message) { } + /// + public bool LogAssert(bool value, string type, string method, string message) { throw null; } + /// + public void LogError(string type, string method, string message) { } + /// + public void LogInfo(string type, string method, string message) { } + } /// public static partial class SqlClientMetaDataCollectionNames { @@ -376,8 +471,10 @@ public static partial class SqlClientMetaDataCollectionNames public static readonly string AllColumns; /// public static readonly string ColumnSetColumns; + /// + public static readonly string StructuredTypeMembers; } -#if NETCOREAPP || NETSTANDARD21_AND_ABOVE +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER /// public enum SqlConnectionAttestationProtocol { @@ -387,15 +484,56 @@ public enum SqlConnectionAttestationProtocol /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, /// HGS = 3 } #endif + /// + public enum SqlConnectionIPAddressPreference + { + /// + IPv4First = 0, // default + + /// + IPv6First = 1, + + /// + UsePlatformDefault = 2 + } + /// + public sealed class SqlConnectionEncryptOption + { + /// + public static SqlConnectionEncryptOption Parse(string value) => throw null; + /// + public static bool TryParse(string value, out SqlConnectionEncryptOption result) => throw null; + /// + public static SqlConnectionEncryptOption Optional => throw null; + + /// + public static SqlConnectionEncryptOption Mandatory => throw null; + + /// + public static SqlConnectionEncryptOption Strict => throw null; + + /// + public static implicit operator SqlConnectionEncryptOption(bool value) => throw null; + + /// + public static implicit operator bool(SqlConnectionEncryptOption value) => throw null; + + /// + public override string ToString() { throw null; } + + /// + public override bool Equals(object obj) { throw null; } + + /// + public override int GetHashCode() { throw null; } + } /// public partial class SqlColumnEncryptionCertificateStoreProvider : Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider { @@ -457,6 +595,8 @@ protected SqlColumnEncryptionKeyStoreProvider() { } public virtual byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { throw null; } /// public virtual bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { throw null; } + /// + public virtual System.TimeSpan? ColumnEncryptionKeyCacheTtl { get { throw null; } set { } } } /// public enum SqlCommandColumnEncryptionSetting @@ -472,7 +612,6 @@ public enum SqlCommandColumnEncryptionSetting } /// [System.ComponentModel.DefaultEventAttribute("RecordsAffected")] - [System.ComponentModel.DesignerAttribute("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] [System.ComponentModel.ToolboxItemAttribute(true)] public sealed partial class SqlCommand : System.Data.Common.DbCommand, System.ICloneable { @@ -492,7 +631,6 @@ public SqlCommand(string cmdText, Microsoft.Data.SqlClient.SqlConnection connect public Microsoft.Data.SqlClient.SqlCommandColumnEncryptionSetting ColumnEncryptionSetting { get { throw null; } } /// [System.ComponentModel.DefaultValueAttribute("")] - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public override string CommandText { get { throw null; } set { } } /// @@ -503,7 +641,6 @@ public SqlCommand(string cmdText, Microsoft.Data.SqlClient.SqlConnection connect public override System.Data.CommandType CommandType { get { throw null; } set { } } /// [System.ComponentModel.DefaultValueAttribute(null)] - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public new Microsoft.Data.SqlClient.SqlConnection Connection { get { throw null; } set { } } /// protected override System.Data.Common.DbConnection DbConnection { get { throw null; } set { } } @@ -517,6 +654,8 @@ public SqlCommand(string cmdText, Microsoft.Data.SqlClient.SqlConnection connect [System.ComponentModel.DesignOnlyAttribute(true)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool DesignTimeVisible { get { throw null; } set { } } + /// + public bool EnableOptimizedParameterBinding { get { throw null; } set { } } /// public new Microsoft.Data.SqlClient.SqlParameterCollection Parameters { get { throw null; } } /// @@ -595,9 +734,13 @@ public override void Prepare() { } [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] public Microsoft.Data.Sql.SqlNotificationRequest Notification { get { throw null; } set { } } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(System.Collections.Generic.IDictionary customProviders) { } /// [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Content)] public void ResetCommandTimeout() { } + /// + public SqlRetryLogicBaseProvider RetryLogicProvider { get { throw null; } set { } } } /// public sealed class SqlCommandBuilder : System.Data.Common.DbCommandBuilder @@ -685,6 +828,8 @@ public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCreden public static System.Collections.Generic.IDictionary> ColumnEncryptionTrustedMasterKeyPaths { get { throw null; } } /// public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections.Generic.IDictionary customProviders) { } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(System.Collections.Generic.IDictionary customProviders) { } /// [System.ComponentModel.BrowsableAttribute(false)] [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] @@ -696,7 +841,7 @@ public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections. /// /// for internal test only /// - [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] + [System.ComponentModel.DesignerSerializationVisibilityAttribute(0)] internal string SQLDNSCachingSupportedState { get { throw null; } } /// /// for internal test only @@ -710,7 +855,6 @@ public static void RegisterColumnEncryptionKeyStoreProviders(System.Collections. public int CommandTimeout { get { throw null; } } /// [System.ComponentModel.DefaultValueAttribute("")] - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] [System.ComponentModel.SettingsBindableAttribute(true)] public override string ConnectionString { get { throw null; } set { } } @@ -793,9 +937,10 @@ public void Open(SqlConnectionOverrides overrides) { } public void ResetStatistics() { } /// public System.Collections.IDictionary RetrieveStatistics() { throw null; } - /// public System.Collections.Generic.IDictionary RetrieveInternalInfo() { throw null; } + /// + public SqlRetryLogicBaseProvider RetryLogicProvider { get { throw null; } set { } } } /// public enum SqlConnectionColumnEncryptionSetting @@ -866,7 +1011,7 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Data Source")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string DataSource { get { throw null; } set { } } -#if NETCOREAPP || NETSTANDARD21_AND_ABOVE +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER /// [System.ComponentModel.DisplayNameAttribute("Attestation Protocol")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -876,10 +1021,23 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string EnclaveAttestationUrl { get { throw null; } set { } } #endif + /// + [System.ComponentModel.DisplayNameAttribute("IP Address Preference")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public Microsoft.Data.SqlClient.SqlConnectionIPAddressPreference IPAddressPreference { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Encrypt")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] - public bool Encrypt { get { throw null; } set { } } + public SqlConnectionEncryptOption Encrypt { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Host Name In Certificate")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string HostNameInCertificate { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Server Certificate")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string ServerCertificate { get { throw null; } set { } } + /// [System.ComponentModel.DisplayNameAttribute("Enlist")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -888,6 +1046,10 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Failover Partner")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string FailoverPartner { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Failover Partner SPN")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string FailoverPartnerSPN { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Initial Catalog")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -935,7 +1097,10 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Persist Security Info")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public bool PersistSecurityInfo { get { throw null; } set { } } - /// + /// + [System.ComponentModel.DisplayNameAttribute("Pool Blocking Period")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public PoolBlockingPeriod PoolBlockingPeriod { get { throw null; } set { } }/// [System.ComponentModel.DisplayNameAttribute("Pooling")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public bool Pooling { get { throw null; } set { } } @@ -943,6 +1108,10 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Replication")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public bool Replication { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Server SPN")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string ServerSPN { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Transaction Binding")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -992,8 +1161,6 @@ public SqlCredential(string userId, System.Security.SecureString password) { } } /// [System.ComponentModel.DefaultEventAttribute("RowUpdated")] - [System.ComponentModel.DesignerAttribute("Microsoft.VSDesigner.Data.VS.SqlDataAdapterDesigner, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] - [System.ComponentModel.ToolboxItemAttribute("Microsoft.VSDesigner.Data.VS.SqlDataAdapterToolboxItem, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public sealed partial class SqlDataAdapter : System.Data.Common.DbDataAdapter, System.Data.IDataAdapter, System.Data.IDbDataAdapter, System.ICloneable { /// @@ -1006,15 +1173,12 @@ public SqlDataAdapter(string selectCommandText, Microsoft.Data.SqlClient.SqlConn public SqlDataAdapter(string selectCommandText, string selectConnectionString) { } /// [System.ComponentModel.DefaultValueAttribute(null)] - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public new Microsoft.Data.SqlClient.SqlCommand DeleteCommand { get { throw null; } set { } } /// [System.ComponentModel.DefaultValueAttribute(null)] - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public new Microsoft.Data.SqlClient.SqlCommand InsertCommand { get { throw null; } set { } } /// [System.ComponentModel.DefaultValueAttribute(null)] - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public new Microsoft.Data.SqlClient.SqlCommand SelectCommand { get { throw null; } set { } } System.Data.IDbCommand System.Data.IDbDataAdapter.DeleteCommand { get { throw null; } set { } } System.Data.IDbCommand System.Data.IDbDataAdapter.InsertCommand { get { throw null; } set { } } @@ -1024,7 +1188,6 @@ public SqlDataAdapter(string selectCommandText, string selectConnectionString) { public override int UpdateBatchSize { get { throw null; } set { } } /// [System.ComponentModel.DefaultValueAttribute(null)] - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public new Microsoft.Data.SqlClient.SqlCommand UpdateCommand { get { throw null; } set { } } /// public event Microsoft.Data.SqlClient.SqlRowUpdatedEventHandler RowUpdated { add { } remove { } } @@ -1073,7 +1236,8 @@ public override void Close() { } public override char GetChar(int i) { throw null; } /// public override long GetChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) { throw null; } - /// + /// + public System.Collections.ObjectModel.ReadOnlyCollection GetColumnSchema() { throw null; }/// public override string GetDataTypeName(int i) { throw null; } /// public override System.DateTime GetDateTime(int i) { throw null; } @@ -1273,7 +1437,9 @@ internal SqlException() { } /// public byte State { get { throw null; } } /// +#if !NET6_0_OR_GREATER [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)] +#endif public override void GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { } /// public override string ToString() { throw null; } @@ -1472,7 +1638,6 @@ public void ResetSqlDbType() { } public override string ToString() { throw null; } } /// - [System.ComponentModel.EditorAttribute("Microsoft.VSDesigner.Data.Design.DBParametersEditor, Microsoft.VSDesigner, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] [System.ComponentModel.ListBindableAttribute(false)] public sealed partial class SqlParameterCollection : System.Data.Common.DbParameterCollection { @@ -1604,44 +1769,129 @@ protected override void Dispose(bool disposing) { } /// public override void Rollback() { } /// +#if NET6_0_OR_GREATER + public override void Rollback(string transactionName) { } +#else public void Rollback(string transactionName) { } +#endif + /// +#if NET6_0_OR_GREATER + public override void Save(string savePointName) { } +#else public void Save(string savePointName) { } +#endif + } + /// + public sealed class SqlRetryingEventArgs : System.EventArgs + { + /// + public SqlRetryingEventArgs(int retryCount, System.TimeSpan delay, System.Collections.Generic.IList exceptions) { } + /// + public int RetryCount { get { throw null; } } + /// + public System.TimeSpan Delay { get { throw null; } } + /// + public bool Cancel { get { throw null; } set { } } + /// + public System.Collections.Generic.IList Exceptions { get { throw null; } } + } + /// + public abstract class SqlRetryIntervalBaseEnumerator : System.Collections.Generic.IEnumerator, System.ICloneable + { + private readonly System.TimeSpan _minValue = System.TimeSpan.Zero; + private readonly System.TimeSpan _maxValue = System.TimeSpan.FromSeconds(120); + /// + public System.TimeSpan GapTimeInterval { get { throw null; } protected set { } } + /// + public System.TimeSpan MaxTimeInterval { get { throw null; } protected set { } } + /// + public System.TimeSpan MinTimeInterval { get { throw null; } protected set { } } + /// + public System.TimeSpan Current { get { throw null; } protected set { } } + object System.Collections.IEnumerator.Current { get { throw null; } } + /// + public SqlRetryIntervalBaseEnumerator() { } + /// + public SqlRetryIntervalBaseEnumerator(System.TimeSpan timeInterval, System.TimeSpan maxTime, System.TimeSpan minTime) { } + /// + public virtual void Reset() { } + /// + protected virtual void Validate(System.TimeSpan timeInterval, System.TimeSpan maxTimeInterval, System.TimeSpan minTimeInterval) { } + /// + protected abstract System.TimeSpan GetNextInterval(); + /// + public virtual bool MoveNext() { throw null; } + /// + public virtual void Dispose() { } + /// + public virtual object Clone() { throw null; } + } + /// + public abstract class SqlRetryLogicBase : System.ICloneable + { + /// + public int NumberOfTries { get { throw null; } protected set { } } + /// + public int Current { get { throw null; } protected set { } } + /// + public SqlRetryIntervalBaseEnumerator RetryIntervalEnumerator { get { throw null; } protected set { } } + /// + public System.Predicate TransientPredicate { get { throw null; } protected set { } } + /// + public virtual bool RetryCondition(object sender) { throw null; } + /// + public abstract bool TryNextInterval(out System.TimeSpan intervalTime); + /// + public abstract void Reset(); + /// + public virtual object Clone() { throw null; } + } + /// + public abstract class SqlRetryLogicBaseProvider + { + /// + public System.EventHandler Retrying { get { throw null; } set { } } + /// + public SqlRetryLogicBase RetryLogic { get { throw null; } protected set { } } + /// + public abstract TResult Execute(object sender, System.Func function); + /// + public abstract System.Threading.Tasks.Task ExecuteAsync(object sender, System.Func> function, System.Threading.CancellationToken cancellationToken = default); + /// + public abstract System.Threading.Tasks.Task ExecuteAsync(object sender, System.Func function, System.Threading.CancellationToken cancellationToken = default); + } + /// + public sealed class SqlRetryLogicOption + { + /// + public int NumberOfTries { get { throw null; } set { } } + /// + public System.TimeSpan DeltaTime { get { throw null; } set { } } + /// + public System.TimeSpan MinTimeInterval { get { throw null; } set { } } + /// + public System.TimeSpan MaxTimeInterval { get { throw null; } set { } } + /// + public System.Collections.Generic.IEnumerable TransientErrors { get { throw null; } set { } } + /// + public System.Predicate AuthorizedSqlCondition { get { throw null; } set { } } + } + /// + public sealed class SqlConfigurableRetryFactory + { + /// + public static SqlRetryLogicBaseProvider CreateExponentialRetryProvider(SqlRetryLogicOption retryLogicOption) { throw null; } + /// + public static SqlRetryLogicBaseProvider CreateIncrementalRetryProvider(SqlRetryLogicOption retryLogicOption) { throw null; } + /// + public static SqlRetryLogicBaseProvider CreateFixedRetryProvider(SqlRetryLogicOption retryLogicOption) { throw null; } + /// + public static SqlRetryLogicBaseProvider CreateNoneRetryProvider() { throw null; } } } namespace Microsoft.Data.SqlClient.Server { - /// - public enum DataAccessKind - { - /// - None = 0, - /// - Read = 1 - } - /// - public enum Format - { - /// - Unknown = 0, - /// - Native = 1, - /// - UserDefined = 2 - } - /// - public interface IBinarySerialize - { - /// - void Read(System.IO.BinaryReader r); - /// - void Write(System.IO.BinaryWriter w); - } - /// - public sealed partial class InvalidUdtException : System.SystemException - { - internal InvalidUdtException() { } - } /// public partial class SqlDataRecord : System.Data.IDataRecord { @@ -1812,44 +2062,6 @@ public virtual void SetValue(int ordinal, object value) { } /// public virtual int SetValues(params object[] values) { throw null; } } - /// - [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue | System.AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - public partial class SqlFacetAttribute : System.Attribute - { - /// - public SqlFacetAttribute() { } - /// - public bool IsFixedLength { get { throw null; } set { } } - /// - public bool IsNullable { get { throw null; } set { } } - /// - public int MaxSize { get { throw null; } set { } } - /// - public int Precision { get { throw null; } set { } } - /// - public int Scale { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] - public partial class SqlFunctionAttribute : System.Attribute - { - /// - public SqlFunctionAttribute() { } - /// - public bool IsDeterministic { get { throw null; } set { } } - /// - public DataAccessKind DataAccess { get { throw null; } set { } } - /// - public SystemDataAccessKind SystemDataAccess { get { throw null; } set { } } - /// - public bool IsPrecise { get { throw null; } set { } } - /// - public string Name { get { throw null; } set { } } - /// - public string TableDefinition { get { throw null; } set { } } - /// - public string FillRowMethodName { get { throw null; } set { } } - } /// public sealed partial class SqlMetaData { @@ -1988,69 +2200,6 @@ public SqlMetaData(string name, System.Data.SqlDbType dbType, System.Type userDe /// public static Microsoft.Data.SqlClient.Server.SqlMetaData InferFromValue(object value, string name) { throw null; } } - /// - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] - public sealed partial class SqlMethodAttribute : SqlFunctionAttribute - { - /// - public SqlMethodAttribute() { } - /// - public bool OnNullCall { get { throw null; } set { } } - /// - public bool IsMutator { get { throw null; } set { } } - /// - public bool InvokeIfReceiverIsNull { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] - public sealed partial class SqlUserDefinedAggregateAttribute : System.Attribute - { - /// - public const int MaxByteSizeValue = 8000; - /// - public SqlUserDefinedAggregateAttribute(Format format) { } - /// - public int MaxByteSize { get { throw null; } set { } } - /// - public bool IsInvariantToDuplicates { get { throw null; } set { } } - /// - public bool IsInvariantToNulls { get { throw null; } set { } } - /// - public bool IsInvariantToOrder { get { throw null; } set { } } - /// - public bool IsNullIfEmpty { get { throw null; } set { } } - /// - public Format Format { get { throw null; } } - /// - public string Name { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] - public sealed partial class SqlUserDefinedTypeAttribute : System.Attribute - { - /// - public SqlUserDefinedTypeAttribute(Format format) { } - /// - public int MaxByteSize { get { throw null; } set { } } - /// - public bool IsFixedLength { get { throw null; } set { } } - /// - public bool IsByteOrdered { get { throw null; } set { } } - /// - public Format Format { get { throw null; } } - /// - public string ValidationMethodName { get { throw null; } set { } } - /// - public string Name { get { throw null; } set { } } - } - /// - public enum SystemDataAccessKind - { - /// - None = 0, - /// - Read = 1 - } } namespace Microsoft.Data.SqlClient.DataClassification { 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 9a6db6b167..41e1263abc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj @@ -1,29 +1,22 @@  false - netcoreapp2.1;netcoreapp3.1;netstandard2.0;netstandard2.1 + net6.0;netstandard2.0;netstandard2.1 netstandard2.1 $(ObjFolder)$(Configuration)\$(AssemblyName)\ref\ $(BinFolder)$(Configuration)\$(AssemblyName)\ref\ $(OutputPath)\$(TargetFramework)\Microsoft.Data.SqlClient.xml Core $(BaseProduct) - Debug;Release;netcoreapp2.1-Debug;netcoreapp2.1-Release;netcoreapp3.1-Debug;netcoreapp3.1-Release - netcoreapp - netstandard + Debug;Release; + netcoreapp + netstandard AnyCPU;x64;x86 - - $(DefineConstants);NETSTANDARD21_AND_ABOVE - - - $(DefineConstants);NETCOREAPP - - @@ -32,4 +25,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/.editorconfig b/src/Microsoft.Data.SqlClient/netcore/src/.editorconfig new file mode 100644 index 0000000000..ecc808aa66 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/.editorconfig @@ -0,0 +1,12 @@ +# editorconfig.org + +# top-most EditorConfig file +root = false + +[*.cs] + +# IDE0090: Use 'new(...)' +csharp_style_implicit_object_creation_when_type_is_apparent = false + +# IDE0063: Use simple 'using' statement +csharp_prefer_simple_using_statement = false diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.cs deleted file mode 100644 index 33f0a6168a..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.cs +++ /dev/null @@ -1,46 +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. - -using System; -using System.IO; -using System.Reflection; -using System.Collections.Generic; -using Microsoft.Xunit.Performance.Api; - -public class PerfHarness -{ - public static int Main(string[] args) - { - try - { - using (XunitPerformanceHarness harness = new XunitPerformanceHarness(args)) - { - foreach(var testName in GetTestAssemblies()) - { - harness.RunBenchmarks(GetTestAssembly(testName)); - } - } - - return 0; - } - catch (Exception ex) - { - Console.WriteLine("[ERROR] Benchmark execution failed."); - Console.WriteLine($" {ex.ToString()}"); - return 1; - } - } - - private static string GetTestAssembly(string testName) - { - // Assume test assemblies are colocated/restored next to the PerfHarness. - return Path.Combine( - Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), testName); - } - - private static IEnumerable GetTestAssemblies() - { - return Directory.EnumerateFiles(".", "*.Performance.Tests.dll"); - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.csproj deleted file mode 100644 index 0d6a6a78be..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - {F5E941C8-AF2F-47AB-A066-FF25470CE382} - Exe - PerfRunner - PerfRunner - true - false - 0436 - true - netstandard-Debug;netstandard-Release;netstandard1.3-Debug;netstandard1.3-Release - - - - - - - - Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs - - - - - PreserveNewest - - - \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.runtimeconfig.json b/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.runtimeconfig.json deleted file mode 100644 index 0e7c179be9..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/perf/PerfRunner/PerfRunner.runtimeconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "runtimeOptions": { - "framework": { - "name": "Microsoft.NETCore.App", - "version": "9.9.9" - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs index 2defe1a735..b08b41aeb2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssApiException.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Data; using System; using System.Runtime.InteropServices; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs index 80a32e6c5a..0f479a8c62 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.GssBuffer.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.Data; using System; using System.Diagnostics; using System.Runtime.InteropServices; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs index e3e3e3759b..489d1cf8a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs @@ -11,49 +11,52 @@ internal static partial class Interop { internal static partial class NetSecurityNative { - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseGssBuffer")] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_ReleaseGssBuffer")] internal static extern void ReleaseGssBuffer( IntPtr bufferPtr, ulong length); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DisplayMinorStatus")] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_EnsureGssInitialized")] + private static extern int EnsureGssInitialized(); + + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_DisplayMinorStatus")] internal static extern Status DisplayMinorStatus( out Status minorStatus, Status statusValue, ref GssBuffer buffer); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DisplayMajorStatus")] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_DisplayMajorStatus")] internal static extern Status DisplayMajorStatus( out Status minorStatus, Status statusValue, ref GssBuffer buffer); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ImportUserName", CharSet = CharSet.Unicode)] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_ImportUserName")] internal static extern Status ImportUserName( out Status minorStatus, string inputName, int inputNameByteCount, out SafeGssNameHandle outputName); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ImportPrincipalName", CharSet = CharSet.Unicode)] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_ImportPrincipalName")] internal static extern Status ImportPrincipalName( out Status minorStatus, string inputName, int inputNameByteCount, out SafeGssNameHandle outputName); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseName")] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_ReleaseName")] internal static extern Status ReleaseName( out Status minorStatus, ref IntPtr inputName); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredSpNego")] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_InitiateCredSpNego")] internal static extern Status InitiateCredSpNego( out Status minorStatus, SafeGssNameHandle desiredName, out SafeGssCredHandle outputCredHandle); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitiateCredWithPassword", CharSet = CharSet.Unicode)] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_InitiateCredWithPassword")] internal static extern Status InitiateCredWithPassword( out Status minorStatus, bool isNtlm, @@ -62,12 +65,12 @@ internal static extern Status InitiateCredWithPassword( int passwordLen, out SafeGssCredHandle outputCredHandle); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseCred")] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_ReleaseCred")] internal static extern Status ReleaseCred( out Status minorStatus, ref IntPtr credHandle); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_InitSecContext")] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_InitSecContext")] internal static extern Status InitSecContext( out Status minorStatus, SafeGssCredHandle initiatorCredHandle, @@ -81,7 +84,7 @@ internal static extern Status InitSecContext( out uint retFlags, out int isNtlmUsed); - [DllImport(Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DeleteSecContext")] + [DllImport(Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_DeleteSecContext")] internal static extern Status DeleteSecContext( out Status minorStatus, ref IntPtr contextHandle); @@ -109,5 +112,17 @@ internal enum GssFlags : uint GSS_C_EXTENDED_ERROR_FLAG = 0x4000, GSS_C_DELEG_POLICY_FLAG = 0x8000 } + + // This constructor is added to address the issue with net6 regarding + // Shim gss api on Linux to delay loading libgssapi_krb5.so + // issue https://github.com/dotnet/SqlClient/issues/1390 + // dotnet runtime issue https://github.com/dotnet/runtime/pull/55037 + static NetSecurityNative() + { + if (Environment.Version.Major >= 6) + { + EnsureGssInitialized(); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIAuthType.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIAuthType.cs index cfec6e4e44..cdb3819605 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIAuthType.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIAuthType.cs @@ -4,6 +4,7 @@ using System.Net.Security; using System.Runtime.InteropServices; +using Microsoft.Data; namespace System.Net { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPISecureChannelType.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPISecureChannelType.cs index 1f0f472d40..4152a89a7d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPISecureChannelType.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPISecureChannelType.cs @@ -4,6 +4,7 @@ using System.Net.Security; using System.Runtime.InteropServices; +using Microsoft.Data; namespace System.Net { @@ -129,7 +130,7 @@ public unsafe int QueryContextAttributes(SafeDeleteContext phContext, Interop.Ss } else { - throw new ArgumentException(System.StringsHelper.Format(Strings.SSPIInvalidHandleType, handleType.FullName), nameof(handleType)); + throw new ArgumentException(StringsHelper.Format(Strings.SSPIInvalidHandleType, handleType.FullName), nameof(handleType)); } } fixed (byte* bufferPtr = buffer) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs index ea8e713529..f8231f9069 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Windows/sspicli/SSPIWrapper.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Net.Security; using System.Runtime.InteropServices; +using Microsoft.Data; namespace System.Net { @@ -101,7 +102,7 @@ public static SafeFreeCredentials AcquireDefaultCredential(SSPIInterface secModu if (errorCode != 0) { if (NetEventSource.IsEnabled) - NetEventSource.Error(null, System.StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(AcquireDefaultCredential), $"0x{errorCode:X}")); + NetEventSource.Error(null, StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(AcquireDefaultCredential), $"0x{errorCode:X}")); throw new Win32Exception(errorCode); } return outCredential; @@ -118,7 +119,7 @@ public static SafeFreeCredentials AcquireCredentialsHandle(SSPIInterface secModu if (errorCode != 0) { if (NetEventSource.IsEnabled) - NetEventSource.Error(null, System.StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}")); + NetEventSource.Error(null, StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}")); throw new Win32Exception(errorCode); } @@ -143,7 +144,7 @@ public static SafeFreeCredentials AcquireCredentialsHandle(SSPIInterface secModu if (errorCode != 0) { if (NetEventSource.IsEnabled) - NetEventSource.Error(null, System.StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}")); + NetEventSource.Error(null, StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}")); throw new Win32Exception(errorCode); } @@ -359,11 +360,11 @@ private static unsafe int EncryptDecryptHelper(OP op, SSPIInterface secModule, S { if (errorCode == Interop.SspiCli.SEC_I_RENEGOTIATE) { - NetEventSource.Error(null, System.StringsHelper.Format(Strings.event_OperationReturnedSomething, op, "SEC_I_RENEGOTIATE")); + NetEventSource.Error(null, StringsHelper.Format(Strings.event_OperationReturnedSomething, op, "SEC_I_RENEGOTIATE")); } else { - NetEventSource.Error(null, System.StringsHelper.Format(Strings.net_log_operation_failed_with_error, op, $"0x{0:X}")); + NetEventSource.Error(null, StringsHelper.Format(Strings.net_log_operation_failed_with_error, op, $"0x{0:X}")); } } @@ -466,7 +467,7 @@ public static object QueryContextAttributes(SSPIInterface secModule, SafeDeleteC break; default: - throw new ArgumentException(System.StringsHelper.Format(Strings.net_invalid_enum, nameof(contextAttribute)), nameof(contextAttribute)); + throw new ArgumentException(StringsHelper.Format(Strings.net_invalid_enum, nameof(contextAttribute)), nameof(contextAttribute)); } SafeHandle sspiHandle = null; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.Drivers.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.Drivers.cs deleted file mode 100644 index 8647b4f81c..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.Drivers.cs +++ /dev/null @@ -1,35 +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. - -using System.Threading; - -namespace Microsoft.Data.Common -{ - internal static partial class ADP - { - - internal static Timer UnsafeCreateTimer(TimerCallback callback, object state, int dueTime, int period) - { - // Don't capture the current ExecutionContext and its AsyncLocals onto - // a global timer causing them to live forever - bool restoreFlow = false; - try - { - if (!ExecutionContext.IsFlowSuppressed()) - { - ExecutionContext.SuppressFlow(); - restoreFlow = true; - } - - return new Timer(callback, state, dueTime, period); - } - finally - { - // Restore the current ExecutionContext - if (restoreFlow) - ExecutionContext.RestoreFlow(); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs deleted file mode 100644 index 396d43caa6..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/AdapterUtil.cs +++ /dev/null @@ -1,531 +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. - -using System; -using System.Collections; -using System.Data; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.Runtime.CompilerServices; -using System.Security; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Transactions; -using Microsoft.Data.SqlClient; - -namespace Microsoft.Data.Common -{ - internal static partial class ADP - { - // NOTE: Initializing a Task in SQL CLR requires the "UNSAFE" permission set (http://msdn.microsoft.com/en-us/library/ms172338.aspx) - // Therefore we are lazily initializing these Tasks to avoid forcing customers to use the "UNSAFE" set when they are actually using no Async features - private static Task _trueTask; - internal static Task TrueTask => _trueTask ?? (_trueTask = Task.FromResult(true)); - - private static Task _falseTask; - internal static Task FalseTask => _falseTask ?? (_falseTask = Task.FromResult(false)); - - internal const CompareOptions DefaultCompareOptions = CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase; - - internal const int DefaultConnectionTimeout = DbConnectionStringDefaults.ConnectTimeout; - internal const int InfiniteConnectionTimeout = 0; // infinite connection timeout identifier in seconds - internal const int MaxBufferAccessTokenExpiry = 600; // max duration for buffer in seconds - - static private void TraceException(string trace, Exception e) - { - Debug.Assert(null != e, "TraceException: null Exception"); - if (null != e) - { - SqlClientEventSource.Log.TryTraceEvent(trace, e); - } - } - - internal static void TraceExceptionAsReturnValue(Exception e) - { - TraceException(" '{0}'", e); - } - - internal static void TraceExceptionWithoutRethrow(Exception e) - { - Debug.Assert(ADP.IsCatchableExceptionType(e), "Invalid exception type, should have been re-thrown!"); - TraceException(" '{0}'", e); - } - - internal static ArgumentException Argument(string error) - { - ArgumentException e = new ArgumentException(error); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentException Argument(string error, Exception inner) - { - ArgumentException e = new ArgumentException(error, inner); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentException Argument(string error, string parameter) - { - ArgumentException e = new ArgumentException(error, parameter); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentNullException ArgumentNull(string parameter) - { - ArgumentNullException e = new ArgumentNullException(parameter); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentNullException ArgumentNull(string parameter, string error) - { - ArgumentNullException e = new ArgumentNullException(parameter, error); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentOutOfRangeException ArgumentOutOfRange(string parameterName) - { - ArgumentOutOfRangeException e = new ArgumentOutOfRangeException(parameterName); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentOutOfRangeException ArgumentOutOfRange(string message, string parameterName) - { - ArgumentOutOfRangeException e = new ArgumentOutOfRangeException(parameterName, message); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static IndexOutOfRangeException IndexOutOfRange(string error) - { - IndexOutOfRangeException e = new IndexOutOfRangeException(error); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static InvalidCastException InvalidCast(string error) - { - return InvalidCast(error, null); - } - - internal static InvalidCastException InvalidCast(string error, Exception inner) - { - InvalidCastException e = new InvalidCastException(error, inner); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static InvalidOperationException InvalidOperation(string error) - { - InvalidOperationException e = new InvalidOperationException(error); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static NotSupportedException NotSupported() - { - NotSupportedException e = new NotSupportedException(); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static NotSupportedException NotSupported(string error) - { - NotSupportedException e = new NotSupportedException(error); - TraceExceptionAsReturnValue(e); - return e; - } - - // the return value is true if the string was quoted and false if it was not - // this allows the caller to determine if it is an error or not for the quotedString to not be quoted - internal static bool RemoveStringQuotes(string quotePrefix, string quoteSuffix, string quotedString, out string unquotedString) - { - int prefixLength = quotePrefix != null ? quotePrefix.Length : 0; - int suffixLength = quoteSuffix != null ? quoteSuffix.Length : 0; - - if ((suffixLength + prefixLength) == 0) - { - unquotedString = quotedString; - return true; - } - - if (quotedString == null) - { - unquotedString = quotedString; - return false; - } - - int quotedStringLength = quotedString.Length; - - // is the source string too short to be quoted - if (quotedStringLength < prefixLength + suffixLength) - { - unquotedString = quotedString; - return false; - } - - // is the prefix present? - if (prefixLength > 0) - { - if (!quotedString.StartsWith(quotePrefix, StringComparison.Ordinal)) - { - unquotedString = quotedString; - return false; - } - } - - // is the suffix present? - if (suffixLength > 0) - { - if (!quotedString.EndsWith(quoteSuffix, StringComparison.Ordinal)) - { - unquotedString = quotedString; - return false; - } - unquotedString = quotedString.Substring(prefixLength, quotedStringLength - (prefixLength + suffixLength)).Replace(quoteSuffix + quoteSuffix, quoteSuffix); - } - else - { - unquotedString = quotedString.Substring(prefixLength, quotedStringLength - prefixLength); - } - return true; - } - - internal static ArgumentOutOfRangeException NotSupportedEnumerationValue(Type type, string value, string method) - { - return ArgumentOutOfRange(System.StringsHelper.Format(Strings.ADP_NotSupportedEnumerationValue, type.Name, value, method), type.Name); - } - - internal static InvalidOperationException DataAdapter(string error) - { - return InvalidOperation(error); - } - - private static InvalidOperationException Provider(string error) - { - return InvalidOperation(error); - } - - internal static ArgumentException InvalidMultipartName(string property, string value) - { - ArgumentException e = new ArgumentException(System.StringsHelper.Format(Strings.ADP_InvalidMultipartName, property, value)); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentException InvalidMultipartNameIncorrectUsageOfQuotes(string property, string value) - { - ArgumentException e = new ArgumentException(System.StringsHelper.Format(Strings.ADP_InvalidMultipartNameQuoteUsage, property, value)); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentException InvalidMultipartNameToManyParts(string property, string value, int limit) - { - ArgumentException e = new ArgumentException(System.StringsHelper.Format(Strings.ADP_InvalidMultipartNameToManyParts, property, value, limit)); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static void CheckArgumentNull(object value, string parameterName) - { - if (null == value) - { - throw ArgumentNull(parameterName); - } - } - - // only StackOverflowException & ThreadAbortException are sealed classes - private static readonly Type s_stackOverflowType = typeof(StackOverflowException); - private static readonly Type s_outOfMemoryType = typeof(OutOfMemoryException); - private static readonly Type s_threadAbortType = typeof(ThreadAbortException); - private static readonly Type s_nullReferenceType = typeof(NullReferenceException); - private static readonly Type s_accessViolationType = typeof(AccessViolationException); - private static readonly Type s_securityType = typeof(SecurityException); - - internal static bool IsCatchableExceptionType(Exception e) - { - // a 'catchable' exception is defined by what it is not. - Debug.Assert(e != null, "Unexpected null exception!"); - Type type = e.GetType(); - - return ((type != s_stackOverflowType) && - (type != s_outOfMemoryType) && - (type != s_threadAbortType) && - (type != s_nullReferenceType) && - (type != s_accessViolationType) && - !s_securityType.IsAssignableFrom(type)); - } - - internal static bool IsCatchableOrSecurityExceptionType(Exception e) - { - // a 'catchable' exception is defined by what it is not. - // since IsCatchableExceptionType defined SecurityException as not 'catchable' - // this method will return true for SecurityException has being catchable. - - // the other way to write this method is, but then SecurityException is checked twice - // return ((e is SecurityException) || IsCatchableExceptionType(e)); - - Debug.Assert(e != null, "Unexpected null exception!"); - Type type = e.GetType(); - - return ((type != s_stackOverflowType) && - (type != s_outOfMemoryType) && - (type != s_threadAbortType) && - (type != s_nullReferenceType) && - (type != s_accessViolationType)); - } - - // Invalid Enumeration - internal static ArgumentOutOfRangeException InvalidEnumerationValue(Type type, int value) - { - return ArgumentOutOfRange(System.StringsHelper.Format(Strings.ADP_InvalidEnumerationValue, type.Name, value.ToString(CultureInfo.InvariantCulture)), type.Name); - } - - // - // DbConnectionOptions, DataAccess - // - internal static ArgumentException ConnectionStringSyntax(int index) - { - return Argument(System.StringsHelper.Format(Strings.ADP_ConnectionStringSyntax, index)); - } - internal static ArgumentException KeywordNotSupported(string keyword) - { - return Argument(System.StringsHelper.Format(Strings.ADP_KeywordNotSupported, keyword)); - } - internal static ArgumentException ConvertFailed(Type fromType, Type toType, Exception innerException) - { - return ADP.Argument(System.StringsHelper.Format(Strings.SqlConvert_ConvertFailed, fromType.FullName, toType.FullName), innerException); - } - - // - // DbConnectionOptions, DataAccess, SqlClient - // - internal static Exception InvalidConnectionOptionValue(string key) - { - return InvalidConnectionOptionValue(key, null); - } - internal static Exception InvalidConnectionOptionValue(string key, Exception inner) - { - return Argument(System.StringsHelper.Format(Strings.ADP_InvalidConnectionOptionValue, key), inner); - } - static internal InvalidOperationException InvalidDataDirectory() - { - InvalidOperationException e = new InvalidOperationException(Strings.ADP_InvalidDataDirectory); - return e; - } - - // - // Generic Data Provider Collection - // - internal static ArgumentException CollectionRemoveInvalidObject(Type itemType, ICollection collection) - { - return Argument(System.StringsHelper.Format(Strings.ADP_CollectionRemoveInvalidObject, itemType.Name, collection.GetType().Name)); - } - internal static ArgumentNullException CollectionNullValue(string parameter, Type collection, Type itemType) - { - return ArgumentNull(parameter, System.StringsHelper.Format(Strings.ADP_CollectionNullValue, collection.Name, itemType.Name)); - } - internal static IndexOutOfRangeException CollectionIndexInt32(int index, Type collection, int count) - { - return IndexOutOfRange(System.StringsHelper.Format(Strings.ADP_CollectionIndexInt32, index.ToString(CultureInfo.InvariantCulture), collection.Name, count.ToString(CultureInfo.InvariantCulture))); - } - internal static IndexOutOfRangeException CollectionIndexString(Type itemType, string propertyName, string propertyValue, Type collection) - { - return IndexOutOfRange(System.StringsHelper.Format(Strings.ADP_CollectionIndexString, itemType.Name, propertyName, propertyValue, collection.Name)); - } - internal static InvalidCastException CollectionInvalidType(Type collection, Type itemType, object invalidValue) - { - return InvalidCast(System.StringsHelper.Format(Strings.ADP_CollectionInvalidType, collection.Name, itemType.Name, invalidValue.GetType().Name)); - } - - // - // DbConnection - // - private static string ConnectionStateMsg(ConnectionState state) - { - switch (state) - { - case (ConnectionState.Closed): - case (ConnectionState.Connecting | ConnectionState.Broken): // treated the same as closed - return Strings.ADP_ConnectionStateMsg_Closed; - case (ConnectionState.Connecting): - return Strings.ADP_ConnectionStateMsg_Connecting; - case (ConnectionState.Open): - return Strings.ADP_ConnectionStateMsg_Open; - case (ConnectionState.Open | ConnectionState.Executing): - return Strings.ADP_ConnectionStateMsg_OpenExecuting; - case (ConnectionState.Open | ConnectionState.Fetching): - return Strings.ADP_ConnectionStateMsg_OpenFetching; - default: - return System.StringsHelper.Format(Strings.ADP_ConnectionStateMsg, state.ToString()); - } - } - - // - // : Stream - // - internal static Exception StreamClosed([CallerMemberName] string method = "") - { - return InvalidOperation(System.StringsHelper.Format(Strings.ADP_StreamClosed, method)); - } - - internal static string BuildQuotedString(string quotePrefix, string quoteSuffix, string unQuotedString) - { - var resultString = new StringBuilder(); - if (!string.IsNullOrEmpty(quotePrefix)) - { - resultString.Append(quotePrefix); - } - - // Assuming that the suffix is escaped by doubling it. i.e. foo"bar becomes "foo""bar". - if (!string.IsNullOrEmpty(quoteSuffix)) - { - resultString.Append(unQuotedString.Replace(quoteSuffix, quoteSuffix + quoteSuffix)); - resultString.Append(quoteSuffix); - } - else - { - resultString.Append(unQuotedString); - } - - return resultString.ToString(); - } - - static internal string BuildMultiPartName(string[] strings) - { - StringBuilder bld = new StringBuilder(); - // Assume we want to build a full multi-part name with all parts except trimming separators for - // leading empty names (null or empty strings, but not whitespace). Separators in the middle - // should be added, even if the name part is null/empty, to maintain proper location of the parts. - for (int i = 0; i < strings.Length; i++) - { - if (0 < bld.Length) - { - bld.Append('.'); - } - if (null != strings[i] && 0 != strings[i].Length) - { - bld.Append(BuildQuotedString("[", "]", strings[i])); - } - } - return bld.ToString(); - } - - // - // Generic Data Provider Collection - // - internal static ArgumentException ParametersIsNotParent(Type parameterType, ICollection collection) - { - return Argument(System.StringsHelper.Format(Strings.ADP_CollectionIsNotParent, parameterType.Name, collection.GetType().Name)); - } - internal static ArgumentException ParametersIsParent(Type parameterType, ICollection collection) - { - return Argument(System.StringsHelper.Format(Strings.ADP_CollectionIsNotParent, parameterType.Name, collection.GetType().Name)); - } - - - internal enum InternalErrorCode - { - UnpooledObjectHasOwner = 0, - UnpooledObjectHasWrongOwner = 1, - PushingObjectSecondTime = 2, - PooledObjectHasOwner = 3, - PooledObjectInPoolMoreThanOnce = 4, - CreateObjectReturnedNull = 5, - NewObjectCannotBePooled = 6, - NonPooledObjectUsedMoreThanOnce = 7, - AttemptingToPoolOnRestrictedToken = 8, - // ConnectionOptionsInUse = 9, - ConvertSidToStringSidWReturnedNull = 10, - // UnexpectedTransactedObject = 11, - AttemptingToConstructReferenceCollectionOnStaticObject = 12, - AttemptingToEnlistTwice = 13, - CreateReferenceCollectionReturnedNull = 14, - PooledObjectWithoutPool = 15, - UnexpectedWaitAnyResult = 16, - SynchronousConnectReturnedPending = 17, - CompletedConnectReturnedPending = 18, - - NameValuePairNext = 20, - InvalidParserState1 = 21, - InvalidParserState2 = 22, - InvalidParserState3 = 23, - - InvalidBuffer = 30, - - UnimplementedSMIMethod = 40, - InvalidSmiCall = 41, - - SqlDependencyObtainProcessDispatcherFailureObjectHandle = 50, - SqlDependencyProcessDispatcherFailureCreateInstance = 51, - SqlDependencyProcessDispatcherFailureAppDomain = 52, - SqlDependencyCommandHashIsNotAssociatedWithNotification = 53, - - UnknownTransactionFailure = 60, - } - - internal static Exception InternalError(InternalErrorCode internalError) - { - return InvalidOperation(System.StringsHelper.Format(Strings.ADP_InternalProviderError, (int)internalError)); - } - - // - // : DbDataReader - // - internal static Exception DataReaderClosed([CallerMemberName] string method = "") - { - return InvalidOperation(System.StringsHelper.Format(Strings.ADP_DataReaderClosed, method)); - } - internal static ArgumentOutOfRangeException InvalidSourceBufferIndex(int maxLen, long srcOffset, string parameterName) - { - return ArgumentOutOfRange(System.StringsHelper.Format(Strings.ADP_InvalidSourceBufferIndex, maxLen.ToString(CultureInfo.InvariantCulture), srcOffset.ToString(CultureInfo.InvariantCulture)), parameterName); - } - internal static ArgumentOutOfRangeException InvalidDestinationBufferIndex(int maxLen, int dstOffset, string parameterName) - { - return ArgumentOutOfRange(System.StringsHelper.Format(Strings.ADP_InvalidDestinationBufferIndex, maxLen.ToString(CultureInfo.InvariantCulture), dstOffset.ToString(CultureInfo.InvariantCulture)), parameterName); - } - internal static IndexOutOfRangeException InvalidBufferSizeOrIndex(int numBytes, int bufferIndex) - { - return IndexOutOfRange(System.StringsHelper.Format(Strings.SQL_InvalidBufferSizeOrIndex, numBytes.ToString(CultureInfo.InvariantCulture), bufferIndex.ToString(CultureInfo.InvariantCulture))); - } - internal static Exception InvalidDataLength(long length) - { - return IndexOutOfRange(System.StringsHelper.Format(Strings.SQL_InvalidDataLength, length.ToString(CultureInfo.InvariantCulture))); - } - - internal static bool CompareInsensitiveInvariant(string strvalue, string strconst) => - 0 == CultureInfo.InvariantCulture.CompareInfo.Compare(strvalue, strconst, CompareOptions.IgnoreCase); - - internal static int DstCompare(string strA, string strB) => CultureInfo.CurrentCulture.CompareInfo.Compare(strA, strB, ADP.DefaultCompareOptions); - - internal static bool IsEmptyArray(string[] array) => (null == array) || (0 == array.Length); - - internal static bool IsNull(object value) - { - if ((null == value) || (DBNull.Value == value)) - { - return true; - } - INullable nullable = (value as INullable); - return ((null != nullable) && nullable.IsNull); - } - - internal static Exception InvalidSeekOrigin(string parameterName) - { - return ArgumentOutOfRange(Strings.ADP_InvalidSeekOrigin, parameterName); - } - - internal static void SetCurrentTransaction(Transaction transaction) - { - Transaction.Current = transaction; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/BasicFieldNameLookup.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/BasicFieldNameLookup.cs deleted file mode 100644 index 6d5b5b8891..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/BasicFieldNameLookup.cs +++ /dev/null @@ -1,142 +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. - -using System.Collections.Generic; -using System.Data; -using System.Globalization; -using Microsoft.Data.Common; - -namespace Microsoft.Data.ProviderBase -{ - internal class BasicFieldNameLookup - { - // Dictionary stores the index into the _fieldNames, match via case-sensitive - private Dictionary _fieldNameLookup; - - // original names for linear searches when exact matches fail - private readonly string[] _fieldNames; - - // By default _compareInfo is set to InvariantCulture CompareInfo - private CompareInfo _compareInfo; - - public BasicFieldNameLookup(string[] fieldNames) - { - if (null == fieldNames) - { - throw ADP.ArgumentNull(nameof(fieldNames)); - } - _fieldNames = fieldNames; - } - - public BasicFieldNameLookup(System.Collections.ObjectModel.ReadOnlyCollection columnNames) - { - int length = columnNames.Count; - string[] fieldNames = new string[length]; - for (int i = 0; i < length; ++i) - { - fieldNames[i] = columnNames[i]; - } - _fieldNames = fieldNames; - GenerateLookup(); - } - - public BasicFieldNameLookup(IDataReader reader) - { - int length = reader.FieldCount; - string[] fieldNames = new string[length]; - for (int i = 0; i < length; ++i) - { - fieldNames[i] = reader.GetName(i); - } - _fieldNames = fieldNames; - } - - public int GetOrdinal(string fieldName) - { - if (null == fieldName) - { - throw ADP.ArgumentNull(nameof(fieldName)); - } - int index = IndexOf(fieldName); - if (-1 == index) - { - throw ADP.IndexOutOfRange(fieldName); - } - return index; - } - - public int IndexOfName(string fieldName) - { - if (null == _fieldNameLookup) - { - GenerateLookup(); - } - - int value; - // via case sensitive search, first match with lowest ordinal matches - return _fieldNameLookup.TryGetValue(fieldName, out value) ? value : -1; - } - - public int IndexOf(string fieldName) - { - if (null == _fieldNameLookup) - { - GenerateLookup(); - } - int index; - // via case sensitive search, first match with lowest ordinal matches - if (!_fieldNameLookup.TryGetValue(fieldName, out index)) - { - // via case insensitive search, first match with lowest ordinal matches - index = LinearIndexOf(fieldName, CompareOptions.IgnoreCase); - if (-1 == index) - { - // do the slow search now (kana, width insensitive comparison) - index = LinearIndexOf(fieldName, ADP.DefaultCompareOptions); - } - } - - return index; - } - - protected virtual CompareInfo GetCompareInfo() - { - return CultureInfo.InvariantCulture.CompareInfo; - } - - private int LinearIndexOf(string fieldName, CompareOptions compareOptions) - { - if (null == _compareInfo) - { - _compareInfo = GetCompareInfo(); - } - - int length = _fieldNames.Length; - for (int i = 0; i < length; ++i) - { - if (0 == _compareInfo.Compare(fieldName, _fieldNames[i], compareOptions)) - { - _fieldNameLookup[fieldName] = i; // add an exact match for the future - return i; - } - } - return -1; - } - - // RTM common code for generating Dictionary from array of column names - private void GenerateLookup() - { - int length = _fieldNames.Length; - Dictionary hash = new Dictionary(length); - - // via case sensitive search, first match with lowest ordinal matches - for (int i = length - 1; 0 <= i; --i) - { - string fieldName = _fieldNames[i]; - hash[fieldName] = i; - } - _fieldNameLookup = hash; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/FieldNameLookup.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/FieldNameLookup.cs deleted file mode 100644 index f0edbf377b..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/FieldNameLookup.cs +++ /dev/null @@ -1,44 +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. - -using System.Data; -using System.Globalization; - -namespace Microsoft.Data.ProviderBase -{ - internal sealed class FieldNameLookup : BasicFieldNameLookup - { - private readonly int _defaultLocaleID; - - public FieldNameLookup(string[] fieldNames, int defaultLocaleID) : base(fieldNames) - { - _defaultLocaleID = defaultLocaleID; - } - - public FieldNameLookup(System.Collections.ObjectModel.ReadOnlyCollection columnNames, int defaultLocaleID) : base(columnNames) - { - _defaultLocaleID = defaultLocaleID; - } - - public FieldNameLookup(IDataReader reader, int defaultLocaleID) : base(reader) - { - _defaultLocaleID = defaultLocaleID; - } - - //The compare info is specified by the server by specifying the default LocaleId. - protected override CompareInfo GetCompareInfo() - { - CompareInfo compareInfo = null; - if (-1 != _defaultLocaleID) - { - compareInfo = CompareInfo.GetCompareInfo(_defaultLocaleID); - } - if (null == compareInfo) - { - compareInfo = base.GetCompareInfo(); - } - return compareInfo; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/SQLResource.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/SQLResource.cs deleted file mode 100644 index 9d4a0818cb..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/Common/SQLResource.cs +++ /dev/null @@ -1,71 +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. - -using System; - -namespace Microsoft.Data.SqlTypes -{ - internal static class SQLResource - { - internal static string NullString => Strings.SqlMisc_NullString; - - internal static string MessageString => Strings.SqlMisc_MessageString; - - internal static string ArithOverflowMessage => Strings.SqlMisc_ArithOverflowMessage; - - internal static string DivideByZeroMessage => Strings.SqlMisc_DivideByZeroMessage; - - internal static string NullValueMessage => Strings.SqlMisc_NullValueMessage; - - internal static string TruncationMessage => Strings.SqlMisc_TruncationMessage; - - internal static string DateTimeOverflowMessage => Strings.SqlMisc_DateTimeOverflowMessage; - - internal static string ConcatDiffCollationMessage => Strings.SqlMisc_ConcatDiffCollationMessage; - - internal static string CompareDiffCollationMessage => Strings.SqlMisc_CompareDiffCollationMessage; - - internal static string InvalidFlagMessage => Strings.SqlMisc_InvalidFlagMessage; - - internal static string NumeToDecOverflowMessage => Strings.SqlMisc_NumeToDecOverflowMessage; - - internal static string ConversionOverflowMessage => Strings.SqlMisc_ConversionOverflowMessage; - - internal static string InvalidDateTimeMessage => Strings.SqlMisc_InvalidDateTimeMessage; - - internal static string TimeZoneSpecifiedMessage => Strings.SqlMisc_TimeZoneSpecifiedMessage; - - internal static string InvalidArraySizeMessage => Strings.SqlMisc_InvalidArraySizeMessage; - - internal static string InvalidPrecScaleMessage => Strings.SqlMisc_InvalidPrecScaleMessage; - - internal static string FormatMessage => Strings.SqlMisc_FormatMessage; - - internal static string NotFilledMessage => Strings.SqlMisc_NotFilledMessage; - - internal static string AlreadyFilledMessage => Strings.SqlMisc_AlreadyFilledMessage; - - internal static string ClosedXmlReaderMessage => Strings.SqlMisc_ClosedXmlReaderMessage; - - internal static string InvalidOpStreamClosed(string method) - { - return System.StringsHelper.Format(Strings.SqlMisc_InvalidOpStreamClosed, method); - } - - internal static string InvalidOpStreamNonWritable(string method) - { - return System.StringsHelper.Format(Strings.SqlMisc_InvalidOpStreamNonWritable, method); - } - - internal static string InvalidOpStreamNonReadable(string method) - { - return System.StringsHelper.Format(Strings.SqlMisc_InvalidOpStreamNonReadable, method); - } - - internal static string InvalidOpStreamNonSeekable(string method) - { - return System.StringsHelper.Format(Strings.SqlMisc_InvalidOpStreamNonSeekable, method); - } - } // SqlResource -} // namespace System diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index 1ee4cba196..df16ee4d23 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -25,7 +25,7 @@ internal abstract partial class DbConnectionFactory private static int _objectTypeCount; // EventSource counter internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); - // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to + // s_pendingOpenNonPooled is an array of tasks used to throttle creation of non-pooled connections to // a maximum of Environment.ProcessorCount at a time. private static uint s_pendingOpenNonPooledNext = 0; private static Task[] s_pendingOpenNonPooled = new Task[Environment.ProcessorCount]; @@ -49,8 +49,7 @@ abstract public DbProviderFactory ProviderFactory public void ClearAllPools() { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" connectionPoolGroups = _connectionPoolGroups; foreach (KeyValuePair entry in connectionPoolGroups) @@ -62,17 +61,12 @@ public void ClearAllPools() } } } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } public void ClearPool(DbConnection connection) { ADP.CheckArgumentNull(connection, nameof(connection)); - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", GetObjectId(connection)); - try + using (TryEventScope.Create(" {0}", GetObjectId(connection))) { DbConnectionPoolGroup poolGroup = GetConnectionPoolGroup(connection); if (null != poolGroup) @@ -80,30 +74,20 @@ public void ClearPool(DbConnection connection) poolGroup.Clear(); } } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } public void ClearPool(DbConnectionPoolKey key) { Debug.Assert(key != null, "key cannot be null"); ADP.CheckArgumentNull(key.ConnectionString, nameof(key) + "." + nameof(key.ConnectionString)); - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" connectionString"); - try + using (TryEventScope.Create(" connectionString")) { - DbConnectionPoolGroup poolGroup; Dictionary connectionPoolGroups = _connectionPoolGroups; - if (connectionPoolGroups.TryGetValue(key, out poolGroup)) + if (connectionPoolGroups.TryGetValue(key, out DbConnectionPoolGroup poolGroup)) { poolGroup.Clear(); } } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } internal virtual DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions) @@ -124,6 +108,7 @@ internal DbConnectionInternal CreateNonPooledConnection(DbConnection owningConne DbConnectionInternal newConnection = CreateConnection(connectionOptions, poolKey, poolGroupProviderInfo, null, owningConnection, userOptions); if (null != newConnection) { + SqlClientEventSource.Log.HardConnectRequest(); newConnection.MakeNonPooledObject(owningConnection); } SqlClientEventSource.Log.TryTraceEvent(" {0}, Non-pooled database connection created.", ObjectID); @@ -138,6 +123,7 @@ internal DbConnectionInternal CreatePooledConnection(DbConnectionPool pool, DbCo DbConnectionInternal newConnection = CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningObject, userOptions); if (null != newConnection) { + SqlClientEventSource.Log.HardConnectRequest(); newConnection.MakePooledConnection(pool); } SqlClientEventSource.Log.TryTraceEvent(" {0}, Pooled database connection created.", ObjectID); @@ -281,6 +267,7 @@ internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnectionPoolKey key, D // lock prevents race condition with PruneConnectionPoolGroups newConnectionPoolGroups.Add(key, newConnectionPoolGroup); + SqlClientEventSource.Log.EnterActiveConnectionPoolGroup(); connectionPoolGroup = newConnectionPoolGroup; _connectionPoolGroups = newConnectionPoolGroups; } @@ -304,7 +291,7 @@ private void PruneConnectionPoolGroups(object state) { // when debugging this method, expect multiple threads at the same time SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}", ObjectID); - + // First, walk the pool release list and attempt to clear each // pool, when the pool is finally empty, we dispose of it. If the // pool isn't empty, it's because there are active connections or @@ -324,6 +311,7 @@ private void PruneConnectionPoolGroups(object state) { _poolsToRelease.Remove(pool); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, ReleasePool={1}", ObjectID, pool.ObjectID); + SqlClientEventSource.Log.ExitInactiveConnectionPool(); } } } @@ -348,6 +336,7 @@ private void PruneConnectionPoolGroups(object state) { _poolGroupsToRelease.Remove(poolGroup); SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, ReleasePoolGroup={1}", ObjectID, poolGroup.ObjectID); + SqlClientEventSource.Log.ExitInactiveConnectionPoolGroup(); } } } @@ -372,7 +361,8 @@ private void PruneConnectionPoolGroups(object state) // move idle entries from last prune pass to a queue for pending release // otherwise process entry which may move it from active to idle if (entry.Value.Prune()) - { // may add entries to _poolsToRelease + { + // may add entries to _poolsToRelease QueuePoolGroupForRelease(entry.Value); } else @@ -405,6 +395,8 @@ internal void QueuePoolForRelease(DbConnectionPool pool, bool clearing) } _poolsToRelease.Add(pool); } + SqlClientEventSource.Log.EnterInactiveConnectionPool(); + SqlClientEventSource.Log.ExitActiveConnectionPool(); } internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) @@ -416,6 +408,8 @@ internal void QueuePoolGroupForRelease(DbConnectionPoolGroup poolGroup) { _poolGroupsToRelease.Add(poolGroup); } + SqlClientEventSource.Log.EnterInactiveConnectionPoolGroup(); + SqlClientEventSource.Log.ExitActiveConnectionPoolGroup(); } virtual protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) @@ -474,5 +468,10 @@ protected virtual DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal i abstract internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from); abstract internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to); + + virtual internal void Unload() + { + _pruningTimer.Dispose(); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs index 0e92ed4f00..516aa9b7a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs @@ -24,7 +24,7 @@ internal abstract partial class DbConnectionInternal private readonly bool _hidePassword; private readonly ConnectionState _state; - private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections) + private readonly WeakReference _owningObject = new WeakReference(null, false); // [usage must be thread safe] the owning object, when not in the pool. (both Pooled and Non-Pooled connections) private DbConnectionPool _connectionPool; // the pooler that the connection came from (Pooled connections only) private DbReferenceCollection _referenceCollection; // collection of objects that we need to notify in some way when we're being deactivated @@ -63,8 +63,7 @@ internal bool CanBePooled { get { - bool flag = (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.IsAlive); - return flag; + return (!_connectionIsDoomed && !_cannotBePooled && !_owningObject.TryGetTarget(out DbConnection _)); } } @@ -103,8 +102,7 @@ internal bool IsEmancipated // of the pool and it's owning object is no longer around to // return it. - bool value = (_pooledCount < 1) && !_owningObject.IsAlive; - return value; + return (_pooledCount < 1) && !_owningObject.TryGetTarget(out DbConnection _); } } @@ -118,13 +116,17 @@ internal bool IsInPool } - protected internal object Owner + protected internal DbConnection Owner { // We use a weak reference to the owning object so we can identify when // it has been garbage collected without throwing exceptions. get { - return _owningObject.Target; + if (_owningObject.TryGetTarget(out DbConnection connection)) + { + return connection; + } + return null; } } @@ -231,6 +233,7 @@ internal void DeactivateConnection() int activateCount = Interlocked.Decrement(ref _activateCount); #endif // DEBUG + SqlClientEventSource.Log.ExitActiveConnection(); if (!_connectionIsDoomed && Pool.UseLoadBalancing) { @@ -275,13 +278,13 @@ protected internal virtual DataTable GetSchema(DbConnectionFactory factory, DbCo return metaDataFactory.GetSchema(outerConnection, collectionName, restrictions); } - internal void MakeNonPooledObject(object owningObject) + internal void MakeNonPooledObject(DbConnection owningObject) { // Used by DbConnectionFactory to indicate that this object IS NOT part of // a connection pool. _connectionPool = null; - _owningObject.Target = owningObject; + _owningObject.SetTarget(owningObject); _pooledCount = -1; } @@ -367,14 +370,15 @@ internal void PrePush(object expectedOwner) // ReclaimEmancipatedObjects. //3 // The following tests are retail assertions of things we can't allow to happen. - if (null == expectedOwner) + bool isAlive = _owningObject.TryGetTarget(out DbConnection connection); + if (expectedOwner == null) { - if (null != _owningObject.Target) + if (isAlive) { throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasOwner); // new unpooled object has an owner } } - else if (_owningObject.Target != expectedOwner) + else if (isAlive && connection != expectedOwner) { throw ADP.InternalError(ADP.InternalErrorCode.UnpooledObjectHasWrongOwner); // unpooled object has incorrect owner } @@ -385,10 +389,10 @@ internal void PrePush(object expectedOwner) SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Preparing to push into pool, owning connection {1}, pooledCount={2}", ObjectID, 0, _pooledCount); _pooledCount++; - _owningObject.Target = null; // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2% + _owningObject.SetTarget(null); // NOTE: doing this and checking for InternalError.PooledObjectHasOwner degrades the close by 2% } - internal void PostPop(object newOwner) + internal void PostPop(DbConnection newOwner) { // Called by DbConnectionPool right after it pulls this from it's pool, we // take this opportunity to ensure ownership and pool counts are legit. @@ -405,12 +409,11 @@ internal void PostPop(object newOwner) // IMPORTANT NOTE: You must have taken a lock on the object before // you call this method to prevent race conditions with Clear and // ReclaimEmancipatedObjects. - - if (null != _owningObject.Target) + if (_owningObject.TryGetTarget(out DbConnection _)) { throw ADP.InternalError(ADP.InternalErrorCode.PooledObjectHasOwner); // pooled connection already has an owner! } - _owningObject.Target = newOwner; + _owningObject.SetTarget(newOwner); _pooledCount--; SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Preparing to pop from pool, owning connection {1}, pooledCount={2}", ObjectID, 0, _pooledCount); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs index 958dedb4e5..e7efdad252 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs @@ -14,21 +14,21 @@ internal abstract class DbReferenceCollection private struct CollectionEntry { private int _tag; // information about the reference - private WeakReference _weak; // the reference itself. + private WeakReference _weak; // the reference itself. public void NewTarget(int tag, object target) { - Debug.Assert(!HasTarget, "Entry already has a valid target"); + Debug.Assert(!TryGetTarget(out object _) , "Entry already has a valid target"); Debug.Assert(tag != 0, "Bad tag"); Debug.Assert(target != null, "Invalid target"); if (_weak == null) { - _weak = new WeakReference(target, false); + _weak = new WeakReference(target, false); } else { - _weak.Target = target; + _weak.SetTarget(target); } _tag = tag; } @@ -36,30 +36,15 @@ public void NewTarget(int tag, object target) public void RemoveTarget() { _tag = 0; + _weak.SetTarget(null); } - public bool HasTarget - { - get - { - return ((_tag != 0) && (_weak.IsAlive)); - } - } - - public int Tag - { - get - { - return _tag; - } - } + public int Tag => _tag; - public object Target + public bool TryGetTarget(out object target) { - get - { - return (_tag == 0 ? null : _weak.Target); - } + target = null; + return _tag != 0 && _weak.TryGetTarget(out target); } } @@ -94,7 +79,7 @@ protected void AddItem(object value, int tag) if (_items[i].Tag == 0) { _items[i].NewTarget(tag, value); - Debug.Assert(_items[i].HasTarget, "missing expected target"); + Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); itemAdded = true; break; } @@ -113,10 +98,10 @@ protected void AddItem(object value, int tag) { for (int i = 0; i <= _lastItemIndex; ++i) { - if (!_items[i].HasTarget) + if (!_items[i].TryGetTarget(out object _)) { _items[i].NewTarget(tag, value); - Debug.Assert(_items[i].HasTarget, "missing expected target"); + Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); itemAdded = true; break; } @@ -145,20 +130,15 @@ internal T FindItem(int tag, Func filterMethod) where T : class { if (_optimisticCount > 0) { - // Loop through the items for (int counter = 0; counter <= _lastItemIndex; counter++) { // Check tag (should be easiest and quickest) if (_items[counter].Tag == tag) { - // NOTE: Check if the returned value is null twice may seem wasteful, but this if for performance - // Since checking for null twice is cheaper than calling both HasTarget and Target OR always attempting to typecast - object value = _items[counter].Target; - if (value != null) + if (_items[counter].TryGetTarget(out object value)) { // Make sure the item has the correct type and passes the filtering - T tempItem = value as T; - if ((tempItem != null) && (filterMethod(tempItem))) + if (value is T tempItem && filterMethod(tempItem)) { return tempItem; } @@ -194,13 +174,12 @@ public void Notify(int message) { for (int index = 0; index <= _lastItemIndex; ++index) { - object value = _items[index].Target; // checks tag & gets target - if (null != value) + if (_items[index].TryGetTarget(out object value)) { NotifyItem(message, _items[index].Tag, value); _items[index].RemoveTarget(); } - Debug.Assert(!_items[index].HasTarget, "Unexpected target after notifying"); + Debug.Assert(!_items[index].TryGetTarget(out object _), "Unexpected target after notifying"); } _optimisticCount = 0; } @@ -244,8 +223,8 @@ protected void RemoveItem(object value) { for (int index = 0; index <= _lastItemIndex; ++index) { - if (value == _items[index].Target) - { // checks tag & gets target + if (_items[index].TryGetTarget(out object target) && value == target) + { _items[index].RemoveTarget(); _optimisticCount--; break; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs index ecdd521763..5b908a3b8e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs @@ -13,7 +13,7 @@ using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -#if NET46 +#if net462 using System.Security; #endif @@ -45,7 +45,7 @@ namespace System.Net // method that takes an object and optionally provides a string representation of it, in case a particular library wants to customize further. /// Provides logging facilities for System.Net libraries. -#if NET46 +#if net462 [SecuritySafeCritical] #endif internal sealed partial class NetEventSource : EventSource diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 5858b77b44..70c1a74377 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -135,7 +135,10 @@ private static SecurityStatusPal EstablishSecurityContext( } catch (Exception ex) { - if (NetEventSource.IsEnabled) NetEventSource.Error(null, ex); + if (NetEventSource.IsEnabled) + { + NetEventSource.Error(null, ex); + } return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex); } } @@ -143,7 +146,7 @@ private static SecurityStatusPal EstablishSecurityContext( internal static SecurityStatusPal InitializeSecurityContext( SafeFreeCredentials credentialsHandle, ref SafeDeleteContext securityContext, - string spn, + string[] spns, ContextFlagsPal requestedContextFlags, SecurityBuffer[] inSecurityBufferArray, SecurityBuffer outSecurityBuffer, @@ -156,20 +159,33 @@ internal static SecurityStatusPal InitializeSecurityContext( } SafeFreeNegoCredentials negoCredentialsHandle = (SafeFreeNegoCredentials)credentialsHandle; + SecurityStatusPal status = default; - if (negoCredentialsHandle.IsDefault && string.IsNullOrEmpty(spn)) + foreach (string spn in spns) { - throw new PlatformNotSupportedException(Strings.net_nego_not_supported_empty_target_with_defaultcreds); - } + if (negoCredentialsHandle.IsDefault && string.IsNullOrEmpty(spn)) + { + throw new PlatformNotSupportedException(Strings.net_nego_not_supported_empty_target_with_defaultcreds); + } - SecurityStatusPal status = EstablishSecurityContext( - negoCredentialsHandle, - ref securityContext, - spn, - requestedContextFlags, - ((inSecurityBufferArray != null && inSecurityBufferArray.Length != 0) ? inSecurityBufferArray[0] : null), - outSecurityBuffer, - ref contextFlags); + status = EstablishSecurityContext( + negoCredentialsHandle, + ref securityContext, + spn, + requestedContextFlags, + ((inSecurityBufferArray != null && inSecurityBufferArray.Length != 0) ? inSecurityBufferArray[0] : null), + outSecurityBuffer, + ref contextFlags); + + if (status.ErrorCode != SecurityStatusPalErrorCode.InternalError) + { + break; // Successful case, exit the loop with current SPN. + } + else + { + securityContext = null; // Reset security context to be generated again for next SPN. + } + } // Confidentiality flag should not be set if not requested if (status.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded) @@ -180,7 +196,6 @@ internal static SecurityStatusPal InitializeSecurityContext( throw new PlatformNotSupportedException(Strings.net_nego_protection_level_not_supported); } } - return status; } @@ -224,7 +239,7 @@ internal static SafeFreeCredentials AcquireCredentialsHandle(string package, boo new SafeFreeNegoCredentials(false, string.Empty, string.Empty, string.Empty) : new SafeFreeNegoCredentials(ntlmOnly, credential.UserName, credential.Password, credential.Domain); } - catch(Exception ex) + catch (Exception ex) { throw new Win32Exception(NTE_FAIL, ex.Message); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs index 58bf635657..fe56d8ed91 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.ComponentModel; +using Microsoft.Data; namespace System.Net.Security { @@ -40,7 +41,7 @@ internal static unsafe SafeFreeCredentials AcquireCredentialsHandle(string packa if (result != Interop.SECURITY_STATUS.OK) { if (NetEventSource.IsEnabled) - NetEventSource.Error(null, System.StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(Interop.SspiCli.SspiEncodeStringsAsAuthIdentity), $"0x{(int)result:X}")); + NetEventSource.Error(null, StringsHelper.Format(Strings.net_log_operation_failed_with_error, nameof(Interop.SspiCli.SspiEncodeStringsAsAuthIdentity), $"0x{(int)result:X}")); throw new Win32Exception((int)result); } @@ -70,7 +71,7 @@ internal static string QueryContextAuthenticationPackage(SafeDeleteContext secur internal static SecurityStatusPal InitializeSecurityContext( SafeFreeCredentials credentialsHandle, ref SafeDeleteContext securityContext, - string spn, + string[] spn, ContextFlagsPal requestedContextFlags, SecurityBuffer[] inSecurityBufferArray, SecurityBuffer outSecurityBuffer, @@ -81,7 +82,7 @@ internal static SecurityStatusPal InitializeSecurityContext( GlobalSSPI.SSPIAuth, credentialsHandle, ref securityContext, - spn, + spn[0], ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags), Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP, inSecurityBufferArray, diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs index ed33b6897a..0b09ea3341 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs @@ -3,11 +3,15 @@ // See the LICENSE file in the project root for more information. using Microsoft.Data.SqlClient.SNI; +using System; +using System.Runtime.InteropServices; namespace Microsoft.Data.SqlClient { internal static partial class SNINativeMethodWrapper { + private const string SNI = "Microsoft.Data.SqlClient.SNI.dll"; + internal enum SniSpecialErrors : uint { LocalDBErrorCode = SNICommon.LocalDBErrorCode, diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs index e3b91c6ee5..c8591a8c11 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs @@ -2,18 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Data.Common; -using Microsoft.Data.SqlClient; using System; using System.Runtime.InteropServices; using System.Text; +using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.SqlClient { internal static partial class SNINativeMethodWrapper { - private const string SNI = "Microsoft.Data.SqlClient.SNI.dll"; - private static int s_sniMaxComposedSpnLength = -1; private const int SniOpenTimeOut = -1; // infinite @@ -21,6 +19,8 @@ internal static partial class SNINativeMethodWrapper [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate void SqlAsyncCallbackDelegate(IntPtr m_ConsKey, IntPtr pPacket, uint dwError); + internal delegate IntPtr SqlClientCertificateDelegate(IntPtr pCallbackContext); + internal const int SniIP6AddrStringBufferLength = 48; // from SNI layer internal static int SniMaxComposedSpnLength @@ -45,6 +45,24 @@ internal struct ConsumerInfo internal IntPtr key; } + [StructLayout(LayoutKind.Sequential)] + internal struct AuthProviderInfo + { + public uint flags; + [MarshalAs(UnmanagedType.Bool)] + public bool tlsFirst; + public object certContext; + [MarshalAs(UnmanagedType.LPWStr)] + public string certId; + [MarshalAs(UnmanagedType.Bool)] + public bool certHash; + public object clientCertificateCallbackContext; + public SqlClientCertificateDelegate clientCertificateCallback; + [MarshalAs(UnmanagedType.LPWStr)] + public string serverCertFileName; + }; + + internal enum ConsumerNumber { SNI_Consumer_SNI, @@ -150,6 +168,8 @@ private unsafe struct SNI_CLIENT_CONSUMER_INFO public Sni_Consumer_Info ConsumerInfo; [MarshalAs(UnmanagedType.LPWStr)] public string wszConnectionString; + [MarshalAs(UnmanagedType.LPWStr)] + public string HostNameInCertificate; public PrefixEnum networkLibrary; public byte* szSPN; public uint cchSPN; @@ -165,6 +185,7 @@ private unsafe struct SNI_CLIENT_CONSUMER_INFO public TransparentNetworkResolutionMode transparentNetworkResolution; public int totalTimeout; public bool isAzureSqlServerEndpoint; + public SqlConnectionIPAddressPreference ipAddressPreference; public SNI_DNSCache_Info DNSCacheInfo; } @@ -199,9 +220,13 @@ internal struct SNI_Error #endregion #region DLL Imports + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIAddProviderWrapper")] internal static extern uint SNIAddProvider(SNIHandle pConn, ProviderEnum ProvNum, [In] ref uint pInfo); + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIAddProviderWrapper")] + internal static extern uint SNIAddProvider(SNIHandle pConn, ProviderEnum ProvNum, [In] ref AuthProviderInfo pInfo); + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNICheckConnectionWrapper")] internal static extern uint SNICheckConnection([In] SNIHandle pConn); @@ -275,6 +300,7 @@ private static extern uint SNIOpenWrapper( [In] SNIHandle pConn, out IntPtr ppConn, [MarshalAs(UnmanagedType.Bool)] bool fSync, + SqlConnectionIPAddressPreference ipPreference, [In] ref SNI_DNSCache_Info pDNSCachedInfo); [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] @@ -304,24 +330,30 @@ private static extern unsafe uint SNISecGenClientContextWrapper( [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIWriteSyncOverAsync(SNIHandle pConn, [In] SNIPacket pPacket); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern bool RegisterTraceProviderWrapper(int eventKeyword); - - [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] - internal static extern void UnregisterTraceProviderWrapper(); - #endregion + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumOpenWrapper")] + internal static extern IntPtr SNIServerEnumOpen(); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumCloseWrapper")] + internal static extern void SNIServerEnumClose([In] IntPtr packet); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper", CharSet = CharSet.Unicode)] + internal static extern int SNIServerEnumRead([In] IntPtr packet, + [In][MarshalAs(UnmanagedType.LPArray)] char[] readBuffer, + [In] int bufferLength, + [MarshalAs(UnmanagedType.Bool)] out bool more); + #endregion internal static uint SniGetConnectionId(SNIHandle pConn, ref Guid connId) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_CONNID, out connId); } - + internal static uint SniGetProviderNumber(SNIHandle pConn, ref ProviderEnum provNum) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PROVIDERNUM, out provNum); } - + internal static uint SniGetConnectionPort(SNIHandle pConn, ref ushort portNum) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PEERPORT, out portNum); @@ -347,7 +379,7 @@ internal static uint SNIInitialize() return SNIInitialize(IntPtr.Zero); } - internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) { // initialize consumer info for MARS Sni_Consumer_Info native_consumerInfo = new Sni_Consumer_Info(); @@ -359,11 +391,24 @@ internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHan native_cachedDNSInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; native_cachedDNSInfo.wszCachedTcpPort = cachedDNSInfo?.Port; - return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ref native_cachedDNSInfo); + return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ipPreference, ref native_cachedDNSInfo); } - internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenSyncEx( + ConsumerInfo consumerInfo, + string constring, + ref IntPtr pConn, + byte[] spnBuffer, + byte[] instanceName, + bool fOverrideCache, + bool fSync, + int timeout, + bool fParallel, + SqlConnectionIPAddressPreference ipPreference, + SQLDNSInfo cachedDNSInfo, + string hostNameInCertificate) { + fixed (byte* pin_instanceName = &instanceName[0]) { SNI_CLIENT_CONSUMER_INFO clientConsumerInfo = new SNI_CLIENT_CONSUMER_INFO(); @@ -372,8 +417,8 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons MarshalConsumerInfo(consumerInfo, ref clientConsumerInfo.ConsumerInfo); clientConsumerInfo.wszConnectionString = constring; + clientConsumerInfo.HostNameInCertificate = hostNameInCertificate; clientConsumerInfo.networkLibrary = PrefixEnum.UNKNOWN_PREFIX; - clientConsumerInfo.szInstanceName = pin_instanceName; clientConsumerInfo.cchInstanceName = (uint)instanceName.Length; clientConsumerInfo.fOverrideLastConnectCache = fOverrideCache; @@ -385,6 +430,7 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons clientConsumerInfo.totalTimeout = SniOpenTimeOut; clientConsumerInfo.isAzureSqlServerEndpoint = ADP.IsAzureSqlServerEndpoint(constring); + clientConsumerInfo.ipAddressPreference = ipPreference; clientConsumerInfo.DNSCacheInfo.wszCachedFQDN = cachedDNSInfo?.FQDN; clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv4 = cachedDNSInfo?.AddrIPv4; clientConsumerInfo.DNSCacheInfo.wszCachedTcpIPv6 = cachedDNSInfo?.AddrIPv6; @@ -467,18 +513,6 @@ private static void MarshalConsumerInfo(ConsumerInfo consumerInfo, ref Sni_Consu : IntPtr.Zero; native_consumerInfo.ConsumerKey = consumerInfo.key; } - - internal static bool RegisterTraceProvider(int eventKeyword) - { - // Registers the TraceLogging provider, enabling it to generate events. - // Return true if enabled, otherwise false. - return RegisterTraceProviderWrapper(eventKeyword); - } - - internal static void UnregisterTraceProvider() - { - UnregisterTraceProviderWrapper(); - } } } 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 e46d123395..bc755d3486 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -1,17 +1,17 @@  Microsoft.Data.SqlClient - netcoreapp2.1;netcoreapp3.1;netstandard2.0;netstandard2.1 + net6.0;netstandard2.0;netstandard2.1 netstandard2.1 - Strings.PlatformNotSupported_DataSqlClient + Microsoft.Data.SqlClient is not supported on this platform. $(OS) true true false - netcoreapp - netstandard - Debug;Release;netcoreapp2.1-Debug;netcoreapp2.1-Release;netcoreapp3.1-Debug;netcoreapp3.1-Release + netcoreapp + netstandard + Debug;Release; AnyCPU;x64;x86 $(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName)\netcore\ $(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)\netcore\ @@ -19,49 +19,38 @@ true Core $(BaseProduct) - - $(DefineConstants);NETCOREAPP; - - - $(DefineConstants);NETSTANDARD; - - - $(DefineConstants);NETCOREAPP31_AND_ABOVE - portable true - - - Microsoft\Data\SqlClient\SqlClientEventSource.Windows.cs - - - - Microsoft\Data\SqlClient\SqlClientEventSource.cs + + Microsoft\Data\Common\ActivityCorrelator.cs - - Microsoft\Data\SqlClient\SqlClientLogger.cs + + Microsoft\Data\Common\AdapterUtil.cs - - Microsoft\Data\Sql\SqlNotificationRequest.cs + + Microsoft\Data\Common\DbConnectionOptions.Common.cs - - Microsoft\Data\Common\ActivityCorrelator.cs + + Microsoft\Data\Common\DbConnectionPoolKey.cs - - Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs + + Microsoft\Data\Common\DbConnectionStringCommon.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs + + Microsoft\Data\Common\MultipartIdentifier.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs + + Microsoft\Data\Common\NameValuePair.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs + + Microsoft\Data\DataException.cs + + + Microsoft\Data\OperationAbortedException.cs Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs @@ -69,24 +58,123 @@ Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs + + + Microsoft\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs + + + Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs + + + Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs + + + Microsoft\Data\ProviderBase\DbMetaDataFactory.cs + + + Microsoft\Data\ProviderBase\FieldNameLookup.cs + + + Microsoft\Data\ProviderBase\TimeoutTimer.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumerator.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorManagedHelper.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorUtil.cs + + + Microsoft\Data\Sql\SqlNotificationRequest.cs + + + Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs + Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs Microsoft\Data\SqlClient\ApplicationIntent.cs - - Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs + + Microsoft\Data\SqlClient\AssemblyRef.cs + + + Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs + + + Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs + + + Microsoft\Data\SqlClient\DisposableTemporaryOnStack.cs + + + Microsoft\Data\SqlClient\EnclaveDelegate.cs + + + Microsoft\Data\SqlClient\EnclavePackage.cs + + + Microsoft\Data\SqlClient\LocalAppContextSwitches.cs + + + Microsoft\Data\SqlClient\OnChangedEventHandler.cs + + + Microsoft\Data\SqlClient\ParameterPeekAheadValue.cs + + + Microsoft\Data\SqlClient\PoolBlockingPeriod.cs + + + Microsoft\Data\SqlClient\Reliability\AppConfigManager.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryingEventArgs.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalBaseEnumerator.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogic.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBase.cs - - Microsoft\Data\SqlClient\AzureManagedIdentityAuthenticationProvider.cs + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBaseProvider.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicProvider.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryFactory.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicLoader.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalEnumerators.cs + + + Microsoft\Data\SqlClient\RowsCopiedEventArgs.cs + + + Microsoft\Data\SqlClient\RowsCopiedEventHandler.cs + + + Microsoft\Data\SqlClient\SqlSequentialTextReader.cs Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs - - Microsoft\Data\SqlClient\Server\IBinarySerialize.cs - Microsoft\Data\SqlClient\Server\ITypedGetters.cs @@ -102,51 +190,66 @@ Microsoft\Data\SqlClient\Server\MemoryRecordBuffer.cs + + Microsoft\Data\SqlClient\Server\MetadataUtilsSmi.cs + + + Microsoft\Data\SqlClient\Server\SmiEventSink.cs + + + Microsoft\Data\SqlClient\Server\SmiEventSink_Default.Common.cs + Microsoft\Data\SqlClient\Server\SmiGettersStream.cs + + Microsoft\Data\SqlClient\Server\SmiMetaData.cs + + + Microsoft\Data\SqlClient\Server\SmiMetaDataProperty.cs + + + Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs + Microsoft\Data\SqlClient\Server\SmiSettersStream.cs - - Microsoft\Data\SqlClient\Server\SmiXetterTypeCode.cs + + Microsoft\Data\SqlClient\Server\SmiTypedGetterSetter.cs - - Microsoft\Data\SqlClient\Server\SqlFacetAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiXetterAccessMap.Common.cs - - Microsoft\Data\SqlClient\Server\SqlFunctionAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiXetterTypeCode.cs - - Microsoft\Data\SqlClient\Server\SqlMethodAttribute.cs + + Microsoft\Data\SqlClient\Server\SqlDataRecord.cs - - Microsoft\Data\SqlClient\Server\SqlUserDefinedTypeAttribute.cs + + Microsoft\Data\SqlClient\Server\SqlDataRecord.netcore.cs - - Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs + + Microsoft\Data\SqlClient\Server\SqlMetaData.cs - - Microsoft\Data\SqlClient\OnChangedEventHandler.cs + + Microsoft\Data\SqlClient\Server\SqlNormalizer.cs - - Microsoft\Data\SqlClient\ParameterPeekAheadValue.cs + + Microsoft\Data\SqlClient\Server\SqlRecordBuffer.cs - - Microsoft\Data\SqlClient\PoolBlockingPeriod.cs + + Microsoft\Data\SqlClient\SqlTransaction.Common.cs - - Microsoft\Data\SqlClient\RowsCopiedEventArgs.cs + + Microsoft\Data\SqlClient\Server\ValueUtilsSmi.cs - - Microsoft\Data\SqlClient\RowsCopiedEventHandler.cs + + Microsoft\Data\SqlClient\SignatureVerificationCache.cs Microsoft\Data\SqlClient\SortOrder.cs - - Microsoft\Data\SqlClient\SqlAuthenticationToken.cs - Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256Algorithm.cs @@ -162,18 +265,30 @@ Microsoft\Data\SqlClient\SqlAuthenticationProvider.cs + + Microsoft\Data\SqlClient\SqlBuffer.cs + + + Microsoft\Data\SqlClient\SqlAuthenticationToken.cs + Microsoft\Data\SqlClient\SqlBulkCopyColumnMapping.cs - - Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs + + Microsoft\Data\SqlClient\SqlBulkCopyColumnMappingCollection.cs Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHint.cs - + Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHintCollection.cs + + Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs + + + Microsoft\Data\SqlClient\SqlCachedBuffer.cs + Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithm.cs @@ -186,18 +301,108 @@ Microsoft\Data\SqlClient\SqlClientEncryptionType.cs + + Microsoft\Data\SqlClient\SqlClientEventSource.cs + + + Microsoft\Data\SqlClient\SqlClientLogger.cs + + + Microsoft\Data\SqlClient\SqlClientMetaDataCollectionNames.cs + Microsoft\Data\SqlClient\SqlClientSymmetricKey.cs + + Microsoft\Data\SqlClient\SqlCollation.cs + Microsoft\Data\SqlClient\SqlColumnEncryptionKeyStoreProvider.cs + + Microsoft\Data\SqlClient\SqlCommandBuilder.cs + + + Microsoft\Data\SqlClient\SqlCommandSet.cs + + + Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs + + + Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs + + + Microsoft\Data\SqlClient\SqlConnectionPoolKey.cs + Microsoft\Data\SqlClient\SqlConnectionPoolProviderInfo.cs + + Microsoft\Data\SqlClient\SqlConnectionString.cs + + + Microsoft\Data\SqlClient\SqlConnectionStringBuilder.cs + + + Microsoft\Data\SqlClient\SqlConnectionTimeoutErrorInternal.cs + + + Microsoft\Data\SqlClient\SqlCredential.cs + + + Microsoft\Data\SqlClient\SqlDataAdapter.cs + + + Microsoft\Data\SqlClient\SqlDependency.cs + + + Microsoft\Data\SqlClient\SqlDependencyListener.cs + + + Microsoft\Data\SqlClient\SqlDependencyUtils.cs + + + Microsoft\Data\SqlClient\SqlDependencyUtils.AppDomain.cs + + + Microsoft\Data\SqlClient\SqlEnclaveSession.cs + + + Microsoft\Data\SqlClient\SqlEnums.cs + + + Microsoft\Data\SqlClient\SqlEnvChange.cs + + + Microsoft\Data\SqlClient\SqlError.cs + + + Microsoft\Data\SqlClient\SqlErrorCollection.cs + + + Microsoft\Data\SqlClient\SqlException.cs + + + Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs + + + Microsoft\Data\SqlClient\SqlInfoMessageEvent.cs + Microsoft\Data\SqlClient\SqlInfoMessageEventHandler.cs + + Microsoft\Data\SqlClient\SqlInternalConnection.cs + + + Microsoft\Data\SqlClient\SqlInternalTransaction.cs + + + Microsoft\Data\SqlClient\SqlMetadataFactory.cs + + + Microsoft\Data\SqlClient\SqlNotificationEventArgs.cs + Microsoft\Data\SqlClient\SqlNotificationInfo.cs @@ -207,6 +412,21 @@ Microsoft\Data\SqlClient\SqlNotificationType.cs + + Microsoft\Data\SqlClient\SqlObjectPool.cs + + + Microsoft\Data\SqlClient\SqlParameter.cs + + + Microsoft\Data\SqlClient\SqlParameterCollection.cs + + + Microsoft\Data\SqlClient\SqlQueryMetadataCache.cs + + + Microsoft\Data\SqlClient\SqlReferenceCollection.cs + Microsoft\Data\SqlClient\SqlRowUpdatedEvent.cs @@ -219,56 +439,98 @@ Microsoft\Data\SqlClient\SqlRowUpdatingEventHandler.cs - - Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs + + Microsoft\Data\SqlClient\SqlSecurityUtility.cs - - Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs + + Microsoft\Data\SqlClient\SqlSequentialStream.cs - - Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs + + Microsoft\Data\SqlClient\Server\SqlSer.cs - - Microsoft\Data\OperationAbortedException.cs + + Microsoft\Data\SqlClient\SqlStatistics.cs - - Microsoft\Data\DataException.cs + + Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs - - Microsoft\Data\SqlClient\Server\InvalidUdtException.cs + + Microsoft\Data\SqlClient\SqlUdtInfo.cs - - Microsoft\Data\SqlClient\SignatureVerificationCache.cs + + Microsoft\Data\SqlClient\SqlUtil.cs - - Microsoft\Data\SqlClient\TdsValueSetter.cs + + Microsoft\Data\SqlClient\TdsEnums.cs Microsoft\Data\SqlClient\TdsParameterSetter.cs + + Microsoft\Data\SqlClient\TdsParserStateObject.cs + + + Microsoft\Data\SqlClient\TdsParserStaticMethods.cs + Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs - - Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs + + Microsoft\Data\SqlClient\TdsParserSessionPool.cs + + + Microsoft\Data\SqlClient\TdsValueSetter.cs + + + Microsoft\Data\SQLTypes\SQLResource.cs + + + Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs + + + Microsoft\Data\SqlClient\SqlStream.cs + + + Resources\ResCategoryAttribute.cs + + + Resources\ResDescriptionAttribute.cs + + + Resources\StringsHelper.cs + + + Common\System\Diagnostics\CodeAnalysis.cs - + + Microsoft\Data\SqlClient\EnclaveDelegate.NotSupported.cs + + + Microsoft\Data\SqlClient\SqlEnclaveAttestationParameters.NotSupported.cs + - - - - - + + - - - - Microsoft\Data\SqlClient\AlwaysEncryptedAttestationException.cs + + + + + + + + Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs + + + Microsoft\Data\SqlClient\AzureAttestationBasedEnclaveProvider.cs + + + Microsoft\Data\SqlClient\EnclaveDelegate.Crypto.cs Microsoft\Data\SqlClient\EnclaveProviderBase.cs @@ -276,207 +538,151 @@ Microsoft\Data\SqlClient\EnclaveSessionCache.cs + + Microsoft\Data\SqlClient\SqlEnclaveAttestationParameters.Crypto.cs + + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs + + + Microsoft\Data\SqlClient\NoneAttestationEnclaveProvider.cs + + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs + + - - - - - - - + + + + + - + + Microsoft\Data\SqlClient\SqlDependencyUtils.AssemblyLoadContext.cs + + + - - - + + - - - - - - - - - - - - - - - - - Microsoft\Data\Common\AdapterUtil.cs - - - Microsoft\Data\Common\AdapterUtil.Drivers.cs + + Resources\StringsHelper.NetCore.cs - - - - Microsoft\Data\Common\DbConnectionOptions.Common.cs - - - Microsoft\Data\Common\DbConnectionPoolKey.cs - - - - Microsoft\Data\Common\FieldNameLookup.cs - - - Microsoft\Data\Common\BasicFieldNameLookup.cs - - - Microsoft\Data\Common\MultipartIdentifier.cs + + True + True + Strings.resx - - Microsoft\Data\Common\NameValuePair.cs + + ResXFileCodeGenerator + Strings.Designer.cs + System + + + Common\CoreLib\System\Threading\Tasks\TaskToApm.cs - - Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs + + Common\Microsoft\Data\ProviderBase\DbConnectionClosed.cs Common\Microsoft\Data\ProviderBase\DbConnectionFactory.cs - - Common\Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs - - - Common\Microsoft\Data\ProviderBase\TimeoutTimer.cs + + Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs Common\Microsoft\Data\ProviderBase\DbReferenceCollection.cs - - Common\Microsoft\Data\ProviderBase\DbMetaDataFactory.cs - - - Common\Microsoft\Data\ProviderBase\DbConnectionClosed.cs - + + + - - - - - + + - + + + + + + + + + + + + + + + + + + + - - - + - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - Microsoft\Data\SQLTypes\SQLResource.cs - - - - Common\CoreLib\System\Threading\Tasks\TaskToApm.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Microsoft\Data\Common\AdapterUtil.Windows.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorNativeHelper.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs + + + Microsoft\Data\SqlClient\TdsParserSafeHandles.Windows.cs + + + Microsoft\Data\Common\AdapterUtil.Unix.cs + - - + + - - + + Common\CoreLib\Interop\Windows\kernel32\Interop.FileTypes.cs @@ -495,14 +701,14 @@ Common\Interop\Windows\Interop.UNICODE_STRING.cs - - Common\Interop\Windows\Kernel32\Interop.IoControlCodeAccess.cs - Common\Interop\Windows\Kernel32\Interop.CTL_CODE.cs - Common\Interop\Windows\kernel32\Interop.DeviceIoControl.cs + Common\Interop\Windows\Kernel32\Interop.DeviceIoControl.cs + + + Common\Interop\Windows\Kernel32\Interop.IoControlCodeAccess.cs Common\Interop\Windows\Kernel32\Interop.IoControlTransferType.cs @@ -517,219 +723,219 @@ Common\Interop\Windows\NtDll\Interop.NtCreateFile.cs - Common\Interop\Windows\Interop.RtlNtStatusToDosError.cs + Common\Interop\Windows\NtDll\Interop.RtlNtStatusToDosError.cs - + - - - - + + + + - + + Common\Interop\Windows\kernel32\Interop.LoadLibraryEx.cs + - + - + - - Common\Interop\Windows\kernel32\Interop.LoadLibraryEx.cs - - + + Common\CoreLib\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs + Common\Interop\Windows\kernel32\Interop.FreeLibrary.cs Common\Interop\Windows\kernel32\Interop.GetProcAddress.cs - - Common\CoreLib\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs - + + + Common\CoreLib\Interop\Windows\Kernel32\Interop.CloseHandle.cs + + + Common\Interop\Windows\Crypt32\Interop.certificates.cs + + + Common\Interop\Windows\Crypt32\Interop.certificates_types.cs + + + Common\Interop\Windows\Interop.Libraries.cs + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs - - Common\System\Net\Security\NegotiateStreamPal.Windows.cs + + Common\Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs - - Common\Interop\Windows\sspicliSafeDeleteContext.cs + + Common\Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs - - Common\Interop\Windows\sspicli\SecuritySafeHandles.cs + + Common\Interop\Windows\sspicli\GlobalSSPI.cs Common\Interop\Windows\sspicli\Interop.SSPI.cs - - Common\System\Net\Security\SecurityContextTokenHandle.cs - - - Common\Interop\Windows\Interop.Libraries.cs + + Common\Interop\Windows\sspicli\NegotiationInfoClass.cs - - Common\Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs + + Common\Interop\Windows\sspicli\SafeDeleteContext.cs Common\Interop\Windows\sspicli\SecPkgContext_Bindings.cs - - Common\System\Net\DebugCriticalHandleZeroOrMinusOneIsInvalid.cs - - - Common\System\Net\Security\NetEventSource.Security.cs + + Common\Interop\Windows\sspicli\SecPkgContext_NegotiationInfoW.cs - - Common\CoreLib\Interop\Windows\Kernel32\Interop.CloseHandle.cs + + Common\Interop\Windows\sspicli\SecPkgContext_Sizes.cs - - Common\Interop\Windows\sspicli\GlobalSSPI.cs + + Common\Interop\Windows\sspicli\SecPkgContext_StreamSizes.cs - - Common\Interop\Windows\sspicli\SSPIInterface.cs + + Common\Interop\Windows\sspicli\SecurityPackageInfo.cs Common\Interop\Windows\sspicli\SecurityPackageInfoClass.cs - - Common\Interop\Windows\sspicli\SecurityPackageInfo.cs + + Common\Interop\Windows\sspicli\SecuritySafeHandles.cs Common\Interop\Windows\sspicli\SSPIAuthType.cs + + Common\Interop\Windows\sspicli\SSPIInterface.cs + Common\Interop\Windows\sspicli\SSPISecureChannelType.cs Common\Interop\Windows\sspicli\SSPIWrapper.cs - - Common\System\Net\Security\NetEventSource.Security.Windows.cs - - - Common\Interop\Windows\sspicli\SecPkgContext_Sizes.cs - - - Common\Interop\Windows\sspicli\SecPkgContext_StreamSizes.cs + + Common\System\Collections\Generic\BidirectionalDictionary.cs - - Common\Interop\Windows\sspicli\SecPkgContext_NegotiationInfoW.cs + + Common\System\Net\ContextFlagsAdapterPal.Windows.cs - - Common\Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs + + Common\System\Net\DebugCriticalHandleZeroOrMinusOneIsInvalid.cs - - Common\Interop\Windows\sspicli\NegotiationInfoClass.cs + + Common\System\Net\Security\NegotiateStreamPal.Windows.cs - - Common\Interop\Windows\Crypt32\Interop.certificates.cs + + Common\System\Net\Security\NetEventSource.Security.cs - - Common\Interop\Windows\Crypt32\Interop.certificates_types.cs + + Common\System\Net\Security\NetEventSource.Security.Windows.cs - - Common\System\Net\ContextFlagsAdapterPal.Windows.cs + + Common\System\Net\Security\SecurityContextTokenHandle.cs Common\System\Net\SecurityStatusAdapterPal.Windows.cs - - Common\System\Collections\Generic\BidirectionalDictionary.cs - Common\System\Net\ContextFlagsPal.cs - - Common\System\Net\SecurityStatusPal.cs - - - Common\System\Net\Security\SecurityBufferType.cs - - - Common\System\Net\Security\SecurityBuffer.cs + + Common\System\Net\DebugCriticalHandleMinusOneIsInvalid.cs Common\System\Net\DebugSafeHandle.cs - - Common\System\Net\DebugCriticalHandleMinusOneIsInvalid.cs - - - Common\System\Net\Logging\NetEventSource.Common.cs + + Common\System\Net\InternalException.cs Common\System\Net\Logging\DebugThreadTracking.cs - - Common\System\Net\InternalException.cs + + Common\System\Net\Logging\NetEventSource.Common.cs Common\System\Net\NegotiationInfoClass.cs + + Common\System\Net\Security\SecurityBuffer.cs + + + Common\System\Net\Security\SecurityBufferType.cs + + + Common\System\Net\SecurityStatusPal.cs + - - - - Common\System\Net\Security\NegotiateStreamPal.Unix.cs - - - Common\System\Net\Security\Unix\SafeDeleteContext.cs + + Common\Interop\Unix\Interop.Libraries.cs - - Common\System\Net\Security\Unix\SafeFreeCredentials.cs + + Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs - - Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs + + Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs - - Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs + + Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs - - Common\Interop\Unix\Interop.Libraries.cs + + Common\System\Net\ContextFlagsAdapterPal.Unix.cs - - Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs + + Common\System\Net\Security\NegotiateStreamPal.Unix.cs + + + Common\System\Net\Security\Unix\SafeDeleteContext.cs Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs - - Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs + + Common\System\Net\Security\Unix\SafeFreeCredentials.cs - - Common\System\Net\ContextFlagsAdapterPal.Unix.cs + + Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs - - + + + - + + + @@ -738,48 +944,32 @@ - - - - True - True - Strings.resx - - - Microsoft.Data.SqlClient.SqlMetaData.xml - - + + + + + + - - + - - - - - + - - - - - - ResXFileCodeGenerator - Strings.Designer.cs - System - + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/AdapterUtil.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/AdapterUtil.SqlClient.cs deleted file mode 100644 index 2c66613ca0..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/AdapterUtil.SqlClient.cs +++ /dev/null @@ -1,941 +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. - -using System; -using System.Data; -using System.Data.Common; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Runtime.CompilerServices; -using System.Transactions; - -namespace Microsoft.Data.Common -{ - internal static partial class ADP - { - // The class ADP defines the exceptions that are specific to the Adapters. - // The class contains functions that take the proper informational variables and then construct - // the appropriate exception with an error string obtained from the resource framework. - // The exception is then returned to the caller, so that the caller may then throw from its - // location so that the catcher of the exception will have the appropriate call stack. - // This class is used so that there will be compile time checking of error messages. - internal static Exception ExceptionWithStackTrace(Exception e) - { - try - { - throw e; - } - catch (Exception caught) - { - return caught; - } - } - - // - // COM+ exceptions - // - internal static IndexOutOfRangeException IndexOutOfRange(int value) - { - IndexOutOfRangeException e = new IndexOutOfRangeException(value.ToString(CultureInfo.InvariantCulture)); - return e; - } - internal static IndexOutOfRangeException IndexOutOfRange() - { - IndexOutOfRangeException e = new IndexOutOfRangeException(); - return e; - } - internal static TimeoutException TimeoutException(string error) - { - TimeoutException e = new TimeoutException(error); - return e; - } - internal static InvalidOperationException InvalidOperation(string error, Exception inner) - { - InvalidOperationException e = new InvalidOperationException(error, inner); - return e; - } - internal static OverflowException Overflow(string error) - { - return Overflow(error, null); - } - internal static OverflowException Overflow(string error, Exception inner) - { - OverflowException e = new OverflowException(error, inner); - return e; - } - internal static TypeLoadException TypeLoad(string error) - { - TypeLoadException e = new TypeLoadException(error); - TraceExceptionAsReturnValue(e); - return e; - } - internal static PlatformNotSupportedException DbTypeNotSupported(string dbType) - { - PlatformNotSupportedException e = new PlatformNotSupportedException(System.StringsHelper.GetString(Strings.SQL_DbTypeNotSupportedOnThisPlatform, dbType)); - return e; - } - internal static InvalidCastException InvalidCast() - { - InvalidCastException e = new InvalidCastException(); - return e; - } - internal static IOException IO(string error) - { - IOException e = new IOException(error); - return e; - } - internal static IOException IO(string error, Exception inner) - { - IOException e = new IOException(error, inner); - return e; - } - internal static ObjectDisposedException ObjectDisposed(object instance) - { - ObjectDisposedException e = new ObjectDisposedException(instance.GetType().Name); - return e; - } - - internal static Exception DataTableDoesNotExist(string collectionName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_DataTableDoesNotExist, collectionName)); - } - - internal static InvalidOperationException MethodCalledTwice(string method) - { - InvalidOperationException e = new InvalidOperationException(System.StringsHelper.GetString(Strings.ADP_CalledTwice, method)); - return e; - } - - - // IDbCommand.CommandType - internal static ArgumentOutOfRangeException InvalidCommandType(CommandType value) - { -#if DEBUG - switch (value) - { - case CommandType.Text: - case CommandType.StoredProcedure: - case CommandType.TableDirect: - Debug.Fail("valid CommandType " + value.ToString()); - break; - } -#endif - return InvalidEnumerationValue(typeof(CommandType), (int)value); - } - - // IDbConnection.BeginTransaction, OleDbTransaction.Begin - internal static ArgumentOutOfRangeException InvalidIsolationLevel(System.Data.IsolationLevel value) - { -#if DEBUG - switch (value) - { - case System.Data.IsolationLevel.Unspecified: - case System.Data.IsolationLevel.Chaos: - case System.Data.IsolationLevel.ReadUncommitted: - case System.Data.IsolationLevel.ReadCommitted: - case System.Data.IsolationLevel.RepeatableRead: - case System.Data.IsolationLevel.Serializable: - case System.Data.IsolationLevel.Snapshot: - Debug.Fail("valid IsolationLevel " + value.ToString()); - break; - } -#endif - return InvalidEnumerationValue(typeof(System.Data.IsolationLevel), (int)value); - } - - - // IDataParameter.Direction - internal static ArgumentOutOfRangeException InvalidParameterDirection(ParameterDirection value) - { -#if DEBUG - switch (value) - { - case ParameterDirection.Input: - case ParameterDirection.Output: - case ParameterDirection.InputOutput: - case ParameterDirection.ReturnValue: - Debug.Fail("valid ParameterDirection " + value.ToString()); - break; - } -#endif - return InvalidEnumerationValue(typeof(ParameterDirection), (int)value); - } - - internal static Exception TooManyRestrictions(string collectionName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_TooManyRestrictions, collectionName)); - } - - - // IDbCommand.UpdateRowSource - internal static ArgumentOutOfRangeException InvalidUpdateRowSource(UpdateRowSource value) - { -#if DEBUG - switch (value) - { - case UpdateRowSource.None: - case UpdateRowSource.OutputParameters: - case UpdateRowSource.FirstReturnedRecord: - case UpdateRowSource.Both: - Debug.Fail("valid UpdateRowSource " + value.ToString()); - break; - } -#endif - return InvalidEnumerationValue(typeof(UpdateRowSource), (int)value); - } - - // - // DbConnectionOptions, DataAccess - // - internal static ArgumentException InvalidMinMaxPoolSizeValues() - { - return ADP.Argument(System.StringsHelper.GetString(Strings.ADP_InvalidMinMaxPoolSizeValues)); - } - - - // - // DbConnection - // - internal static InvalidOperationException NoConnectionString() - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_NoConnectionString)); - } - - internal static Exception MethodNotImplemented([CallerMemberName] string methodName = "") - { - return NotImplemented.ByDesignWithMessage(methodName); - } - - internal static Exception QueryFailed(string collectionName, Exception e) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.MDF_QueryFailed, collectionName), e); - } - - - // - // : DbConnectionOptions, DataAccess, SqlClient - // - internal static Exception InvalidConnectionOptionValueLength(string key, int limit) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidConnectionOptionValueLength, key, limit)); - } - internal static Exception MissingConnectionOptionValue(string key, string requiredAdditionalKey) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_MissingConnectionOptionValue, key, requiredAdditionalKey)); - } - - - // - // DbConnectionPool and related - // - internal static Exception PooledOpenTimeout() - { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.ADP_PooledOpenTimeout)); - } - - internal static Exception NonPooledOpenTimeout() - { - return ADP.TimeoutException(System.StringsHelper.GetString(Strings.ADP_NonPooledOpenTimeout)); - } - - // - // DbProviderException - // - internal static InvalidOperationException TransactionConnectionMismatch() - { - return Provider(System.StringsHelper.GetString(Strings.ADP_TransactionConnectionMismatch)); - } - internal static InvalidOperationException TransactionRequired(string method) - { - return Provider(System.StringsHelper.GetString(Strings.ADP_TransactionRequired, method)); - } - - - internal static Exception CommandTextRequired(string method) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_CommandTextRequired, method)); - } - - internal static Exception NoColumns() - { - return Argument(System.StringsHelper.GetString(Strings.MDF_NoColumns)); - } - - internal static InvalidOperationException ConnectionRequired(string method) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_ConnectionRequired, method)); - } - internal static InvalidOperationException OpenConnectionRequired(string method, ConnectionState state) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_OpenConnectionRequired, method, ADP.ConnectionStateMsg(state))); - } - - internal static Exception OpenReaderExists(bool marsOn) - { - return OpenReaderExists(null, marsOn); - } - - internal static Exception OpenReaderExists(Exception e, bool marsOn) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_OpenReaderExists, marsOn ? ADP.Command : ADP.Connection), e); - } - - - // - // DbDataReader - // - internal static Exception NonSeqByteAccess(long badIndex, long currIndex, string method) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_NonSeqByteAccess, badIndex.ToString(CultureInfo.InvariantCulture), currIndex.ToString(CultureInfo.InvariantCulture), method)); - } - - internal static Exception InvalidXml() - { - return Argument(System.StringsHelper.GetString(Strings.MDF_InvalidXml)); - } - - internal static Exception NegativeParameter(string parameterName) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_NegativeParameter, parameterName)); - } - - internal static Exception InvalidXmlMissingColumn(string collectionName, string columnName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_InvalidXmlMissingColumn, collectionName, columnName)); - } - - // - // SqlMetaData, SqlTypes, SqlClient - // - internal static Exception InvalidMetaDataValue() - { - return ADP.Argument(System.StringsHelper.GetString(Strings.ADP_InvalidMetaDataValue)); - } - - internal static InvalidOperationException NonSequentialColumnAccess(int badCol, int currCol) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_NonSequentialColumnAccess, badCol.ToString(CultureInfo.InvariantCulture), currCol.ToString(CultureInfo.InvariantCulture))); - } - - internal static Exception InvalidXmlInvalidValue(string collectionName, string columnName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_InvalidXmlInvalidValue, collectionName, columnName)); - } - - internal static Exception CollectionNameIsNotUnique(string collectionName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_CollectionNameISNotUnique, collectionName)); - } - - - // - // : IDbCommand - // - internal static Exception InvalidCommandTimeout(int value, [CallerMemberName] string property = "") - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidCommandTimeout, value.ToString(CultureInfo.InvariantCulture)), property); - } - internal static Exception UninitializedParameterSize(int index, Type dataType) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_UninitializedParameterSize, index.ToString(CultureInfo.InvariantCulture), dataType.Name)); - } - - internal static Exception UnableToBuildCollection(string collectionName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_UnableToBuildCollection, collectionName)); - } - - internal static Exception PrepareParameterType(DbCommand cmd) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_PrepareParameterType, cmd.GetType().Name)); - } - - internal static Exception UndefinedCollection(string collectionName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_UndefinedCollection, collectionName)); - } - - internal static Exception UnsupportedVersion(string collectionName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_UnsupportedVersion, collectionName)); - } - - internal static Exception AmbiguousCollectionName(string collectionName) - { - return Argument(System.StringsHelper.GetString(Strings.MDF_AmbiguousCollectionName, collectionName)); - } - - internal static Exception PrepareParameterSize(DbCommand cmd) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_PrepareParameterSize, cmd.GetType().Name)); - } - internal static Exception PrepareParameterScale(DbCommand cmd, string type) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_PrepareParameterScale, cmd.GetType().Name, type)); - } - - internal static Exception MissingDataSourceInformationColumn() - { - return Argument(System.StringsHelper.GetString(Strings.MDF_MissingDataSourceInformationColumn)); - } - - internal static Exception IncorrectNumberOfDataSourceInformationRows() - { - return Argument(System.StringsHelper.GetString(Strings.MDF_IncorrectNumberOfDataSourceInformationRows)); - } - - internal static Exception MismatchedAsyncResult(string expectedMethod, string gotMethod) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_MismatchedAsyncResult, expectedMethod, gotMethod)); - } - - // - // : ConnectionUtil - // - internal static Exception ClosedConnectionError() - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_ClosedConnectionError)); - } - internal static Exception ConnectionAlreadyOpen(ConnectionState state) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_ConnectionAlreadyOpen, ADP.ConnectionStateMsg(state))); - } - internal static Exception TransactionPresent() - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_TransactionPresent)); - } - internal static Exception LocalTransactionPresent() - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_LocalTransactionPresent)); - } - internal static Exception OpenConnectionPropertySet(string property, ConnectionState state) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_OpenConnectionPropertySet, property, ADP.ConnectionStateMsg(state))); - } - internal static Exception EmptyDatabaseName() - { - return Argument(System.StringsHelper.GetString(Strings.ADP_EmptyDatabaseName)); - } - - internal enum ConnectionError - { - BeginGetConnectionReturnsNull, - GetConnectionReturnsNull, - ConnectionOptionsMissing, - CouldNotSwitchToClosedPreviouslyOpenedState, - } - - internal static Exception MissingRestrictionColumn() - { - return Argument(System.StringsHelper.GetString(Strings.MDF_MissingRestrictionColumn)); - } - - internal static Exception InternalConnectionError(ConnectionError internalError) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_InternalConnectionError, (int)internalError)); - } - - internal static Exception InvalidConnectRetryCountValue() - { - return Argument(System.StringsHelper.GetString(Strings.SQLCR_InvalidConnectRetryCountValue)); - } - - internal static Exception MissingRestrictionRow() - { - return Argument(System.StringsHelper.GetString(Strings.MDF_MissingRestrictionRow)); - } - - internal static Exception InvalidConnectRetryIntervalValue() - { - return Argument(System.StringsHelper.GetString(Strings.SQLCR_InvalidConnectRetryIntervalValue)); - } - - // - // : DbDataReader - // - internal static InvalidOperationException AsyncOperationPending() - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_PendingAsyncOperation)); - } - - // - // : Stream - // - internal static IOException ErrorReadingFromStream(Exception internalException) - { - return IO(System.StringsHelper.GetString(Strings.SqlMisc_StreamErrorMessage), internalException); - } - - internal static ArgumentException InvalidDataType(TypeCode typecode) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidDataType, typecode.ToString())); - } - - internal static ArgumentException UnknownDataType(Type dataType) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_UnknownDataType, dataType.FullName)); - } - - internal static ArgumentException DbTypeNotSupported(DbType type, Type enumtype) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_DbTypeNotSupported, type.ToString(), enumtype.Name)); - } - internal static ArgumentException UnknownDataTypeCode(Type dataType, TypeCode typeCode) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_UnknownDataTypeCode, ((int)typeCode).ToString(CultureInfo.InvariantCulture), dataType.FullName)); - } - internal static ArgumentException InvalidOffsetValue(int value) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidOffsetValue, value.ToString(CultureInfo.InvariantCulture))); - } - internal static ArgumentException InvalidSizeValue(int value) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidSizeValue, value.ToString(CultureInfo.InvariantCulture))); - } - internal static ArgumentException ParameterValueOutOfRange(decimal value) - { - return ADP.Argument(System.StringsHelper.GetString(Strings.ADP_ParameterValueOutOfRange, value.ToString((IFormatProvider)null))); - } - internal static ArgumentException ParameterValueOutOfRange(SqlDecimal value) - { - return ADP.Argument(System.StringsHelper.GetString(Strings.ADP_ParameterValueOutOfRange, value.ToString())); - } - internal static ArgumentException ParameterValueOutOfRange(String value) - { - return ADP.Argument(System.StringsHelper.GetString(Strings.ADP_ParameterValueOutOfRange, value)); - } - internal static ArgumentException VersionDoesNotSupportDataType(string typeName) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_VersionDoesNotSupportDataType, typeName)); - } - internal static Exception ParameterConversionFailed(object value, Type destType, Exception inner) - { - Debug.Assert(null != value, "null value on conversion failure"); - Debug.Assert(null != inner, "null inner on conversion failure"); - - Exception e; - string message = System.StringsHelper.GetString(Strings.ADP_ParameterConversionFailed, value.GetType().Name, destType.Name); - if (inner is ArgumentException) - { - e = new ArgumentException(message, inner); - } - else if (inner is FormatException) - { - e = new FormatException(message, inner); - } - else if (inner is InvalidCastException) - { - e = new InvalidCastException(message, inner); - } - else if (inner is OverflowException) - { - e = new OverflowException(message, inner); - } - else - { - e = inner; - } - return e; - } - - // - // : IDataParameterCollection - // - internal static Exception ParametersMappingIndex(int index, DbParameterCollection collection) - { - return CollectionIndexInt32(index, collection.GetType(), collection.Count); - } - internal static Exception ParametersSourceIndex(string parameterName, DbParameterCollection collection, Type parameterType) - { - return CollectionIndexString(parameterType, ADP.ParameterName, parameterName, collection.GetType()); - } - internal static Exception ParameterNull(string parameter, DbParameterCollection collection, Type parameterType) - { - return CollectionNullValue(parameter, collection.GetType(), parameterType); - } - - internal static Exception UndefinedPopulationMechanism(string populationMechanism) - { - throw new NotImplementedException(); - } - - internal static Exception InvalidParameterType(DbParameterCollection collection, Type parameterType, object invalidValue) - { - return CollectionInvalidType(collection.GetType(), parameterType, invalidValue); - } - - // - // : IDbTransaction - // - internal static Exception ParallelTransactionsNotSupported(DbConnection obj) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_ParallelTransactionsNotSupported, obj.GetType().Name)); - } - internal static Exception TransactionZombied(DbTransaction obj) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_TransactionZombied, obj.GetType().Name)); - } - - // global constant strings - internal const string ColumnEncryptionSystemProviderNamePrefix = "MSSQL_"; - internal const string Command = "Command"; - internal const string Connection = "Connection"; - internal const string Parameter = "Parameter"; - internal const string ParameterName = "ParameterName"; - internal const string ParameterSetPosition = "set_Position"; - - internal const int DefaultCommandTimeout = 30; - internal const float FailoverTimeoutStep = 0.08F; // fraction of timeout to use for fast failover connections - - // security issue, don't rely upon public static readonly values - internal static readonly string StrEmpty = ""; // String.Empty - - internal const int CharSize = sizeof(char); - - internal static Delegate FindBuilder(MulticastDelegate mcd) - { - if (null != mcd) - { - foreach (Delegate del in mcd.GetInvocationList()) - { - if (del.Target is DbCommandBuilder) - return del; - } - } - - return null; - } - - internal static void TimerCurrent(out long ticks) - { - ticks = DateTime.UtcNow.ToFileTimeUtc(); - } - - internal static long TimerCurrent() - { - return DateTime.UtcNow.ToFileTimeUtc(); - } - - internal static long TimerFromSeconds(int seconds) - { - long result = checked((long)seconds * TimeSpan.TicksPerSecond); - return result; - } - - internal static long TimerFromMilliseconds(long milliseconds) - { - long result = checked(milliseconds * TimeSpan.TicksPerMillisecond); - return result; - } - - internal static bool TimerHasExpired(long timerExpire) - { - bool result = TimerCurrent() > timerExpire; - return result; - } - - internal static long TimerRemaining(long timerExpire) - { - long timerNow = TimerCurrent(); - long result = checked(timerExpire - timerNow); - return result; - } - - internal static long TimerRemainingMilliseconds(long timerExpire) - { - long result = TimerToMilliseconds(TimerRemaining(timerExpire)); - return result; - } - - internal static long TimerRemainingSeconds(long timerExpire) - { - long result = TimerToSeconds(TimerRemaining(timerExpire)); - return result; - } - - internal static long TimerToMilliseconds(long timerValue) - { - long result = timerValue / TimeSpan.TicksPerMillisecond; - return result; - } - - private static long TimerToSeconds(long timerValue) - { - long result = timerValue / TimeSpan.TicksPerSecond; - return result; - } - - internal static string MachineName() - { - return Environment.MachineName; - } - - internal static Transaction GetCurrentTransaction() - { - return Transaction.Current; - } - - internal static bool IsDirection(DbParameter value, ParameterDirection condition) - { -#if DEBUG - IsDirectionValid(condition); -#endif - return (condition == (condition & value.Direction)); - } -#if DEBUG - private static void IsDirectionValid(ParameterDirection value) - { - switch (value) - { // @perfnote: Enum.IsDefined - case ParameterDirection.Input: - case ParameterDirection.Output: - case ParameterDirection.InputOutput: - case ParameterDirection.ReturnValue: - break; - default: - throw ADP.InvalidParameterDirection(value); - } - } -#endif - - internal static void IsNullOrSqlType(object value, out bool isNull, out bool isSqlType) - { - if ((value == null) || (value == DBNull.Value)) - { - isNull = true; - isSqlType = false; - } - else - { - INullable nullable = (value as INullable); - if (nullable != null) - { - isNull = nullable.IsNull; - // Duplicated from DataStorage.cs - // For back-compat, SqlXml is not in this list - isSqlType = ((value is SqlBinary) || - (value is SqlBoolean) || - (value is SqlByte) || - (value is SqlBytes) || - (value is SqlChars) || - (value is SqlDateTime) || - (value is SqlDecimal) || - (value is SqlDouble) || - (value is SqlGuid) || - (value is SqlInt16) || - (value is SqlInt32) || - (value is SqlInt64) || - (value is SqlMoney) || - (value is SqlSingle) || - (value is SqlString)); - } - else - { - isNull = false; - isSqlType = false; - } - } - } - - private static Version s_systemDataVersion; - - internal static Version GetAssemblyVersion() - { - // NOTE: Using lazy thread-safety since we don't care if two threads both happen to update the value at the same time - if (s_systemDataVersion == null) - { - s_systemDataVersion = new Version(ThisAssembly.InformationalVersion); - } - - return s_systemDataVersion; - } - - - internal static readonly string[] AzureSqlServerEndpoints = {System.StringsHelper.GetString(Strings.AZURESQL_GenericEndpoint), - System.StringsHelper.GetString(Strings.AZURESQL_GermanEndpoint), - System.StringsHelper.GetString(Strings.AZURESQL_UsGovEndpoint), - System.StringsHelper.GetString(Strings.AZURESQL_ChinaEndpoint)}; - - // This method assumes dataSource parameter is in TCP connection string format. - internal static bool IsAzureSqlServerEndpoint(string dataSource) - { - // remove server port - int i = dataSource.LastIndexOf(','); - if (i >= 0) - { - dataSource = dataSource.Substring(0, i); - } - - // check for the instance name - i = dataSource.LastIndexOf('\\'); - if (i >= 0) - { - dataSource = dataSource.Substring(0, i); - } - - // trim redundant whitespace - dataSource = dataSource.Trim(); - - // check if servername end with any azure endpoints - for (i = 0; i < AzureSqlServerEndpoints.Length; i++) - { - if (dataSource.EndsWith(AzureSqlServerEndpoints[i], StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - - internal static ArgumentOutOfRangeException InvalidDataRowVersion(DataRowVersion value) - { -#if DEBUG - switch (value) - { - case DataRowVersion.Default: - case DataRowVersion.Current: - case DataRowVersion.Original: - case DataRowVersion.Proposed: - Debug.Fail($"Invalid DataRowVersion {value}"); - break; - } -#endif - return InvalidEnumerationValue(typeof(DataRowVersion), (int)value); - } - - internal static ArgumentException SingleValuedProperty(string propertyName, string value) - { - ArgumentException e = new ArgumentException(System.StringsHelper.GetString(Strings.ADP_SingleValuedProperty, propertyName, value)); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentException DoubleValuedProperty(string propertyName, string value1, string value2) - { - ArgumentException e = new ArgumentException(System.StringsHelper.GetString(Strings.ADP_DoubleValuedProperty, propertyName, value1, value2)); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentException InvalidPrefixSuffix() - { - ArgumentException e = new ArgumentException(System.StringsHelper.GetString(Strings.ADP_InvalidPrefixSuffix)); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentOutOfRangeException InvalidCommandBehavior(CommandBehavior value) - { - Debug.Assert((0 > (int)value) || ((int)value > 0x3F), "valid CommandType " + value.ToString()); - - return InvalidEnumerationValue(typeof(CommandBehavior), (int)value); - } - - internal static void ValidateCommandBehavior(CommandBehavior value) - { - if (((int)value < 0) || (0x3F < (int)value)) - { - throw InvalidCommandBehavior(value); - } - } - - internal static ArgumentOutOfRangeException NotSupportedCommandBehavior(CommandBehavior value, string method) - { - return NotSupportedEnumerationValue(typeof(CommandBehavior), value.ToString(), method); - } - - internal static ArgumentException BadParameterName(string parameterName) - { - ArgumentException e = new ArgumentException(System.StringsHelper.GetString(Strings.ADP_BadParameterName, parameterName)); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static Exception DeriveParametersNotSupported(IDbCommand value) - { - return DataAdapter(System.StringsHelper.GetString(Strings.ADP_DeriveParametersNotSupported, value.GetType().Name, value.CommandType.ToString())); - } - - internal static Exception NoStoredProcedureExists(string sproc) - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_NoStoredProcedureExists, sproc)); - } - - // - // DbProviderException - // - internal static InvalidOperationException TransactionCompletedButNotDisposed() - { - return Provider(System.StringsHelper.GetString(Strings.ADP_TransactionCompletedButNotDisposed)); - } - - internal static ArgumentOutOfRangeException InvalidUserDefinedTypeSerializationFormat(Microsoft.Data.SqlClient.Server.Format value) - { - return InvalidEnumerationValue(typeof(Microsoft.Data.SqlClient.Server.Format), (int)value); - } - - internal static ArgumentOutOfRangeException NotSupportedUserDefinedTypeSerializationFormat(Microsoft.Data.SqlClient.Server.Format value, string method) - { - return NotSupportedEnumerationValue(typeof(Microsoft.Data.SqlClient.Server.Format), value.ToString(), method); - } - - internal static ArgumentOutOfRangeException ArgumentOutOfRange(string message, string parameterName, object value) - { - ArgumentOutOfRangeException e = new ArgumentOutOfRangeException(parameterName, value, message); - TraceExceptionAsReturnValue(e); - return e; - } - - internal static ArgumentException InvalidArgumentLength(string argumentName, int limit) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidArgumentLength, argumentName, limit)); - } - - internal static ArgumentException MustBeReadOnly(string argumentName) - { - return Argument(System.StringsHelper.GetString(Strings.ADP_MustBeReadOnly, argumentName)); - } - - internal static InvalidOperationException InvalidMixedUsageOfSecureAndClearCredential() - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfSecureAndClearCredential)); - } - - internal static ArgumentException InvalidMixedArgumentOfSecureAndClearCredential() - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfSecureAndClearCredential)); - } - - internal static InvalidOperationException InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity() - { - return InvalidOperation(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity)); - } - - internal static ArgumentException InvalidMixedArgumentOfSecureCredentialAndIntegratedSecurity() - { - return Argument(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity)); - } - internal static InvalidOperationException InvalidMixedUsageOfAccessTokenAndIntegratedSecurity() - { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenAndIntegratedSecurity)); - } - static internal InvalidOperationException InvalidMixedUsageOfAccessTokenAndUserIDPassword() - { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenAndUserIDPassword)); - } - - static internal InvalidOperationException InvalidMixedUsageOfAccessTokenAndAuthentication() - { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfAccessTokenAndAuthentication)); - } - - static internal Exception InvalidMixedUsageOfCredentialAndAccessToken() - { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfCredentialAndAccessToken)); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs index c90bb1f3bf..35440e9e11 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Text; @@ -12,104 +10,25 @@ namespace Microsoft.Data.Common { internal partial class DbConnectionOptions { - // instances of this class are intended to be immutable, i.e readonly - // used by pooling classes so it is much easier to verify correctness - // when not worried about the class being modified during execution - - public DbConnectionOptions(string connectionString, Dictionary synonyms) - { - _parsetable = new Dictionary(); - _usersConnectionString = ((null != connectionString) ? connectionString : ""); - - // first pass on parsing, initial syntax check - if (0 < _usersConnectionString.Length) - { - _keyChain = ParseInternal(_parsetable, _usersConnectionString, true, synonyms, false); - HasPasswordKeyword = (_parsetable.ContainsKey(KEY.Password) || _parsetable.ContainsKey(SYNONYM.Pwd)); - HasUserIdKeyword = (_parsetable.ContainsKey(KEY.User_ID) || _parsetable.ContainsKey(SYNONYM.UID)); - } - } - - protected DbConnectionOptions(DbConnectionOptions connectionOptions) - { // Clone used by SqlConnectionString - _usersConnectionString = connectionOptions._usersConnectionString; - _parsetable = connectionOptions._parsetable; - _keyChain = connectionOptions._keyChain; - HasPasswordKeyword = connectionOptions.HasPasswordKeyword; - HasUserIdKeyword = connectionOptions.HasUserIdKeyword; - } - - public bool IsEmpty => _keyChain == null; - - internal bool TryGetParsetableValue(string key, out string value) => _parsetable.TryGetValue(key, out value); - - // same as Boolean, but with SSPI thrown in as valid yes - public bool ConvertValueToIntegratedSecurity() + internal string ExpandAttachDbFileName(string replacementValue) { - string value; - return _parsetable.TryGetValue(KEY.Integrated_Security, out value) && value != null ? - ConvertValueToIntegratedSecurityInternal(value) : - false; - } + int copyPosition = 0; - internal bool ConvertValueToIntegratedSecurityInternal(string stringValue) - { - if (CompareInsensitiveInvariant(stringValue, "sspi") || CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes")) - return true; - else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no")) - return false; - else + StringBuilder builder = new(_usersConnectionString.Length); + for (NameValuePair current = _keyChain; null != current; current = current.Next) { - string tmp = stringValue.Trim(); // Remove leading & trailing whitespace. - if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes")) - return true; - else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no")) - return false; + if (string.Equals(current.Name, DbConnectionStringKeywords.AttachDBFilename, StringComparison.InvariantCultureIgnoreCase)) + { + builder.Append($"{current.Name}={replacementValue};"); + } else { - throw ADP.InvalidConnectionOptionValue(KEY.Integrated_Security); + builder.Append(_usersConnectionString, copyPosition, current.Length); } + copyPosition += current.Length; } - } - - public int ConvertValueToInt32(string keyName, int defaultValue) - { - string value; - return _parsetable.TryGetValue(keyName, out value) && value != null ? - ConvertToInt32Internal(keyName, value) : - defaultValue; - } - - internal static int ConvertToInt32Internal(string keyname, string stringValue) - { - try - { - return int.Parse(stringValue, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture); - } - catch (FormatException e) - { - throw ADP.InvalidConnectionOptionValue(keyname, e); - } - catch (OverflowException e) - { - throw ADP.InvalidConnectionOptionValue(keyname, e); - } - } - public string ConvertValueToString(string keyName, string defaultValue) - { - string value; - return _parsetable.TryGetValue(keyName, out value) && value != null ? value : defaultValue; - } - - public bool ContainsKey(string keyword) - { - return _parsetable.ContainsKey(keyword); - } - - protected internal virtual string Expand() - { - return _usersConnectionString; + return builder.ToString(); } // SxS notes: @@ -151,25 +70,5 @@ internal static string ExpandDataDirectory(string keyword, string value) return fullPath; } - internal string ExpandAttachDbFileName(string replacementValue) - { - int copyPosition = 0; - - StringBuilder builder = new StringBuilder(_usersConnectionString.Length); - for (NameValuePair current = _keyChain; null != current; current = current.Next) - { - if (current.Name == KEY.AttachDBFileName) - { - builder.Append($"{KEY.AttachDBFileName}={replacementValue};"); - } - else - { - builder.Append(_usersConnectionString, copyPosition, current.Length); - } - copyPosition += current.Length; - } - - return builder.ToString(); - } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.NetCoreApp.cs deleted file mode 100644 index 21de544144..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.NetCoreApp.cs +++ /dev/null @@ -1,147 +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. - -using System; -using System.Diagnostics; -using Microsoft.Data.SqlClient; - -namespace Microsoft.Data.Common -{ - internal static partial class DbConnectionStringBuilderUtil - { - #region <> - internal static bool TryConvertToPoolBlockingPeriod(string value, out PoolBlockingPeriod result) - { - Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed"); - Debug.Assert(null != value, "TryConvertToPoolBlockingPeriod(null,...)"); - - if (StringComparer.OrdinalIgnoreCase.Equals(value, nameof(PoolBlockingPeriod.Auto))) - { - result = PoolBlockingPeriod.Auto; - return true; - } - else if (StringComparer.OrdinalIgnoreCase.Equals(value, nameof(PoolBlockingPeriod.AlwaysBlock))) - { - result = PoolBlockingPeriod.AlwaysBlock; - return true; - } - else if (StringComparer.OrdinalIgnoreCase.Equals(value, nameof(PoolBlockingPeriod.NeverBlock))) - { - result = PoolBlockingPeriod.NeverBlock; - return true; - } - else - { - result = DbConnectionStringDefaults.PoolBlockingPeriod; - return false; - } - } - - internal static bool IsValidPoolBlockingPeriodValue(PoolBlockingPeriod value) - { - Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed"); - return (uint)value <= (uint)PoolBlockingPeriod.NeverBlock; - } - - internal static string PoolBlockingPeriodToString(PoolBlockingPeriod value) - { - Debug.Assert(IsValidPoolBlockingPeriodValue(value)); - - switch (value) - { - case PoolBlockingPeriod.AlwaysBlock: - return nameof(PoolBlockingPeriod.AlwaysBlock); - case PoolBlockingPeriod.NeverBlock: - return nameof(PoolBlockingPeriod.NeverBlock); - default: - return nameof(PoolBlockingPeriod.Auto); - } - } - - /// - /// This method attempts to convert the given value to a PoolBlockingPeriod enum. The algorithm is: - /// * if the value is from type string, it will be matched against PoolBlockingPeriod enum names only, using ordinal, case-insensitive comparer - /// * if the value is from type PoolBlockingPeriod, it will be used as is - /// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum - /// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException - /// - /// in any case above, if the converted value is out of valid range, the method raises ArgumentOutOfRangeException. - /// - /// PoolBlockingPeriod value in the valid range - internal static PoolBlockingPeriod ConvertToPoolBlockingPeriod(string keyword, object value) - { - Debug.Assert(null != value, "ConvertToPoolBlockingPeriod(null)"); - string sValue = (value as string); - PoolBlockingPeriod result; - if (null != sValue) - { - // We could use Enum.TryParse here, but it accepts value combinations like - // "ReadOnly, ReadWrite" which are unwelcome here - // Also, Enum.TryParse is 100x slower than plain StringComparer.OrdinalIgnoreCase.Equals method. - if (TryConvertToPoolBlockingPeriod(sValue, out result)) - { - return result; - } - - // try again after remove leading & trailing whitespaces. - sValue = sValue.Trim(); - if (TryConvertToPoolBlockingPeriod(sValue, out result)) - { - return result; - } - - // string values must be valid - throw ADP.InvalidConnectionOptionValue(keyword); - } - else - { - // the value is not string, try other options - PoolBlockingPeriod eValue; - - if (value is PoolBlockingPeriod) - { - // quick path for the most common case - eValue = (PoolBlockingPeriod)value; - } - else if (value.GetType().IsEnum) - { - // explicitly block scenarios in which user tries to use wrong enum types, like: - // builder["PoolBlockingPeriod"] = EnvironmentVariableTarget.Process; - // workaround: explicitly cast non-PoolBlockingPeriod enums to int - throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), null); - } - else - { - try - { - // Enum.ToObject allows only integral and enum values (enums are blocked above), raising ArgumentException for the rest - eValue = (PoolBlockingPeriod)Enum.ToObject(typeof(PoolBlockingPeriod), value); - } - catch (ArgumentException e) - { - // to be consistent with the messages we send in case of wrong type usage, replace - // the error with our exception, and keep the original one as inner one for troubleshooting - throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), e); - } - } - - // ensure value is in valid range - if (IsValidPoolBlockingPeriodValue(eValue)) - { - return eValue; - } - else - { - throw ADP.InvalidEnumerationValue(typeof(ApplicationIntent), (int)eValue); - } - } - } - #endregion - } - - internal static partial class DbConnectionStringDefaults - { - internal const PoolBlockingPeriod PoolBlockingPeriod = Microsoft.Data.SqlClient.PoolBlockingPeriod.Auto; - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs deleted file mode 100644 index eb23fcdfef..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ /dev/null @@ -1,863 +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. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.Reflection; -using Microsoft.Data.SqlClient; - -namespace Microsoft.Data.Common -{ - internal static partial class DbConnectionStringBuilderUtil - { - internal static bool ConvertToBoolean(object value) - { - Debug.Assert(null != value, "ConvertToBoolean(null)"); - string svalue = (value as string); - if (null != svalue) - { - if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "true") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "yes")) - return true; - else if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "false") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "no")) - return false; - else - { - string tmp = svalue.Trim(); // Remove leading & trailing whitespace. - if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "true") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "yes")) - return true; - else if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "false") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "no")) - return false; - } - return bool.Parse(svalue); - } - try - { - return Convert.ToBoolean(value); - } - catch (InvalidCastException e) - { - throw ADP.ConvertFailed(value.GetType(), typeof(bool), e); - } - } - - internal static bool ConvertToIntegratedSecurity(object value) - { - Debug.Assert(null != value, "ConvertToIntegratedSecurity(null)"); - string svalue = (value as string); - if (null != svalue) - { - if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "sspi") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "true") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "yes")) - return true; - else if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "false") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "no")) - return false; - else - { - string tmp = svalue.Trim(); // Remove leading & trailing whitespace. - if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "sspi") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "true") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "yes")) - return true; - else if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "false") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "no")) - return false; - } - return bool.Parse(svalue); - } - try - { - return Convert.ToBoolean(value); - } - catch (InvalidCastException e) - { - throw ADP.ConvertFailed(value.GetType(), typeof(bool), e); - } - } - - internal static int ConvertToInt32(object value) - { - try - { - return Convert.ToInt32(value); - } - catch (InvalidCastException e) - { - throw ADP.ConvertFailed(value.GetType(), typeof(int), e); - } - } - - internal static string ConvertToString(object value) - { - try - { - return Convert.ToString(value); - } - catch (InvalidCastException e) - { - throw ADP.ConvertFailed(value.GetType(), typeof(string), e); - } - } - - private const string ApplicationIntentReadWriteString = "ReadWrite"; - private const string ApplicationIntentReadOnlyString = "ReadOnly"; - const string SqlPasswordString = "Sql Password"; - const string ActiveDirectoryPasswordString = "Active Directory Password"; - const string ActiveDirectoryIntegratedString = "Active Directory Integrated"; - const string ActiveDirectoryInteractiveString = "Active Directory Interactive"; - const string ActiveDirectoryServicePrincipalString = "Active Directory Service Principal"; - const string ActiveDirectoryDeviceCodeFlowString = "Active Directory Device Code Flow"; - internal const string ActiveDirectoryManagedIdentityString = "Active Directory Managed Identity"; - internal const string ActiveDirectoryMSIString = "Active Directory MSI"; - - internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result) - { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); - - bool isSuccess = false; - - if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlPasswordString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.SqlPassword, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.SqlPassword; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryPasswordString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryPassword, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryPassword; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryIntegratedString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryIntegrated, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryIntegrated; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryInteractiveString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryInteractive, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryInteractive; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryServicePrincipalString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryServicePrincipal; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryDeviceCodeFlowString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryManagedIdentityString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryManagedIdentity; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryMSIString) - || StringComparer.InvariantCultureIgnoreCase.Equals(value, Convert.ToString(SqlAuthenticationMethod.ActiveDirectoryMSI, CultureInfo.InvariantCulture))) - { - result = SqlAuthenticationMethod.ActiveDirectoryMSI; - isSuccess = true; - } - else - { - result = DbConnectionStringDefaults.Authentication; - } - return isSuccess; - } - - internal static bool TryConvertToApplicationIntent(string value, out ApplicationIntent result) - { - Debug.Assert(Enum.GetNames(typeof(ApplicationIntent)).Length == 2, "ApplicationIntent enum has changed, update needed"); - Debug.Assert(null != value, "TryConvertToApplicationIntent(null,...)"); - - if (StringComparer.OrdinalIgnoreCase.Equals(value, ApplicationIntentReadOnlyString)) - { - result = ApplicationIntent.ReadOnly; - return true; - } - else if (StringComparer.OrdinalIgnoreCase.Equals(value, ApplicationIntentReadWriteString)) - { - result = ApplicationIntent.ReadWrite; - return true; - } - else - { - result = DbConnectionStringDefaults.ApplicationIntent; - return false; - } - } - - /// - /// Column Encryption Setting. - /// - const string ColumnEncryptionSettingEnabledString = "Enabled"; - const string ColumnEncryptionSettingDisabledString = "Disabled"; - - /// - /// Convert a string value to the corresponding SqlConnectionColumnEncryptionSetting. - /// - /// - /// - /// - internal static bool TryConvertToColumnEncryptionSetting(string value, out SqlConnectionColumnEncryptionSetting result) - { - bool isSuccess = false; - - if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ColumnEncryptionSettingEnabledString)) - { - result = SqlConnectionColumnEncryptionSetting.Enabled; - isSuccess = true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ColumnEncryptionSettingDisabledString)) - { - result = SqlConnectionColumnEncryptionSetting.Disabled; - isSuccess = true; - } - else - { - result = DbConnectionStringDefaults.ColumnEncryptionSetting; - } - - return isSuccess; - } - - /// - /// Is it a valid connection level column encryption setting ? - /// - /// - /// - internal static bool IsValidColumnEncryptionSetting(SqlConnectionColumnEncryptionSetting value) - { - Debug.Assert(Enum.GetNames(typeof(SqlConnectionColumnEncryptionSetting)).Length == 2, "SqlConnectionColumnEncryptionSetting enum has changed, update needed"); - return value == SqlConnectionColumnEncryptionSetting.Enabled || value == SqlConnectionColumnEncryptionSetting.Disabled; - } - - /// - /// Convert connection level column encryption setting value to string. - /// - /// - /// - internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryptionSetting value) - { - Debug.Assert(IsValidColumnEncryptionSetting(value), "value is not a valid connection level column encryption setting."); - - switch (value) - { - case SqlConnectionColumnEncryptionSetting.Enabled: - return ColumnEncryptionSettingEnabledString; - case SqlConnectionColumnEncryptionSetting.Disabled: - return ColumnEncryptionSettingDisabledString; - - default: - return null; - } - } - - #region <> - - /// - /// Attestation Protocol. - /// - const string AttestationProtocolHGS = "HGS"; - const string AttestationProtocolAAS = "AAS"; -#if ENCLAVE_SIMULATOR - const string AttestationProtocolSIM = "SIM"; -#endif - - /// - /// Convert a string value to the corresponding SqlConnectionAttestationProtocol - /// - /// - /// - /// - internal static bool TryConvertToAttestationProtocol(string value, out SqlConnectionAttestationProtocol result) - { - if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolHGS)) - { - result = SqlConnectionAttestationProtocol.HGS; - return true; - } - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolAAS)) - { - result = SqlConnectionAttestationProtocol.AAS; - return true; - } -#if ENCLAVE_SIMULATOR - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, AttestationProtocolSIM)) - { - result = SqlConnectionAttestationProtocol.SIM; - return true; - } -#endif - else - { - result = DbConnectionStringDefaults.AttestationProtocol; - return false; - } - } - - internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) - { -#if ENCLAVE_SIMULATOR - Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 4, "SqlConnectionAttestationProtocol enum has changed, update needed"); - return value == SqlConnectionAttestationProtocol.NotSpecified - || value == SqlConnectionAttestationProtocol.HGS - || value == SqlConnectionAttestationProtocol.AAS - || value == SqlConnectionAttestationProtocol.SIM; -#else - Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); - return value == SqlConnectionAttestationProtocol.NotSpecified - || value == SqlConnectionAttestationProtocol.HGS - || value == SqlConnectionAttestationProtocol.AAS; -#endif - } - - internal static string AttestationProtocolToString(SqlConnectionAttestationProtocol value) - { - Debug.Assert(IsValidAttestationProtocol(value), "value is not a valid attestation protocol"); - - switch (value) - { - case SqlConnectionAttestationProtocol.HGS: - return AttestationProtocolHGS; - case SqlConnectionAttestationProtocol.AAS: - return AttestationProtocolAAS; -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return AttestationProtocolSIM; -#endif - default: - return null; - } - } - - internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) - { - if (null == value) - { - return DbConnectionStringDefaults.AttestationProtocol; - } - - string sValue = (value as string); - SqlConnectionAttestationProtocol result; - - if (null != sValue) - { - // try again after remove leading & trailing whitespaces. - sValue = sValue.Trim(); - if (TryConvertToAttestationProtocol(sValue, out result)) - { - return result; - } - - // string values must be valid - throw ADP.InvalidConnectionOptionValue(keyword); - } - else - { - // the value is not string, try other options - SqlConnectionAttestationProtocol eValue; - - if (value is SqlConnectionAttestationProtocol) - { - eValue = (SqlConnectionAttestationProtocol)value; - } - else if (value.GetType().IsEnum) - { - // explicitly block scenarios in which user tries to use wrong enum types, like: - // builder["SqlConnectionAttestationProtocol"] = EnvironmentVariableTarget.Process; - // workaround: explicitly cast non-SqlConnectionAttestationProtocol enums to int - throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), null); - } - else - { - try - { - // Enum.ToObject allows only integral and enum values (enums are blocked above), raising ArgumentException for the rest - eValue = (SqlConnectionAttestationProtocol)Enum.ToObject(typeof(SqlConnectionAttestationProtocol), value); - } - catch (ArgumentException e) - { - // to be consistent with the messages we send in case of wrong type usage, replace - // the error with our exception, and keep the original one as inner one for troubleshooting - throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionAttestationProtocol), e); - } - } - - if (IsValidAttestationProtocol(eValue)) - { - return eValue; - } - else - { - throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)eValue); - } - } - } - - #endregion - - internal static bool IsValidApplicationIntentValue(ApplicationIntent value) - { - Debug.Assert(Enum.GetNames(typeof(ApplicationIntent)).Length == 2, "ApplicationIntent enum has changed, update needed"); - return value == ApplicationIntent.ReadOnly || value == ApplicationIntent.ReadWrite; - } - - internal static string ApplicationIntentToString(ApplicationIntent value) - { - Debug.Assert(IsValidApplicationIntentValue(value)); - if (value == ApplicationIntent.ReadOnly) - { - return ApplicationIntentReadOnlyString; - } - else - { - return ApplicationIntentReadWriteString; - } - } - - /// - /// This method attempts to convert the given value tp ApplicationIntent enum. The algorithm is: - /// * if the value is from type string, it will be matched against ApplicationIntent enum names only, using ordinal, case-insensitive comparer - /// * if the value is from type ApplicationIntent, it will be used as is - /// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum - /// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException - /// - /// in any case above, if the converted value is out of valid range, the method raises ArgumentOutOfRangeException. - /// - /// application intent value in the valid range - internal static ApplicationIntent ConvertToApplicationIntent(string keyword, object value) - { - Debug.Assert(null != value, "ConvertToApplicationIntent(null)"); - string sValue = (value as string); - ApplicationIntent result; - if (null != sValue) - { - // We could use Enum.TryParse here, but it accepts value combinations like - // "ReadOnly, ReadWrite" which are unwelcome here - // Also, Enum.TryParse is 100x slower than plain StringComparer.OrdinalIgnoreCase.Equals method. - - if (TryConvertToApplicationIntent(sValue, out result)) - { - return result; - } - - // try again after remove leading & trailing whitespace. - sValue = sValue.Trim(); - if (TryConvertToApplicationIntent(sValue, out result)) - { - return result; - } - - // string values must be valid - throw ADP.InvalidConnectionOptionValue(keyword); - } - else - { - // the value is not string, try other options - ApplicationIntent eValue; - - if (value is ApplicationIntent) - { - // quick path for the most common case - eValue = (ApplicationIntent)value; - } - else if (value.GetType().GetTypeInfo().IsEnum) - { - // explicitly block scenarios in which user tries to use wrong enum types, like: - // builder["ApplicationIntent"] = EnvironmentVariableTarget.Process; - // workaround: explicitly cast non-ApplicationIntent enums to int - throw ADP.ConvertFailed(value.GetType(), typeof(ApplicationIntent), null); - } - else - { - try - { - // Enum.ToObject allows only integral and enum values (enums are blocked above), raising ArgumentException for the rest - eValue = (ApplicationIntent)Enum.ToObject(typeof(ApplicationIntent), value); - } - catch (ArgumentException e) - { - // to be consistent with the messages we send in case of wrong type usage, replace - // the error with our exception, and keep the original one as inner one for troubleshooting - throw ADP.ConvertFailed(value.GetType(), typeof(ApplicationIntent), e); - } - } - - // ensure value is in valid range - if (IsValidApplicationIntentValue(eValue)) - { - return eValue; - } - else - { - throw ADP.InvalidEnumerationValue(typeof(ApplicationIntent), (int)eValue); - } - } - } - - internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value) - { - Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 9, "SqlAuthenticationMethod enum has changed, update needed"); - return value == SqlAuthenticationMethod.SqlPassword - || value == SqlAuthenticationMethod.ActiveDirectoryPassword - || value == SqlAuthenticationMethod.ActiveDirectoryIntegrated - || value == SqlAuthenticationMethod.ActiveDirectoryInteractive - || value == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal - || value == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow - || value == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity - || value == SqlAuthenticationMethod.ActiveDirectoryMSI - || value == SqlAuthenticationMethod.NotSpecified; - } - - internal static string AuthenticationTypeToString(SqlAuthenticationMethod value) - { - Debug.Assert(IsValidAuthenticationTypeValue(value)); - - switch (value) - { - case SqlAuthenticationMethod.SqlPassword: - return SqlPasswordString; - case SqlAuthenticationMethod.ActiveDirectoryPassword: - return ActiveDirectoryPasswordString; - case SqlAuthenticationMethod.ActiveDirectoryIntegrated: - return ActiveDirectoryIntegratedString; - case SqlAuthenticationMethod.ActiveDirectoryInteractive: - return ActiveDirectoryInteractiveString; - case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal: - return ActiveDirectoryServicePrincipalString; - case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: - return ActiveDirectoryDeviceCodeFlowString; - case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: - return ActiveDirectoryManagedIdentityString; - case SqlAuthenticationMethod.ActiveDirectoryMSI: - return ActiveDirectoryMSIString; - default: - return null; - } - } - - internal static SqlAuthenticationMethod ConvertToAuthenticationType(string keyword, object value) - { - if (null == value) - { - return DbConnectionStringDefaults.Authentication; - } - - string sValue = (value as string); - SqlAuthenticationMethod result; - if (null != sValue) - { - if (TryConvertToAuthenticationType(sValue, out result)) - { - return result; - } - - // try again after remove leading & trailing whitespaces. - sValue = sValue.Trim(); - if (TryConvertToAuthenticationType(sValue, out result)) - { - return result; - } - - // string values must be valid - throw ADP.InvalidConnectionOptionValue(keyword); - } - else - { - // the value is not string, try other options - SqlAuthenticationMethod eValue; - - if (value is SqlAuthenticationMethod) - { - // quick path for the most common case - eValue = (SqlAuthenticationMethod)value; - } - else if (value.GetType().IsEnum) - { - // explicitly block scenarios in which user tries to use wrong enum types, like: - // builder["ApplicationIntent"] = EnvironmentVariableTarget.Process; - // workaround: explicitly cast non-ApplicationIntent enums to int - throw ADP.ConvertFailed(value.GetType(), typeof(SqlAuthenticationMethod), null); - } - else - { - try - { - // Enum.ToObject allows only integral and enum values (enums are blocked above), raising ArgumentException for the rest - eValue = (SqlAuthenticationMethod)Enum.ToObject(typeof(SqlAuthenticationMethod), value); - } - catch (ArgumentException e) - { - // to be consistent with the messages we send in case of wrong type usage, replace - // the error with our exception, and keep the original one as inner one for troubleshooting - throw ADP.ConvertFailed(value.GetType(), typeof(SqlAuthenticationMethod), e); - } - } - - // ensure value is in valid range - if (IsValidAuthenticationTypeValue(eValue)) - { - return eValue; - } - else - { - throw ADP.InvalidEnumerationValue(typeof(SqlAuthenticationMethod), (int)eValue); - } - } - } - - /// - /// Convert the provided value to a SqlConnectionColumnEncryptionSetting. - /// - /// - /// - /// - internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSetting(string keyword, object value) - { - if (null == value) - { - return DbConnectionStringDefaults.ColumnEncryptionSetting; - } - - string sValue = (value as string); - SqlConnectionColumnEncryptionSetting result; - if (null != sValue) - { - if (TryConvertToColumnEncryptionSetting(sValue, out result)) - { - return result; - } - - // try again after remove leading & trailing whitespaces. - sValue = sValue.Trim(); - if (TryConvertToColumnEncryptionSetting(sValue, out result)) - { - return result; - } - - // string values must be valid - throw ADP.InvalidConnectionOptionValue(keyword); - } - else - { - // the value is not string, try other options - SqlConnectionColumnEncryptionSetting eValue; - - if (value is SqlConnectionColumnEncryptionSetting) - { - // quick path for the most common case - eValue = (SqlConnectionColumnEncryptionSetting)value; - } - else if (value.GetType().IsEnum) - { - // explicitly block scenarios in which user tries to use wrong enum types, like: - // builder["SqlConnectionColumnEncryptionSetting"] = EnvironmentVariableTarget.Process; - // workaround: explicitly cast non-SqlConnectionColumnEncryptionSetting enums to int - throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionColumnEncryptionSetting), null); - } - else - { - try - { - // Enum.ToObject allows only integral and enum values (enums are blocked above), raising ArgumentException for the rest - eValue = (SqlConnectionColumnEncryptionSetting)Enum.ToObject(typeof(SqlConnectionColumnEncryptionSetting), value); - } - catch (ArgumentException e) - { - // to be consistent with the messages we send in case of wrong type usage, replace - // the error with our exception, and keep the original one as inner one for troubleshooting - throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionColumnEncryptionSetting), e); - } - } - - // ensure value is in valid range - if (IsValidColumnEncryptionSetting(eValue)) - { - return eValue; - } - else - { - throw ADP.InvalidEnumerationValue(typeof(SqlConnectionColumnEncryptionSetting), (int)eValue); - } - } - } - } - - internal static partial class DbConnectionStringDefaults - { - // all - // internal const string NamedConnection = ""; - - private const string _emptyString = ""; - // SqlClient - internal const ApplicationIntent ApplicationIntent = Microsoft.Data.SqlClient.ApplicationIntent.ReadWrite; - internal const string ApplicationName = "Core Microsoft SqlClient Data Provider"; - internal const string AttachDBFilename = _emptyString; - internal const int CommandTimeout = 30; - internal const int ConnectTimeout = 15; - internal const string CurrentLanguage = _emptyString; - internal const string DataSource = _emptyString; - internal const bool Encrypt = false; - internal const bool Enlist = true; - internal const string FailoverPartner = _emptyString; - internal const string InitialCatalog = _emptyString; - internal const bool IntegratedSecurity = false; - internal const int LoadBalanceTimeout = 0; // default of 0 means don't use - internal const bool MultipleActiveResultSets = false; - internal const bool MultiSubnetFailover = false; - internal const int MaxPoolSize = 100; - internal const int MinPoolSize = 0; - internal const int PacketSize = 8000; - internal const string Password = _emptyString; - internal const bool PersistSecurityInfo = false; - internal const bool Pooling = true; - internal const bool TrustServerCertificate = false; - internal const string TypeSystemVersion = "Latest"; - internal const string UserID = _emptyString; - internal const bool UserInstance = false; - internal const bool Replication = false; - internal const string WorkstationID = _emptyString; - internal const string TransactionBinding = "Implicit Unbind"; - internal const int ConnectRetryCount = 1; - internal const int ConnectRetryInterval = 10; - internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; - internal const SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; - internal const string EnclaveAttestationUrl = _emptyString; - internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; - } - - - internal static partial class DbConnectionStringKeywords - { - // all - // internal const string NamedConnection = "Named Connection"; - - // SqlClient - internal const string ApplicationIntent = "Application Intent"; - internal const string ApplicationName = "Application Name"; - internal const string AsynchronousProcessing = "Asynchronous Processing"; - internal const string AttachDBFilename = "AttachDbFilename"; - internal const string CommandTimeout = "Command Timeout"; - internal const string ConnectTimeout = "Connect Timeout"; - internal const string ConnectionReset = "Connection Reset"; - internal const string ContextConnection = "Context Connection"; - internal const string CurrentLanguage = "Current Language"; - internal const string Encrypt = "Encrypt"; - internal const string FailoverPartner = "Failover Partner"; - internal const string InitialCatalog = "Initial Catalog"; - internal const string MultipleActiveResultSets = "Multiple Active Result Sets"; - internal const string MultiSubnetFailover = "Multi Subnet Failover"; - internal const string NetworkLibrary = "Network Library"; - internal const string PacketSize = "Packet Size"; - internal const string Replication = "Replication"; - internal const string TransactionBinding = "Transaction Binding"; - internal const string TrustServerCertificate = "Trust Server Certificate"; - internal const string TypeSystemVersion = "Type System Version"; - internal const string UserInstance = "User Instance"; - internal const string WorkstationID = "Workstation ID"; - internal const string ConnectRetryCount = "Connect Retry Count"; - internal const string ConnectRetryInterval = "Connect Retry Interval"; - internal const string Authentication = "Authentication"; - internal const string ColumnEncryptionSetting = "Column Encryption Setting"; - internal const string EnclaveAttestationUrl = "Enclave Attestation Url"; - internal const string AttestationProtocol = "Attestation Protocol"; - - // common keywords (OleDb, OracleClient, SqlClient) - internal const string DataSource = "Data Source"; - internal const string IntegratedSecurity = "Integrated Security"; - internal const string Password = "Password"; - internal const string Driver = "Driver"; - internal const string PersistSecurityInfo = "Persist Security Info"; - internal const string UserID = "User ID"; - - // managed pooling (OracleClient, SqlClient) - internal const string Enlist = "Enlist"; - internal const string LoadBalanceTimeout = "Load Balance Timeout"; - internal const string MaxPoolSize = "Max Pool Size"; - internal const string Pooling = "Pooling"; - internal const string MinPoolSize = "Min Pool Size"; -#if NETCOREAPP - internal const string PoolBlockingPeriod = "Pool Blocking Period"; -#endif - } - - internal static class DbConnectionStringSynonyms - { - //internal const string AsynchronousProcessing = Async; - internal const string Async = "async"; - - //internal const string ApplicationName = APP; - internal const string APP = "app"; - - //internal const string ApplicationIntent = APPLICATIONINTENT; - internal const string APPLICATIONINTENT = "ApplicationIntent"; - - //internal const string AttachDBFilename = EXTENDEDPROPERTIES+","+INITIALFILENAME; - internal const string EXTENDEDPROPERTIES = "extended properties"; - internal const string INITIALFILENAME = "initial file name"; - - //internal const string ConnectTimeout = CONNECTIONTIMEOUT+","+TIMEOUT; - internal const string CONNECTIONTIMEOUT = "connection timeout"; - internal const string TIMEOUT = "timeout"; - - //internal const string ConnectRetryCount = CONNECTRETRYCOUNT; - internal const string CONNECTRETRYCOUNT = "ConnectRetryCount"; - - //internal const string ConnectRetryInterval = CONNECTRETRYINTERVAL; - internal const string CONNECTRETRYINTERVAL = "ConnectRetryInterval"; - - //internal const string CurrentLanguage = LANGUAGE; - internal const string LANGUAGE = "language"; - - //internal const string OraDataSource = SERVER; - //internal const string SqlDataSource = ADDR+","+ADDRESS+","+SERVER+","+NETWORKADDRESS; - internal const string ADDR = "addr"; - internal const string ADDRESS = "address"; - internal const string SERVER = "server"; - internal const string NETWORKADDRESS = "network address"; - - //internal const string InitialCatalog = DATABASE; - internal const string DATABASE = "database"; - - //internal const string IntegratedSecurity = TRUSTEDCONNECTION; - internal const string TRUSTEDCONNECTION = "trusted_connection"; // underscore introduced in Everett - - //internal const string LoadBalanceTimeout = ConnectionLifetime; - internal const string ConnectionLifetime = "connection lifetime"; - - //internal const string MultipleActiveResultSets = MULTIPLEACTIVERESULTSETS; - internal const string MULTIPLEACTIVERESULTSETS = "MultipleActiveResultSets"; - - //internal const string MultiSubnetFailover = MULTISUBNETFAILOVER; - internal const string MULTISUBNETFAILOVER = "MultiSubnetFailover"; - - //internal const string NetworkLibrary = NET+","+NETWORK; - internal const string NET = "net"; - internal const string NETWORK = "network"; - -#if NETCOREAPP - //internal const string PoolBlockingPeriod = POOLBLOCKINGPERIOD; - internal const string POOLBLOCKINGPERIOD = "PoolBlockingPeriod"; -#endif - - //internal const string Password = Pwd; - internal const string Pwd = "pwd"; - - //internal const string PersistSecurityInfo = PERSISTSECURITYINFO; - internal const string PERSISTSECURITYINFO = "persistsecurityinfo"; - - //internal const string TrustServerCertificate = TRUSTSERVERCERTIFICATE; - internal const string TRUSTSERVERCERTIFICATE = "TrustServerCertificate"; - - //internal const string UserID = UID+","+User; - internal const string UID = "uid"; - internal const string User = "user"; - - //internal const string WorkstationID = WSID; - internal const string WSID = "wsid"; - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs index d620caa34a..1a2e809147 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionFactory.cs @@ -108,6 +108,7 @@ internal bool TryGetConnection(DbConnection owningConnection, TaskCompletionSour } connection = CreateNonPooledConnection(owningConnection, poolGroup, userOptions); + SqlClientEventSource.Log.EnterNonPooledConnection(); } else { @@ -209,6 +210,10 @@ private static void TryGetConnectionCompletedContinuation(Task {0}, Non-Pooled Connection has Delegated Transaction, waiting to Dispose.", ObjectID); + SqlClientEventSource.Log.EnterStasisConnection(); } private void TerminateStasis(bool returningToPool) @@ -494,6 +499,7 @@ private void TerminateStasis(bool returningToPool) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Delegated Transaction has ended, connection is closed/leaked. Disposing.", ObjectID); } + SqlClientEventSource.Log.ExitStasisConnection(); _isInStasis = false; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs index 11c153e5b3..c85d042b2a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs @@ -11,14 +11,6 @@ namespace Microsoft.Data.ProviderBase { sealed internal partial class DbConnectionPool { - partial void CheckPoolBlockingPeriod(Exception e) - { - if (!IsBlockingPeriodEnabled()) - { - throw e; - } - } - private bool IsBlockingPeriodEnabled() { var poolGroupConnectionOptions = _connectionPoolGroup.ConnectionOptions as SqlConnectionString; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index d0921da04c..7858adc93c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -231,6 +231,7 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal } SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Added.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); } + SqlClientEventSource.Log.EnterFreeConnection(); } internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) @@ -293,6 +294,7 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra // connections, we'll put it back... if (0 <= entry) { + SqlClientEventSource.Log.ExitFreeConnection(); Pool.PutObjectFromTransactedPool(transactedObject); } } @@ -600,6 +602,7 @@ private void CleanupCallback(object state) { Debug.Assert(obj != null, "null connection is not expected"); // If we obtained one from the old stack, destroy it. + SqlClientEventSource.Log.ExitFreeConnection(); // Transaction roots must survive even aging out (TxEnd event will clean them up). bool shouldDestroy = true; @@ -696,11 +699,13 @@ internal void Clear() while (_stackNew.TryPop(out obj)) { Debug.Assert(obj != null, "null connection is not expected"); + SqlClientEventSource.Log.ExitFreeConnection(); DestroyObject(obj); } while (_stackOld.TryPop(out obj)) { Debug.Assert(obj != null, "null connection is not expected"); + SqlClientEventSource.Log.ExitFreeConnection(); DestroyObject(obj); } @@ -742,6 +747,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio } _objectList.Add(newObj); _totalObjects = _objectList.Count; + SqlClientEventSource.Log.EnterPooledConnection(); } // If the old connection belonged to another pool, we need to remove it from that @@ -770,9 +776,21 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio throw; } - CheckPoolBlockingPeriod(e); +#if NETCOREAPP + if (!IsBlockingPeriodEnabled()) + { + throw; + } +#endif + + // Close associated Parser if connection already established. + if (newObj?.IsConnectionAlive() == true) + { + newObj.Dispose(); + } newObj = null; // set to null, so we do not return bad new object + // Failed to create instance _resError = e; @@ -811,9 +829,6 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio return newObj; } - //This method is implemented in DbConnectionPool.NetCoreApp - partial void CheckPoolBlockingPeriod(Exception e); - private void DeactivateObject(DbConnectionInternal obj) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Deactivating.", ObjectID, obj.ObjectID); @@ -967,9 +982,11 @@ internal void DestroyObject(DbConnectionInternal obj) if (removed) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Removed from pool.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ExitPooledConnection(); } obj.Dispose(); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Disposed.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.HardDisconnectRequest(); } } @@ -1301,6 +1318,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj } connection = obj; + SqlClientEventSource.Log.SoftConnectRequest(); return true; } @@ -1337,6 +1355,7 @@ internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbCon if (newConnection != null) { + SqlClientEventSource.Log.SoftConnectRequest(); PrepareConnection(owningObject, newConnection, oldConnection.EnlistedTransaction); oldConnection.PrepareForReplaceConnection(); oldConnection.DeactivateConnection(); @@ -1374,6 +1393,7 @@ private DbConnectionInternal GetFromGeneralPool() if (null != obj) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from general pool.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ExitFreeConnection(); } return (obj); } @@ -1390,6 +1410,7 @@ private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) if (null != obj) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from transacted pool.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ExitFreeConnection(); if (obj.IsTransactionRoot) { @@ -1544,12 +1565,13 @@ internal void PutNewObject(DbConnectionInternal obj) _stackNew.Push(obj); _waitHandles.PoolSemaphore.Release(1); + SqlClientEventSource.Log.EnterFreeConnection(); } internal void PutObject(DbConnectionInternal obj, object owningObject) { Debug.Assert(null != obj, "null obj?"); - + SqlClientEventSource.Log.SoftDisconnectRequest(); // Once a connection is closing (which is the state that we're in at // this point in time) you cannot delegate a transaction to or enlist @@ -1662,6 +1684,8 @@ private bool ReclaimEmancipatedObjects() { DbConnectionInternal obj = reclaimedObjects[i]; SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Reclaiming.", ObjectID, obj.ObjectID); + SqlClientEventSource.Log.ReclaimedConnectionRequest(); + emancipatedObjectFound = true; obj.DetachCurrentTransactionIfEnded(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Unix.cs new file mode 100644 index 0000000000..6ee3fe3329 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Unix.cs @@ -0,0 +1,15 @@ +// 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.Data; +using System.Data.Common; +using Microsoft.Data.SqlClient.Server; + +namespace Microsoft.Data.Sql +{ + /// + public sealed partial class SqlDataSourceEnumerator : DbDataSourceEnumerator + { + private partial DataTable GetDataSourcesInternal() => SqlDataSourceEnumeratorManagedHelper.GetDataSources(); + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AAsyncCallContext.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AAsyncCallContext.cs index 56e369593a..76710ff980 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AAsyncCallContext.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AAsyncCallContext.cs @@ -17,38 +17,68 @@ namespace Microsoft.Data.SqlClient // CONSIDER creating your own Set method that calls the base Set rather than providing a parameterized ctor, it is friendlier to caching // DO NOT use this class' state after Dispose has been called. It will not throw ObjectDisposedException but it will be a cleared object - internal abstract class AAsyncCallContext : IDisposable + internal abstract class AAsyncCallContext : AAsyncBaseCallContext where TOwner : class + where TDisposable : IDisposable { - protected TOwner _owner; - protected TaskCompletionSource _source; - protected IDisposable _disposable; + protected TDisposable _disposable; protected AAsyncCallContext() { } - protected AAsyncCallContext(TOwner owner, TaskCompletionSource source, IDisposable disposable = null) + protected AAsyncCallContext(TOwner owner, TaskCompletionSource source, TDisposable disposable = default) { Set(owner, source, disposable); } - protected void Set(TOwner owner, TaskCompletionSource source, IDisposable disposable = null) + protected void Set(TOwner owner, TaskCompletionSource source, TDisposable disposable = default) + { + base.Set(owner, source); + _disposable = disposable; + } + + protected override void DisposeCore() + { + TDisposable copyDisposable = _disposable; + _disposable = default; + copyDisposable?.Dispose(); + } + } + + internal abstract class AAsyncBaseCallContext + { + protected TOwner _owner; + protected TaskCompletionSource _source; + protected bool _isDisposed; + + protected AAsyncBaseCallContext() + { + } + + protected void Set(TOwner owner, TaskCompletionSource source) { _owner = owner ?? throw new ArgumentNullException(nameof(owner)); _source = source ?? throw new ArgumentNullException(nameof(source)); - _disposable = disposable; + _isDisposed = false; } protected void ClearCore() { _source = null; _owner = default; - IDisposable copyDisposable = _disposable; - _disposable = null; - copyDisposable?.Dispose(); + try + { + DisposeCore(); + } + finally + { + _isDisposed = true; + } } + protected abstract void DisposeCore(); + /// /// override this method to cleanup instance data before ClearCore is called which will blank the base data /// @@ -65,16 +95,19 @@ protected virtual void AfterCleared(TOwner owner) public void Dispose() { - TOwner owner = _owner; - try - { - Clear(); - } - finally + if (!_isDisposed) { - ClearCore(); + TOwner owner = _owner; + try + { + Clear(); + } + finally + { + ClearCore(); + } + AfterCleared(owner); } - AfterCleared(owner); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs deleted file mode 100644 index 23abf6b247..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedEnclaveProviderUtils.cs +++ /dev/null @@ -1,153 +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. - -using System; -using System.Diagnostics; -using System.Linq; -using System.Security.Cryptography; - -namespace Microsoft.Data.SqlClient -{ - internal class EnclavePublicKey - { - public byte[] PublicKey { get; set; } - - public EnclavePublicKey(byte[] payload) - { - PublicKey = payload; - } - } - - internal class EnclaveDiffieHellmanInfo - { - public int Size { get; private set; } - - public byte[] PublicKey { get; private set; } - - public byte[] PublicKeySignature { get; private set; } - - public EnclaveDiffieHellmanInfo(byte[] payload) - { - Size = payload.Length; - - int offset = 0; - int publicKeySize = BitConverter.ToInt32(payload, offset); - offset += sizeof(int); - - int publicKeySignatureSize = BitConverter.ToInt32(payload, offset); - offset += sizeof(int); - - PublicKey = payload.Skip(offset).Take(publicKeySize).ToArray(); - offset += publicKeySize; - - PublicKeySignature = payload.Skip(offset).Take(publicKeySignatureSize).ToArray(); - offset += publicKeySignatureSize; - } - } - - internal enum EnclaveType - { - None = 0, - - Vbs = 1, - - Sgx = 2 - } - - // Contains methods to convert cryptography keys between different formats. - internal sealed class KeyConverter - { - // The RSA public key blob is structured as follows: - // BCRYPT_RSAKEY_BLOB header - // byte[ExponentSize] publicExponent - // byte[ModulusSize] modulus - private readonly struct RSAPublicKeyBlob - { - // Size of an RSA public key blob - internal static readonly int Size = 539; - // Size of the BCRYPT_RSAKEY_BLOB header - internal static readonly int HeaderSize = 27; - // Size of the exponent (final 3 bytes of the header) - internal static readonly int ExponentSize = 3; - // Size of the modulus (remaining bytes after the header) - internal static readonly int ModulusSize = Size - HeaderSize; - internal static readonly int ExponentOffset = HeaderSize - ExponentSize; - internal static readonly int ModulusOffset = HeaderSize; - } - - // Extracts the public key's modulus and exponent from an RSA public key blob - // and returns an RSAParameters object - internal static RSAParameters RSAPublicKeyBlobToParams(byte[] keyBlob) - { - Debug.Assert(keyBlob.Length == RSAPublicKeyBlob.Size, - $"RSA public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {RSAPublicKeyBlob.Size}"); - return new RSAParameters() - { - Exponent = keyBlob.Skip(RSAPublicKeyBlob.ExponentOffset).Take(RSAPublicKeyBlob.ExponentSize).ToArray(), - Modulus = keyBlob.Skip(RSAPublicKeyBlob.ModulusOffset).Take(RSAPublicKeyBlob.ModulusSize).ToArray() - }; - } - - // The ECC public key blob is structured as follows: - // BCRYPT_ECCKEY_BLOB header - // byte[KeySize] X - // byte[KeySize] Y - private readonly struct ECCPublicKeyBlob - { - // Size of an ECC public key blob - internal static readonly int Size = 104; - // Size of the BCRYPT_ECCKEY_BLOB header - internal static readonly int HeaderSize = 8; - // Size of each coordinate - internal static readonly int KeySize = (Size - HeaderSize) / 2; - } - - // Magic numbers identifying blob types - // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/cba27df5-4880-4f95-a879-783f8657e53b - private readonly struct KeyBlobMagicNumber - { - internal static readonly byte[] ECDHPublicP384 = new byte[] { 0x45, 0x43, 0x4b, 0x33 }; - } - - // Extracts the public key's X and Y coordinates from an ECC public key blob - // and returns an ECParameters object - internal static ECParameters ECCPublicKeyBlobToParams(byte[] keyBlob) - { - Debug.Assert(keyBlob.Length == ECCPublicKeyBlob.Size, - $"ECC public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {ECCPublicKeyBlob.Size}"); - return new ECParameters - { - Curve = ECCurve.NamedCurves.nistP384, - Q = new ECPoint - { - X = keyBlob.Skip(ECCPublicKeyBlob.HeaderSize).Take(ECCPublicKeyBlob.KeySize).ToArray(), - Y = keyBlob.Skip(ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize).Take(ECCPublicKeyBlob.KeySize).ToArray() - }, - }; - } - - // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob - // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export - // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific" - // from https://github.com/dotnet/runtime/issues/27276 - // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix - internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey publicKey) - { - byte[] keyBlob = new byte[ECCPublicKeyBlob.Size]; - - // Set magic number - Array.Copy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); - // Set key size - keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize; - - ECPoint ecPoint = publicKey.ExportParameters().Q; - Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize, - $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}"); - // Copy x and y coordinates to key blob - Array.Copy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize); - Array.Copy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize); - return keyBlob; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedHelperClasses.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedHelperClasses.cs index 5d8cda08e4..0a4fe787a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedHelperClasses.cs @@ -14,7 +14,7 @@ namespace Microsoft.Data.SqlClient /// Represents a single encrypted value for a CEK. It contains the encrypted CEK, /// the store type, name,the key path and encryption algorithm. /// - internal struct SqlEncryptionKeyInfo + internal class SqlEncryptionKeyInfo { internal byte[] encryptedKey; // the encrypted "column encryption key" internal int databaseId; @@ -32,7 +32,7 @@ internal struct SqlEncryptionKeyInfo /// rotation scenario) We need to keep all these around until we can resolve the CEK /// using the correct master key. /// - internal struct SqlTceCipherInfoEntry + internal class SqlTceCipherInfoEntry { /// @@ -180,7 +180,7 @@ internal void Add(byte[] encryptedKey, int databaseId, int cekId, int cekVersion /// Constructor. /// /// - internal SqlTceCipherInfoEntry(int ordinal = 0) : this() + internal SqlTceCipherInfoEntry(int ordinal = 0) { _ordinal = ordinal; _databaseId = 0; @@ -196,7 +196,7 @@ internal SqlTceCipherInfoEntry(int ordinal = 0) : this() /// may have been encrypted using multiple master keys (giving us multiple CEK values). All these values form one single /// entry in this table. /// - internal struct SqlTceCipherInfoTable + internal class SqlTceCipherInfoTable { private readonly SqlTceCipherInfoEntry[] keyList; @@ -231,9 +231,9 @@ internal int Size sealed internal partial class _SqlMetaDataSet { - internal readonly SqlTceCipherInfoTable? cekTable; // table of "column encryption keys" used for this metadataset + internal readonly SqlTceCipherInfoTable cekTable; // table of "column encryption keys" used for this metadataset - internal _SqlMetaDataSet(int count, SqlTceCipherInfoTable? cipherTable) + internal _SqlMetaDataSet(int count, SqlTceCipherInfoTable cipherTable) : this(count) { cekTable = cipherTable; @@ -249,7 +249,7 @@ internal class SqlCipherMetadata /// /// Cipher Info Entry. /// - private SqlTceCipherInfoEntry? _sqlTceCipherInfoEntry; + private SqlTceCipherInfoEntry _sqlTceCipherInfoEntry; /// /// Encryption Algorithm Id. @@ -279,7 +279,7 @@ internal class SqlCipherMetadata /// /// Sql Encryption Key Info. /// - private SqlEncryptionKeyInfo? _sqlEncryptionKeyInfo; + private SqlEncryptionKeyInfo _sqlEncryptionKeyInfo; /// /// Ordinal (into the Cek Table). @@ -289,7 +289,7 @@ internal class SqlCipherMetadata /// /// Return the Encryption Info Entry. /// - internal SqlTceCipherInfoEntry? EncryptionInfo + internal SqlTceCipherInfoEntry EncryptionInfo { get { @@ -297,7 +297,7 @@ internal SqlTceCipherInfoEntry? EncryptionInfo } set { - Debug.Assert(!_sqlTceCipherInfoEntry.HasValue, "We can only set the EncryptionInfo once."); + Debug.Assert(_sqlTceCipherInfoEntry == null, "We can only set the EncryptionInfo once."); _sqlTceCipherInfoEntry = value; } } @@ -365,7 +365,7 @@ internal SqlClientEncryptionAlgorithm CipherAlgorithm /// /// Return Encryption Key Info. /// - internal SqlEncryptionKeyInfo? EncryptionKeyInfo + internal SqlEncryptionKeyInfo EncryptionKeyInfo { get { @@ -374,7 +374,7 @@ internal SqlEncryptionKeyInfo? EncryptionKeyInfo set { - Debug.Assert(!_sqlEncryptionKeyInfo.HasValue, "_sqlEncryptionKeyInfo should not be set more than once."); + Debug.Assert(_sqlEncryptionKeyInfo == null, "_sqlEncryptionKeyInfo should not be set more than once."); _sqlEncryptionKeyInfo = value; } } @@ -399,7 +399,7 @@ internal ushort CekTableOrdinal /// /// /// - internal SqlCipherMetadata(SqlTceCipherInfoEntry? sqlTceCipherInfoEntry, + internal SqlCipherMetadata(SqlTceCipherInfoEntry sqlTceCipherInfoEntry, ushort ordinal, byte cipherAlgorithmId, string cipherAlgorithmName, @@ -519,7 +519,7 @@ internal SqlColumnEncryptionInputParameterInfo(SmiParameterMetaData smiParameter { Debug.Assert(smiParameterMetadata != null, "smiParameterMetadata should not be null."); Debug.Assert(cipherMetadata != null, "cipherMetadata should not be null"); - Debug.Assert(cipherMetadata.EncryptionKeyInfo.HasValue, "cipherMetadata.EncryptionKeyInfo.HasValue should be true."); + Debug.Assert(cipherMetadata.EncryptionKeyInfo != null, "cipherMetadata.EncryptionKeyInfo.HasValue should be true."); _smiParameterMetadata = smiParameterMetadata; _cipherMetadata = cipherMetadata; @@ -549,7 +549,7 @@ private byte[] SerializeToWriteFormat() totalLength += sizeof(int); // Metadata version of the encryption key. - totalLength += _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length; + totalLength += _cipherMetadata.EncryptionKeyInfo.cekMdVersion.Length; // Normalization Rule Version. totalLength += sizeof(byte); @@ -566,17 +566,17 @@ private byte[] SerializeToWriteFormat() serializedWireFormat[consumedBytes++] = _cipherMetadata.EncryptionType; // 3 - Write the database id of the encryption key. - SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.databaseId, serializedWireFormat, ref consumedBytes); + SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.databaseId, serializedWireFormat, ref consumedBytes); // 4 - Write the id of the encryption key. - SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.cekId, serializedWireFormat, ref consumedBytes); + SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.cekId, serializedWireFormat, ref consumedBytes); // 5 - Write the version of the encryption key. - SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.cekVersion, serializedWireFormat, ref consumedBytes); + SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.cekVersion, serializedWireFormat, ref consumedBytes); // 6 - Write the metadata version of the encryption key. - Buffer.BlockCopy(_cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion, 0, serializedWireFormat, consumedBytes, _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length); - consumedBytes += _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length; + Buffer.BlockCopy(_cipherMetadata.EncryptionKeyInfo.cekMdVersion, 0, serializedWireFormat, consumedBytes, _cipherMetadata.EncryptionKeyInfo.cekMdVersion.Length); + consumedBytes += _cipherMetadata.EncryptionKeyInfo.cekMdVersion.Length; // 7 - Write Normalization Rule Version. serializedWireFormat[consumedBytes++] = _cipherMetadata.NormalizationRuleVersion; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.CrossPlatform.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.CrossPlatform.cs new file mode 100644 index 0000000000..8b3def875c --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/AlwaysEncryptedKeyConverter.CrossPlatform.cs @@ -0,0 +1,135 @@ +// 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.Diagnostics; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace Microsoft.Data.SqlClient +{ + internal sealed partial class KeyConverter + { + // Magic numbers identifying blob types + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/cba27df5-4880-4f95-a879-783f8657e53b + private readonly struct KeyBlobMagicNumber + { + internal static readonly byte[] ECDHPublicP384 = new byte[] { 0x45, 0x43, 0x4b, 0x33 }; + } + + // The ECC public key blob is structured as follows: + // BCRYPT_ECCKEY_BLOB header + // byte[KeySize] X + // byte[KeySize] Y + private readonly struct ECCPublicKeyBlob + { + // Size of an ECC public key blob + internal const int Size = 104; + // Size of the BCRYPT_ECCKEY_BLOB header + internal const int HeaderSize = 8; + // Size of each coordinate + internal const int KeySize = (Size - HeaderSize) / 2; + } + + // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob + // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export + // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific" + // from https://github.com/dotnet/runtime/issues/27276 + // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix + internal static byte[] GetECDiffieHellmanPublicKeyBlob(ECDiffieHellman ecDiffieHellman) + { + byte[] keyBlob = new byte[ECCPublicKeyBlob.Size]; + + // Set magic number + Buffer.BlockCopy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); + // Set key size + keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize; + + ECPoint ecPoint = ecDiffieHellman.PublicKey.ExportParameters().Q; + Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize, + $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}"); + // Copy x and y coordinates to key blob + Buffer.BlockCopy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize); + Buffer.BlockCopy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize); + return keyBlob; + } + + // The RSA public key blob is structured as follows: + // BCRYPT_RSAKEY_BLOB header + // byte[ExponentSize] publicExponent + // byte[ModulusSize] modulus + private readonly struct RSAPublicKeyBlob + { + // Size of an RSA public key blob + internal const int Size = 539; + // Size of the BCRYPT_RSAKEY_BLOB header + internal const int HeaderSize = 27; + // Size of the exponent (final 3 bytes of the header) + internal const int ExponentSize = 3; + // Size of the modulus (remaining bytes after the header) + internal const int ModulusSize = Size - HeaderSize; + internal const int ExponentOffset = HeaderSize - ExponentSize; + internal const int ModulusOffset = HeaderSize; + } + + internal static RSA CreateRSAFromPublicKeyBlob(byte[] keyBlob) + { + Debug.Assert(keyBlob.Length == RSAPublicKeyBlob.Size, $"RSA public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {RSAPublicKeyBlob.Size}"); + + byte[] exponent = new byte[RSAPublicKeyBlob.ExponentSize]; + byte[] modulus = new byte[RSAPublicKeyBlob.ModulusSize]; + Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ExponentOffset, exponent, 0, RSAPublicKeyBlob.ExponentSize); + Buffer.BlockCopy(keyBlob, RSAPublicKeyBlob.ModulusOffset, modulus, 0, RSAPublicKeyBlob.ModulusSize); + var rsaParameters = new RSAParameters() + { + Exponent = exponent, + Modulus = modulus + }; + return RSA.Create(rsaParameters); + } + + internal static ECDiffieHellman CreateECDiffieHellmanFromPublicKeyBlob(byte[] keyBlob) + { + Debug.Assert(keyBlob.Length == ECCPublicKeyBlob.Size, $"ECC public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {ECCPublicKeyBlob.Size}"); + + byte[] x = new byte[ECCPublicKeyBlob.KeySize]; + byte[] y = new byte[ECCPublicKeyBlob.KeySize]; + Buffer.BlockCopy(keyBlob, ECCPublicKeyBlob.HeaderSize, x, 0, ECCPublicKeyBlob.KeySize); + Buffer.BlockCopy(keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, y, 0, ECCPublicKeyBlob.KeySize); + + var parameters = new ECParameters + { + Curve = ECCurve.NamedCurves.nistP384, + Q = new ECPoint + { + X = x, + Y = y + }, + }; + + return ECDiffieHellman.Create(parameters); + } + + internal static ECDiffieHellman CreateECDiffieHellman(int keySize) + { + // platform agnostic creates a key of the correct size but does not + // set the key derivation type or algorithm, these must be set by calling + // DeriveKeyFromHash later in DeriveKey + ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); + clientDHKey.KeySize = keySize; + return clientDHKey; + } + + internal static byte[] DeriveKey(ECDiffieHellman ecd, ECDiffieHellmanPublicKey publicKey) + { + // see notes in CreateECDDiffieHellman + return ecd.DeriveKeyFromHash(publicKey, HashAlgorithmName.SHA256); + } + + internal static RSA GetRSAFromCertificate(X509Certificate2 certificate) + { + return certificate.GetRSAPublicKey(); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs index f072900b6e..f30ddf4243 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Common.cs @@ -38,7 +38,7 @@ private static LocalDBFormatMessageDelegate LocalDBFormatMessage { // SNI checks for LocalDBFormatMessage during DLL loading, so it is practically impossible to get this error. int hResult = Marshal.GetLastWin32Error(); - SqlClientEventSource.Log.TryTraceEvent(" GetProcAddress for LocalDBFormatMessage error 0x{0}", hResult); + SqlClientEventSource.Log.TryTraceEvent("LocalDBAPI.LocalDBFormatMessage> GetProcAddress for LocalDBFormatMessage error 0x{0}", hResult); throw CreateLocalDBException(errorMessage: Strings.LocalDB_MethodNotFound); } s_localDBFormatMessage = Marshal.GetDelegateForFunctionPointer(functionAddr); @@ -68,14 +68,14 @@ internal static string GetLocalDBMessage(int hrCode) uint len = (uint)buffer.Capacity; - // First try for current culture + // First try for current culture int hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: (uint)CultureInfo.CurrentCulture.LCID, buffer: buffer, buflen: ref len); if (hResult >= 0) return buffer.ToString(); else { - // Message is not available for current culture, try default + // Message is not available for current culture, try default buffer = new StringBuilder((int)const_ErrorMessageBufferSize); len = (uint)buffer.Capacity; hResult = LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: 0 /* thread locale with fallback to English */, diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs index 6334e5873d..1b4679bfe4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs @@ -22,14 +22,13 @@ private static IntPtr UserInstanceDLLHandle if (s_userInstanceDLLHandle == IntPtr.Zero) { SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle); - if(s_userInstanceDLLHandle != IntPtr.Zero) + if (s_userInstanceDLLHandle != IntPtr.Zero) { - SqlClientEventSource.Log.TryTraceEvent(" LocalDB - handle obtained"); + SqlClientEventSource.Log.TryTraceEvent("LocalDBAPI.UserInstanceDLLHandle | LocalDB - handle obtained"); } else { - SNINativeMethodWrapper.SNI_Error sniError; - SNINativeMethodWrapper.SNIGetLastError(out sniError); + SNINativeMethodWrapper.SNIGetLastError(out SNINativeMethodWrapper.SNI_Error sniError); throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs index 4f3fd13dce..ba2371c232 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs @@ -10,25 +10,37 @@ namespace Microsoft.Data { internal static partial class LocalDBAPI { - private const string const_localDbPrefix = @"(localdb)\"; + private const string LocalDbPrefix = @"(localdb)\"; + private const string LocalDbPrefix_NP = @"np:\\.\pipe\LOCALDB#"; [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] private delegate int LocalDBFormatMessageDelegate(int hrLocalDB, uint dwFlags, uint dwLanguageId, StringBuilder buffer, ref uint buflen); // check if name is in format (localdb)\ and return instance name if it is + // localDB can also have a format of np:\\.\pipe\LOCALDB#\tsql\query internal static string GetLocalDbInstanceNameFromServerName(string serverName) { - if (serverName == null) - return null; - serverName = serverName.TrimStart(); // it can start with spaces if specified in quotes - if (!serverName.StartsWith(const_localDbPrefix, StringComparison.OrdinalIgnoreCase)) - return null; - string instanceName = serverName.Substring(const_localDbPrefix.Length).Trim(); - if (instanceName.Length == 0) - return null; - else - return instanceName; + if (serverName is not null) + { + // it can start with spaces if specified in quotes + // Memory allocation is reduced by using ReadOnlySpan + ReadOnlySpan input = serverName.AsSpan().Trim(); + if (input.StartsWith(LocalDbPrefix.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + input = input.Slice(LocalDbPrefix.Length); + if (!input.IsEmpty) + { + return input.ToString(); + } + } + else if (input.StartsWith(LocalDbPrefix_NP.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return input.ToString(); + } + + } + return null; } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.NetCoreApp.cs new file mode 100644 index 0000000000..bf007b6910 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicManager.NetCoreApp.cs @@ -0,0 +1,89 @@ +// 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.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace Microsoft.Data.SqlClient +{ + /// + /// Configurable retry logic manager + /// + internal sealed partial class SqlConfigurableRetryLogicLoader + { + #region Type resolution + /// + /// Performs a case-sensitive search to resolve the specified type name + /// and its related assemblies in default assembly load context if they aren't loaded yet. + /// + /// Resolved type if it could resolve the type; otherwise, the `SqlConfigurableRetryFactory` type. + private static Type LoadType(string fullyQualifiedName) + { + string methodName = MethodBase.GetCurrentMethod().Name; + SqlClientEventSource.Log.TryTraceEvent(" Entry point.", TypeName, methodName); + + var result = Type.GetType(fullyQualifiedName, AssemblyResolver, TypeResolver); + if (result != null) + { + SqlClientEventSource.Log.TryTraceEvent(" The '{2}' type is resolved.", + TypeName, methodName, result.FullName); + } + else + { + result = typeof(SqlConfigurableRetryFactory); + SqlClientEventSource.Log.TryTraceEvent(" Couldn't resolve the requested type by '{2}'; The internal `{3}` type is returned.", + TypeName, methodName, fullyQualifiedName, result.FullName); + } + SqlClientEventSource.Log.TryTraceEvent(" Exit point.", TypeName, methodName); + return result; + } + + /// + /// If the caller does not have sufficient permissions to read the specified file, + /// no exception is thrown and the method returns null regardless of the existence of path. + /// + private static string MakeFullPath(string directory, string assemblyName, string extension = ".dll") + { + string methodName = MethodBase.GetCurrentMethod().Name; + SqlClientEventSource.Log.TryTraceEvent(" Looking for '{2}' assembly in '{3}' directory." + , TypeName, methodName, assemblyName, directory); + string fullPath = Path.Combine(directory, assemblyName); + fullPath = string.IsNullOrEmpty(Path.GetExtension(fullPath)) ? fullPath + extension : fullPath; + SqlClientEventSource.Log.TryTraceEvent(" Looking for '{2}' assembly by '{3}' full path." + , TypeName, methodName, assemblyName, fullPath); + return File.Exists(fullPath) ? fullPath : null; + } + + private static Assembly AssemblyResolver(AssemblyName arg) + { + string methodName = MethodBase.GetCurrentMethod().Name; + + string fullPath = MakeFullPath(Environment.CurrentDirectory, arg.Name); + SqlClientEventSource.Log.TryTraceEvent(" Looking for '{2}' assembly by '{3}' full path." + , TypeName, methodName, arg, fullPath); + + return fullPath == null ? null : AssemblyLoadContext.Default.LoadFromAssemblyPath(fullPath); + } + + private static Type TypeResolver(Assembly arg1, string arg2, bool arg3) => arg1?.ExportedTypes.Single(t => t.FullName == arg2); + + /// + /// Load assemblies on request. + /// + private static Assembly Default_Resolving(AssemblyLoadContext arg1, AssemblyName arg2) + { + string methodName = MethodBase.GetCurrentMethod().Name; + + string target = MakeFullPath(Environment.CurrentDirectory, arg2.Name); + SqlClientEventSource.Log.TryTraceEvent(" Looking for '{2}' assembly that is requested by '{3}' ALC from '{4}' path." + , TypeName, methodName, arg2, arg1, target); + + return target == null ? null : arg1.LoadFromAssemblyPath(target); + } + #endregion + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/ConcurrentQueueSemaphore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/ConcurrentQueueSemaphore.cs new file mode 100644 index 0000000000..46d3b70a25 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/ConcurrentQueueSemaphore.cs @@ -0,0 +1,60 @@ +// 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.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Data.SqlClient.SNI +{ + /// + /// This class implements a FIFO Queue with SemaphoreSlim for ordered execution of parallel tasks. + /// Currently used in Managed SNI (SNISslStream) to override SslStream's WriteAsync implementation. + /// + internal sealed partial class ConcurrentQueueSemaphore + { + private readonly SemaphoreSlim _semaphore; + private readonly ConcurrentQueue> _queue; + + public ConcurrentQueueSemaphore(int initialCount) + { + _semaphore = new SemaphoreSlim(initialCount); + _queue = new ConcurrentQueue>(); + } + + public Task WaitAsync(CancellationToken cancellationToken) + { + // try sync wait with 0 which will not block to see if we need to do an async wait + if (_semaphore.Wait(0, cancellationToken)) + { + return Task.CompletedTask; + } + else + { + var tcs = new TaskCompletionSource(); + _queue.Enqueue(tcs); + _semaphore.WaitAsync().ContinueWith( + continuationAction: static (Task task, object state) => + { + ConcurrentQueue> queue = (ConcurrentQueue>)state; + if (queue.TryDequeue(out TaskCompletionSource popped)) + { + popped.SetResult(true); + } + }, + state: _queue, + cancellationToken: cancellationToken + ); + return tcs.Task; + } + } + + public void Release() + { + _semaphore.Release(); + } + } + +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs index e77b93f5f0..68eaa25486 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs @@ -26,7 +26,7 @@ internal sealed class LocalDB private IntPtr _startInstanceHandle = IntPtr.Zero; // Local Db api doc https://msdn.microsoft.com/en-us/library/hh217143.aspx - // HRESULT LocalDBStartInstance( [Input ] PCWSTR pInstanceName, [Input ] DWORD dwFlags,[Output] LPWSTR wszSqlConnection,[Input/Output] LPDWORD lpcchSqlConnection); + // HRESULT LocalDBStartInstance( [Input ] PCWSTR pInstanceName, [Input ] DWORD dwFlags,[Output] LPWSTR wszSqlConnection,[Input/Output] LPDWORD lpcchSqlConnection); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int LocalDBStartInstance( [In] [MarshalAs(UnmanagedType.LPWStr)] string localDBInstanceName, @@ -64,37 +64,45 @@ internal static uint MapLocalDBErrorStateToCode(LocalDBErrorState errorState) switch (errorState) { case LocalDBErrorState.NO_INSTALLATION: - SqlClientEventSource.Log.TrySNITraceEvent(" LocalDB is not installed. Error State ={0}", errorState); return SNICommon.LocalDBNoInstallation; - case LocalDBErrorState.INVALID_CONFIG: - SqlClientEventSource.Log.TrySNITraceEvent(" Invalid configuration. Error State ={0}", errorState); return SNICommon.LocalDBInvalidConfig; - case LocalDBErrorState.NO_SQLUSERINSTANCEDLL_PATH: - SqlClientEventSource.Log.TrySNITraceEvent(" No SQL user instance path. Error State ={0}", errorState); return SNICommon.LocalDBNoSqlUserInstanceDllPath; - case LocalDBErrorState.INVALID_SQLUSERINSTANCEDLL_PATH: - SqlClientEventSource.Log.TrySNITraceEvent(" Invalid SQL user instance path. Error State ={0}", errorState); return SNICommon.LocalDBInvalidSqlUserInstanceDllPath; - case LocalDBErrorState.NONE: return 0; - default: - SqlClientEventSource.Log.TrySNITraceEvent(" Invalid configuration. Error State ={0}", errorState); return SNICommon.LocalDBInvalidConfig; } } + internal static string MapLocalDBErrorStateToErrorMessage(LocalDBErrorState errorState) + { + switch (errorState) + { + case LocalDBErrorState.NO_INSTALLATION: + return Strings.SNI_ERROR_52; + case LocalDBErrorState.INVALID_CONFIG: + return Strings.SNI_ERROR_53; + case LocalDBErrorState.NO_SQLUSERINSTANCEDLL_PATH: + return Strings.SNI_ERROR_54; + case LocalDBErrorState.INVALID_SQLUSERINSTANCEDLL_PATH: + return Strings.SNI_ERROR_55; + case LocalDBErrorState.NONE: + return Strings.SNI_ERROR_50; + default: + return Strings.SNI_ERROR_53; + } + } + /// /// Loads the User Instance dll. /// private bool LoadUserInstanceDll() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(LocalDB))) { // Check in a non thread-safe way if the handle is already set for performance. if (_sqlUserInstanceLibraryHandle != null) @@ -117,16 +125,16 @@ private bool LoadUserInstanceDll() // If there was no DLL path found, then there is an error. if (dllPath == null) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, MapLocalDBErrorStateToCode(registryQueryErrorState), string.Empty); - SqlClientEventSource.Log.TrySNITraceEvent("User instance DLL path is null."); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, MapLocalDBErrorStateToCode(registryQueryErrorState), MapLocalDBErrorStateToErrorMessage(registryQueryErrorState)); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "User instance DLL path is null."); return false; } // In case the registry had an empty path for dll if (string.IsNullOrWhiteSpace(dllPath)) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBInvalidSqlUserInstanceDllPath, string.Empty); - SqlClientEventSource.Log.TrySNITraceEvent(" User instance DLL path is invalid. DLL path ={0}", dllPath); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBInvalidSqlUserInstanceDllPath, Strings.SNI_ERROR_55); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "User instance DLL path is invalid. DLL path = {0}", dllPath); return false; } @@ -135,8 +143,8 @@ private bool LoadUserInstanceDll() if (libraryHandle.IsInvalid) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBFailedToLoadDll, string.Empty); - SqlClientEventSource.Log.TrySNITraceEvent(" Library Handle is invalid. Could not load the dll."); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBFailedToLoadDll, Strings.SNI_ERROR_56); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Library Handle is invalid. Could not load the dll."); libraryHandle.Dispose(); return false; } @@ -146,8 +154,8 @@ private bool LoadUserInstanceDll() if (_startInstanceHandle == IntPtr.Zero) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, string.Empty); - SqlClientEventSource.Log.TrySNITraceEvent(" Was not able to load the PROC from DLL. Bad Runtime."); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, Strings.SNI_ERROR_57); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Was not able to load the PROC from DLL. Bad Runtime."); libraryHandle.Dispose(); return false; } @@ -157,21 +165,17 @@ private bool LoadUserInstanceDll() if (localDBStartInstanceFunc == null) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, string.Empty); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBBadRuntime, Strings.SNI_ERROR_57); libraryHandle.Dispose(); _startInstanceHandle = IntPtr.Zero; return false; } _sqlUserInstanceLibraryHandle = libraryHandle; - SqlClientEventSource.Log.TrySNITraceEvent(" User Instance DLL was loaded successfully."); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.INFO, "User Instance DLL was loaded successfully."); return true; } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -181,8 +185,7 @@ private bool LoadUserInstanceDll() /// private string GetUserInstanceDllPath(out LocalDBErrorState errorState) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(" GetUserInstanceDllPath"); - try + using (TrySNIEventScope.Create(nameof(LocalDB))) { string dllPath = null; using (RegistryKey key = Registry.LocalMachine.OpenSubKey(LocalDBInstalledVersionRegistryKey)) @@ -190,7 +193,7 @@ private string GetUserInstanceDllPath(out LocalDBErrorState errorState) if (key == null) { errorState = LocalDBErrorState.NO_INSTALLATION; - SqlClientEventSource.Log.TrySNITraceEvent(" not installed. Error state ={0}.", errorState); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "No installation found."); return null; } @@ -205,7 +208,7 @@ private string GetUserInstanceDllPath(out LocalDBErrorState errorState) if (!Version.TryParse(subKey, out currentKeyVersion)) { errorState = LocalDBErrorState.INVALID_CONFIG; - SqlClientEventSource.Log.TrySNITraceEvent(" Invalid Configuration. state ={0}.", errorState); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Invalid Configuration."); return null; } @@ -219,7 +222,7 @@ private string GetUserInstanceDllPath(out LocalDBErrorState errorState) if (latestVersion.Equals(zeroVersion)) { errorState = LocalDBErrorState.INVALID_CONFIG; - SqlClientEventSource.Log.TrySNITraceEvent(" Invalid Configuration. state ={0}.", errorState); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Invalid Configuration."); return null; } @@ -232,7 +235,7 @@ private string GetUserInstanceDllPath(out LocalDBErrorState errorState) if (instanceAPIPathRegistryObject == null) { errorState = LocalDBErrorState.NO_SQLUSERINSTANCEDLL_PATH; - SqlClientEventSource.Log.TrySNITraceEvent(" No SQL user instance DLL. Instance API Path Registry Object Error. state ={0}.", errorState); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "No SQL user instance DLL. Instance API Path Registry Object Error."); return null; } @@ -241,7 +244,7 @@ private string GetUserInstanceDllPath(out LocalDBErrorState errorState) if (valueKind != RegistryValueKind.String) { errorState = LocalDBErrorState.INVALID_SQLUSERINSTANCEDLL_PATH; - SqlClientEventSource.Log.TrySNITraceEvent(" No SQL user instance DLL. state ={0}. Registry value kind error.", errorState); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Invalid SQL user instance DLL path. Registry value kind mismatch."); return null; } @@ -252,10 +255,6 @@ private string GetUserInstanceDllPath(out LocalDBErrorState errorState) } } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs index 475660aa2b..b92746098f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; @@ -108,6 +109,7 @@ internal class SNICommon internal const int ConnTimeoutError = 11; internal const int ConnNotUsableError = 19; internal const int InvalidConnStringError = 25; + internal const int ErrorLocatingServerInstance = 26; internal const int HandshakeFailureError = 31; internal const int InternalExceptionError = 35; internal const int ConnOpenFailedError = 40; @@ -126,33 +128,32 @@ internal class SNICommon internal const int LocalDBBadRuntime = 57; /// - /// Validate server certificate callback for SSL + /// We only validate Server name in Certificate to match with "targetServerName". + /// Certificate validation and chain trust validations are done by SSLStream class [System.Net.Security.SecureChannel.VerifyRemoteCertificate method] + /// This method is called as a result of callback for SSL Stream Certificate validation. /// /// Server that client is expecting to connect to - /// Sender object /// X.509 certificate - /// X.509 chain /// Policy errors /// True if certificate is valid - internal static bool ValidateSslServerCertificate(string targetServerName, object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors) + internal static bool ValidateSslServerCertificate(string targetServerName, X509Certificate cert, SslPolicyErrors policyErrors) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create("SNICommon.ValidateSslServerCertificate | SNI | SCOPE | INFO | Entering Scope {0} ")) { if (policyErrors == SslPolicyErrors.None) { - SqlClientEventSource.Log.TrySNITraceEvent(" SSL Server certificate validated."); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.INFO, "targetServerName {0}, SSL Server certificate not validated as PolicyErrors set to None.", args0: targetServerName); return true; } if ((policyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) { - SqlClientEventSource.Log.TrySNITraceEvent(" SSL Remote certificate name mismatched."); string certServerName = cert.Subject.Substring(cert.Subject.IndexOf('=') + 1); // Verify that target server name matches subject in the certificate if (targetServerName.Length > certServerName.Length) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "targetServerName {0}, Target Server name is of greater length than Subject in Certificate.", args0: targetServerName); return false; } else if (targetServerName.Length == certServerName.Length) @@ -160,6 +161,7 @@ internal static bool ValidateSslServerCertificate(string targetServerName, objec // Both strings have the same length, so targetServerName must be a FQDN if (!targetServerName.Equals(certServerName, StringComparison.OrdinalIgnoreCase)) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "targetServerName {0}, Target Server name does not match Subject in Certificate.", args0: targetServerName); return false; } } @@ -167,6 +169,7 @@ internal static bool ValidateSslServerCertificate(string targetServerName, objec { if (string.Compare(targetServerName, 0, certServerName, 0, targetServerName.Length, StringComparison.OrdinalIgnoreCase) != 0) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "targetServerName {0}, Target Server name does not match Subject in Certificate.", args0: targetServerName); return false; } @@ -176,6 +179,7 @@ internal static bool ValidateSslServerCertificate(string targetServerName, objec // (Names have different lengths, so the target server can't be a FQDN.) if (certServerName[targetServerName.Length] != '.') { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "targetServerName {0}, Target Server name does not match Subject in Certificate.", args0: targetServerName); return false; } } @@ -183,13 +187,64 @@ internal static bool ValidateSslServerCertificate(string targetServerName, objec else { // Fail all other SslPolicy cases besides RemoteCertificateNameMismatch + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "targetServerName {0}, SslPolicyError {1}, SSL Policy invalidated certificate.", args0: targetServerName, args1: policyErrors); return false; } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.INFO, "targetServerName {0}, Client certificate validated successfully.", args0: targetServerName); return true; } - finally + } + + /// + /// We validate the provided certificate provided by the client with the one from the server to see if it matches. + /// Certificate validation and chain trust validations are done by SSLStream class [System.Net.Security.SecureChannel.VerifyRemoteCertificate method] + /// This method is called as a result of callback for SSL Stream Certificate validation. + /// + /// X.509 certificate provided by the client + /// X.509 certificate provided by the server + /// Policy errors + /// True if certificate is valid + internal static bool ValidateSslServerCertificate(X509Certificate clientCert, X509Certificate serverCert, SslPolicyErrors policyErrors) + { + using (TrySNIEventScope.Create("SNICommon.ValidateSslServerCertificate | SNI | SCOPE | INFO | Entering Scope {0} ")) + { + if (policyErrors == SslPolicyErrors.None) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.INFO, "serverCert {0}, SSL Server certificate not validated as PolicyErrors set to None.", args0: clientCert.Subject); + return true; + } + + if ((policyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) + { + // Verify that subject name matches + if (serverCert.Subject != clientCert.Subject) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "certificate subject from server is {0}, and does not match with the certificate provided client.", args0: serverCert.Subject); + return false; + } + if (!serverCert.Equals(clientCert)) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "certificate from server does not match with the certificate provided client.", args0: serverCert.Subject); + return false; + } + } + else + { + // Fail all other SslPolicy cases besides RemoteCertificateNameMismatch + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "certificate subject: {0}, SslPolicyError {1}, SSL Policy invalidated certificate.", args0: clientCert.Subject, args1: policyErrors); + return false; + } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.INFO, "certificate subject {0}, Client certificate validated successfully.", args0: clientCert.Subject); + return true; + } + } + + internal static IPAddress[] GetDnsIpAddresses(string serverName) + { + using (TrySNIEventScope.Create(nameof(GetDnsIpAddresses))) { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.INFO, "Getting DNS host entries for serverName {0}.", args0: serverName); + return Dns.GetHostAddresses(serverName); } } @@ -203,7 +258,7 @@ internal static bool ValidateSslServerCertificate(string targetServerName, objec /// internal static uint ReportSNIError(SNIProviders provider, uint nativeError, uint sniError, string errorMessage) { - SqlClientEventSource.Log.TrySNITraceEvent(" Provider ={0}, native Error ={1}, SNI Error ={2}, Error Message ={3}", provider, nativeError, sniError, errorMessage); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "Provider = {0}, native Error = {1}, SNI Error = {2}, Error Message = {3}", args0: provider, args1: nativeError, args2: sniError, args3: errorMessage); return ReportSNIError(new SNIError(provider, nativeError, sniError, errorMessage)); } @@ -213,11 +268,12 @@ internal static uint ReportSNIError(SNIProviders provider, uint nativeError, uin /// SNI provider /// SNI error code /// SNI Exception + /// Native SNI error code /// - internal static uint ReportSNIError(SNIProviders provider, uint sniError, Exception sniException) + internal static uint ReportSNIError(SNIProviders provider, uint sniError, Exception sniException, uint nativeErrorCode = 0) { - SqlClientEventSource.Log.TrySNITraceEvent(" Provider ={0}, SNI Error ={1}, Exception ={2}", provider, sniError, sniException.Message); - return ReportSNIError(new SNIError(provider, sniError, sniException)); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.ERR, "Provider = {0}, SNI Error = {1}, Exception = {2}", args0: provider, args1: sniError, args2: sniException?.Message); + return ReportSNIError(new SNIError(provider, sniError, sniException, nativeErrorCode)); } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIError.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIError.cs index 412efac189..080e274f94 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIError.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIError.cs @@ -11,6 +11,9 @@ namespace Microsoft.Data.SqlClient.SNI /// internal class SNIError { + // Error numbers from native SNI implementation + internal const uint CertificateValidationErrorCode = 2148074277; + public readonly SNIProviders provider; public readonly string errorMessage; public readonly uint nativeError; @@ -21,24 +24,24 @@ internal class SNIError public SNIError(SNIProviders provider, uint nativeError, uint sniErrorCode, string errorMessage) { - this.lineNumber = 0; - this.function = string.Empty; + lineNumber = 0; + function = string.Empty; this.provider = provider; this.nativeError = nativeError; - this.sniError = sniErrorCode; + sniError = sniErrorCode; this.errorMessage = errorMessage; - this.exception = null; + exception = null; } - public SNIError(SNIProviders provider, uint sniErrorCode, Exception sniException) + public SNIError(SNIProviders provider, uint sniErrorCode, Exception sniException, uint nativeErrorCode = 0) { - this.lineNumber = 0; - this.function = string.Empty; + lineNumber = 0; + function = string.Empty; this.provider = provider; - this.nativeError = 0; - this.sniError = sniErrorCode; - this.errorMessage = string.Empty; - this.exception = sniException; + nativeError = nativeErrorCode; + sniError = sniErrorCode; + errorMessage = string.Empty; + exception = sniException; } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs index bd3facd5fc..354ce3eff5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs @@ -3,6 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Net.Security; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.Data.SqlClient.SNI { @@ -11,6 +17,32 @@ namespace Microsoft.Data.SqlClient.SNI /// internal abstract class SNIHandle { + protected static readonly SslProtocols s_supportedProtocols = SslProtocols.None; + +#if !NETSTANDARD2_0 + protected static readonly List s_tdsProtocols = new List(1) { new(TdsEnums.TDS8_Protocol) }; + + protected static async Task AuthenticateAsClientAsync(SslStream sslStream, string serverNameIndication, X509CertificateCollection certificate, CancellationToken token) + { + SslClientAuthenticationOptions sslClientOptions = new() + { + TargetHost = serverNameIndication, + ApplicationProtocols = s_tdsProtocols, + ClientCertificates = certificate + }; + await sslStream.AuthenticateAsClientAsync(sslClientOptions, token); + } +#endif + + protected static void AuthenticateAsClient(SslStream sslStream, string serverNameIndication, X509CertificateCollection certificate) + { +#if !NETSTANDARD2_0 + AuthenticateAsClientAsync(sslStream, serverNameIndication, certificate, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); +#else + throw new NotSupportedException(Strings.SQL_TDS8_NotSupported_Netstandard2_0); +#endif + } + /// /// Dispose class /// @@ -40,9 +72,8 @@ internal abstract class SNIHandle /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public abstract uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null); + public abstract uint SendAsync(SNIPacket packet); /// /// Receive a packet synchronously diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs index 6e885462e5..c1a5e1a573 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs @@ -14,7 +14,7 @@ internal class SNILoadHandle public static readonly SNILoadHandle SingletonInstance = new SNILoadHandle(); public readonly EncryptionOptions _encryptionOption = EncryptionOptions.OFF; - public ThreadLocal _lastError = new ThreadLocal(() => { return new SNIError(SNIProviders.INVALID_PROV, 0, TdsEnums.SNI_SUCCESS, string.Empty); }); + public ThreadLocal _lastError = new ThreadLocal(static () => new SNIError(SNIProviders.INVALID_PROV, 0, TdsEnums.SNI_SUCCESS, string.Empty)); private readonly uint _status = TdsEnums.SNI_SUCCESS; @@ -30,7 +30,6 @@ public SNIError LastError set { - SqlClientEventSource.Log.TrySNITraceEvent(" Last Error Value = {0}", value); _lastError.Value = value; } } @@ -56,5 +55,11 @@ public EncryptionOptions Options return _encryptionOption; } } + + /// + /// Verify client encryption possibility + /// + // TODO: by adding support ENCRYPT_NOT_SUP, it could be calculated. + public bool ClientOSEncryptionSupport => true; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs index 555f368241..90bf83dacc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs @@ -14,6 +14,8 @@ namespace Microsoft.Data.SqlClient.SNI /// internal class SNIMarsConnection { + private const string s_className = nameof(SNIMarsConnection); + private readonly Guid _connectionId = Guid.NewGuid(); private readonly Dictionary _sessions = new Dictionary(); private readonly byte[] _headerBytes = new byte[SNISMUXHeader.HEADER_LENGTH]; @@ -44,6 +46,7 @@ public Guid ConnectionId public SNIMarsConnection(SNIHandle lowerHandle) { _lowerHandle = lowerHandle; + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "Created MARS Session Id {0}", args0: ConnectionId); _lowerHandle.SetAsyncCallbacks(HandleReceiveComplete, HandleSendComplete); } @@ -54,6 +57,7 @@ public SNIMarsHandle CreateMarsSession(object callbackObject, bool async) ushort sessionId = _nextSessionId++; SNIMarsHandle handle = new SNIMarsHandle(this, sessionId, callbackObject, async); _sessions.Add(sessionId, handle); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, SNI MARS Handle Id {1}, created new MARS Session {2}", args0: ConnectionId, args1: handle?.ConnectionId, args2: sessionId); return handle; } } @@ -64,18 +68,18 @@ public SNIMarsHandle CreateMarsSession(object callbackObject, bool async) /// public uint StartReceive() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(" StartReceive"); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { SNIPacket packet = null; if (ReceiveAsync(ref packet) == TdsEnums.SNI_SUCCESS_IO_PENDING) { - SqlClientEventSource.Log.TrySNITraceEvent(" Success IO pending."); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, Success IO pending.", args0: ConnectionId); return TdsEnums.SNI_SUCCESS_IO_PENDING; } - SqlClientEventSource.Log.TrySNITraceEvent(" Connection not useable."); - return SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnNotUsableError, string.Empty); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Connection not usable.", args0: ConnectionId); + return SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnNotUsableError, Strings.SNI_ERROR_19); } finally { @@ -90,7 +94,7 @@ public uint StartReceive() /// SNI error code public uint Send(SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(" Send"); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { lock (this) @@ -108,16 +112,15 @@ public uint Send(SNIPacket packet) /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public uint SendAsync(SNIPacket packet, SNIAsyncCallback callback) + public uint SendAsync(SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(" SendAsync"); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { lock (this) { - return _lowerHandle.SendAsync(packet, callback); + return _lowerHandle.SendAsync(packet); } } finally @@ -133,18 +136,25 @@ public uint SendAsync(SNIPacket packet, SNIAsyncCallback callback) /// SNI error code public uint ReceiveAsync(ref SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(" SendAsync"); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { if (packet != null) { ReturnPacket(packet); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, Packet {1} returned", args0: ConnectionId, args1: packet?._id); +#endif packet = null; } lock (this) { - return _lowerHandle.ReceiveAsync(ref packet); + var response = _lowerHandle.ReceiveAsync(ref packet); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, Received new packet {1}", args0: ConnectionId, args1: packet?._id); +#endif + return response; } } finally @@ -159,7 +169,7 @@ public uint ReceiveAsync(ref SNIPacket packet) /// SNI error status public uint CheckConnection() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { lock (this) @@ -179,15 +189,18 @@ public uint CheckConnection() public void HandleReceiveError(SNIPacket packet) { Debug.Assert(Monitor.IsEntered(this), "HandleReceiveError was called without being locked."); - if (!Monitor.IsEntered(this)) - { - SqlClientEventSource.Log.TrySNITraceEvent(" HandleReceiveError was called without being locked."); - } foreach (SNIMarsHandle handle in _sessions.Values) { - if (packet.HasCompletionCallback) + if (packet.HasAsyncIOCompletionCallback) { handle.HandleReceiveError(packet); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Packet {1} has Completion Callback", args0: ConnectionId, args1: packet?._id); + } + else + { + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Packet {1} does not have Completion Callback, error not handled.", args0: ConnectionId, args1: packet?._id); +#endif } } Debug.Assert(!packet.IsInvalid, "packet was returned by MarsConnection child, child sessions should not release the packet"); @@ -201,7 +214,7 @@ public void HandleReceiveError(SNIPacket packet) /// SNI error code public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) { - packet.InvokeCompletionCallback(sniErrorCode); + packet.InvokeAsyncIOCompletionCallback(sniErrorCode); } /// @@ -211,7 +224,7 @@ public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) /// SNI error code public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { SNISMUXHeader currentHeader = null; @@ -223,7 +236,7 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) lock (this) { HandleReceiveError(packet); - SqlClientEventSource.Log.TrySNITraceEvent(" not successful."); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } @@ -246,22 +259,25 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) if (bytesTaken == 0) { sniErrorCode = ReceiveAsync(ref packet); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, Non-SMUX Header SNI Packet received with code {1}", args0: ConnectionId, args1: sniErrorCode); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { - SqlClientEventSource.Log.TrySNITraceEvent(" not successful."); return; } HandleReceiveError(packet); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } _currentHeader.Read(_headerBytes); - _dataBytesLeft = (int)_currentHeader.length; _currentPacket = _lowerHandle.RentPacket(headerSize: 0, dataSize: (int)_currentHeader.length); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, _dataBytesLeft {1}, _currentPacket {2}, Reading data of length: _currentHeader.length {3}", args0: _lowerHandle?.ConnectionId, args1: _dataBytesLeft, args2: currentPacket?._id, args3: _currentHeader?.length); +#endif } currentHeader = _currentHeader; @@ -277,6 +293,7 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) if (_dataBytesLeft > 0) { sniErrorCode = ReceiveAsync(ref packet); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, SMUX DATA Header SNI Packet received with code {1}, _dataBytesLeft {2}", args0: ConnectionId, args1: sniErrorCode, args2: _dataBytesLeft); if (sniErrorCode == TdsEnums.SNI_SUCCESS_IO_PENDING) { @@ -284,6 +301,7 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) } HandleReceiveError(packet); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, Handled receive error code: {1}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } @@ -293,8 +311,9 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) if (!_sessions.ContainsKey(_currentHeader.sessionId)) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, string.Empty); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.InvalidParameterError, Strings.SNI_ERROR_5); HandleReceiveError(packet); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "Current Header Session Id {0} not found, MARS Session Id {1} will be destroyed, New SNI error created: {2}", args0: _currentHeader?.sessionId, args1: _lowerHandle?.ConnectionId, args2: sniErrorCode); _lowerHandle.Dispose(); _lowerHandle = null; return; @@ -303,16 +322,19 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_FIN) { _sessions.Remove(_currentHeader.sessionId); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_FIN | MARS Session Id {0}, SMUX_FIN flag received, Current Header Session Id {1} removed", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } else { currentSession = _sessions[_currentHeader.sessionId]; + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "MARS Session Id {0}, Current Session assigned to Session Id {1}", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } } if (currentHeader.flags == (byte)SNISMUXFlags.SMUX_DATA) { currentSession.HandleReceiveComplete(currentPacket, currentHeader); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_DATA | MARS Session Id {0}, Current Session {1} completed receiving Data", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } if (_currentHeader.flags == (byte)SNISMUXFlags.SMUX_ACK) @@ -320,13 +342,17 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) try { currentSession.HandleAck(currentHeader.highwater); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_ACK | MARS Session Id {0}, Current Session {1} handled ack", args0: _lowerHandle?.ConnectionId, args1: _currentHeader?.sessionId); } catch (Exception e) { + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "SMUX_ACK | MARS Session Id {0}, Exception occurred: {2}", args0: _currentHeader?.sessionId, args1: e?.Message); SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); } - +#if DEBUG Debug.Assert(_currentPacket == currentPacket, "current and _current are not the same"); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.INFO, "SMUX_ACK | MARS Session Id {0}, Current Packet {1} returned", args0: _lowerHandle?.ConnectionId, args1: currentPacket?._id); +#endif ReturnPacket(currentPacket); currentPacket = null; _currentPacket = null; @@ -344,6 +370,7 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) } HandleReceiveError(packet); + SqlClientEventSource.Log.TrySNITraceEvent(s_className, EventType.ERR, "MARS Session Id {0}, packet.DataLeft 0, SNI error {2}", args0: _lowerHandle?.ConnectionId, args1: sniErrorCode); return; } } @@ -360,7 +387,7 @@ public void HandleReceiveComplete(SNIPacket packet, uint sniErrorCode) /// public uint EnableSsl(uint options) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { return _lowerHandle.EnableSsl(options); @@ -376,7 +403,7 @@ public uint EnableSsl(uint options) /// public void DisableSsl() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { _lowerHandle.DisableSsl(); @@ -403,7 +430,7 @@ public void ReturnPacket(SNIPacket packet) /// public void KillConnection() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); + long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { _lowerHandle.KillConnection(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs index 5f339f00a2..8246ce3d6f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs @@ -19,9 +19,9 @@ internal sealed class SNIMarsHandle : SNIHandle private readonly SNIMarsConnection _connection; private readonly uint _status = TdsEnums.SNI_UNINITIALIZED; private readonly Queue _receivedPacketQueue = new Queue(); - private readonly Queue _sendPacketQueue = new Queue(); + private readonly Queue _sendPacketQueue = new Queue(); private readonly object _callbackObject; - private readonly Guid _connectionId = Guid.NewGuid(); + private readonly Guid _connectionId; private readonly ushort _sessionId; private readonly ManualResetEventSlim _packetEvent = new ManualResetEventSlim(false); private readonly ManualResetEventSlim _ackEvent = new ManualResetEventSlim(false); @@ -54,19 +54,18 @@ internal sealed class SNIMarsHandle : SNIHandle /// public override void Dispose() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { - SendControlPacket(SNISMUXFlags.SMUX_FIN); - } - catch (Exception e) - { - SqlClientEventSource.Log.TrySNITraceEvent(" internal exception error = {0}, Member Name={1}", e.Message, e.GetType().Name); - SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); - } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); + try + { + SendControlPacket(SNISMUXFlags.SMUX_FIN); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, Sent SMUX_FIN packet to terminate session.", args0: ConnectionId); + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.ERR, "MARS Session Id {0}, Internal exception error = {1}, Member Name={2}", args0: ConnectionId, args1: e?.Message, args2: e?.GetType()?.Name); + SNICommon.ReportSNIError(SNIProviders.SMUX_PROV, SNICommon.InternalExceptionError, e); + } } } @@ -81,9 +80,11 @@ public SNIMarsHandle(SNIMarsConnection connection, ushort sessionId, object call { _sessionId = sessionId; _connection = connection; + _connectionId = connection.ConnectionId; _callbackObject = callbackObject; _handleSendCompleteCallback = HandleSendComplete; SendControlPacket(SNISMUXFlags.SMUX_SYN); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, Sent SMUX_SYN packet to start a new session, session Id {1}", args0: ConnectionId, args1: _sessionId); _status = TdsEnums.SNI_SUCCESS; } @@ -93,11 +94,12 @@ public SNIMarsHandle(SNIMarsConnection connection, ushort sessionId, object call /// SMUX header flags private void SendControlPacket(SNISMUXFlags flags) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create("SNIMarsHandle.SendControlPacket | SNI | INFO | SCOPE | Entering Scope {0}")) { SNIPacket packet = RentPacket(headerSize: SNISMUXHeader.HEADER_LENGTH, dataSize: 0); - +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, Packet rented {1}, packet dataLeft {2}", args0: ConnectionId, args1: packet?._id, args2: packet?.DataLeft); +#endif lock (this) { SetupSMUXHeader(0, flags); @@ -107,16 +109,16 @@ private void SendControlPacket(SNISMUXFlags flags) _connection.Send(packet); ReturnPacket(packet); - } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, Packet returned {1}, packet dataLeft {2}", args0: ConnectionId, args1: packet?._id, args2: packet?.DataLeft); + ; +#endif } } private void SetupSMUXHeader(int length, SNISMUXFlags flags) { - Debug.Assert(Monitor.IsEntered(this), "must take lock on self before updating mux header"); + Debug.Assert(Monitor.IsEntered(this), "must take lock on self before updating smux header"); _currentHeader.SMID = 83; _currentHeader.flags = (byte)flags; @@ -134,12 +136,14 @@ private void SetupSMUXHeader(int length, SNISMUXFlags flags) /// The packet with the SMUx header set. private SNIPacket SetPacketSMUXHeader(SNIPacket packet) { - Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to mux packet without mux reservation"); + Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to smux packet without smux reservation"); SetupSMUXHeader(packet.Length, SNISMUXFlags.SMUX_DATA); _currentHeader.Write(packet.GetHeaderBuffer(SNISMUXHeader.HEADER_LENGTH)); packet.SetHeaderActive(); - +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, Setting SMUX_DATA header in current header for packet {1}", args0: ConnectionId, args1: packet?._id); +#endif return packet; } @@ -150,9 +154,8 @@ private SNIPacket SetPacketSMUXHeader(SNIPacket packet) /// SNI error code public override uint Send(SNIPacket packet) { - Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to send muxed packet without mux reservation in Send"); - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to send muxed packet without smux reservation in Send"); + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { while (true) { @@ -164,10 +167,12 @@ public override uint Send(SNIPacket packet) } } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, Waiting for Acknowledgment event.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); _ackEvent.Wait(); lock (this) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sendPacketQueue count found {1}, Acknowledgment event Reset", args0: ConnectionId, args1: _sendPacketQueue?.Count); _ackEvent.Reset(); } } @@ -177,43 +182,35 @@ public override uint Send(SNIPacket packet) { muxedPacket = SetPacketSMUXHeader(packet); } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, SMUX Packet is going to be sent.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); return _connection.Send(muxedPacket); } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// /// Send packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - private uint InternalSendAsync(SNIPacket packet, SNIAsyncCallback callback) + private uint InternalSendAsync(SNIPacket packet) { - Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to send muxed packet without mux reservation in InternalSendAsync"); - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to send muxed packet without smux reservation in InternalSendAsync"); + using (TrySNIEventScope.Create("SNIMarsHandle.InternalSendAsync | SNI | INFO | SCOPE | Entering Scope {0}")) { lock (this) { if (_sequenceNumber >= _sendHighwater) { - SqlClientEventSource.Log.TrySNITraceEvent(" SNI Queue is full"); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, SNI Queue is full", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); return TdsEnums.SNI_QUEUE_FULL; } SNIPacket muxedPacket = SetPacketSMUXHeader(packet); - muxedPacket.SetCompletionCallback(callback ?? HandleSendComplete); - return _connection.SendAsync(muxedPacket, callback); + muxedPacket.SetAsyncIOCompletionCallback(_handleSendCompleteCallback); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, Sending packet", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); + return _connection.SendAsync(muxedPacket); } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -222,10 +219,10 @@ private uint InternalSendAsync(SNIPacket packet, SNIAsyncCallback callback) /// SNI error code private uint SendPendingPackets() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - SNIMarsQueuedPacket packet = null; - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { + SNIPacket packet = null; + while (true) { lock (this) @@ -235,20 +232,22 @@ private uint SendPendingPackets() if (_sendPacketQueue.Count != 0) { packet = _sendPacketQueue.Peek(); - uint result = InternalSendAsync(packet.Packet, packet.Callback); + uint result = InternalSendAsync(packet); if (result != TdsEnums.SNI_SUCCESS && result != TdsEnums.SNI_SUCCESS_IO_PENDING) { - SqlClientEventSource.Log.TrySNITraceEvent(" InternalSendAsync result is not SNI_SUCCESS and is not SNI_SUCCESS_IO_PENDING"); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.ERR, "MARS Session Id {0}, InternalSendAsync result is not SNI_SUCCESS and is not SNI_SUCCESS_IO_PENDING", args0: ConnectionId); return result; } _sendPacketQueue.Dequeue(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sendPacketQueue dequeued, count {1}", args0: ConnectionId, args1: _sendPacketQueue?.Count); continue; } else { _ackEvent.Set(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sendPacketQueue count found {1}, acknowledgment set", args0: ConnectionId, args1: _sendPacketQueue?.Count); } } @@ -258,35 +257,28 @@ private uint SendPendingPackets() return TdsEnums.SNI_SUCCESS; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public override uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null) + public override uint SendAsync(SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { + packet.SetAsyncIOCompletionCallback(_handleSendCompleteCallback); lock (this) { - _sendPacketQueue.Enqueue(new SNIMarsQueuedPacket(packet, callback ?? _handleSendCompleteCallback)); + _sendPacketQueue.Enqueue(packet); } SendPendingPackets(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sendPacketQueue enqueued, count {1}", args0: ConnectionId, args1: _sendPacketQueue?.Count); + return TdsEnums.SNI_SUCCESS_IO_PENDING; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -296,8 +288,7 @@ public override uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = nul /// SNI error code public override uint ReceiveAsync(ref SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { lock (_receivedPacketQueue) { @@ -305,12 +296,15 @@ public override uint ReceiveAsync(ref SNIPacket packet) if (_connectionError != null) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.ERR, "MARS Session Id {0}, _asyncReceives {1}, _receiveHighwater {2}, _sendHighwater {3}, _receiveHighwaterLastAck {4}, _connectionError {5}", args0: ConnectionId, args1: _asyncReceives, args2: _receiveHighwater, args3: _sendHighwater, args4: _receiveHighwaterLastAck, args5: _connectionError); return SNICommon.ReportSNIError(_connectionError); } if (queueCount == 0) { _asyncReceives++; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, queueCount 0, _asyncReceives {1}, _receiveHighwater {2}, _sendHighwater {3}, _receiveHighwaterLastAck {4}", args0: ConnectionId, args1: _asyncReceives, args2: _receiveHighwater, args3: _sendHighwater, args4: _receiveHighwaterLastAck); + return TdsEnums.SNI_SUCCESS_IO_PENDING; } @@ -318,6 +312,9 @@ public override uint ReceiveAsync(ref SNIPacket packet) if (queueCount == 1) { +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, packet dequeued {1}, packet Owner {2}, packet refCount {3}, received Packet Queue count {4}", args0: ConnectionId, args1: packet?._id, args2: packet?._owner, args3: packet?._refCount, args4: _receivedPacketQueue?.Count); +#endif _packetEvent.Reset(); } } @@ -327,13 +324,10 @@ public override uint ReceiveAsync(ref SNIPacket packet) _receiveHighwater++; } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _asyncReceives {1}, _receiveHighwater {2}, _sendHighwater {3}, _receiveHighwaterLastAck {4}, queueCount {5}", args0: ConnectionId, args1: _asyncReceives, args2: _receiveHighwater, args3: _sendHighwater, args4: _receiveHighwaterLastAck, args5: _receivedPacketQueue?.Count); SendAckIfNecessary(); return TdsEnums.SNI_SUCCESS; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -341,25 +335,21 @@ public override uint ReceiveAsync(ref SNIPacket packet) /// public void HandleReceiveError(SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { // SNIMarsHandle should only receive calls to this function from the SNIMarsConnection aggregator class - // which should handle ownership of the packet because the individual mars handles are not aware of + // which should handle ownership of the packet because the individual mars handles are not aware of // each other and cannot know if they are the last one in the list and that it is safe to return the packet lock (_receivedPacketQueue) { _connectionError = SNILoadHandle.SingletonInstance.LastError; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.ERR, "MARS Session Id {0}, _connectionError to be handled: {1}", args0: ConnectionId, args1: _connectionError); _packetEvent.Set(); } ((TdsParserStateObject)_callbackObject).ReadAsyncCallback(PacketHandle.FromManagedPacket(packet), 1); } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -369,8 +359,7 @@ public void HandleReceiveError(SNIPacket packet) /// SNI error code public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { lock (this) { @@ -379,35 +368,30 @@ public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) ((TdsParserStateObject)_callbackObject).WriteAsyncCallback(PacketHandle.FromManagedPacket(packet), sniErrorCode); } _connection.ReturnPacket(packet); - } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, Returned Packet: {1}", args0: ConnectionId, args1: packet?._id); +#endif } } /// - /// Handle SMUX acknowledgement + /// Handle SMUX acknowledgment /// /// Send highwater mark public void HandleAck(uint highwater) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { lock (this) { if (_sendHighwater != highwater) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, Setting _sendHighwater {1} to highwater {2} and send pending packets.", args0: ConnectionId, args1: _sendHighwater, args2: highwater); _sendHighwater = highwater; SendPendingPackets(); } } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -417,13 +401,13 @@ public void HandleAck(uint highwater) /// SMUX header public void HandleReceiveComplete(SNIPacket packet, SNISMUXHeader header) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { lock (this) { if (_sendHighwater != header.highwater) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, header.highwater {1}, _sendHighwater {2}, Handle Ack with header.highwater", args0: ConnectionId, args1: header?.highwater, args2: _sendHighwater); HandleAck(header.highwater); } @@ -433,11 +417,13 @@ public void HandleReceiveComplete(SNIPacket packet, SNISMUXHeader header) { _receivedPacketQueue.Enqueue(packet); _packetEvent.Set(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, _receivedPacketQueue count {3}, packet event set", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater, args3: _receivedPacketQueue?.Count); return; } _asyncReceives--; Debug.Assert(_callbackObject != null); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, _asyncReceives {3}", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater, args3: _asyncReceives); ((TdsParserStateObject)_callbackObject).ReadAsyncCallback(PacketHandle.FromManagedPacket(packet), 0); } @@ -449,13 +435,9 @@ public void HandleReceiveComplete(SNIPacket packet, SNISMUXHeader header) { _receiveHighwater++; } - + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _asyncReceives {1}, _receiveHighwater {2}, _sendHighwater {3}, _receiveHighwaterLastAck {4}", args0: ConnectionId, args1: _asyncReceives, args2: _receiveHighwater, args3: _sendHighwater, args4: _receiveHighwaterLastAck); SendAckIfNecessary(); } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -475,6 +457,7 @@ private void SendAckIfNecessary() if (receiveHighwater - receiveHighwaterLastAck > ACK_THRESHOLD) { SendControlPacket(SNISMUXFlags.SMUX_ACK); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _asyncReceives {1}, _receiveHighwater {2}, _sendHighwater {3}, _receiveHighwaterLastAck {4} Sending acknowledgment ACK_THRESHOLD {5}", args0: ConnectionId, args1: _asyncReceives, args2: _receiveHighwater, args3: _sendHighwater, args4: _receiveHighwaterLastAck, args5: ACK_THRESHOLD); } } @@ -486,8 +469,7 @@ private void SendAckIfNecessary() /// SNI error code public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { packet = null; int queueCount; @@ -499,10 +481,12 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) { if (_connectionError != null) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.ERR, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, _connectionError found: {3}.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater, args3: _connectionError); return SNICommon.ReportSNIError(_connectionError); } queueCount = _receivedPacketQueue.Count; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, W_receivedPacketQueue count {3}.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater, args3: queueCount); if (queueCount > 0) { @@ -511,6 +495,7 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) if (queueCount == 1) { _packetEvent.Reset(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, packet event reset, _receivedPacketQueue count 1.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); } result = TdsEnums.SNI_SUCCESS; @@ -525,20 +510,19 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) } SendAckIfNecessary(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, returning with result {3}.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater, args3: result); return result; } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, Waiting for packet event.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); if (!_packetEvent.Wait(timeoutInMilliseconds)) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnTimeoutError, string.Empty); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.SMUX_PROV, 0, SNICommon.ConnTimeoutError, Strings.SNI_ERROR_11); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, _packetEvent wait timed out.", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); return TdsEnums.SNI_WAIT_TIMEOUT; } } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsQueuedPacket.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsQueuedPacket.cs deleted file mode 100644 index 0f97eb4978..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsQueuedPacket.cs +++ /dev/null @@ -1,30 +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.SqlClient.SNI -{ - /// - /// Mars queued packet - /// - internal sealed class SNIMarsQueuedPacket - { - private readonly SNIPacket _packet; - private readonly SNIAsyncCallback _callback; - - /// - /// Constructor - /// - /// SNI packet - /// Completion callback - public SNIMarsQueuedPacket(SNIPacket packet, SNIAsyncCallback callback) - { - _packet = packet; - _callback = callback; - } - - public SNIPacket Packet => _packet; - - public SNIAsyncCallback Callback => _callback; - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs index 0132d7df58..b48ea36958 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs @@ -19,12 +19,11 @@ namespace Microsoft.Data.SqlClient.SNI internal sealed class SNINpHandle : SNIPhysicalHandle { internal const string DefaultPipePath = @"sql\query"; // e.g. \\HOSTNAME\pipe\sql\query - private const int MAX_PIPE_INSTANCES = 255; + // private const int MAX_PIPE_INSTANCES = 255; // TODO: Investigate pipe instance limit. private readonly string _targetServer; - private readonly object _callbackObject; private readonly object _sendSync; - + private readonly bool _tlsFirst; private Stream _stream; private NamedPipeClientStream _pipeStream; private SslOverTdsStream _sslOverTdsStream; @@ -38,16 +37,15 @@ internal sealed class SNINpHandle : SNIPhysicalHandle private int _bufferSize = TdsEnums.DEFAULT_LOGIN_PACKET_SIZE; private readonly Guid _connectionId = Guid.NewGuid(); - public SNINpHandle(string serverName, string pipeName, long timerExpire, object callbackObject) + public SNINpHandle(string serverName, string pipeName, long timerExpire, bool tlsFirst) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(" Constructor"); - SqlClientEventSource.Log.TrySNITraceEvent(" Constructor. server name = {0}, pipe name = {1}", serverName, pipeName); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Setting server name = {1}, pipe name = {2}", args0: _connectionId, args1: serverName, args2: pipeName); + _sendSync = new object(); _targetServer = serverName; - _callbackObject = callbackObject; - + _tlsFirst = tlsFirst; try { _pipeStream = new NamedPipeClientStream( @@ -59,7 +57,7 @@ public SNINpHandle(string serverName, string pipeName, long timerExpire, object bool isInfiniteTimeOut = long.MaxValue == timerExpire; if (isInfiniteTimeOut) { - _pipeStream.Connect(System.Threading.Timeout.Infinite); + _pipeStream.Connect(Timeout.Infinite); } else { @@ -73,52 +71,42 @@ public SNINpHandle(string serverName, string pipeName, long timerExpire, object { SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.ConnOpenFailedError, te); _status = TdsEnums.SNI_ERROR; - SqlClientEventSource.Log.TrySNITraceEvent(" Timed out. Exception = {0}", te.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, Connection Timed out. Error Code 1 Exception = {1}", args0: _connectionId, args1: te?.Message); return; } catch (IOException ioe) { SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.ConnOpenFailedError, ioe); _status = TdsEnums.SNI_ERROR; - SqlClientEventSource.Log.TrySNITraceEvent(" IOException = {0}", ioe.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, IO Exception occurred. Error Code 1 Exception = {1}", args0: _connectionId, args1: ioe?.Message); return; } if (!_pipeStream.IsConnected || !_pipeStream.CanWrite || !_pipeStream.CanRead) { - SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.ConnOpenFailedError, string.Empty); + SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.ConnOpenFailedError, Strings.SNI_ERROR_40); _status = TdsEnums.SNI_ERROR; - SqlClientEventSource.Log.TrySNITraceEvent(" Pipe stream is not connected or cannot write or read to/from it."); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, Pipe Stream not operational. Error Code 1 Exception = {1}", args0: _connectionId, args1: Strings.SNI_ERROR_1); return; } - _sslOverTdsStream = new SslOverTdsStream(_pipeStream); - _sslStream = new SNISslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); + Stream stream = _pipeStream; + + if (!_tlsFirst) + { + _sslOverTdsStream = new SslOverTdsStream(_pipeStream, _connectionId); + stream = _sslOverTdsStream; + } + _sslStream = new SNISslStream(stream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); _stream = _pipeStream; _status = TdsEnums.SNI_SUCCESS; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } - public override Guid ConnectionId - { - get - { - return _connectionId; - } - } + public override Guid ConnectionId => _connectionId; - public override uint Status - { - get - { - return _status; - } - } + public override uint Status => _status; public override int ProtocolVersion { @@ -137,23 +125,19 @@ public override int ProtocolVersion public override uint CheckConnection() { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { if (!_stream.CanWrite || !_stream.CanRead) { - SqlClientEventSource.Log.TrySNITraceEvent(" cannot write or read to/from the stream"); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, Cannot write or read to/from the stream", args0: _connectionId); return TdsEnums.SNI_ERROR; } else { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Can read and write to/from stream.", args0: _connectionId); return TdsEnums.SNI_SUCCESS; } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } public override void Dispose() @@ -180,13 +164,13 @@ public override void Dispose() //Release any references held by _stream. _stream = null; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, All streams disposed and references cleared.", args0: _connectionId); } } public override uint Receive(out SNIPacket packet, int timeout) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { SNIPacket errorPacket; lock (this) @@ -196,13 +180,14 @@ public override uint Receive(out SNIPacket packet, int timeout) { packet = RentPacket(headerSize: 0, dataSize: _bufferSize); packet.ReadFromStream(_stream); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Rented and read packet, dataLeft {1}", args0: _connectionId, args1: packet?.DataLeft); if (packet.Length == 0) { errorPacket = packet; packet = null; var e = new Win32Exception(); - SqlClientEventSource.Log.TrySNITraceEvent(" packet length is 0."); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, Packet length found 0, Win32 exception raised: {1}", args0: _connectionId, args1: e?.Message); return ReportErrorAndReleasePacket(errorPacket, (uint)e.NativeErrorCode, 0, e.Message); } } @@ -210,69 +195,60 @@ public override uint Receive(out SNIPacket packet, int timeout) { errorPacket = packet; packet = null; - SqlClientEventSource.Log.TrySNITraceEvent(" ObjectDisposedException message = {0}.", ode.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, ObjectDisposedException occurred: {1}.", args0: _connectionId, args1: ode?.Message); return ReportErrorAndReleasePacket(errorPacket, ode); } catch (IOException ioe) { errorPacket = packet; packet = null; - SqlClientEventSource.Log.TrySNITraceEvent(" IOException message = {0}.", ioe.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, IOException occurred: {1}.", args0: _connectionId, args1: ioe?.Message); return ReportErrorAndReleasePacket(errorPacket, ioe); } return TdsEnums.SNI_SUCCESS; } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } public override uint ReceiveAsync(ref SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { SNIPacket errorPacket; packet = RentPacket(headerSize: 0, dataSize: _bufferSize); - + packet.SetAsyncIOCompletionCallback(_receiveCallback); try { - packet.ReadFromStreamAsync(_stream, _receiveCallback); + packet.ReadFromStreamAsync(_stream); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Rented and read packet asynchronously, dataLeft {1}", args0: _connectionId, args1: packet?.DataLeft); return TdsEnums.SNI_SUCCESS_IO_PENDING; } catch (ObjectDisposedException ode) { errorPacket = packet; packet = null; - SqlClientEventSource.Log.TrySNITraceEvent(" ObjectDisposedException message = {0}.", ode.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, ObjectDisposedException occurred: {1}.", args0: _connectionId, args1: ode?.Message); return ReportErrorAndReleasePacket(errorPacket, ode); } catch (IOException ioe) { errorPacket = packet; packet = null; - SqlClientEventSource.Log.TrySNITraceEvent(" IOException message = {0}.", ioe.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, IOException occurred: {1}.", args0: _connectionId, args1: ioe?.Message); return ReportErrorAndReleasePacket(errorPacket, ioe); } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } public override uint Send(SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { bool releaseLock = false; try { // is the packet is marked out out-of-band (attention packets only) it must be - // sent immediately even if a send of recieve operation is already in progress + // sent immediately even if a send of receive operation is already in progress // because out of band packets are used to cancel ongoing operations // so try to take the lock if possible but continue even if it can't be taken if (packet.IsOutOfBand) @@ -292,18 +268,18 @@ public override uint Send(SNIPacket packet) { try { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Packet writing to stream, dataLeft {1}", args0: _connectionId, args1: packet?.DataLeft); packet.WriteToStream(_stream); return TdsEnums.SNI_SUCCESS; } catch (ObjectDisposedException ode) { - SqlClientEventSource.Log.TrySNITraceEvent(" ObjectDisposedException message = {0}.", ode.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, ObjectDisposedException occurred: {1}.", args0: _connectionId, args1: ode?.Message); return ReportErrorAndReleasePacket(packet, ode); } catch (IOException ioe) { - SqlClientEventSource.Log.TrySNITraceEvent(" IOException message = {0}.", ioe.Message); - + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, IOException occurred: {1}.", args0: _connectionId, args1: ioe?.Message); return ReportErrorAndReleasePacket(packet, ioe); } } @@ -316,25 +292,16 @@ public override uint Send(SNIPacket packet) } } } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } - public override uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null) + public override uint SendAsync(SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { - SNIAsyncCallback cb = callback ?? _sendCallback; - packet.WriteToStreamAsync(_stream, cb, SNIProviders.NP_PROV); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Packet writing to stream, dataLeft {1}", args0: _connectionId, args1: packet?.DataLeft); + packet.WriteToStreamAsync(_stream, _sendCallback, SNIProviders.NP_PROV); return TdsEnums.SNI_SUCCESS_IO_PENDING; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } public override void SetAsyncCallbacks(SNIAsyncCallback receiveCallback, SNIAsyncCallback sendCallback) @@ -345,39 +312,45 @@ public override void SetAsyncCallbacks(SNIAsyncCallback receiveCallback, SNIAsyn public override uint EnableSsl(uint options) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; try { - _sslStream.AuthenticateAsClient(_targetServer); - _sslOverTdsStream.FinishHandshake(); + if (_tlsFirst) + { + AuthenticateAsClient(_sslStream, _targetServer, null); + } + else + { + // TODO: Resolve whether to send _serverNameIndication or _targetServer. _serverNameIndication currently results in error. Why? + _sslStream.AuthenticateAsClient(_targetServer, null, s_supportedProtocols, false); + } + if (_sslOverTdsStream is not null) + { + _sslOverTdsStream.FinishHandshake(); + } } catch (AuthenticationException aue) { - SqlClientEventSource.Log.TrySNITraceEvent(" AuthenticationException message = {0}.", aue.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, AuthenticationException message = {1}.", args0: ConnectionId, args1: aue?.Message); return SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.InternalExceptionError, aue); } catch (InvalidOperationException ioe) { - SqlClientEventSource.Log.TrySNITraceEvent("InvalidOperationException message = {0}.", ioe.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.ERR, "Connection Id {0}, InvalidOperationException message = {1}.", args0: ConnectionId, args1: ioe?.Message); return SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.InternalExceptionError, ioe); } _stream = _sslStream; return TdsEnums.SNI_SUCCESS; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } public override void DisableSsl() { _sslStream.Dispose(); _sslStream = null; - _sslOverTdsStream.Dispose(); + _sslOverTdsStream?.Dispose(); _sslOverTdsStream = null; _stream = _pipeStream; @@ -393,19 +366,16 @@ public override void DisableSsl() /// true if valid private bool ValidateServerCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNINpHandle))) { if (!_validateCert) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Certificate validation not requested.", args0: ConnectionId); return true; } - return SNICommon.ValidateSslServerCertificate(_targetServer, sender, cert, chain, policyErrors); - } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Proceeding to SSL certificate validation.", args0: ConnectionId); + return SNICommon.ValidateSslServerCertificate(_targetServer, cert, policyErrors); } } @@ -424,6 +394,7 @@ private uint ReportErrorAndReleasePacket(SNIPacket packet, Exception sniExceptio { ReturnPacket(packet); } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Packet returned, error occurred: {1}", args0: ConnectionId, args1: sniException?.Message); return SNICommon.ReportSNIError(SNIProviders.NP_PROV, SNICommon.InternalExceptionError, sniException); } @@ -433,6 +404,7 @@ private uint ReportErrorAndReleasePacket(SNIPacket packet, uint nativeError, uin { ReturnPacket(packet); } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Packet returned, error occurred: {1}", args0: ConnectionId, args1: errorMessage); return SNICommon.ReportSNIError(SNIProviders.NP_PROV, nativeError, sniError, errorMessage); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs index 0ab375b129..d83682021b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs @@ -2,11 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// #define TRACE_HISTORY // this is used for advanced debugging when you need to trace the entire lifetime of a single packet, be very careful with it + // #define TRACE_HISTORY // this is used for advanced debugging when you need to trace the entire lifetime of a single packet, be very careful with it using System; using System.Buffers; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; @@ -19,14 +18,14 @@ namespace Microsoft.Data.SqlClient.SNI /// internal sealed class SNIPacket { + private static readonly Action, object> s_readCallback = ReadFromStreamAsyncContinuation; private int _dataLength; // the length of the data in the data segment, advanced by Append-ing data, does not include smux header length private int _dataCapacity; // the total capacity requested, if the array is rented this may be less than the _data.Length, does not include smux header length private int _dataOffset; // the start point of the data in the data segment, advanced by Take-ing data private int _headerLength; // the amount of space at the start of the array reserved for the smux header, this is zeroed in SetHeader // _headerOffset is not needed because it is always 0 private byte[] _data; - private SNIAsyncCallback _completionCallback; - private readonly Action, object> _readCallback; + private SNIAsyncCallback _asyncIOCompletionCallback; #if DEBUG internal readonly int _id; // in debug mode every packet is assigned a unique id so that the entire lifetime can be tracked when debugging /// refcount = 0 means that a packet should only exist in the pool @@ -36,21 +35,23 @@ internal sealed class SNIPacket internal readonly SNIHandle _owner; // used in debug builds to check that packets are being returned to the correct pool internal string _traceTag; // used in debug builds to assist tracing what steps the packet has been through +#if TRACE_HISTORY [DebuggerDisplay("{Action.ToString(),nq}")] internal struct History { public enum Direction { - Rent=0, - Return=1, + Rent = 0, + Return = 1, } public Direction Action; public int RefCount; public string Stack; } - + internal List _history = null; +#endif /// /// uses the packet refcount in debug mode to identify if the packet is considered active @@ -58,14 +59,15 @@ public enum Direction /// public bool IsActive => _refCount == 1; - public SNIPacket(SNIHandle owner,int id) + public SNIPacket(SNIHandle owner, int id) : this() { #if TRACE_HISTORY - _history = new List(); + _history = new List(); #endif _id = id; _owner = owner; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} instantiated,", args0: _owner?.ConnectionId, args1: _id); } // the finalizer is only included in debug builds and is used to ensure that all packets are correctly recycled @@ -75,14 +77,13 @@ public SNIPacket(SNIHandle owner,int id) { if (_data != null) { - Debug.Fail($@"finalizer called for unreleased SNIPacket, tag: {_traceTag}"); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Finalizer called for unreleased SNIPacket, Connection Id {0}, Packet Id {1}, _refCount {2}, DataLeft {3}, tag {4}", args0: _owner?.ConnectionId, args1: _id, args2: _refCount, args3: DataLeft, args4: _traceTag); } } #endif public SNIPacket() { - _readCallback = ReadFromStreamAsyncContinuation; } /// @@ -107,25 +108,19 @@ public SNIPacket() public int ReservedHeaderSize => _headerLength; - public bool HasCompletionCallback => !(_completionCallback is null); + public bool HasAsyncIOCompletionCallback => _asyncIOCompletionCallback is not null; /// - /// Set async completion callback + /// Set async receive callback /// - /// Completion callback - public void SetCompletionCallback(SNIAsyncCallback completionCallback) - { - _completionCallback = completionCallback; - } + /// Completion callback + public void SetAsyncIOCompletionCallback(SNIAsyncCallback asyncIOCompletionCallback) => _asyncIOCompletionCallback = asyncIOCompletionCallback; /// - /// Invoke the completion callback + /// Invoke the receive callback /// /// SNI error - public void InvokeCompletionCallback(uint sniErrorCode) - { - _completionCallback(this, sniErrorCode); - } + public void InvokeAsyncIOCompletionCallback(uint sniErrorCode) => _asyncIOCompletionCallback(this, sniErrorCode); /// /// Allocate space for data @@ -139,13 +134,16 @@ public void Allocate(int headerLength, int dataLength) _dataLength = 0; _dataOffset = 0; _headerLength = headerLength; +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} allocated with _headerLength {2}, _dataCapacity {3}", args0: _owner?.ConnectionId, args1: _id, args2: _headerLength, args3: _dataCapacity); +#endif } /// /// Read packet data into a buffer without removing it from the packet /// /// Buffer - /// Number of bytes read from the packet into the buffer + /// Number of bytes read from the packet into the buffer public void GetData(byte[] buffer, ref int dataSize) { Buffer.BlockCopy(_data, _headerLength, buffer, 0, _dataLength); @@ -162,6 +160,9 @@ public int TakeData(SNIPacket packet, int size) { int dataSize = TakeData(packet._data, packet._headerLength + packet._dataLength, size); packet._dataLength += dataSize; +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} took data from Packet Id {2} dataSize {3}, _dataLength {4}", args0: _owner?.ConnectionId, args1: _id, args2: packet?._id, args3: dataSize, args4: packet._dataLength); +#endif return dataSize; } @@ -174,6 +175,9 @@ public void AppendData(byte[] data, int size) { Buffer.BlockCopy(data, 0, _data, _headerLength + _dataLength, size); _dataLength += size; +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} was appended with size {2}, _dataLength {3}", args0: _owner?.ConnectionId, args1: _id, args2: size, args3: _dataLength); +#endif } /// @@ -181,7 +185,7 @@ public void AppendData(byte[] data, int size) /// /// Buffer /// Data offset to write data at - /// Number of bytes to read from the packet into the buffer + /// Number of bytes to read from the packet into the buffer /// public int TakeData(byte[] buffer, int dataOffset, int size) { @@ -197,6 +201,9 @@ public int TakeData(byte[] buffer, int dataOffset, int size) Buffer.BlockCopy(_data, _headerLength + _dataOffset, buffer, dataOffset, size); _dataOffset += size; +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} took data size {2}, _dataLength {3}, _dataOffset {4}", args0: _owner?.ConnectionId, args1: _id, args2: size, args3: _dataLength, args4: _dataOffset); +#endif return size; } @@ -214,6 +221,9 @@ public void SetHeaderActive() _dataCapacity += _headerLength; _dataLength += _headerLength; _headerLength = 0; +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _dataLength {2} header set to active.", args0: _owner?.ConnectionId, args1: _id, args2: _dataLength); +#endif } /// @@ -229,10 +239,13 @@ public void Release() _data = null; _dataCapacity = 0; } +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _headerLength {2} and _dataLength {3} released.", args0: _owner?.ConnectionId, args1: _id, args2: _headerLength, args3: _dataLength); +#endif _dataLength = 0; _dataOffset = 0; _headerLength = 0; - _completionCallback = null; + _asyncIOCompletionCallback = null; IsOutOfBand = false; } @@ -243,47 +256,57 @@ public void Release() public void ReadFromStream(Stream stream) { _dataLength = stream.Read(_data, _headerLength, _dataCapacity); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _dataLength {2} read from stream.", args0: _owner?.ConnectionId, args1: _id, args2: _dataLength); +#endif } /// /// Read data from a stream asynchronously /// /// Stream to read from - /// Completion callback - public void ReadFromStreamAsync(Stream stream, SNIAsyncCallback callback) + public void ReadFromStreamAsync(Stream stream) { stream.ReadAsync(_data, 0, _dataCapacity, CancellationToken.None) .ContinueWith( - continuationAction: _readCallback, - state: callback, + continuationAction: s_readCallback, + state: this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default ); } - private void ReadFromStreamAsyncContinuation(Task t, object state) + private static void ReadFromStreamAsyncContinuation(Task task, object state) { - SNIAsyncCallback callback = (SNIAsyncCallback)state; + SNIPacket packet = (SNIPacket)state; bool error = false; - Exception e = t.Exception?.InnerException; + Exception e = task.Exception?.InnerException; if (e != null) { SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, SNICommon.InternalExceptionError, e); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Connection Id {0}, Internal Exception occurred while reading data: {1}", args0: packet._owner?.ConnectionId, args1: e?.Message); +#endif error = true; } else { - _dataLength = t.Result; - - if (_dataLength == 0) + packet._dataLength = task.Result; +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _dataLength {2} read from stream.", args0: packet._owner?.ConnectionId, args1: packet._id, args2: packet._dataLength); +#endif + if (packet._dataLength == 0) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, 0, SNICommon.ConnTerminatedError, string.Empty); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, 0, SNICommon.ConnTerminatedError, Strings.SNI_ERROR_2); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Connection Id {0}, No data read from stream, connection was terminated.", args0: packet._owner?.ConnectionId); +#endif error = true; } } - callback(this, error ? TdsEnums.SNI_ERROR : TdsEnums.SNI_SUCCESS); + packet.InvokeAsyncIOCompletionCallback(error ? TdsEnums.SNI_ERROR : TdsEnums.SNI_SUCCESS); } /// @@ -293,6 +316,9 @@ private void ReadFromStreamAsyncContinuation(Task t, object state) public void WriteToStream(Stream stream) { stream.Write(_data, _headerLength, _dataLength); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _dataLength {2} written to stream.", args0: _owner?.ConnectionId, args1: _id, args2: _dataLength); +#endif } /// @@ -307,10 +333,16 @@ public async void WriteToStreamAsync(Stream stream, SNIAsyncCallback callback, S try { await stream.WriteAsync(_data, 0, _dataLength, CancellationToken.None).ConfigureAwait(false); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _dataLength {2} written to stream.", args0: _owner?.ConnectionId, args1: _id, args2: _dataLength); +#endif } catch (Exception e) { SNILoadHandle.SingletonInstance.LastError = new SNIError(provider, SNICommon.InternalExceptionError, e); +#if DEBUG + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Connection Id {0}, Internal Exception occurred while writing data: {1}", args0: _owner?.ConnectionId, args1: e?.Message); +#endif status = TdsEnums.SNI_ERROR; } callback(this, status); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs index 65a4432f80..94f37d0c6a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs @@ -4,24 +4,23 @@ using System; using System.Diagnostics; -using System.Collections.Concurrent; using System.Threading; using System.Linq; -using System.Text; namespace Microsoft.Data.SqlClient.SNI { internal abstract class SNIPhysicalHandle : SNIHandle { protected const int DefaultPoolSize = 4; + #if DEBUG private static int s_packetId; #endif - private SNIPacketPool _pool; + private SqlObjectPool _pool; protected SNIPhysicalHandle(int poolSize = DefaultPoolSize) { - _pool = new SNIPacketPool(poolSize); + _pool = new SqlObjectPool(poolSize); } public override SNIPacket RentPacket(int headerSize, int dataSize) @@ -40,16 +39,18 @@ public override SNIPacket RentPacket(int headerSize, int dataSize) else { Debug.Assert(packet != null, "dequeue returned null SNIPacket"); - Debug.Assert(!packet.IsActive, "SNIPacket _refcount must be 1 or a lifetime issue has occured, trace with the #TRACE_HISTORY define"); + Debug.Assert(!packet.IsActive, "SNIPacket _refcount must be 1 or a lifetime issue has occurred, trace with the #TRACE_HISTORY define"); Debug.Assert(packet.IsInvalid, "dequeue returned valid packet"); GC.ReRegisterForFinalize(packet); } +#if TRACE_HISTORY if (packet._history != null) { packet._history.Add(new SNIPacket.History { Action = SNIPacket.History.Direction.Rent, Stack = GetStackParts(), RefCount = packet._refCount }); } +#endif Interlocked.Add(ref packet._refCount, 1); - Debug.Assert(packet.IsActive, "SNIPacket _refcount must be 1 or a lifetime issue has occured, trace with the #TRACE_HISTORY define"); + Debug.Assert(packet.IsActive, "SNIPacket _refcount must be 1 or a lifetime issue has occurred, trace with the #TRACE_HISTORY define"); #endif packet.Allocate(headerSize, dataSize); return packet; @@ -57,21 +58,23 @@ public override SNIPacket RentPacket(int headerSize, int dataSize) public override void ReturnPacket(SNIPacket packet) { - Debug.Assert(packet != null, "releasing null SNIPacket"); #if DEBUG - Debug.Assert(packet.IsActive, "SNIPacket _refcount must be 1 or a lifetime issue has occured, trace with the #TRACE_HISTORY define"); + Debug.Assert(packet != null, "releasing null SNIPacket"); + Debug.Assert(packet.IsActive, "SNIPacket _refcount must be 1 or a lifetime issue has occurred, trace with the #TRACE_HISTORY define"); Debug.Assert(ReferenceEquals(packet._owner, this), "releasing SNIPacket that belongs to another physical handle"); -#endif Debug.Assert(!packet.IsInvalid, "releasing already released SNIPacket"); +#endif packet.Release(); #if DEBUG Interlocked.Add(ref packet._refCount, -1); packet._traceTag = null; +#if TRACE_HISTORY if (packet._history != null) { packet._history.Add(new SNIPacket.History { Action = SNIPacket.History.Direction.Return, Stack = GetStackParts(), RefCount = packet._refCount }); } +#endif GC.SuppressFinalize(packet); #endif _pool.Return(packet); @@ -82,7 +85,7 @@ private string GetStackParts() { return string.Join(Environment.NewLine, Environment.StackTrace - .Split(new string[] { Environment.NewLine },StringSplitOptions.None) + .Split(new string[] { Environment.NewLine }, StringSplitOptions.None) .Skip(3) // trims off the common parts at the top of the stack so you can see what the actual caller was .Take(7) // trims off most of the bottom of the stack because when running under xunit there's a lot of spam ); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs index 8038220844..ac4d3599dd 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs @@ -21,48 +21,9 @@ internal class SNIProxy private const int DefaultSqlServerDacPort = 1434; private const string SqlServerSpnHeader = "MSSQLSvc"; - internal class SspiClientContextResult - { - internal const uint OK = 0; - internal const uint Failed = 1; - internal const uint KerberosTicketMissing = 2; - } - - internal static readonly SNIProxy s_singleton = new SNIProxy(); - - internal static SNIProxy GetInstance() - { - return s_singleton; - } - - /// - /// Enable SSL on a connection - /// - /// Connection handle - /// - /// SNI error code - internal uint EnableSsl(SNIHandle handle, uint options) - { - try - { - return handle.EnableSsl(options); - } - catch (Exception e) - { - return SNICommon.ReportSNIError(SNIProviders.SSL_PROV, SNICommon.HandshakeFailureError, e); - } - } + private static readonly SNIProxy s_singleton = new SNIProxy(); - /// - /// Disable SSL on a connection - /// - /// Connection handle - /// SNI error code - internal uint DisableSsl(SNIHandle handle) - { - handle.DisableSsl(); - return TdsEnums.SNI_SUCCESS; - } + internal static SNIProxy Instance => s_singleton; /// /// Generate SSPI context @@ -72,7 +33,7 @@ internal uint DisableSsl(SNIHandle handle) /// Send buffer /// Service Principal Name buffer /// SNI error code - internal void GenSspiClientContext(SspiClientContextStatus sspiClientContextStatus, byte[] receivedBuff, ref byte[] sendBuff, byte[] serverName) + internal static void GenSspiClientContext(SspiClientContextStatus sspiClientContextStatus, byte[] receivedBuff, ref byte[] sendBuff, byte[][] serverName) { SafeDeleteContext securityContext = sspiClientContextStatus.SecurityContext; ContextFlagsPal contextFlags = sspiClientContextStatus.ContextFlags; @@ -104,12 +65,15 @@ internal void GenSspiClientContext(SspiClientContextStatus sspiClientContextStat | ContextFlagsPal.Delegate | ContextFlagsPal.MutualAuth; - string serverSPN = System.Text.Encoding.UTF8.GetString(serverName); - + string[] serverSPNs = new string[serverName.Length]; + for (int i = 0; i < serverName.Length; i++) + { + serverSPNs[i] = Encoding.Unicode.GetString(serverName[i]); + } SecurityStatusPal statusCode = NegotiateStreamPal.InitializeSecurityContext( credentialsHandle, ref securityContext, - serverSPN, + serverSPNs, requestedContextFlags, inSecurityBufferArray, outSecurityBuffer, @@ -162,99 +126,43 @@ private static bool IsErrorStatus(SecurityStatusPalErrorCode errorCode) errorCode != SecurityStatusPalErrorCode.Renegotiate; } - /// - /// Set connection buffer size - /// - /// SNI handle - /// Buffer size - /// SNI error code - internal uint SetConnectionBufferSize(SNIHandle handle, uint bufferSize) - { - handle.SetBufferSize((int)bufferSize); - return TdsEnums.SNI_SUCCESS; - } - - /// - /// Copies data in SNIPacket to given byte array parameter - /// - /// SNIPacket object containing data packets - /// Destination byte array where data packets are copied to - /// Length of data packets - /// SNI error status - internal uint PacketGetData(SNIPacket packet, byte[] inBuff, ref uint dataSize) - { - int dataSizeInt = 0; - packet.GetData(inBuff, ref dataSizeInt); - dataSize = (uint)dataSizeInt; - - return TdsEnums.SNI_SUCCESS; - } - - /// - /// Read synchronously - /// - /// SNI handle - /// SNI packet - /// Timeout - /// SNI error status - internal uint ReadSyncOverAsync(SNIHandle handle, out SNIPacket packet, int timeout) - { - return handle.Receive(out packet, timeout); - } - - /// - /// Get SNI connection ID - /// - /// SNI handle - /// Client connection ID - /// SNI error status - internal uint GetConnectionId(SNIHandle handle, ref Guid clientConnectionId) - { - clientConnectionId = handle.ConnectionId; - - return TdsEnums.SNI_SUCCESS; - } - - /// - /// Send a packet - /// - /// SNI handle - /// SNI packet - /// true if synchronous, false if asynchronous - /// SNI error status - internal uint WritePacket(SNIHandle handle, SNIPacket packet, bool sync) - { - uint result; - if (sync) - { - result = handle.Send(packet); - handle.ReturnPacket(packet); - } - else - { - result = handle.SendAsync(packet); - } - - return result; - } - /// /// Create a SNI connection handle /// - /// Asynchronous I/O callback object /// Full server name from connection string /// Ignore open timeout /// Timer expiration /// Instance name /// SPN + /// pre-defined SPN /// Flush packet cache /// Asynchronous connection /// Attempt parallel connects /// + /// IP address preference /// Used for DNS Cache /// Used for DNS Cache + /// Support TDS8.0 + /// Used for the HostName in certificate + /// Used for the path to the Server Certificate /// SNI handle - internal SNIHandle CreateConnectionHandle(object callbackObject, string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[] spnBuffer, bool flushCache, bool async, bool parallel, bool isIntegratedSecurity, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + internal static SNIHandle CreateConnectionHandle( + string fullServerName, + bool ignoreSniOpenTimeout, + long timerExpire, + out byte[] instanceName, + ref byte[][] spnBuffer, + string serverSPN, + bool flushCache, + bool async, + bool parallel, + bool isIntegratedSecurity, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + bool tlsFirst, + string hostNameInCertificate, + string serverCertificateFilename) { instanceName = new byte[1]; @@ -265,7 +173,6 @@ internal SNIHandle CreateConnectionHandle(object callbackObject, string fullServ { return null; } - // If a localDB Data source is available, we need to use it. fullServerName = localDBDataSource ?? fullServerName; @@ -281,10 +188,11 @@ internal SNIHandle CreateConnectionHandle(object callbackObject, string fullServ case DataSource.Protocol.Admin: case DataSource.Protocol.None: // default to using tcp if no protocol is provided case DataSource.Protocol.TCP: - sniHandle = CreateTcpHandle(details, timerExpire, callbackObject, parallel, cachedFQDN, ref pendingDNSInfo); + sniHandle = CreateTcpHandle(details, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo, + tlsFirst, hostNameInCertificate, serverCertificateFilename); break; case DataSource.Protocol.NP: - sniHandle = CreateNpHandle(details, timerExpire, callbackObject, parallel); + sniHandle = CreateNpHandle(details, timerExpire, parallel, tlsFirst); break; default: Debug.Fail($"Unexpected connection protocol: {details._connectionProtocol}"); @@ -295,7 +203,7 @@ internal SNIHandle CreateConnectionHandle(object callbackObject, string fullServ { try { - spnBuffer = GetSqlServerSPN(details); + spnBuffer = GetSqlServerSPNs(details, serverSPN); } catch (Exception e) { @@ -303,12 +211,17 @@ internal SNIHandle CreateConnectionHandle(object callbackObject, string fullServ } } + SqlClientEventSource.Log.TryTraceEvent("SNIProxy.CreateConnectionHandle | Info | Session Id {0}, SNI Handle Type: {1}", sniHandle?.ConnectionId, sniHandle?.GetType()); return sniHandle; } - private static byte[] GetSqlServerSPN(DataSource dataSource) + private static byte[][] GetSqlServerSPNs(DataSource dataSource, string serverSPN) { Debug.Assert(!string.IsNullOrWhiteSpace(dataSource.ServerName)); + if (!string.IsNullOrWhiteSpace(serverSPN)) + { + return new byte[1][] { Encoding.Unicode.GetBytes(serverSPN) }; + } string hostName = dataSource.ServerName; string postfix = null; @@ -320,16 +233,12 @@ private static byte[] GetSqlServerSPN(DataSource dataSource) { postfix = dataSource.InstanceName; } - // For handling tcp: format - else if (dataSource._connectionProtocol == DataSource.Protocol.TCP) - { - postfix = DefaultSqlServerPort.ToString(); - } - return GetSqlServerSPN(hostName, postfix); + SqlClientEventSource.Log.TryTraceEvent("SNIProxy.GetSqlServerSPN | Info | ServerName {0}, InstanceName {1}, Port {2}, postfix {3}", dataSource?.ServerName, dataSource?.InstanceName, dataSource?.Port, postfix); + return GetSqlServerSPNs(hostName, postfix, dataSource._connectionProtocol); } - private static byte[] GetSqlServerSPN(string hostNameOrAddress, string portOrInstanceName) + private static byte[][] GetSqlServerSPNs(string hostNameOrAddress, string portOrInstanceName, DataSource.Protocol protocol) { Debug.Assert(!string.IsNullOrWhiteSpace(hostNameOrAddress)); IPHostEntry hostEntry = null; @@ -348,16 +257,24 @@ private static byte[] GetSqlServerSPN(string hostNameOrAddress, string portOrIns // If the DNS lookup failed, then resort to using the user provided hostname to construct the SPN. fullyQualifiedDomainName = hostEntry?.HostName ?? hostNameOrAddress; } + string serverSpn = SqlServerSpnHeader + "/" + fullyQualifiedDomainName; + if (!string.IsNullOrWhiteSpace(portOrInstanceName)) { serverSpn += ":" + portOrInstanceName; } - else + else if (protocol == DataSource.Protocol.None || protocol == DataSource.Protocol.TCP) // Default is TCP { - serverSpn += $":{DefaultSqlServerPort}"; + string serverSpnWithDefaultPort = serverSpn + $":{DefaultSqlServerPort}"; + // Set both SPNs with and without Port as Port is optional for default instance + SqlClientEventSource.Log.TryAdvancedTraceEvent("SNIProxy.GetSqlServerSPN | Info | ServerSPNs {0} and {1}", serverSpn, serverSpnWithDefaultPort); + return new byte[][] { Encoding.Unicode.GetBytes(serverSpn), Encoding.Unicode.GetBytes(serverSpnWithDefaultPort) }; } - return Encoding.UTF8.GetBytes(serverSpn); + // else Named Pipes do not need to valid port + + SqlClientEventSource.Log.TryAdvancedTraceEvent("SNIProxy.GetSqlServerSPN | Info | ServerSPN {0}", serverSpn); + return new byte[][] { Encoding.Unicode.GetBytes(serverSpn) }; } /// @@ -365,12 +282,24 @@ private static byte[] GetSqlServerSPN(string hostNameOrAddress, string portOrIns /// /// Data source /// Timer expiration - /// Asynchronous I/O callback object /// Should MultiSubnetFailover be used + /// IP address preference /// Key for DNS Cache /// Used for DNS Cache + /// Support TDS8.0 + /// Host name in certificate + /// Used for the path to the Server Certificate /// SNITCPHandle - private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, object callbackObject, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + private static SNITCPHandle CreateTcpHandle( + DataSource details, + long timerExpire, + bool parallel, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + bool tlsFirst, + string hostNameInCertificate, + string serverCertificateFilename) { // TCP Format: // tcp:\ @@ -379,7 +308,7 @@ private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, objec string hostName = details.ServerName; if (string.IsNullOrWhiteSpace(hostName)) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, 0, SNICommon.InvalidConnStringError, string.Empty); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, 0, SNICommon.InvalidConnStringError, Strings.SNI_ERROR_25); return null; } @@ -390,12 +319,12 @@ private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, objec try { port = isAdminConnection ? - SSRP.GetDacPortByInstanceName(hostName, details.InstanceName) : - SSRP.GetPortByInstanceName(hostName, details.InstanceName); + SSRP.GetDacPortByInstanceName(hostName, details.InstanceName, timerExpire, parallel, ipPreference) : + SSRP.GetPortByInstanceName(hostName, details.InstanceName, timerExpire, parallel, ipPreference); } catch (SocketException se) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, SNICommon.InvalidConnStringError, se); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, SNICommon.ErrorLocatingServerInstance, se); return null; } } @@ -408,60 +337,27 @@ private SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, objec port = isAdminConnection ? DefaultSqlServerDacPort : DefaultSqlServerPort; } - return new SNITCPHandle(hostName, port, timerExpire, callbackObject, parallel, cachedFQDN, ref pendingDNSInfo); + return new SNITCPHandle(hostName, port, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo, + tlsFirst, hostNameInCertificate, serverCertificateFilename); } - - /// /// Creates an SNINpHandle object /// /// Data source /// Timer expiration - /// Asynchronous I/O callback object /// Should MultiSubnetFailover be used. Only returns an error for named pipes. + /// /// SNINpHandle - private SNINpHandle CreateNpHandle(DataSource details, long timerExpire, object callbackObject, bool parallel) + private static SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool parallel, bool tlsFirst) { if (parallel) { - SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.MultiSubnetFailoverWithNonTcpProtocol, string.Empty); + // Connecting to a SQL Server instance using the MultiSubnetFailover connection option is only supported when using the TCP protocol + SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.MultiSubnetFailoverWithNonTcpProtocol, Strings.SNI_ERROR_49); return null; } - return new SNINpHandle(details.PipeHostName, details.PipeName, timerExpire, callbackObject); - } - - /// - /// Read packet asynchronously - /// - /// SNI handle - /// Packet - /// SNI error status - internal uint ReadAsync(SNIHandle handle, out SNIPacket packet) - { - packet = null; - return handle.ReceiveAsync(ref packet); - } - - /// - /// Set packet data - /// - /// SNI packet - /// Data - /// Length - internal void PacketSetData(SNIPacket packet, byte[] data, int length) - { - packet.AppendData(data, length); - } - - /// - /// Check SNI handle connection - /// - /// - /// SNI error status - internal uint CheckConnection(SNIHandle handle) - { - return handle.CheckConnection(); + return new SNINpHandle(details.PipeHostName, details.PipeName, timerExpire, tlsFirst); } /// @@ -479,11 +375,10 @@ internal SNIError GetLastError() /// The data source /// Set true when an error occurred while getting LocalDB up /// - private string GetLocalDBDataSource(string fullServerName, out bool error) + private static string GetLocalDBDataSource(string fullServerName, out bool error) { string localDBConnectionString = null; - bool isBadLocalDBDataSource; - string localDBInstance = DataSource.GetLocalDBInstance(fullServerName, out isBadLocalDBDataSource); + string localDBInstance = DataSource.GetLocalDBInstance(fullServerName, out bool isBadLocalDBDataSource); if (isBadLocalDBDataSource) { @@ -521,6 +416,7 @@ internal class DataSource private const string Slash = @"/"; private const string PipeToken = "pipe"; private const string LocalDbHost = "(localdb)"; + private const string LocalDbHost_NP = @"np:\\.\pipe\LOCALDB#"; private const string NamedPipeInstanceNameHeader = "mssql$"; private const string DefaultPipeName = "sql\\query"; @@ -556,6 +452,7 @@ internal enum Protocol { TCP, NP, None, Admin }; private string _workingDataSource; private string _dataSourceAfterTrimmingProtocol; + internal bool IsBadDataSource { get; private set; } = false; internal bool IsSsrpRequired { get; private set; } = false; @@ -613,30 +510,41 @@ private void PopulateProtocol() } } + // LocalDbInstance name always starts with (localdb) + // possible scenarios: + // (localdb)\ + // or (localdb)\. which goes to default localdb + // or (localdb)\.\ internal static string GetLocalDBInstance(string dataSource, out bool error) { string instanceName = null; - - string workingDataSource = dataSource.ToLowerInvariant(); - - string[] tokensByBackSlash = workingDataSource.Split(BackSlashCharacter); - + // ReadOnlySpan is not supported in netstandard 2.0, but installing System.Memory solves the issue + ReadOnlySpan input = dataSource.AsSpan().TrimStart(); error = false; - - // All LocalDb endpoints are of the format host\instancename where host is always (LocalDb) (case-insensitive) - if (tokensByBackSlash.Length == 2 && LocalDbHost.Equals(tokensByBackSlash[0].TrimStart())) + // NetStandard 2.0 does not support passing a string to ReadOnlySpan + if (input.StartsWith(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) { - if (!string.IsNullOrWhiteSpace(tokensByBackSlash[1])) + // When netcoreapp support for netcoreapp2.1 is dropped these slice calls could be converted to System.Range\System.Index + // Such ad input = input[1..]; + input = input.Slice(LocalDbHost.Length); + if (!input.IsEmpty && input[0] == BackSlashCharacter) { - instanceName = tokensByBackSlash[1].Trim(); + input = input.Slice(1); + } + if (!input.IsEmpty) + { + instanceName = input.Trim().ToString(); } else { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBNoInstanceName, string.Empty); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBNoInstanceName, Strings.SNI_ERROR_51); error = true; - return null; } } + else if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) + { + instanceName = input.Trim().ToString(); + } return instanceName; } @@ -671,10 +579,12 @@ internal static DataSource ParseServerName(string dataSource) private void InferLocalServerName() { // If Server name is empty or localhost, then use "localhost" - if (string.IsNullOrEmpty(ServerName) || IsLocalHost(ServerName)) + if (string.IsNullOrEmpty(ServerName) || IsLocalHost(ServerName) || + (Environment.MachineName.Equals(ServerName, StringComparison.CurrentCultureIgnoreCase) && + _connectionProtocol == Protocol.Admin)) { - ServerName = _connectionProtocol == Protocol.Admin ? - Environment.MachineName : DefaultHostName; + // For DAC use "localhost" instead of the server name. + ServerName = DefaultHostName; } } @@ -685,7 +595,7 @@ private bool InferConnectionDetails() int commaIndex = _dataSourceAfterTrimmingProtocol.IndexOf(CommaSeparator); - int backSlashIndex = _dataSourceAfterTrimmingProtocol.IndexOf(PipeBeginning); + int backSlashIndex = _dataSourceAfterTrimmingProtocol.IndexOf(BackSlashCharacter); // Check the parameters. The parameters are Comma separated in the Data Source. The parameter we really care about is the port // If Comma exists, the try to get the port number @@ -758,7 +668,7 @@ private bool InferConnectionDetails() private void ReportSNIError(SNIProviders provider) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(provider, 0, SNICommon.InvalidConnStringError, string.Empty); + SNILoadHandle.SingletonInstance.LastError = new SNIError(provider, 0, SNICommon.InvalidConnStringError, Strings.SNI_ERROR_25); IsBadDataSource = true; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.Task.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.Task.cs new file mode 100644 index 0000000000..a1bf4a9e0e --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.Task.cs @@ -0,0 +1,93 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Data.SqlClient.SNI +{ + // NetCore2.1: + // DO NOT OVERRIDE ValueTask versions of ReadAsync and WriteAsync because the underlying SslStream implements them + // by calling the Task versions which are already overridden meaning that if a caller uses Task WriteAsync this would + // call ValueTask WriteAsync which then called TaskWriteAsync introducing a lock cycle and never return + + internal sealed partial class SNISslStream + { + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await _readAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + } + catch (System.Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _readAsyncSemaphore.Release(); + } + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await _writeAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + } + catch (System.Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _writeAsyncSemaphore.Release(); + } + } + } + + internal sealed partial class SNINetworkStream + { + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await _readAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + } + catch (System.Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _readAsyncSemaphore.Release(); + } + } + + // Prevent the WriteAsync collisions by running the task in a Semaphore Slim + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await _writeAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + } + catch (System.Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _writeAsyncSemaphore.Release(); + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.ValueTask.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.ValueTask.cs new file mode 100644 index 0000000000..f5f38f0efe --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.ValueTask.cs @@ -0,0 +1,109 @@ +// 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.Threading; +using System.Threading.Tasks; +using System; + +namespace Microsoft.Data.SqlClient.SNI +{ + internal sealed partial class SNISslStream + { + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + await _readAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + return await base.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _readAsyncSemaphore.Release(); + } + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return WriteAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + await _writeAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + await base.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _writeAsyncSemaphore.Release(); + } + } + } + + internal sealed partial class SNINetworkStream + { + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + await _readAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + return await base.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _readAsyncSemaphore.Release(); + } + } + + // Prevent the WriteAsync collisions by running the task in a Semaphore Slim + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return WriteAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + await _writeAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + await base.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message); + throw; + } + finally + { + _writeAsyncSemaphore.Release(); + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.cs index eb8661d022..389f25eeae 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.cs @@ -4,8 +4,6 @@ using System.Net.Security; using System.IO; -using System.Threading; -using System.Threading.Tasks; using System.Net.Sockets; namespace Microsoft.Data.SqlClient.SNI @@ -13,7 +11,7 @@ namespace Microsoft.Data.SqlClient.SNI /// /// This class extends SslStream to customize stream behavior for Managed SNI implementation. /// - internal class SNISslStream : SslStream + internal sealed partial class SNISslStream : SslStream { private readonly ConcurrentQueueSemaphore _writeAsyncSemaphore; private readonly ConcurrentQueueSemaphore _readAsyncSemaphore; @@ -24,40 +22,12 @@ public SNISslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertifi _writeAsyncSemaphore = new ConcurrentQueueSemaphore(1); _readAsyncSemaphore = new ConcurrentQueueSemaphore(1); } - - // Prevent ReadAsync collisions by running the task in a Semaphore Slim - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - await _readAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - } - finally - { - _readAsyncSemaphore.Release(); - } - } - - // Prevent the WriteAsync collisions by running the task in a Semaphore Slim - public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - await _writeAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - } - finally - { - _writeAsyncSemaphore.Release(); - } - } } /// /// This class extends NetworkStream to customize stream behavior for Managed SNI implementation. /// - internal class SNINetworkStream : NetworkStream + internal sealed partial class SNINetworkStream : NetworkStream { private readonly ConcurrentQueueSemaphore _writeAsyncSemaphore; private readonly ConcurrentQueueSemaphore _readAsyncSemaphore; @@ -67,33 +37,5 @@ public SNINetworkStream(Socket socket, bool ownsSocket) : base(socket, ownsSocke _writeAsyncSemaphore = new ConcurrentQueueSemaphore(1); _readAsyncSemaphore = new ConcurrentQueueSemaphore(1); } - - // Prevent ReadAsync collisions by running the task in a Semaphore Slim - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - await _readAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - } - finally - { - _readAsyncSemaphore.Release(); - } - } - - // Prevent the WriteAsync collisions by running the task in a Semaphore Slim - public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - await _writeAsyncSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - } - finally - { - _writeAsyncSemaphore.Release(); - } - } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs index ef85841d24..103af2ed2d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs @@ -23,10 +23,12 @@ namespace Microsoft.Data.SqlClient.SNI internal sealed class SNITCPHandle : SNIPhysicalHandle { private readonly string _targetServer; - private readonly object _callbackObject; private readonly object _sendSync; private readonly Socket _socket; private NetworkStream _tcpStream; + private readonly string _hostNameInCertificate; + private readonly string _serverCertificateFilename; + private readonly bool _tlsFirst; private Stream _stream; private SslStream _sslStream; @@ -68,6 +70,7 @@ public override void Dispose() //Release any references held by _stream. _stream = null; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, All streams disposed.", args0: _connectionId); } } @@ -114,133 +117,182 @@ public override int ProtocolVersion /// Server name /// TCP port number /// Connection timer expiration - /// Callback object /// Parallel executions + /// IP address preference /// Key for DNS Cache /// Used for DNS Cache - public SNITCPHandle(string serverName, int port, long timerExpire, object callbackObject, bool parallel, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + /// Support TDS8.0 + /// Host Name in Certificate + /// Used for the path to the Server Certificate + public SNITCPHandle( + string serverName, + int port, + long timerExpire, + bool parallel, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + bool tlsFirst, + string hostNameInCertificate, + string serverCertificateFilename) { - _callbackObject = callbackObject; - _targetServer = serverName; - _sendSync = new object(); - - SQLDNSInfo cachedDNSInfo; - bool hasCachedDNSInfo = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); - - try + using (TrySNIEventScope.Create(nameof(SNITCPHandle))) { - TimeSpan ts = default(TimeSpan); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Setting server name = {1}", args0: _connectionId, args1: serverName); - // In case the Timeout is Infinite, we will receive the max value of Int64 as the tick count - // The infinite Timeout is a function of ConnectionString Timeout=0 - bool isInfiniteTimeOut = long.MaxValue == timerExpire; - if (!isInfiniteTimeOut) - { - ts = DateTime.FromFileTime(timerExpire) - DateTime.Now; - ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts; - } + _targetServer = serverName; + _tlsFirst = tlsFirst; + _hostNameInCertificate = hostNameInCertificate; + _serverCertificateFilename = serverCertificateFilename; + _sendSync = new object(); - bool reportError = true; + SQLDNSInfo cachedDNSInfo; + bool hasCachedDNSInfo = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); - // We will always first try to connect with serverName as before and let the DNS server to resolve the serverName. - // If the DSN resolution fails, we will try with IPs in the DNS cache if existed. We try with IPv4 first and followed by IPv6 if - // IPv4 fails. The exceptions will be throw to upper level and be handled as before. try { - if (parallel) - { - _socket = TryConnectParallel(serverName, port, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); - } - else + TimeSpan ts = default(TimeSpan); + + // In case the Timeout is Infinite, we will receive the max value of Int64 as the tick count + // The infinite Timeout is a function of ConnectionString Timeout=0 + bool isInfiniteTimeOut = long.MaxValue == timerExpire; + if (!isInfiniteTimeOut) { - _socket = Connect(serverName, port, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + ts = DateTime.FromFileTime(timerExpire) - DateTime.Now; + ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts; } - } - catch (Exception ex) - { - // Retry with cached IP address - if (ex is SocketException || ex is ArgumentException || ex is AggregateException) + + bool reportError = true; + + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Connecting to serverName {1} and port {2}", args0: _connectionId, args1: serverName, args2: port); + // We will always first try to connect with serverName as before and let DNS resolve the serverName. + // If DNS resolution fails, we will try with IPs in the DNS cache if they exist. We try with cached IPs based on IPAddressPreference. + // Exceptions will be thrown to the caller and be handled as before. + try { - if (hasCachedDNSInfo == false) + if (parallel) { - throw; + _socket = TryConnectParallel(serverName, port, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); } else { - int portRetry = String.IsNullOrEmpty(cachedDNSInfo.Port) ? port : Int32.Parse(cachedDNSInfo.Port); - - try + _socket = Connect(serverName, port, ts, isInfiniteTimeOut, ipPreference, cachedFQDN, ref pendingDNSInfo); + } + } + catch (Exception ex) + { + // Retry with cached IP address + if (ex is SocketException || ex is ArgumentException || ex is AggregateException) + { + if (hasCachedDNSInfo == false) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Cached DNS Info not found, exception occurred thrown: {1}", args0: _connectionId, args1: ex?.Message); + throw; + } + else { - if (parallel) + int portRetry = string.IsNullOrEmpty(cachedDNSInfo.Port) ? port : int.Parse(cachedDNSInfo.Port); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Retrying with cached DNS IP Address {1} and port {2}", args0: _connectionId, args1: cachedDNSInfo.AddrIPv4, args2: cachedDNSInfo.Port); + + string firstCachedIP; + string secondCachedIP; + + if (SqlConnectionIPAddressPreference.IPv6First == ipPreference) { - _socket = TryConnectParallel(cachedDNSInfo.AddrIPv4, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); + firstCachedIP = cachedDNSInfo.AddrIPv6; + secondCachedIP = cachedDNSInfo.AddrIPv4; } else { - _socket = Connect(cachedDNSInfo.AddrIPv4, portRetry, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + firstCachedIP = cachedDNSInfo.AddrIPv4; + secondCachedIP = cachedDNSInfo.AddrIPv6; } - } - catch (Exception exRetry) - { - if (exRetry is SocketException || exRetry is ArgumentNullException - || exRetry is ArgumentException || exRetry is ArgumentOutOfRangeException || exRetry is AggregateException) + + try { if (parallel) { - _socket = TryConnectParallel(cachedDNSInfo.AddrIPv6, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); + _socket = TryConnectParallel(firstCachedIP, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); } else { - _socket = Connect(cachedDNSInfo.AddrIPv6, portRetry, ts, isInfiniteTimeOut, cachedFQDN, ref pendingDNSInfo); + _socket = Connect(firstCachedIP, portRetry, ts, isInfiniteTimeOut, ipPreference, cachedFQDN, ref pendingDNSInfo); } } - else + catch (Exception exRetry) { - throw; + if (exRetry is SocketException || exRetry is ArgumentNullException + || exRetry is ArgumentException || exRetry is ArgumentOutOfRangeException || exRetry is AggregateException) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Retrying exception {1}", args0: _connectionId, args1: exRetry?.Message); + if (parallel) + { + _socket = TryConnectParallel(secondCachedIP, portRetry, ts, isInfiniteTimeOut, ref reportError, cachedFQDN, ref pendingDNSInfo); + } + else + { + _socket = Connect(secondCachedIP, portRetry, ts, isInfiniteTimeOut, ipPreference, cachedFQDN, ref pendingDNSInfo); + } + } + else + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Retry failed, exception occurred: {1}", args0: _connectionId, args1: exRetry?.Message); + throw; + } } } } + else + { + throw; + } } - else - { - throw; - } - } - if (_socket == null || !_socket.Connected) - { - if (_socket != null) + if (_socket == null || !_socket.Connected) { - _socket.Dispose(); - _socket = null; + if (_socket != null) + { + _socket.Dispose(); + _socket = null; + } + + if (reportError) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0} could not be opened, exception occurred: {1}", args0: _connectionId, args1: Strings.SNI_ERROR_40); + ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, Strings.SNI_ERROR_40); + } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0} Socket could not be opened.", args0: _connectionId); + return; } - if (reportError) + _socket.NoDelay = true; + _tcpStream = new SNINetworkStream(_socket, true); + + Stream stream = _tcpStream; + if (!_tlsFirst) { - ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); + _sslOverTdsStream = new SslOverTdsStream(_tcpStream, _connectionId); + stream = _sslOverTdsStream; } + _sslStream = new SNISslStream(stream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); + } + catch (SocketException se) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0} Socket exception occurred: Error Code {1}, Message {2}", args0: _connectionId, args1: se?.SocketErrorCode, args2: se?.Message); + ReportTcpSNIError(se); + return; + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0} Exception occurred: {1}", args0: _connectionId, args1: e?.Message); + ReportTcpSNIError(e); return; } - _socket.NoDelay = true; - _tcpStream = new SNINetworkStream(_socket, true); - - _sslOverTdsStream = new SslOverTdsStream(_tcpStream); - _sslStream = new SNISslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); + _stream = _tcpStream; + _status = TdsEnums.SNI_SUCCESS; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0} Socket opened successfully, TCP stream ready.", args0: _connectionId); } - catch (SocketException se) - { - ReportTcpSNIError(se); - return; - } - catch (Exception e) - { - ReportTcpSNIError(e); - return; - } - - _stream = _tcpStream; - _status = TdsEnums.SNI_SUCCESS; } // Connect to server with hostName and port in parellel mode. @@ -251,15 +303,14 @@ private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool i Socket availableSocket = null; Task connectTask; - Task serverAddrTask = Dns.GetHostAddressesAsync(hostName); - serverAddrTask.Wait(ts); - IPAddress[] serverAddresses = serverAddrTask.Result; + IPAddress[] serverAddresses = SNICommon.GetDnsIpAddresses(hostName); if (serverAddresses.Length > MaxParallelIpAddresses) { // Fail if above 64 to match legacy behavior callerReportError = false; - ReportTcpSNIError(0, SNICommon.MultiSubnetFailoverWithMoreThan64IPs, string.Empty); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0} serverAddresses.Length {1} Exception: {2}", args0: _connectionId, args1: serverAddresses.Length, args2: Strings.SNI_ERROR_47); + ReportTcpSNIError(0, SNICommon.MultiSubnetFailoverWithMoreThan64IPs, Strings.SNI_ERROR_47); return availableSocket; } @@ -288,65 +339,49 @@ private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool i if (!(isInfiniteTimeOut ? connectTask.Wait(-1) : connectTask.Wait(ts))) { callerReportError = false; - ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, string.Empty); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0} Connection timed out, Exception: {1}", args0: _connectionId, args1: Strings.SNI_ERROR_40); + ReportTcpSNIError(0, SNICommon.ConnOpenFailedError, Strings.SNI_ERROR_40); return availableSocket; } availableSocket = connectTask.Result; return availableSocket; - } // Connect to server with hostName and port. // The IP information will be collected temporarily as the pendingDNSInfo but is not stored in the DNS cache at this point. // Only write to the DNS cache when we receive IsSupported flag as true in the Feature Ext Ack from server. - private static Socket Connect(string serverName, int port, TimeSpan timeout, bool isInfiniteTimeout, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + private static Socket Connect(string serverName, int port, TimeSpan timeout, bool isInfiniteTimeout, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) { - IPAddress[] ipAddresses = Dns.GetHostAddresses(serverName); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "IP preference : {0}", Enum.GetName(typeof(SqlConnectionIPAddressPreference), ipPreference)); + + IPAddress[] ipAddresses = SNICommon.GetDnsIpAddresses(serverName); string IPv4String = null; string IPv6String = null; - IPAddress serverIPv4 = null; - IPAddress serverIPv6 = null; - foreach (IPAddress ipAddress in ipAddresses) + // Returning null socket is handled by the caller function. + if (ipAddresses == null || ipAddresses.Length == 0) { - if (ipAddress.AddressFamily == AddressFamily.InterNetwork) - { - serverIPv4 = ipAddress; - IPv4String = ipAddress.ToString(); - } - else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) - { - serverIPv6 = ipAddress; - IPv6String = ipAddress.ToString(); - } + return null; } - ipAddresses = new IPAddress[] { serverIPv4, serverIPv6 }; - Socket[] sockets = new Socket[2]; - if (IPv4String != null || IPv6String != null) + Socket[] sockets = new Socket[ipAddresses.Length]; + AddressFamily[] preferedIPFamilies = new AddressFamily[2]; + + if (ipPreference == SqlConnectionIPAddressPreference.IPv4First) { - pendingDNSInfo = new SQLDNSInfo(cachedFQDN, IPv4String, IPv6String, port.ToString()); + preferedIPFamilies[0] = AddressFamily.InterNetwork; + preferedIPFamilies[1] = AddressFamily.InterNetworkV6; } - - CancellationTokenSource cts = null; - - void Cancel() + else if (ipPreference == SqlConnectionIPAddressPreference.IPv6First) { - for (int i = 0; i < sockets.Length; ++i) - { - try - { - if (sockets[i] != null && !sockets[i].Connected) - { - sockets[i].Dispose(); - sockets[i] = null; - } - } - catch { } - } + preferedIPFamilies[0] = AddressFamily.InterNetworkV6; + preferedIPFamilies[1] = AddressFamily.InterNetwork; } + // else -> UsePlatformDefault + + CancellationTokenSource cts = null; if (!isInfiniteTimeout) { @@ -357,30 +392,71 @@ void Cancel() Socket availableSocket = null; try { - for (int i = 0; i < sockets.Length; ++i) + // We go through the IP list twice. + // In the first traversal, we only try to connect with the preferedIPFamilies[0]. + // In the second traversal, we only try to connect with the preferedIPFamilies[1]. + // For UsePlatformDefault preference, we do traversal once. + for (int i = 0; i < preferedIPFamilies.Length; ++i) { - try + for (int n = 0; n < ipAddresses.Length; n++) { - if (ipAddresses[i] != null) + IPAddress ipAddress = ipAddresses[n]; + try { - sockets[i] = new Socket(ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); - sockets[i].Connect(ipAddresses[i], port); - if (sockets[i] != null) // sockets[i] can be null if cancel callback is executed during connect() + if (ipAddress != null) { - if (sockets[i].Connected) + if (ipAddress.AddressFamily != preferedIPFamilies[i] && ipPreference != SqlConnectionIPAddressPreference.UsePlatformDefault) { - availableSocket = sockets[i]; - break; + continue; } - else + + sockets[n] = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + // enable keep-alive on socket + SetKeepAliveValues(ref sockets[n]); + + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connecting to IP address {0} and port {1} using {2} address family.", + args0: ipAddress, + args1: port, + args2: ipAddress.AddressFamily); + sockets[n].Connect(ipAddress, port); + if (sockets[n] != null) // sockets[n] can be null if cancel callback is executed during connect() { - sockets[i].Dispose(); - sockets[i] = null; + if (sockets[n].Connected) + { + availableSocket = sockets[n]; + if (ipAddress.AddressFamily == AddressFamily.InterNetwork) + { + IPv4String = ipAddress.ToString(); + } + else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + IPv6String = ipAddress.ToString(); + } + + break; + } + else + { + sockets[n].Dispose(); + sockets[n] = null; + } } } } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message); + SqlClientEventSource.Log.TryAdvancedTraceEvent($"{nameof(SNITCPHandle)}.{System.Reflection.MethodBase.GetCurrentMethod().Name}{EventType.ERR}THIS EXCEPTION IS BEING SWALLOWED: {e}"); + } + } + + // If we have already got a valid Socket, or the platform default was prefered + // we won't do the second traversal. + if (availableSocket is not null || ipPreference == SqlConnectionIPAddressPreference.UsePlatformDefault) + { + break; } - catch { } } } finally @@ -388,7 +464,32 @@ void Cancel() cts?.Dispose(); } + // we only record the ip we can connect with successfully. + if (IPv4String != null || IPv6String != null) + { + pendingDNSInfo = new SQLDNSInfo(cachedFQDN, IPv4String, IPv6String, port.ToString()); + } + return availableSocket; + + void Cancel() + { + for (int i = 0; i < sockets.Length; ++i) + { + try + { + if (sockets[i] != null && !sockets[i].Connected) + { + sockets[i].Dispose(); + sockets[i] = null; + } + } + catch (Exception e) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "THIS EXCEPTION IS BEING SWALLOWED: {0}", args0: e?.Message); + } + } + } } private static Task ParallelConnectAsync(IPAddress[] serverAddresses, int port) @@ -498,24 +599,41 @@ private static async void ParallelConnectHelper( /// public override uint EnableSsl(uint options) { - _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; - - try - { - _sslStream.AuthenticateAsClient(_targetServer); - _sslOverTdsStream.FinishHandshake(); - } - catch (AuthenticationException aue) - { - return ReportTcpSNIError(aue); - } - catch (InvalidOperationException ioe) + using (TrySNIEventScope.Create(nameof(SNIHandle))) { - return ReportTcpSNIError(ioe); - } + _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; - _stream = _sslStream; - return TdsEnums.SNI_SUCCESS; + try + { + if (_tlsFirst) + { + AuthenticateAsClient(_sslStream, _targetServer, null); + } + else + { + // TODO: Resolve whether to send _serverNameIndication or _targetServer. _serverNameIndication currently results in error. Why? + _sslStream.AuthenticateAsClient(_targetServer, null, s_supportedProtocols, false); + } + if (_sslOverTdsStream is not null) + { + _sslOverTdsStream.FinishHandshake(); + } + } + catch (AuthenticationException aue) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Authentication exception occurred: {1}", args0: _connectionId, args1: aue?.Message); + return ReportTcpSNIError(aue, SNIError.CertificateValidationErrorCode); + } + catch (InvalidOperationException ioe) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Invalid Operation Exception occurred: {1}", args0: _connectionId, args1: ioe?.Message); + return ReportTcpSNIError(ioe); + } + + _stream = _sslStream; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, SSL enabled successfully.", args0: _connectionId); + return TdsEnums.SNI_SUCCESS; + } } /// @@ -525,27 +643,55 @@ public override void DisableSsl() { _sslStream.Dispose(); _sslStream = null; - _sslOverTdsStream.Dispose(); + _sslOverTdsStream?.Dispose(); _sslOverTdsStream = null; _stream = _tcpStream; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, SSL Disabled. Communication will continue on TCP Stream.", args0: _connectionId); } /// /// Validate server certificate callback /// /// Sender object - /// X.509 certificate + /// X.509 certificate provided from the server /// X.509 chain /// Policy errors /// True if certificate is valid - private bool ValidateServerCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors) + private bool ValidateServerCertificate(object sender, X509Certificate serverCertificate, X509Chain chain, SslPolicyErrors policyErrors) { if (!_validateCert) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Certificate will not be validated.", args0: _connectionId); return true; } + + string serverNameToValidate; + if (!string.IsNullOrEmpty(_hostNameInCertificate)) + { + serverNameToValidate = _hostNameInCertificate; + } + else + { + serverNameToValidate = _targetServer; + } - return SNICommon.ValidateSslServerCertificate(_targetServer, sender, cert, chain, policyErrors); + if (!string.IsNullOrEmpty(_serverCertificateFilename)) + { + X509Certificate clientCertificate = null; + try + { + clientCertificate = new X509Certificate(_serverCertificateFilename); + return SNICommon.ValidateSslServerCertificate(clientCertificate, serverCertificate, policyErrors); + } + catch (Exception e) + { + // if this fails, then fall back to the HostNameInCertificate or TargetServer validation. + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, IOException occurred: {1}", args0: _connectionId, args1: e.Message); + } + } + + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Certificate will be validated for Target Server name", args0: _connectionId); + return SNICommon.ValidateSslServerCertificate(serverNameToValidate, serverCertificate, policyErrors); } /// @@ -566,45 +712,49 @@ public override uint Send(SNIPacket packet) { bool releaseLock = false; try + { + // is the packet is marked out out-of-band (attention packets only) it must be + // sent immediately even if a send of recieve operation is already in progress + // because out of band packets are used to cancel ongoing operations + // so try to take the lock if possible but continue even if it can't be taken + if (packet.IsOutOfBand) + { + Monitor.TryEnter(this, ref releaseLock); + } + else { - // is the packet is marked out out-of-band (attention packets only) it must be - // sent immediately even if a send of recieve operation is already in progress - // because out of band packets are used to cancel ongoing operations - // so try to take the lock if possible but continue even if it can't be taken - if (packet.IsOutOfBand) + Monitor.Enter(this); + releaseLock = true; + } + + // this lock ensures that two packets are not being written to the transport at the same time + // so that sending a standard and an out-of-band packet are both written atomically no data is + // interleaved + lock (_sendSync) + { + try { - Monitor.TryEnter(this, ref releaseLock); + packet.WriteToStream(_stream); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Data sent to stream synchronously", args0: _connectionId); + return TdsEnums.SNI_SUCCESS; } - else + catch (ObjectDisposedException ode) { - Monitor.Enter(this); - releaseLock = true; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, ObjectDisposedException occurred: {1}", args0: _connectionId, args1: ode?.Message); + return ReportTcpSNIError(ode); } - - // this lock ensures that two packets are not being written to the transport at the same time - // so that sending a standard and an out-of-band packet are both written atomically no data is - // interleaved - lock (_sendSync) + catch (SocketException se) { - try - { - packet.WriteToStream(_stream); - return TdsEnums.SNI_SUCCESS; - } - catch (ObjectDisposedException ode) - { - return ReportTcpSNIError(ode); - } - catch (SocketException se) - { - return ReportTcpSNIError(se); - } - catch (IOException ioe) - { - return ReportTcpSNIError(ioe); - } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, SocketException occurred: {1}", args0: _connectionId, args1: se?.Message); + return ReportTcpSNIError(se); + } + catch (IOException ioe) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, IOException occurred: {1}", args0: _connectionId, args1: ioe?.Message); + return ReportTcpSNIError(ioe); } } + } finally { if (releaseLock) @@ -640,7 +790,8 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) else { // otherwise it is timeout for 0 or less than -1 - ReportTcpSNIError(0, SNICommon.ConnTimeoutError, string.Empty); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Error 258, Timeout error occurred.", args0: _connectionId); + ReportTcpSNIError(0, SNICommon.ConnTimeoutError, Strings.SNI_ERROR_11); return TdsEnums.SNI_WAIT_TIMEOUT; } @@ -652,21 +803,25 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) errorPacket = packet; packet = null; var e = new Win32Exception(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Win32 exception occurred: {1}", args0: _connectionId, args1: e?.Message); return ReportErrorAndReleasePacket(errorPacket, (uint)e.NativeErrorCode, 0, e.Message); } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Data read from stream synchronously", args0: _connectionId); return TdsEnums.SNI_SUCCESS; } catch (ObjectDisposedException ode) { errorPacket = packet; packet = null; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, ObjectDisposedException occurred: {1}", args0: _connectionId, args1: ode?.Message); return ReportErrorAndReleasePacket(errorPacket, ode); } catch (SocketException se) { errorPacket = packet; packet = null; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Socket exception occurred: {1}", args0: _connectionId, args1: se?.Message); return ReportErrorAndReleasePacket(errorPacket, se); } catch (IOException ioe) @@ -676,9 +831,11 @@ public override uint Receive(out SNIPacket packet, int timeoutInMilliseconds) uint errorCode = ReportErrorAndReleasePacket(errorPacket, ioe); if (ioe.InnerException is SocketException socketException && socketException.SocketErrorCode == SocketError.TimedOut) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, IO exception occurred with Wait Timeout (error 258): {1}", args0: _connectionId, args1: ioe?.Message); errorCode = TdsEnums.SNI_WAIT_TIMEOUT; } + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, IO exception occurred: {1}", args0: _connectionId, args1: ioe?.Message); return errorCode; } finally @@ -703,21 +860,15 @@ public override void SetAsyncCallbacks(SNIAsyncCallback receiveCallback, SNIAsyn /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public override uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null) + public override uint SendAsync(SNIPacket packet) { - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SNITCPHandle))) { - SNIAsyncCallback cb = callback ?? _sendCallback; - packet.WriteToStreamAsync(_stream, cb, SNIProviders.TCP_PROV); + packet.WriteToStreamAsync(_stream, _sendCallback, SNIProviders.TCP_PROV); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Data sent to stream asynchronously", args0: _connectionId); return TdsEnums.SNI_SUCCESS_IO_PENDING; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -729,10 +880,11 @@ public override uint ReceiveAsync(ref SNIPacket packet) { SNIPacket errorPacket; packet = RentPacket(headerSize: 0, dataSize: _bufferSize); - + packet.SetAsyncIOCompletionCallback(_receiveCallback); try { - packet.ReadFromStreamAsync(_stream, _receiveCallback); + packet.ReadFromStreamAsync(_stream); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Data received from stream asynchronously", args0: _connectionId); return TdsEnums.SNI_SUCCESS_IO_PENDING; } catch (Exception e) when (e is ObjectDisposedException || e is SocketException || e is IOException) @@ -763,25 +915,28 @@ public override uint CheckConnection() // return true we can safely determine that the connection is no longer active. if (!_socket.Connected || (_socket.Poll(100, SelectMode.SelectRead) && _socket.Available == 0)) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Socket not usable.", args0: _connectionId); return TdsEnums.SNI_ERROR; } } catch (SocketException se) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Socket Exception occurred: {1}", args0: _connectionId, args1: se?.Message); return ReportTcpSNIError(se); } catch (ObjectDisposedException ode) { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, ObjectDisposedException occurred: {1}", args0: _connectionId, args1: ode?.Message); return ReportTcpSNIError(ode); } return TdsEnums.SNI_SUCCESS; } - private uint ReportTcpSNIError(Exception sniException) + private uint ReportTcpSNIError(Exception sniException, uint nativeErrorCode = 0) { _status = TdsEnums.SNI_ERROR; - return SNICommon.ReportSNIError(SNIProviders.TCP_PROV, SNICommon.InternalExceptionError, sniException); + return SNICommon.ReportSNIError(SNIProviders.TCP_PROV, SNICommon.InternalExceptionError, sniException, nativeErrorCode); } private uint ReportTcpSNIError(uint nativeError, uint sniError, string errorMessage) @@ -820,8 +975,7 @@ public override void KillConnection() internal static void SetKeepAliveValues(ref Socket socket) { - -#if NETCOREAPP31_AND_ABOVE +#if NETCOREAPP socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 1); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 30); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs index e4359e2c42..e51175059a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs @@ -3,41 +3,52 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Microsoft.Data.SqlClient.SNI { - internal class SSRP + internal sealed class SSRP { private const char SemicolonSeparator = ';'; - private const int SqlServerBrowserPort = 1434; + private const int SqlServerBrowserPort = 1434; //port SQL Server Browser + private const int RecieveMAXTimeoutsForCLNT_BCAST_EX = 15000; //Default max time for response wait + private const int RecieveTimeoutsForCLNT_BCAST_EX = 1000; //subsequent wait time for response after intial wait + private const int ServerResponseHeaderSizeForCLNT_BCAST_EX = 3;//(SVR_RESP + RESP_SIZE) https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/2e1560c9-5097-4023-9f5e-72b9ff1ec3b1 + private const int ValidResponseSizeForCLNT_BCAST_EX = 4096; //valid reponse size should be less than 4096 + private const int FirstTimeoutForCLNT_BCAST_EX = 5000;//wait for first response for 5 seconds + private const int CLNT_BCAST_EX = 2;//request packet /// /// Finds instance port number for given instance name. /// /// SQL Sever Browser hostname /// instance name to find port number + /// Connection timer expiration + /// query all resolved IP addresses in parallel + /// IP address preference /// port number for given instance name - internal static int GetPortByInstanceName(string browserHostName, string instanceName) + internal static int GetPortByInstanceName(string browserHostName, string instanceName, long timerExpire, bool allIPsInParallel, SqlConnectionIPAddressPreference ipPreference) { Debug.Assert(!string.IsNullOrWhiteSpace(browserHostName), "browserHostName should not be null, empty, or whitespace"); Debug.Assert(!string.IsNullOrWhiteSpace(instanceName), "instanceName should not be null, empty, or whitespace"); - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SSRP))) { byte[] instanceInfoRequest = CreateInstanceInfoRequest(instanceName); byte[] responsePacket = null; try { - responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, instanceInfoRequest); + responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, instanceInfoRequest, timerExpire, allIPsInParallel, ipPreference); } catch (SocketException se) { - SqlClientEventSource.Log.TrySNITraceEvent(" SocketException Message = {0}", se.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.ERR, "SocketException Message = {0}", args0: se?.Message); throw new Exception(SQLMessage.SqlServerBrowserNotAccessible(), se); } @@ -59,10 +70,6 @@ internal static int GetPortByInstanceName(string browserHostName, string instanc return ushort.Parse(elements[tcpIndex + 1]); } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -73,8 +80,7 @@ internal static int GetPortByInstanceName(string browserHostName, string instanc private static byte[] CreateInstanceInfoRequest(string instanceName) { Debug.Assert(!string.IsNullOrWhiteSpace(instanceName), "instanceName should not be null, empty, or whitespace"); - long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(""); - try + using (TrySNIEventScope.Create(nameof(SSRP))) { const byte ClntUcastInst = 0x04; instanceName += char.MinValue; @@ -86,10 +92,6 @@ private static byte[] CreateInstanceInfoRequest(string instanceName) return requestPacket; } - finally - { - SqlClientEventSource.Log.TrySNIScopeLeaveEvent(scopeID); - } } /// @@ -97,14 +99,17 @@ private static byte[] CreateInstanceInfoRequest(string instanceName) /// /// SQL Sever Browser hostname /// instance name to lookup DAC port + /// Connection timer expiration + /// query all resolved IP addresses in parallel + /// IP address preference /// DAC port for given instance name - internal static int GetDacPortByInstanceName(string browserHostName, string instanceName) + internal static int GetDacPortByInstanceName(string browserHostName, string instanceName, long timerExpire, bool allIPsInParallel, SqlConnectionIPAddressPreference ipPreference) { Debug.Assert(!string.IsNullOrWhiteSpace(browserHostName), "browserHostName should not be null, empty, or whitespace"); Debug.Assert(!string.IsNullOrWhiteSpace(instanceName), "instanceName should not be null, empty, or whitespace"); byte[] dacPortInfoRequest = CreateDacPortInfoRequest(instanceName); - byte[] responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, dacPortInfoRequest); + byte[] responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, dacPortInfoRequest, timerExpire, allIPsInParallel, ipPreference); const byte SvrResp = 0x05; const byte ProtocolVersion = 0x01; @@ -141,37 +146,231 @@ private static byte[] CreateDacPortInfoRequest(string instanceName) return requestPacket; } + private class SsrpResult + { + public byte[] ResponsePacket; + public Exception Error; + } + /// /// Sends request to server, and receives response from server by UDP. /// /// UDP server hostname /// UDP server port /// request packet + /// Connection timer expiration + /// query all resolved IP addresses in parallel + /// IP address preference + /// response packet from UDP server + private static byte[] SendUDPRequest(string browserHostname, int port, byte[] requestPacket, long timerExpire, bool allIPsInParallel, SqlConnectionIPAddressPreference ipPreference) + { + using (TrySNIEventScope.Create(nameof(SSRP))) + { + Debug.Assert(!string.IsNullOrWhiteSpace(browserHostname), "browserhostname should not be null, empty, or whitespace"); + Debug.Assert(port >= 0 && port <= 65535, "Invalid port"); + Debug.Assert(requestPacket != null && requestPacket.Length > 0, "requestPacket should not be null or 0-length array"); + + if (IPAddress.TryParse(browserHostname, out IPAddress address)) + { + SsrpResult response = SendUDPRequest(new IPAddress[] { address }, port, requestPacket, allIPsInParallel); + if (response != null && response.ResponsePacket != null) + return response.ResponsePacket; + else if (response != null && response.Error != null) + throw response.Error; + else + return null; + } + + TimeSpan ts = default; + // In case the Timeout is Infinite, we will receive the max value of Int64 as the tick count + // The infinite Timeout is a function of ConnectionString Timeout=0 + if (long.MaxValue != timerExpire) + { + ts = DateTime.FromFileTime(timerExpire) - DateTime.Now; + ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts; + } + + IPAddress[] ipAddresses = SNICommon.GetDnsIpAddresses(browserHostname); + Debug.Assert(ipAddresses.Length > 0, "DNS should throw if zero addresses resolve"); + + switch (ipPreference) + { + case SqlConnectionIPAddressPreference.IPv4First: + { + SsrpResult response4 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetwork).ToArray(), port, requestPacket, allIPsInParallel); + if (response4 != null && response4.ResponsePacket != null) + return response4.ResponsePacket; + + SsrpResult response6 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetworkV6).ToArray(), port, requestPacket, allIPsInParallel); + if (response6 != null && response6.ResponsePacket != null) + return response6.ResponsePacket; + + // No responses so throw first error + if (response4 != null && response4.Error != null) + throw response4.Error; + else if (response6 != null && response6.Error != null) + throw response6.Error; + + break; + } + case SqlConnectionIPAddressPreference.IPv6First: + { + SsrpResult response6 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetworkV6).ToArray(), port, requestPacket, allIPsInParallel); + if (response6 != null && response6.ResponsePacket != null) + return response6.ResponsePacket; + + SsrpResult response4 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetwork).ToArray(), port, requestPacket, allIPsInParallel); + if (response4 != null && response4.ResponsePacket != null) + return response4.ResponsePacket; + + // No responses so throw first error + if (response6 != null && response6.Error != null) + throw response6.Error; + else if (response4 != null && response4.Error != null) + throw response4.Error; + + break; + } + default: + { + SsrpResult response = SendUDPRequest(ipAddresses, port, requestPacket, true); // allIPsInParallel); + if (response != null && response.ResponsePacket != null) + return response.ResponsePacket; + else if (response != null && response.Error != null) + throw response.Error; + + break; + } + } + + return null; + } + } + + /// + /// Sends request to server, and receives response from server by UDP. + /// + /// IP Addresses + /// UDP server port + /// request packet + /// query all resolved IP addresses in parallel /// response packet from UDP server - private static byte[] SendUDPRequest(string browserHostname, int port, byte[] requestPacket) + private static SsrpResult SendUDPRequest(IPAddress[] ipAddresses, int port, byte[] requestPacket, bool allIPsInParallel) { - Debug.Assert(!string.IsNullOrWhiteSpace(browserHostname), "browserhostname should not be null, empty, or whitespace"); - Debug.Assert(port >= 0 && port <= 65535, "Invalid port"); - Debug.Assert(requestPacket != null && requestPacket.Length > 0, "requestPacket should not be null or 0-length array"); + if (ipAddresses.Length == 0) + return null; + + if (allIPsInParallel) // Used for MultiSubnetFailover + { + List> tasks = new(ipAddresses.Length); + CancellationTokenSource cts = new CancellationTokenSource(); + for (int i = 0; i < ipAddresses.Length; i++) + { + IPEndPoint endPoint = new IPEndPoint(ipAddresses[i], port); + tasks.Add(Task.Factory.StartNew(() => SendUDPRequest(endPoint, requestPacket), cts.Token)); + } + List> completedTasks = new(); + while (tasks.Count > 0) + { + int first = Task.WaitAny(tasks.ToArray()); + if (tasks[first].Result.ResponsePacket != null) + { + cts.Cancel(); + return tasks[first].Result; + } + else + { + completedTasks.Add(tasks[first]); + tasks.Remove(tasks[first]); + } + } + + Debug.Assert(completedTasks.Count > 0, "completedTasks should never be 0"); + + // All tasks failed. Return the error from the first failure. + return completedTasks[0].Result; + } + else + { + // If not parallel, use the first IP address provided + IPEndPoint endPoint = new IPEndPoint(ipAddresses[0], port); + return SendUDPRequest(endPoint, requestPacket); + } + } + + private static SsrpResult SendUDPRequest(IPEndPoint endPoint, byte[] requestPacket) + { const int sendTimeOutMs = 1000; const int receiveTimeOutMs = 1000; - IPAddress address = null; - bool isIpAddress = IPAddress.TryParse(browserHostname, out address); + SsrpResult result = new(); - byte[] responsePacket = null; - using (UdpClient client = new UdpClient(!isIpAddress ? AddressFamily.InterNetwork : address.AddressFamily)) + try { - Task sendTask = client.SendAsync(requestPacket, requestPacket.Length, browserHostname, port); - Task receiveTask = null; - if (sendTask.Wait(sendTimeOutMs) && (receiveTask = client.ReceiveAsync()).Wait(receiveTimeOutMs)) + using (UdpClient client = new UdpClient(endPoint.AddressFamily)) { - responsePacket = receiveTask.Result.Buffer; + Task sendTask = client.SendAsync(requestPacket, requestPacket.Length, endPoint); + Task receiveTask = null; + + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Waiting for UDP Client to fetch Port info."); + if (sendTask.Wait(sendTimeOutMs) && (receiveTask = client.ReceiveAsync()).Wait(receiveTimeOutMs)) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Received Port info from UDP Client."); + result.ResponsePacket = receiveTask.Result.Buffer; + } } } + catch (Exception e) + { + result.Error = e; + } + + return result; + } - return responsePacket; + /// + /// Sends request to server, and recieves response from server (SQLBrowser) on port 1434 by UDP + /// Request (https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/a3035afa-c268-4699-b8fd-4f351e5c8e9e) + /// Response (https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/2e1560c9-5097-4023-9f5e-72b9ff1ec3b1) + /// + /// string constaning list of SVR_RESP(just RESP_DATA) + internal static string SendBroadcastUDPRequest() + { + StringBuilder response = new StringBuilder(); + byte[] CLNT_BCAST_EX_Request = new byte[1] { CLNT_BCAST_EX }; //0x02 + // Waits 5 seconds for the first response and every 1 second up to 15 seconds + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/f2640a2d-3beb-464b-a443-f635842ebc3e#Appendix_A_3 + int currentTimeOut = FirstTimeoutForCLNT_BCAST_EX; + + using (TrySNIEventScope.Create(nameof(SSRP))) + { + using (UdpClient clientListener = new UdpClient()) + { + Task sendTask = clientListener.SendAsync(CLNT_BCAST_EX_Request, CLNT_BCAST_EX_Request.Length, new IPEndPoint(IPAddress.Broadcast, SqlServerBrowserPort)); + Task receiveTask = null; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Waiting for UDP Client to fetch list of instances."); + Stopwatch sw = new Stopwatch(); //for waiting until 15 sec elapsed + sw.Start(); + try + { + while ((receiveTask = clientListener.ReceiveAsync()).Wait(currentTimeOut) && sw.ElapsedMilliseconds <= RecieveMAXTimeoutsForCLNT_BCAST_EX && receiveTask != null) + { + currentTimeOut = RecieveTimeoutsForCLNT_BCAST_EX; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Received instnace info from UDP Client."); + if (receiveTask.Result.Buffer.Length < ValidResponseSizeForCLNT_BCAST_EX) //discard invalid response + { + response.Append(Encoding.ASCII.GetString(receiveTask.Result.Buffer, ServerResponseHeaderSizeForCLNT_BCAST_EX, receiveTask.Result.Buffer.Length - ServerResponseHeaderSizeForCLNT_BCAST_EX)); //RESP_DATA(VARIABLE) - 3 (RESP_SIZE + SVR_RESP) + } + } + } + finally + { + sw.Stop(); + } + } + } + return response.ToString(); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetCoreApp.cs index 97a1181ae3..be8d1a0160 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetCoreApp.cs @@ -25,13 +25,12 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati public override int Read(Span buffer) { - if (!_encapsulate) - { - return _stream.Read(buffer); - } - - using (SNIEventScope.Create(" reading encapsulated bytes")) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { + if (!_encapsulate) + { + return _stream.Read(buffer); + } if (_packetBytes > 0) { // there are queued bytes from a previous packet available @@ -76,24 +75,24 @@ public override int Read(Span buffer) public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { - if (!_encapsulate) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { - int read; + if (!_encapsulate) { - ValueTask readValueTask = _stream.ReadAsync(buffer, cancellationToken); - if (readValueTask.IsCompletedSuccessfully) - { - read = readValueTask.Result; - } - else + int read; { - read = await readValueTask.ConfigureAwait(false); + ValueTask readValueTask = _stream.ReadAsync(buffer, cancellationToken); + if (readValueTask.IsCompletedSuccessfully) + { + read = readValueTask.Result; + } + else + { + read = await readValueTask.ConfigureAwait(false); + } } + return read; } - return read; - } - using (SNIEventScope.Create(" reading encapsulated bytes")) - { if (_packetBytes > 0) { // there are queued bytes from a previous packet available @@ -175,17 +174,17 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation public override void Write(ReadOnlySpan buffer) { - // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After - // negotiation, the underlying socket only sees SSL frames. - if (!_encapsulate) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { - _stream.Write(buffer); - _stream.Flush(); - return; - } + // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After + // negotiation, the underlying socket only sees SSL frames. + if (!_encapsulate) + { + _stream.Write(buffer); + _stream.Flush(); + return; + } - using (SNIEventScope.Create(" writing encapsulated bytes")) - { ReadOnlySpan remaining = buffer; byte[] packetBuffer = null; try @@ -228,25 +227,25 @@ public override void Write(ReadOnlySpan buffer) public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { - if (!_encapsulate) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { + if (!_encapsulate) { - ValueTask valueTask = _stream.WriteAsync(buffer, cancellationToken); - if (!valueTask.IsCompletedSuccessfully) { - await valueTask.ConfigureAwait(false); + ValueTask valueTask = _stream.WriteAsync(buffer, cancellationToken); + if (!valueTask.IsCompletedSuccessfully) + { + await valueTask.ConfigureAwait(false); + } } + Task flushTask = _stream.FlushAsync(); + if (flushTask.IsCompletedSuccessfully) + { + await flushTask.ConfigureAwait(false); + } + return; } - Task flushTask = _stream.FlushAsync(); - if (flushTask.IsCompletedSuccessfully) - { - await flushTask.ConfigureAwait(false); - } - return; - } - using (SNIEventScope.Create(" writing encapsulated bytes")) - { ReadOnlyMemory remaining = buffer; byte[] packetBuffer = null; try diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetStandard.cs index 7798b25d06..60bb597974 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetStandard.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.NetStandard.cs @@ -14,13 +14,13 @@ internal sealed partial class SslOverTdsStream : Stream { public override int Read(byte[] buffer, int offset, int count) { - if (!_encapsulate) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { - return _stream.Read(buffer, offset, count); - } + if (!_encapsulate) + { + return _stream.Read(buffer, offset, count); + } - using (SNIEventScope.Create(" reading encapsulated bytes")) - { if (_packetBytes > 0) { // there are queued bytes from a previous packet available @@ -65,18 +65,17 @@ public override int Read(byte[] buffer, int offset, int count) return packetBytesRead; } } - } public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - if (!_encapsulate) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { - return await _stream.ReadAsync(buffer, offset, count, cancellationToken); - } + if (!_encapsulate) + { + return await _stream.ReadAsync(buffer, offset, count, cancellationToken); + } - using (SNIEventScope.Create(" reading encapsulated bytes")) - { if (_packetBytes > 0) { // there are queued bytes from a previous packet available @@ -125,17 +124,17 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, public override void Write(byte[] buffer, int offset, int count) { - // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After - // negotiation, the underlying socket only sees SSL frames. - if (!_encapsulate) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { - _stream.Write(buffer, offset, count); - _stream.Flush(); - return; - } + // During the SSL negotiation phase, SSL is tunnelled over TDS packet type 0x12. After + // negotiation, the underlying socket only sees SSL frames. + if (!_encapsulate) + { + _stream.Write(buffer, offset, count); + _stream.Flush(); + return; + } - using (SNIEventScope.Create(" writing encapsulated bytes")) - { int remainingBytes = count; int dataOffset = offset; byte[] packetBuffer = null; @@ -173,19 +172,19 @@ public override void Write(byte[] buffer, int offset, int count) public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - if (!_encapsulate) + using (TrySNIEventScope.Create(nameof(SslOverTdsStream))) { - await _stream.WriteAsync(buffer, offset, count).ConfigureAwait(false); - Task flushTask = _stream.FlushAsync(); - if (flushTask.Status == TaskStatus.RanToCompletion) + if (!_encapsulate) { - await flushTask.ConfigureAwait(false); + await _stream.WriteAsync(buffer, offset, count).ConfigureAwait(false); + Task flushTask = _stream.FlushAsync(); + if (flushTask.Status == TaskStatus.RanToCompletion) + { + await flushTask.ConfigureAwait(false); + } + return; } - return; - } - using (SNIEventScope.Create(" writing encapsulated bytes")) - { int remainingBytes = count; int dataOffset = offset; byte[] packetBuffer = null; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs index 58384dfd58..e95c5a6cf9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SslOverTdsStream.cs @@ -16,6 +16,7 @@ namespace Microsoft.Data.SqlClient.SNI internal sealed partial class SslOverTdsStream : Stream { private readonly Stream _stream; + private Guid _connectionId; private int _packetBytes = 0; private bool _encapsulate; @@ -27,9 +28,17 @@ internal sealed partial class SslOverTdsStream : Stream /// Constructor /// /// Underlying stream - public SslOverTdsStream(Stream stream) + public SslOverTdsStream(Stream stream) : this(stream, default) { } + + /// + /// Constructor + /// + /// Underlying stream + /// Connection Id of parent stream handle + public SslOverTdsStream(Stream stream, Guid connectionId = default) { _stream = stream; + _connectionId = connectionId; _encapsulate = true; } @@ -39,7 +48,7 @@ public SslOverTdsStream(Stream stream) public void FinishHandshake() { _encapsulate = false; - SqlClientEventSource.Log.TrySNITraceEvent(" switched from encapsulation to passthrough mode"); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SslOverTdsStream), EventType.INFO, "Connection Id {0}, Switched from encapsulation to passthrough mode", args0: _connectionId); } /// @@ -58,6 +67,7 @@ public override void Flush() if (!(_stream is PipeStream)) { _stream.Flush(); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SslOverTdsStream), EventType.INFO, "Connection Id {0}, Flushed stream", args0: _connectionId); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiEventSink.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiEventSink.cs deleted file mode 100644 index 04ae3bedc5..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiEventSink.cs +++ /dev/null @@ -1,25 +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.SqlClient.Server -{ - // SqlEventSink is implemented by calling code. In all methods that accept - // a SqlEventSink directly the sink must be able to handle multiple callbacks - // without control returning from the original call. - - // Methods that do not accept SmiEventSync are (generally) ProcessEvent on - // the SmiEventStream methods returning a SmiEventStream and methods that - // are certain to never call to the server (most will, for in-proc back end). - - // Methods are commented with their corresponding TDS token - - // NOTE: Throwing from these methods will not usually produce the desired - // effect -- the managed to native boundary will eat any exceptions, - // and will cause a simple "Something bad happened" exception to be - // thrown in the native to managed boundary... - internal abstract class SmiEventSink - { - } -} - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiXetterAccessMap.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiXetterAccessMap.cs deleted file mode 100644 index 228e5eaf7d..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SmiXetterAccessMap.cs +++ /dev/null @@ -1,105 +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. - -using System.Diagnostics; - -namespace Microsoft.Data.SqlClient.Server -{ - // Formal encoding of SMI's metadata-to-ITypedSetter/-from-ITypedGetter validity rules - internal class SmiXetterAccessMap - { - // A couple of private constants to make the getter/setter access tables more readable - private const bool X = true; - private const bool _ = false; - - - private static bool[,] s_isSetterAccessValid = { - // Setters as columns (abbreviated from XetterTypeCode names) - // SqlDbTypes as rows - // Current difference between setters and getters is that character setters do - // not need to support SetBytes - // bool, byte, bytes, chars, strng, int16, int32, int64, singl, doubl, sqldec, date, guid, varmd, Xetr, time, dtost - /*BigInt*/ - { _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Binary*/ - { _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Bit*/ - { X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Char*/ - { _ , _ , _ , X , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*DTime*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , }, - /*Decimal*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , }, - /*Float*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , _ , }, - /*Image*/ - { _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Int*/ - { _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Money*/ - { _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*NChar*/ - { _ , _ , _ , X , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*NText*/ - { _ , _ , _ , X , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*NVarChar*/ - { _ , _ , _ , X , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Real*/ - { _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*UniqueId*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , }, - /*SmDTime*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , }, - /*SmInt*/ - { _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*SmMoney*/ - { _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Text*/ - { _ , _ , _ , X , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Tstamp*/ - { _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*TinyInt*/ - { _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*VarBin*/ - { _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*VarChar*/ - { _ , _ , _ , X , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Variant*/ - { X , X , X , X , X , X , X , X , X , X , X , X , X , X , _ , X , X , }, - /* 24 */ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Xml*/ - { _ , _ , X , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /* 26 */ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /* 27 */ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /* 28 */ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Udt*/ - { _ , _ , X , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , }, - /*Struct*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , }, - /*Date*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , }, - /*Time*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , }, - /*DTime2*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , _ , _ , _ , _ , _ , }, - /*DTOffset*/ - { _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ , X , }, - }; - - - internal static bool IsSetterAccessValid(SmiMetaData metaData, SmiXetterTypeCode xetterType) - { - // Make sure no-one adds a new xetter type without updating this file! - Debug.Assert(SmiXetterTypeCode.XetBoolean <= xetterType && SmiXetterTypeCode.XetDateTimeOffset >= xetterType && - SmiXetterTypeCode.GetVariantMetaData != xetterType); - - return s_isSetterAccessValid[(int)metaData.SqlDbType, (int)xetterType]; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs deleted file mode 100644 index bd939993aa..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs +++ /dev/null @@ -1,804 +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. - -using System; -using System.Data; -using System.Data.SqlTypes; -using System.Diagnostics; -using Microsoft.Data.Common; -using Microsoft.Data.ProviderBase; - -namespace Microsoft.Data.SqlClient.Server -{ - /// - public class SqlDataRecord : IDataRecord - { - private SmiRecordBuffer _recordBuffer; - private SmiExtendedMetaData[] _columnSmiMetaData; - private SmiEventSink_Default _eventSink; - private SqlMetaData[] _columnMetaData; - private FieldNameLookup _fieldNameLookup; - private bool _usesStringStorageForXml; - - private static readonly SmiMetaData s_maxNVarCharForXml = new SmiMetaData(SqlDbType.NVarChar, SmiMetaData.UnlimitedMaxLengthIndicator, - SmiMetaData.DefaultNVarChar_NoCollation.Precision, - SmiMetaData.DefaultNVarChar_NoCollation.Scale, - SmiMetaData.DefaultNVarChar.LocaleId, - SmiMetaData.DefaultNVarChar.CompareOptions, - null); - - /// - public virtual int FieldCount - { - get - { - EnsureSubclassOverride(); - return _columnMetaData.Length; - } - } - - /// - public virtual string GetName(int ordinal) - { - EnsureSubclassOverride(); - return GetSqlMetaData(ordinal).Name; - } - - /// - public virtual string GetDataTypeName(int ordinal) - { - EnsureSubclassOverride(); - SqlMetaData metaData = GetSqlMetaData(ordinal); - if (SqlDbType.Udt == metaData.SqlDbType) - { - return metaData.UdtTypeName; - } - else - { - return MetaType.GetMetaTypeFromSqlDbType(metaData.SqlDbType, false).TypeName; - } - } - - /// - public virtual Type GetFieldType(int ordinal) - { - EnsureSubclassOverride(); - { - SqlMetaData md = GetSqlMetaData(ordinal); - return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).ClassType; - } - } - - /// - public virtual object GetValue(int ordinal) - { - EnsureSubclassOverride(); - SmiMetaData metaData = GetSmiMetaData(ordinal); - - return ValueUtilsSmi.GetValue200( - _eventSink, - _recordBuffer, - ordinal, - metaData - ); - } - - /// - public virtual int GetValues(object[] values) - { - EnsureSubclassOverride(); - if (null == values) - { - throw ADP.ArgumentNull(nameof(values)); - } - - int copyLength = (values.Length < FieldCount) ? values.Length : FieldCount; - for (int i = 0; i < copyLength; i++) - { - values[i] = GetValue(i); - } - - return copyLength; - } - - /// - public virtual int GetOrdinal(string name) - { - EnsureSubclassOverride(); - if (null == _fieldNameLookup) - { - string[] names = new string[FieldCount]; - for (int i = 0; i < names.Length; i++) - { - names[i] = GetSqlMetaData(i).Name; - } - - _fieldNameLookup = new FieldNameLookup(names, -1); - } - - return _fieldNameLookup.GetOrdinal(name); - } - - /// - public virtual object this[int ordinal] - { - get - { - EnsureSubclassOverride(); - return GetValue(ordinal); - } - } - - /// - public virtual object this[string name] - { - get - { - EnsureSubclassOverride(); - return GetValue(GetOrdinal(name)); - } - } - - /// - public virtual bool GetBoolean(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual byte GetByte(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length, true); - } - - /// - public virtual char GetChar(int ordinal) - { - EnsureSubclassOverride(); - throw ADP.NotSupported(); - } - - /// - public virtual long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } - - /// - public virtual Guid GetGuid(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual short GetInt16(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual int GetInt32(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual long GetInt64(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual float GetFloat(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual double GetDouble(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual string GetString(int ordinal) - { - EnsureSubclassOverride(); - SmiMetaData colMeta = GetSmiMetaData(ordinal); - if (_usesStringStorageForXml && SqlDbType.Xml == colMeta.SqlDbType) - { - return ValueUtilsSmi.GetString(_eventSink, _recordBuffer, ordinal, s_maxNVarCharForXml); - } - else - { - return ValueUtilsSmi.GetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - } - - /// - public virtual decimal GetDecimal(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual DateTime GetDateTime(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual DateTimeOffset GetDateTimeOffset(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual TimeSpan GetTimeSpan(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual bool IsDBNull(int ordinal) - { - EnsureSubclassOverride(); - ThrowIfInvalidOrdinal(ordinal); - return ValueUtilsSmi.IsDBNull(_eventSink, _recordBuffer, ordinal); - } - - /// - // ISqlRecord implementation - public virtual SqlMetaData GetSqlMetaData(int ordinal) - { - EnsureSubclassOverride(); - return _columnMetaData[ordinal]; - } - - /// - public virtual Type GetSqlFieldType(int ordinal) - { - EnsureSubclassOverride(); - SqlMetaData md = GetSqlMetaData(ordinal); - return MetaType.GetMetaTypeFromSqlDbType(md.SqlDbType, false).SqlType; - } - - /// - public virtual object GetSqlValue(int ordinal) - { - EnsureSubclassOverride(); - SmiMetaData metaData = GetSmiMetaData(ordinal); - return ValueUtilsSmi.GetSqlValue200(_eventSink, _recordBuffer, ordinal, metaData); - } - - /// - public virtual int GetSqlValues(object[] values) - { - EnsureSubclassOverride(); - if (null == values) - { - throw ADP.ArgumentNull(nameof(values)); - } - - - int copyLength = (values.Length < FieldCount) ? values.Length : FieldCount; - for (int i = 0; i < copyLength; i++) - { - values[i] = GetSqlValue(i); - } - - return copyLength; - } - - /// - public virtual SqlBinary GetSqlBinary(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlBytes GetSqlBytes(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlXml GetSqlXml(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlBoolean GetSqlBoolean(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlByte GetSqlByte(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlChars GetSqlChars(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlInt16 GetSqlInt16(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlInt32 GetSqlInt32(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlInt64 GetSqlInt64(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlSingle GetSqlSingle(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlDouble GetSqlDouble(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlMoney GetSqlMoney(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlDateTime GetSqlDateTime(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlDecimal GetSqlDecimal(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlString GetSqlString(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - public virtual SqlGuid GetSqlGuid(int ordinal) - { - EnsureSubclassOverride(); - return ValueUtilsSmi.GetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); - } - - /// - // ISqlUpdateableRecord Implementation - public virtual int SetValues(params object[] values) - { - EnsureSubclassOverride(); - if (null == values) - { - throw ADP.ArgumentNull(nameof(values)); - } - - // Allow values array longer than FieldCount, just ignore the extra cells. - int copyLength = (values.Length > FieldCount) ? FieldCount : values.Length; - - ExtendedClrTypeCode[] typeCodes = new ExtendedClrTypeCode[copyLength]; - - // Verify all data values as acceptable before changing current state. - for (int i = 0; i < copyLength; i++) - { - SqlMetaData metaData = GetSqlMetaData(i); - typeCodes[i] = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, values[i], metaData.Type); - if (ExtendedClrTypeCode.Invalid == typeCodes[i]) - { - throw ADP.InvalidCast(); - } - } - - // Now move the data (it'll only throw if someone plays with the values array between - // the validation loop and here, or if an invalid UDT was sent). - for (int i = 0; i < copyLength; i++) - { - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], 0, 0, null); - } - - return copyLength; - } - - /// - public virtual void SetValue(int ordinal, object value) - { - EnsureSubclassOverride(); - SqlMetaData metaData = GetSqlMetaData(ordinal); - ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCodeForUseWithSqlDbType( - metaData.SqlDbType, false /* isMultiValued */, value, metaData.Type); - if (ExtendedClrTypeCode.Invalid == typeCode) - { - throw ADP.InvalidCast(); - } - - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, 0, 0, null); - } - - /// - public virtual void SetBoolean(int ordinal, bool value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetByte(int ordinal, byte value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } - - /// - public virtual void SetChar(int ordinal, char value) - { - EnsureSubclassOverride(); - throw ADP.NotSupported(); - } - - /// - public virtual void SetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), fieldOffset, buffer, bufferOffset, length); - } - - /// - public virtual void SetInt16(int ordinal, short value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetInt32(int ordinal, int value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetInt64(int ordinal, long value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetFloat(int ordinal, float value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - /// - public virtual void SetDouble(int ordinal, double value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetString(int ordinal, string value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - /// - public virtual void SetDecimal(int ordinal, decimal value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetDateTime(int ordinal, DateTime value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetTimeSpan(int ordinal, TimeSpan value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetTimeSpan(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetDateTimeOffset(int ordinal, DateTimeOffset value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDateTimeOffset(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetDBNull(int ordinal) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetDBNull(_eventSink, _recordBuffer, ordinal, true); - } - - /// - public virtual void SetGuid(int ordinal, Guid value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlBoolean(int ordinal, SqlBoolean value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBoolean(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlByte(int ordinal, SqlByte value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlByte(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlInt16(int ordinal, SqlInt16 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt16(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlInt32(int ordinal, SqlInt32 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt32(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlInt64(int ordinal, SqlInt64 value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlInt64(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlSingle(int ordinal, SqlSingle value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlSingle(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlDouble(int ordinal, SqlDouble value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDouble(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlMoney(int ordinal, SqlMoney value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlMoney(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlDateTime(int ordinal, SqlDateTime value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDateTime(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlXml(int ordinal, SqlXml value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlDecimal(int ordinal, SqlDecimal value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlDecimal(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlString(int ordinal, SqlString value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlString(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlBinary(int ordinal, SqlBinary value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBinary(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlGuid(int ordinal, SqlGuid value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlGuid(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlChars(int ordinal, SqlChars value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - /// - public virtual void SetSqlBytes(int ordinal, SqlBytes value) - { - EnsureSubclassOverride(); - ValueUtilsSmi.SetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value); - } - - // SqlDataRecord public API - /// - public SqlDataRecord(params SqlMetaData[] metaData) - { - // Initial consistency check - if (null == metaData) - { - throw ADP.ArgumentNull(nameof(metaData)); - } - - _columnMetaData = new SqlMetaData[metaData.Length]; - _columnSmiMetaData = new SmiExtendedMetaData[metaData.Length]; - for (int i = 0; i < _columnSmiMetaData.Length; i++) - { - if (null == metaData[i]) - { - throw ADP.ArgumentNull($"{nameof(metaData)}[{i}]"); - } - _columnMetaData[i] = metaData[i]; - _columnSmiMetaData[i] = MetaDataUtilsSmi.SqlMetaDataToSmiExtendedMetaData(_columnMetaData[i]); - } - - _eventSink = new SmiEventSink_Default(); - - _recordBuffer = new MemoryRecordBuffer(_columnSmiMetaData); - _usesStringStorageForXml = true; - _eventSink.ProcessMessagesAndThrow(); - } - - internal SqlDataRecord(SmiRecordBuffer recordBuffer, params SmiExtendedMetaData[] metaData) - { - Debug.Assert(null != recordBuffer, "invalid attempt to instantiate SqlDataRecord with null SmiRecordBuffer"); - Debug.Assert(null != metaData, "invalid attempt to instantiate SqlDataRecord with null SmiExtendedMetaData[]"); - - _columnMetaData = new SqlMetaData[metaData.Length]; - _columnSmiMetaData = new SmiExtendedMetaData[metaData.Length]; - for (int i = 0; i < _columnSmiMetaData.Length; i++) - { - _columnSmiMetaData[i] = metaData[i]; - _columnMetaData[i] = MetaDataUtilsSmi.SmiExtendedMetaDataToSqlMetaData(_columnSmiMetaData[i]); - } - - _eventSink = new SmiEventSink_Default(); - - _recordBuffer = recordBuffer; - _eventSink.ProcessMessagesAndThrow(); - } - - // - // SqlDataRecord private members - // - internal SmiRecordBuffer RecordBuffer - { // used by SqlPipe - get - { - return _recordBuffer; - } - } - - - internal SqlMetaData[] InternalGetMetaData() - { - return _columnMetaData; - } - - internal SmiExtendedMetaData[] InternalGetSmiMetaData() - { - return _columnSmiMetaData; - } - - internal SmiExtendedMetaData GetSmiMetaData(int ordinal) - { - return _columnSmiMetaData[ordinal]; - } - - internal void ThrowIfInvalidOrdinal(int ordinal) - { - if (0 > ordinal || FieldCount <= ordinal) - { - throw ADP.IndexOutOfRange(ordinal); - } - } - private void EnsureSubclassOverride() - { - if (null == _recordBuffer) - { - throw SQL.SubclassMustOverride(); - } - } - - /// - IDataReader System.Data.IDataRecord.GetData(int ordinal) - { - throw ADP.NotSupported(); - } - } -} - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs deleted file mode 100644 index bed6ada9e3..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SimulatorEnclaveProvider.NetCoreApp.cs +++ /dev/null @@ -1,115 +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. - -using System; -using System.Collections.Generic; -using System.Data.SqlClient; -using System.Diagnostics; -using System.Linq; -using System.Runtime.Caching; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Concurrent; - -namespace Microsoft.Data.SqlClient -{ - internal class SimulatorEnclaveProvider : EnclaveProviderBase - { - private static readonly int EnclaveSessionHandleSize = 8; - - // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. - // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. - internal override void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength) - { - GetEnclaveSessionHelper(enclaveSessionParameters, false, out sqlEnclaveSession, out counter, out customData, out customDataLength); - } - - // Gets the information that SqlClient subsequently uses to initiate the process of attesting the enclave and to establish a secure session with the enclave. - internal override SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength) - { - // The key derivation function and hash algorithm name are specified when key derivation is performed - ECDiffieHellman clientDHKey = ECDiffieHellman.Create(); - clientDHKey.KeySize = 384; - - return new SqlEnclaveAttestationParameters(2, new byte[] { }, clientDHKey); - } - - // When overridden in a derived class, performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHellman clientDHKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter) - { - ////for simulator: enclave does not send public key, and sends an empty attestation info - //// The only non-trivial content it sends is the session setup info (DH pubkey of enclave) - - sqlEnclaveSession = null; - counter = 0; - try - { - ThreadRetryCache.Remove(Thread.CurrentThread.ManagedThreadId.ToString()); - sqlEnclaveSession = GetEnclaveSessionFromCache(enclaveSessionParameters, out counter); - - if (sqlEnclaveSession == null) - { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl)) - { - ////Read AttestationInfo - int attestationInfoOffset = 0; - uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); - Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); - - ////read secure session info - uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - - byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); - attestationInfoOffset += EnclaveSessionHandleSize; - - uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); - - byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, - sizeOfTrustedModuleDHPublicKeyBufferInt); - attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; - - byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, - checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - - ECParameters ecParams = KeyConverter.ECCPublicKeyBlobToParams(trustedModuleDHPublicKey); - ECDiffieHellman enclaveDHKey = ECDiffieHellman.Create(ecParams); - byte[] sharedSecret = clientDHKey.DeriveKeyFromHash(enclaveDHKey.PublicKey, HashAlgorithmName.SHA256); - long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); - } - else - { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); - } - } - } - finally - { - UpdateEnclaveSessionLockStatus(sqlEnclaveSession); - } - } - - /// - /// When overridden in a derived class, looks up and evicts an enclave session from the enclave session cache, if the provider implements session caching. - /// - /// The set of parameters required for enclave session. - /// The session to be invalidated. - internal override void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSessionToInvalidate) - { - InvalidateEnclaveSessionHelper(enclaveSessionParameters, enclaveSessionToInvalidate); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAppContextSwitchManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAppContextSwitchManager.NetCoreApp.cs new file mode 100644 index 0000000000..928e543e7d --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAppContextSwitchManager.NetCoreApp.cs @@ -0,0 +1,73 @@ +// 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.Configuration; +using System.Reflection; + +namespace Microsoft.Data.SqlClient +{ + /// + /// AppContext switch manager + /// + internal sealed class SqlAppContextSwitchManager + { + private const string TypeName = nameof(SqlAppContextSwitchManager); + /// + /// To support the AppContext's set switch through the config file for .NET Core; + /// .Net Framework supports it internally through the configuration file by 'AppContextSwitchOverrides' element under 'runtime' section + /// + internal static void ApplyContextSwitches(IAppContextSwitchOverridesSection appContextSwitches) + { + string methodName = MethodBase.GetCurrentMethod().Name; + SqlClientEventSource.Log.TryTraceEvent(" Entry point.", TypeName, methodName); + if (appContextSwitches != null) + { + ApplySwitchValues(appContextSwitches.Value?.Split('=', ';')); + } + + SqlClientEventSource.Log.TryTraceEvent(" Exit point.", TypeName, methodName); + } + + private static bool ApplySwitchValues(string[] switches) + { + string methodName = MethodBase.GetCurrentMethod().Name; + SqlClientEventSource.Log.TryTraceEvent(" Entry point.", TypeName, methodName); + + if (switches == null || switches.Length == 0 || switches.Length % 2 == 1) + { return false; } + + for (int i = 0; i < switches.Length / 2; i++) + { + try + { + AppContext.SetSwitch(switches[i], Convert.ToBoolean(switches[i + 1])); + SqlClientEventSource.Log.TryTraceEvent(" Successfully assigned the AppContext switch '{2}'={3}.", + TypeName, methodName, switches[i], switches[i + 1]); + } + catch (Exception e) + { + throw new ArgumentException(StringsHelper.GetString(Strings.SqlAppContextSwitchManager_InvalidValue, switches[i], switches[i + 1]), e); + } + } + SqlClientEventSource.Log.TryTraceEvent(" Exit point.", TypeName, methodName); + return true; + } + } + + /// + /// The configuration section definition for reading a configuration file. + /// + internal sealed class AppContextSwitchOverridesSection : ConfigurationSection, IAppContextSwitchOverridesSection + { + public const string Name = "AppContextSwitchOverrides"; + + [ConfigurationProperty("value", IsRequired = true)] + public string Value + { + get => this["value"] as string; + set => this["value"] = value; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs index bf44f23184..b204c8df81 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetCoreApp.cs @@ -15,7 +15,6 @@ internal partial class SqlAuthenticationProviderManager static SqlAuthenticationProviderManager() { - var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider(); SqlAuthenticationProviderConfigurationSection configurationSection = null; try @@ -35,14 +34,7 @@ static SqlAuthenticationProviderManager() } Instance = new SqlAuthenticationProviderManager(configurationSection); - var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider); + SetDefaultAuthProviders(Instance); } /// @@ -160,6 +152,8 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe return SqlAuthenticationMethod.ActiveDirectoryManagedIdentity; case ActiveDirectoryMSI: return SqlAuthenticationMethod.ActiveDirectoryMSI; + case ActiveDirectoryDefault: + return SqlAuthenticationMethod.ActiveDirectoryDefault; default: throw SQL.UnsupportedAuthentication(authentication); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs index 71b8b23269..01a84342f8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.NetStandard.cs @@ -8,17 +8,8 @@ internal partial class SqlAuthenticationProviderManager { static SqlAuthenticationProviderManager() { - var azureManagedIdentityAuthenticationProvider = new AzureManagedIdentityAuthenticationProvider(); - Instance = new SqlAuthenticationProviderManager(); - var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(Instance._applicationClientId); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, azureManagedIdentityAuthenticationProvider); - Instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, azureManagedIdentityAuthenticationProvider); + SetDefaultAuthProviders(Instance); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs index f55bb9d8e4..4c101d30df 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs @@ -21,6 +21,7 @@ internal partial class SqlAuthenticationProviderManager private const string ActiveDirectoryDeviceCodeFlow = "active directory device code flow"; private const string ActiveDirectoryManagedIdentity = "active directory managed identity"; private const string ActiveDirectoryMSI = "active directory msi"; + private const string ActiveDirectoryDefault = "active directory default"; private readonly string _typeName; private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider; @@ -30,6 +31,25 @@ internal partial class SqlAuthenticationProviderManager public static readonly SqlAuthenticationProviderManager Instance; + /// + /// Sets default supported Active Directory Authentication providers by the driver + /// on the SqlAuthenticationProviderManager instance. + /// + private static void SetDefaultAuthProviders(SqlAuthenticationProviderManager instance) + { + if (instance != null) + { + var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(instance._applicationClientId); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider); + instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, activeDirectoryAuthProvider); + } + } /// /// Constructor. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 344d517618..d469427274 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -9,6 +9,7 @@ using System.Data.Common; using System.Data.SqlTypes; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -21,8 +22,8 @@ namespace Microsoft.Data.SqlClient // with ColumnOrdinals from the source. internal sealed class _ColumnMapping { - internal int _sourceColumnOrdinal; - internal _SqlMetaData _metadata; + internal readonly int _sourceColumnOrdinal; + internal readonly _SqlMetaData _metadata; internal _ColumnMapping(int columnId, _SqlMetaData metadata) { @@ -33,28 +34,16 @@ internal _ColumnMapping(int columnId, _SqlMetaData metadata) internal sealed class Row { - private object[] _dataFields; + private readonly object[] _dataFields; internal Row(int rowCount) { _dataFields = new object[rowCount]; } - internal object[] DataFields - { - get - { - return _dataFields; - } - } + internal object[] DataFields => _dataFields; - internal object this[int index] - { - get - { - return _dataFields[index]; - } - } + internal object this[int index] => _dataFields[index]; } // The controlling class for one result (metadata + rows) @@ -75,10 +64,7 @@ internal Result(_SqlMetaDataSet metadata) internal Row this[int index] => _rowset[index]; - internal void AddRow(Row row) - { - _rowset.Add(row); - } + internal void AddRow(Row row) => _rowset.Add(row); } // A wrapper object for metadata and rowsets returned by our initial queries @@ -93,7 +79,6 @@ internal BulkCopySimpleResultSet() _results = new List(); } - // Indexer internal Result this[int idx] => _results[idx]; // Callback function for the tdsparser @@ -177,10 +162,13 @@ public SourceColumnMetadata(ValueMethod method, bool isSqlType, bool isDataFeed) private const int DefaultCommandTimeout = 30; + /// + public event SqlRowsCopiedEventHandler SqlRowsCopied; + private bool _enableStreaming = false; private int _batchSize; - private bool _ownConnection; - private SqlBulkCopyOptions _copyOptions; + private readonly bool _ownConnection; + private readonly SqlBulkCopyOptions _copyOptions; private int _timeout = DefaultCommandTimeout; private string _destinationTableName; private int _rowsCopied; @@ -189,8 +177,8 @@ public SourceColumnMetadata(ValueMethod method, bool isSqlType, bool isDataFeed) private bool _insideRowsCopiedEvent; private object _rowSource; - private SqlDataReader _SqlDataReaderRowSource; - private DbDataReader _DbDataReaderRowSource; + private SqlDataReader _sqlDataReaderRowSource; + private DbDataReader _dbDataReaderRowSource; private DataTable _dataTableSource; private SqlBulkCopyColumnMappingCollection _columnMappings; @@ -234,8 +222,6 @@ private int RowNumber private TdsParserStateObject _stateObj; private List<_ColumnMapping> _sortedColumnMappings; - private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; - private static int _objectTypeCount; // EventSource Counter internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); @@ -249,17 +235,11 @@ private int RowNumber private SourceColumnMetadata[] _currentRowMetadata; #if DEBUG - internal static bool _setAlwaysTaskOnWrite = false; //when set and in DEBUG mode, TdsParser::WriteBulkCopyValue will always return a task + internal static bool s_setAlwaysTaskOnWrite; //when set and in DEBUG mode, TdsParser::WriteBulkCopyValue will always return a task internal static bool SetAlwaysTaskOnWrite { - set - { - _setAlwaysTaskOnWrite = value; - } - get - { - return _setAlwaysTaskOnWrite; - } + set => s_setAlwaysTaskOnWrite = value; + get => s_setAlwaysTaskOnWrite; } #endif @@ -314,10 +294,7 @@ public SqlBulkCopy(string connectionString, SqlBulkCopyOptions copyOptions) /// public int BatchSize { - get - { - return _batchSize; - } + get => _batchSize; set { if (value >= 0) @@ -334,10 +311,7 @@ public int BatchSize /// public int BulkCopyTimeout { - get - { - return _timeout; - } + get => _timeout; set { if (value < 0) @@ -351,24 +325,12 @@ public int BulkCopyTimeout /// public bool EnableStreaming { - get - { - return _enableStreaming; - } - set - { - _enableStreaming = value; - } + get => _enableStreaming; + set => _enableStreaming = value; } /// - public SqlBulkCopyColumnMappingCollection ColumnMappings - { - get - { - return _columnMappings; - } - } + public SqlBulkCopyColumnMappingCollection ColumnMappings => _columnMappings; /// public SqlBulkCopyColumnOrderHintCollection ColumnOrderHints @@ -379,10 +341,7 @@ public SqlBulkCopyColumnOrderHintCollection ColumnOrderHints /// public string DestinationTableName { - get - { - return _destinationTableName; - } + get => _destinationTableName; set { if (value == null) @@ -400,10 +359,7 @@ public string DestinationTableName /// public int NotifyAfter { - get - { - return _notifyAfter; - } + get => _notifyAfter; set { if (value >= 0) @@ -417,35 +373,10 @@ public int NotifyAfter } } - internal int ObjectID - { - get - { - return _objectID; - } - } - - /// - public event SqlRowsCopiedEventHandler SqlRowsCopied - { - add - { - _rowsCopiedEventHandler += value; - } - remove - { - _rowsCopiedEventHandler -= value; - } - } + internal int ObjectID => _objectID; /// - public int RowsCopied - { - get - { - return _rowsCopied; - } - } + public int RowsCopied => _rowsCopied; internal SqlStatistics Statistics { @@ -464,7 +395,7 @@ internal SqlStatistics Statistics void IDisposable.Dispose() { - this.Dispose(true); + Dispose(true); GC.SuppressFinalize(this); } @@ -476,23 +407,22 @@ private string CreateInitialQuery() string[] parts; try { - parts = MultipartIdentifier.ParseMultipartIdentifier(this.DestinationTableName, "[\"", "]\"", Strings.SQL_BulkCopyDestinationTableName, true); + parts = MultipartIdentifier.ParseMultipartIdentifier(DestinationTableName, "[\"", "]\"", Strings.SQL_BulkCopyDestinationTableName, true); } catch (Exception e) { - throw SQL.BulkLoadInvalidDestinationTable(this.DestinationTableName, e); + throw SQL.BulkLoadInvalidDestinationTable(DestinationTableName, e); } if (string.IsNullOrEmpty(parts[MultipartIdentifier.TableIndex])) { - throw SQL.BulkLoadInvalidDestinationTable(this.DestinationTableName, null); + throw SQL.BulkLoadInvalidDestinationTable(DestinationTableName, null); } string TDSCommand; - TDSCommand = "select @@trancount; SET FMTONLY ON select * from " + ADP.BuildMultiPartName(parts) + " SET FMTONLY OFF "; string TableCollationsStoredProc; - if (_connection.IsKatmaiOrNewer) + if (_connection.Is2008OrNewer) { TableCollationsStoredProc = "sp_tablecollations_100"; } @@ -553,9 +483,9 @@ private string CreateInitialQuery() private Task CreateAndExecuteInitialQueryAsync(out BulkCopySimpleResultSet result) { string TDSCommand = CreateInitialQuery(); - SqlClientEventSource.Log.TryTraceEvent(" Initial Query: '{0}'", TDSCommand); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - Task executeTask = _parser.TdsExecuteSQLBatch(TDSCommand, this.BulkCopyTimeout, null, _stateObj, sync: !_isAsyncBulkCopy, callerHasConnectionLock: true); + SqlClientEventSource.Log.TryTraceEvent("SqlBulkCopy.CreateAndExecuteInitialQueryAsync | Info | Initial Query: '{0}'", TDSCommand); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlBulkCopy.CreateAndExecuteInitialQueryAsync | Info | Correlation | Object Id {0}, Activity Id {1}", ObjectID, ActivityCorrelator.Current); + Task executeTask = _parser.TdsExecuteSQLBatch(TDSCommand, BulkCopyTimeout, null, _stateObj, sync: !_isAsyncBulkCopy, callerHasConnectionLock: true); if (executeTask == null) { @@ -597,7 +527,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i throw SQL.BulkLoadNoCollation(); } - string[] parts = MultipartIdentifier.ParseMultipartIdentifier(this.DestinationTableName, "[\"", "]\"", Strings.SQL_BulkCopyDestinationTableName, true); + string[] parts = MultipartIdentifier.ParseMultipartIdentifier(DestinationTableName, "[\"", "]\"", Strings.SQL_BulkCopyDestinationTableName, true); updateBulkCommandText.AppendFormat("insert bulk {0} (", ADP.BuildMultiPartName(parts)); int nmatched = 0; // Number of columns that match and are accepted int nrejected = 0; // Number of columns that match but were rejected @@ -750,15 +680,15 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i { updateBulkCommandText.Append(" COLLATE " + collation_name.Value); // Compare collations only if the collation value was set on the metadata - if (null != _SqlDataReaderRowSource && metadata.collation != null) + if (null != _sqlDataReaderRowSource && metadata.collation != null) { // On SqlDataReader we can verify the sourcecolumn collation! int sourceColumnId = _localColumnMappings[assocId]._internalSourceColumnOrdinal; int destinationLcid = metadata.collation.LCID; - int sourceLcid = _SqlDataReaderRowSource.GetLocaleId(sourceColumnId); + int sourceLcid = _sqlDataReaderRowSource.GetLocaleId(sourceColumnId); if (sourceLcid != destinationLcid) { - throw SQL.BulkLoadLcidMismatch(sourceLcid, _SqlDataReaderRowSource.GetName(sourceColumnId), destinationLcid, metadata.column); + throw SQL.BulkLoadLcidMismatch(sourceLcid, _sqlDataReaderRowSource.GetName(sourceColumnId), destinationLcid, metadata.column); } } } @@ -845,15 +775,15 @@ private string TryGetOrderHintText(HashSet destColumnNames) } } - orderHintText.Length = orderHintText.Length - 2; + orderHintText.Length -= 2; orderHintText.Append(")"); return orderHintText.ToString(); } private Task SubmitUpdateBulkCommand(string TDSCommand) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID{0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - Task executeTask = _parser.TdsExecuteSQLBatch(TDSCommand, this.BulkCopyTimeout, null, _stateObj, sync: !_isAsyncBulkCopy, callerHasConnectionLock: true); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlBulkCopy.SubmitUpdateBulkCommand | Info | Correlation | Object Id {0}, Activity Id {1}", ObjectID, ActivityCorrelator.Current); + Task executeTask = _parser.TdsExecuteSQLBatch(TDSCommand, BulkCopyTimeout, null, _stateObj, sync: !_isAsyncBulkCopy, callerHasConnectionLock: true); if (executeTask == null) { @@ -881,7 +811,7 @@ private Task SubmitUpdateBulkCommand(string TDSCommand) // Starts writing the Bulkcopy data stream private void WriteMetaData(BulkCopySimpleResultSet internalResults) { - _stateObj.SetTimeoutSeconds(this.BulkCopyTimeout); + _stateObj.SetTimeoutSeconds(BulkCopyTimeout); _SqlMetaDataSet metadataCollection = internalResults[MetaDataResultId].MetaData; _stateObj._outputMessageType = TdsEnums.MT_BULK; @@ -956,7 +886,7 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b // Handle data feeds (common for both DbDataReader and SqlDataReader) if (_currentRowMetadata[destRowIndex].IsDataFeed) { - if (_DbDataReaderRowSource.IsDBNull(sourceOrdinal)) + if (_dbDataReaderRowSource.IsDBNull(sourceOrdinal)) { isSqlType = false; isDataFeed = false; @@ -971,25 +901,25 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b switch (_currentRowMetadata[destRowIndex].Method) { case ValueMethod.DataFeedStream: - return new StreamDataFeed(_DbDataReaderRowSource.GetStream(sourceOrdinal)); + return new StreamDataFeed(_dbDataReaderRowSource.GetStream(sourceOrdinal)); case ValueMethod.DataFeedText: - return new TextDataFeed(_DbDataReaderRowSource.GetTextReader(sourceOrdinal)); + return new TextDataFeed(_dbDataReaderRowSource.GetTextReader(sourceOrdinal)); case ValueMethod.DataFeedXml: // Only SqlDataReader supports an XmlReader // There is no GetXmlReader on DbDataReader, however if GetValue returns XmlReader we will read it as stream if it is assigned to XML field - Debug.Assert(_SqlDataReaderRowSource != null, "Should not be reading row as an XmlReader if bulk copy source is not a SqlDataReader"); - return new XmlDataFeed(_SqlDataReaderRowSource.GetXmlReader(sourceOrdinal)); + Debug.Assert(_sqlDataReaderRowSource != null, "Should not be reading row as an XmlReader if bulk copy source is not a SqlDataReader"); + return new XmlDataFeed(_sqlDataReaderRowSource.GetXmlReader(sourceOrdinal)); default: Debug.Fail($"Current column is marked as being a DataFeed, but no DataFeed compatible method was provided. Method: {_currentRowMetadata[destRowIndex].Method}"); isDataFeed = false; - object columnValue = _DbDataReaderRowSource.GetValue(sourceOrdinal); + object columnValue = _dbDataReaderRowSource.GetValue(sourceOrdinal); ADP.IsNullOrSqlType(columnValue, out isNull, out isSqlType); return columnValue; } } } // SqlDataReader-specific logic - else if (null != _SqlDataReaderRowSource) + else if (null != _sqlDataReaderRowSource) { if (_currentRowMetadata[destRowIndex].IsSqlType) { @@ -999,19 +929,19 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b switch (_currentRowMetadata[destRowIndex].Method) { case ValueMethod.SqlTypeSqlDecimal: - value = _SqlDataReaderRowSource.GetSqlDecimal(sourceOrdinal); + value = _sqlDataReaderRowSource.GetSqlDecimal(sourceOrdinal); break; case ValueMethod.SqlTypeSqlDouble: // use cast to handle IsNull correctly because no public constructor allows it - value = (SqlDecimal)_SqlDataReaderRowSource.GetSqlDouble(sourceOrdinal); + value = (SqlDecimal)_sqlDataReaderRowSource.GetSqlDouble(sourceOrdinal); break; case ValueMethod.SqlTypeSqlSingle: // use cast to handle IsNull correctly because no public constructor allows it - value = (SqlDecimal)_SqlDataReaderRowSource.GetSqlSingle(sourceOrdinal); + value = (SqlDecimal)_sqlDataReaderRowSource.GetSqlSingle(sourceOrdinal); break; default: Debug.Fail($"Current column is marked as being a SqlType, but no SqlType compatible method was provided. Method: {_currentRowMetadata[destRowIndex].Method}"); - value = (INullable)_SqlDataReaderRowSource.GetSqlValue(sourceOrdinal); + value = (INullable)_sqlDataReaderRowSource.GetSqlValue(sourceOrdinal); break; } @@ -1023,7 +953,7 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b isSqlType = false; isDataFeed = false; - object value = _SqlDataReaderRowSource.GetValue(sourceOrdinal); + object value = _sqlDataReaderRowSource.GetValue(sourceOrdinal); isNull = ((value == null) || (value == DBNull.Value)); if ((!isNull) && (metadata.type == SqlDbType.Udt)) { @@ -1046,7 +976,7 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b IDataReader rowSourceAsIDataReader = (IDataReader)_rowSource; // Only use IsDbNull when streaming is enabled and only for non-SqlDataReader - if ((_enableStreaming) && (_SqlDataReaderRowSource == null) && (rowSourceAsIDataReader.IsDBNull(sourceOrdinal))) + if ((_enableStreaming) && (_sqlDataReaderRowSource == null) && (rowSourceAsIDataReader.IsDBNull(sourceOrdinal))) { isSqlType = false; isNull = true; @@ -1059,6 +989,7 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b return columnValue; } } + case ValueSourceType.DataTable: case ValueSourceType.RowArray: { @@ -1145,17 +1076,22 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b // "more" -- should be used by the caller only when the return value is null. private Task ReadFromRowSourceAsync(CancellationToken cts) { - if (_isAsyncBulkCopy && _DbDataReaderRowSource != null) + if (_isAsyncBulkCopy && _dbDataReaderRowSource != null) { // This will call ReadAsync for DbDataReader (for SqlDataReader it will be truly async read; for non-SqlDataReader it may block.) - return _DbDataReaderRowSource.ReadAsync(cts).ContinueWith((t) => - { - if (t.Status == TaskStatus.RanToCompletion) + return _dbDataReaderRowSource.ReadAsync(cts).ContinueWith( + static (Task task, object state) => { - _hasMoreRowToCopy = t.Result; - } - return t; - }, TaskScheduler.Default).Unwrap(); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (task.Status == TaskStatus.RanToCompletion) + { + sqlBulkCopy._hasMoreRowToCopy = task.Result; + } + return task; + }, + state: this, + scheduler: TaskScheduler.Default + ).Unwrap(); } else { // This will call Read for DataRows, DataTable and IDataReader (this includes all IDataReader except DbDataReader) @@ -1230,7 +1166,8 @@ private SourceColumnMetadata GetColumnMetadata(int ordinal) ValueMethod method; bool isSqlType; bool isDataFeed; - if (((_SqlDataReaderRowSource != null) || (_dataTableSource != null)) && ((metadata.metaType.NullableType == TdsEnums.SQLDECIMALN) || (metadata.metaType.NullableType == TdsEnums.SQLNUMERICN))) + + if (((_sqlDataReaderRowSource != null) || (_dataTableSource != null)) && ((metadata.metaType.NullableType == TdsEnums.SQLDECIMALN) || (metadata.metaType.NullableType == TdsEnums.SQLNUMERICN))) { isDataFeed = false; @@ -1239,7 +1176,7 @@ private SourceColumnMetadata GetColumnMetadata(int ordinal) { case ValueSourceType.DbDataReader: case ValueSourceType.IDataReader: - t = _SqlDataReaderRowSource.GetFieldType(sourceOrdinal); + t = _sqlDataReaderRowSource.GetFieldType(sourceOrdinal); break; case ValueSourceType.DataTable: case ValueSourceType.RowArray: @@ -1277,13 +1214,13 @@ private SourceColumnMetadata GetColumnMetadata(int ordinal) { isSqlType = false; - if (_SqlDataReaderRowSource != null) + if (_sqlDataReaderRowSource != null) { // MetaData property is not set for SMI, but since streaming is disabled we do not need it - MetaType mtSource = _SqlDataReaderRowSource.MetaData[sourceOrdinal].metaType; + MetaType mtSource = _sqlDataReaderRowSource.MetaData[sourceOrdinal].metaType; // There is no memory gain for non-sequential access for binary - if ((metadata.type == SqlDbType.VarBinary) && (mtSource.IsBinType) && (mtSource.SqlDbType != SqlDbType.Timestamp) && _SqlDataReaderRowSource.IsCommandBehavior(CommandBehavior.SequentialAccess)) + if ((metadata.type == SqlDbType.VarBinary) && (mtSource.IsBinType) && (mtSource.SqlDbType != SqlDbType.Timestamp) && _sqlDataReaderRowSource.IsCommandBehavior(CommandBehavior.SequentialAccess)) { isDataFeed = true; method = ValueMethod.DataFeedStream; @@ -1305,7 +1242,7 @@ private SourceColumnMetadata GetColumnMetadata(int ordinal) method = ValueMethod.GetValue; } } - else if (_DbDataReaderRowSource != null) + else if (_dbDataReaderRowSource != null) { if (metadata.type == SqlDbType.VarBinary) { @@ -1701,8 +1638,8 @@ public void WriteToServer(DbDataReader reader) { statistics = SqlStatistics.StartTimer(Statistics); _rowSource = reader; - _DbDataReaderRowSource = reader; - _SqlDataReaderRowSource = reader as SqlDataReader; + _dbDataReaderRowSource = reader; + _sqlDataReaderRowSource = reader as SqlDataReader; _dataTableSource = null; _rowSourceType = ValueSourceType.DbDataReader; @@ -1734,8 +1671,8 @@ public void WriteToServer(IDataReader reader) { statistics = SqlStatistics.StartTimer(Statistics); _rowSource = reader; - _SqlDataReaderRowSource = _rowSource as SqlDataReader; - _DbDataReaderRowSource = _rowSource as DbDataReader; + _sqlDataReaderRowSource = _rowSource as SqlDataReader; + _dbDataReaderRowSource = _rowSource as DbDataReader; _dataTableSource = null; _rowSourceType = ValueSourceType.IDataReader; _isAsyncBulkCopy = false; @@ -1770,7 +1707,7 @@ public void WriteToServer(DataTable table, DataRowState rowState) _rowStateToSkip = ((rowState == 0) || (rowState == DataRowState.Deleted)) ? DataRowState.Deleted : ~rowState | DataRowState.Deleted; _rowSource = table; _dataTableSource = table; - _SqlDataReaderRowSource = null; + _sqlDataReaderRowSource = null; _rowSourceType = ValueSourceType.DataTable; _rowEnumerator = table.Rows.GetEnumerator(); _isAsyncBulkCopy = false; @@ -1812,7 +1749,7 @@ public void WriteToServer(DataRow[] rows) _rowStateToSkip = DataRowState.Deleted; // Don't allow deleted rows _rowSource = rows; _dataTableSource = table; - _SqlDataReaderRowSource = null; + _sqlDataReaderRowSource = null; _rowSourceType = ValueSourceType.RowArray; _rowEnumerator = rows.GetEnumerator(); _isAsyncBulkCopy = false; @@ -1860,7 +1797,7 @@ public Task WriteToServerAsync(DataRow[] rows, CancellationToken cancellationTok _rowStateToSkip = DataRowState.Deleted; // Don't allow deleted rows _rowSource = rows; _dataTableSource = table; - _SqlDataReaderRowSource = null; + _sqlDataReaderRowSource = null; _rowSourceType = ValueSourceType.RowArray; _rowEnumerator = rows.GetEnumerator(); _isAsyncBulkCopy = true; @@ -1895,8 +1832,8 @@ public Task WriteToServerAsync(DbDataReader reader, CancellationToken cancellati { statistics = SqlStatistics.StartTimer(Statistics); _rowSource = reader; - _SqlDataReaderRowSource = reader as SqlDataReader; - _DbDataReaderRowSource = reader; + _sqlDataReaderRowSource = reader as SqlDataReader; + _dbDataReaderRowSource = reader; _dataTableSource = null; _rowSourceType = ValueSourceType.DbDataReader; _isAsyncBulkCopy = true; @@ -1932,8 +1869,8 @@ public Task WriteToServerAsync(IDataReader reader, CancellationToken cancellatio { statistics = SqlStatistics.StartTimer(Statistics); _rowSource = reader; - _SqlDataReaderRowSource = _rowSource as SqlDataReader; - _DbDataReaderRowSource = _rowSource as DbDataReader; + _sqlDataReaderRowSource = _rowSource as SqlDataReader; + _dbDataReaderRowSource = _rowSource as DbDataReader; _dataTableSource = null; _rowSourceType = ValueSourceType.IDataReader; _isAsyncBulkCopy = true; @@ -1976,7 +1913,7 @@ public Task WriteToServerAsync(DataTable table, DataRowState rowState, Cancellat statistics = SqlStatistics.StartTimer(Statistics); _rowStateToSkip = ((rowState == 0) || (rowState == DataRowState.Deleted)) ? DataRowState.Deleted : ~rowState | DataRowState.Deleted; _rowSource = table; - _SqlDataReaderRowSource = null; + _sqlDataReaderRowSource = null; _dataTableSource = table; _rowSourceType = ValueSourceType.DataTable; _rowEnumerator = table.Rows.GetEnumerator(); @@ -2009,7 +1946,7 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok { AsyncHelper.ContinueTaskWithState(writeTask, tcs, state: tcs, - onSuccess: state => ((TaskCompletionSource)state).SetResult(null) + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); } }, ctoken); // We do not need to propagate exception, etc, from reconnect task, we just need to wait for it to finish. @@ -2017,7 +1954,7 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok } else { - AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, () => { throw SQL.CR_ReconnectTimeout(); }, rethrowExceptions: false); + AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, static () => throw SQL.CR_ReconnectTimeout(), rethrowExceptions: false); } } @@ -2038,27 +1975,32 @@ private Task WriteRowSourceToServerAsync(int columnCount, CancellationToken ctok if (resultTask != null) { finishedSynchronously = false; - return resultTask.ContinueWith((t) => - { - try + return resultTask.ContinueWith( + static (Task t, object state) => { - AbortTransaction(); // If there is one, on success transactions will be committed. - } - finally - { - _isBulkCopyingInProgress = false; - if (_parser != null) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + try { - _parser._asyncWrite = false; + sqlBulkCopy.AbortTransaction(); // If there is one, on success transactions will be committed. } - if (_parserLock != null) + finally { - _parserLock.Release(); - _parserLock = null; + sqlBulkCopy._isBulkCopyingInProgress = false; + if (sqlBulkCopy._parser != null) + { + sqlBulkCopy._parser._asyncWrite = false; + } + if (sqlBulkCopy._parserLock != null) + { + sqlBulkCopy._parserLock.Release(); + sqlBulkCopy._parserLock = null; + } } - } - return t; - }, TaskScheduler.Default).Unwrap(); + return t; + }, + state: this, + scheduler: TaskScheduler.Default + ).Unwrap(); } return null; } @@ -2159,14 +2101,13 @@ private void WriteRowSourceToServerCommon(int columnCount) } catch (IndexOutOfRangeException e) { - throw (SQL.BulkLoadNonMatchingColumnName(unquotedColumnName, e)); + throw SQL.BulkLoadNonMatchingColumnName(unquotedColumnName, e); } break; } - if (index == -1) { - throw (SQL.BulkLoadNonMatchingColumnName(unquotedColumnName)); + throw SQL.BulkLoadNonMatchingColumnName(unquotedColumnName); } bulkCopyColumn._internalSourceColumnOrdinal = index; } @@ -2184,15 +2125,6 @@ internal void OnConnectionClosed() } } - private void OnRowsCopied(SqlRowsCopiedEventArgs value) - { - SqlRowsCopiedEventHandler handler = _rowsCopiedEventHandler; - if (handler != null) - { - handler(this, value); - } - } - private bool FireRowsCopiedEvent(long rowsCopied) { // Release lock to prevent possible deadlocks @@ -2204,7 +2136,7 @@ private bool FireRowsCopiedEvent(long rowsCopied) try { _insideRowsCopiedEvent = true; - this.OnRowsCopied(eventArgs); + SqlRowsCopied?.Invoke(this, eventArgs); } finally { @@ -2252,9 +2184,9 @@ private Task ReadWriteColumnValueAsync(int col) // Target type shouldn't be encrypted Debug.Assert(!metadata.isEncrypted, "Can't encrypt SQL Variant type"); SqlBuffer.StorageType variantInternalType = SqlBuffer.StorageType.Empty; - if ((_SqlDataReaderRowSource != null) && (_connection.IsKatmaiOrNewer)) + if ((_sqlDataReaderRowSource != null) && (_connection.Is2008OrNewer)) { - variantInternalType = _SqlDataReaderRowSource.GetVariantInternalStorageType(_sortedColumnMappings[col]._sourceColumnOrdinal); + variantInternalType = _sqlDataReaderRowSource.GetVariantInternalStorageType(_sortedColumnMappings[col]._sourceColumnOrdinal); } if (variantInternalType == SqlBuffer.StorageType.DateTime2) @@ -2333,17 +2265,19 @@ private Task CopyColumnsAsync(int col, TaskCompletionSource source = nul // This is in its own method to avoid always allocating the lambda in CopyColumnsAsync private void CopyColumnsAsyncSetupContinuation(TaskCompletionSource source, Task task, int i) { - AsyncHelper.ContinueTask(task, source, () => - { - if (i + 1 < _sortedColumnMappings.Count) + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - CopyColumnsAsync(i + 1, source); //continue from the next column - } - else - { - source.SetResult(null); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (i + 1 < sqlBulkCopy._sortedColumnMappings.Count) + { + sqlBulkCopy.CopyColumnsAsync(i + 1, source); //continue from the next column + } + else + { + source.SetResult(null); + } } - } ); } @@ -2368,7 +2302,7 @@ private void CheckAndRaiseNotification() // It's also the user's chance to cause an exception. _stateObj.BcpLock = true; abortOperation = FireRowsCopiedEvent(_rowsCopied); - SqlClientEventSource.Log.TryTraceEvent(""); + SqlClientEventSource.Log.TryTraceEvent("SqlBulkCopy.CheckAndRaiseNotification | Info | Rows Copied {0}", _rowsCopied); // In case the target connection is closed accidentally. if (ConnectionState.Open != _connection.State) @@ -2479,7 +2413,10 @@ private Task CopyRowsAsync(int rowsSoFar, int totalRows, CancellationToken cts, } resultTask = source.Task; - AsyncHelper.ContinueTask(readTask, source, () => CopyRowsAsync(i + 1, totalRows, cts, source)); + AsyncHelper.ContinueTaskWithState(readTask, source, this, + onSuccess: (object state) => ((SqlBulkCopy)state).CopyRowsAsync(i + 1, totalRows, cts, source) + + ); return resultTask; // Associated task will be completed when all rows are copied to server/exception/cancelled. } } @@ -2488,22 +2425,26 @@ private Task CopyRowsAsync(int rowsSoFar, int totalRows, CancellationToken cts, source = source ?? new TaskCompletionSource(); resultTask = source.Task; - AsyncHelper.ContinueTask(task, source, onSuccess: () => - { - CheckAndRaiseNotification(); // Check for notification now as the current row copy is done at this moment. - - Task readTask = ReadFromRowSourceAsync(cts); - if (readTask == null) - { - CopyRowsAsync(i + 1, totalRows, cts, source); - } - else + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - AsyncHelper.ContinueTask(readTask, source, onSuccess: () => CopyRowsAsync(i + 1, totalRows, cts, source)); - } - } + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + sqlBulkCopy.CheckAndRaiseNotification(); // Check for notification now as the current row copy is done at this moment. + + Task readTask = sqlBulkCopy.ReadFromRowSourceAsync(cts); + if (readTask == null) + { + sqlBulkCopy.CopyRowsAsync(i + 1, totalRows, cts, source); + } + else + { + AsyncHelper.ContinueTaskWithState(readTask, source, sqlBulkCopy, + onSuccess: (object state2) => ((SqlBulkCopy)state2).CopyRowsAsync(i + 1, totalRows, cts, source) + ); + } + } ); - return resultTask; + return resultTask; } } @@ -2571,14 +2512,15 @@ private Task CopyBatchesAsync(BulkCopySimpleResultSet internalResults, string up source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(commandTask, source, - () => + AsyncHelper.ContinueTaskWithState(commandTask, source, this, + onSuccess: (object state) => { - Task continuedTask = CopyBatchesAsyncContinued(internalResults, updateBulkCommandText, cts, source); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + Task continuedTask = sqlBulkCopy.CopyBatchesAsyncContinued(internalResults, updateBulkCommandText, cts, source); if (continuedTask == null) { // Continuation finished sync, recall into CopyBatchesAsync to continue - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); } } ); @@ -2623,7 +2565,7 @@ private Task CopyBatchesAsyncContinued(BulkCopySimpleResultSet internalResults, // Load encryption keys now (if needed) _parser.LoadColumnEncryptionKeys( internalResults[MetaDataResultId].MetaData, - _connection.DataSource); + _connection); Task task = CopyRowsAsync(0, _savedBatchSize, cts); // This is copying 1 batch of rows and setting _hasMoreRowToCopy = true/false. @@ -2635,18 +2577,19 @@ private Task CopyBatchesAsyncContinued(BulkCopySimpleResultSet internalResults, { // First time only source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(task, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { - Task continuedTask = CopyBatchesAsyncContinuedOnSuccess(internalResults, updateBulkCommandText, cts, source); + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + Task continuedTask = sqlBulkCopy.CopyBatchesAsyncContinuedOnSuccess(internalResults, updateBulkCommandText, cts, source); if (continuedTask == null) { // Continuation finished sync, recall into CopyBatchesAsync to continue - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); } }, - onFailure: (_) => CopyBatchesAsyncContinuedOnError(cleanupParser: false), - onCancellation: () => CopyBatchesAsyncContinuedOnError(cleanupParser: true) + onFailure: static (Exception _, object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: false), + onCancellation: static (object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: true) ); return source.Task; @@ -2694,24 +2637,25 @@ private Task CopyBatchesAsyncContinuedOnSuccess(BulkCopySimpleResultSet internal source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(writeTask, source, - onSuccess: () => + AsyncHelper.ContinueTaskWithState(writeTask, source, this, + onSuccess: (object state) => { + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; try { - RunParser(); - CommitTransaction(); + sqlBulkCopy.RunParser(); + sqlBulkCopy.CommitTransaction(); } catch (Exception) { - CopyBatchesAsyncContinuedOnError(cleanupParser: false); + sqlBulkCopy.CopyBatchesAsyncContinuedOnError(cleanupParser: false); throw; } // Always call back into CopyBatchesAsync - CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); + sqlBulkCopy.CopyBatchesAsync(internalResults, updateBulkCommandText, cts, source); }, - onFailure: (_) => CopyBatchesAsyncContinuedOnError(cleanupParser: false) + onFailure: static (Exception _, object state) => ((SqlBulkCopy)state).CopyBatchesAsyncContinuedOnError(cleanupParser: false) ); return source.Task; } @@ -2783,7 +2727,7 @@ private void CleanUpStateObject(bool isCancelRequested = true) { _stateObj.CancelRequest(); } - _stateObj._internalTimeout = false; + _stateObj.SetTimeoutStateStopped(); _stateObj.CloseSession(); _stateObj._bulkCopyOpperationInProgress = false; _stateObj._bulkCopyWriteTimeout = false; @@ -2831,16 +2775,17 @@ private void WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet int { source = new TaskCompletionSource(); } - AsyncHelper.ContinueTask(task, source, - () => + AsyncHelper.ContinueTaskWithState(task, source, this, + onSuccess: (object state) => { + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; // Bulk copy task is completed at this moment. if (task.IsCanceled) { - _localColumnMappings = null; + sqlBulkCopy._localColumnMappings = null; try { - CleanUpStateObject(); + sqlBulkCopy.CleanUpStateObject(); } finally { @@ -2853,10 +2798,10 @@ private void WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet int } else { - _localColumnMappings = null; + sqlBulkCopy._localColumnMappings = null; try { - CleanUpStateObject(isCancelRequested: false); + sqlBulkCopy.CleanUpStateObject(isCancelRequested: false); } finally { @@ -2958,23 +2903,31 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio { if (_isAsyncBulkCopy) { - CancellationTokenRegistration regReconnectCancel = new CancellationTokenRegistration(); + StrongBox regReconnectCancel = new StrongBox(new CancellationTokenRegistration()); TaskCompletionSource cancellableReconnectTS = new TaskCompletionSource(); if (cts.CanBeCanceled) { - regReconnectCancel = cts.Register(s => ((TaskCompletionSource)s).TrySetCanceled(), cancellableReconnectTS); + regReconnectCancel.Value = cts.Register(static (object tcs) => ((TaskCompletionSource)tcs).TrySetCanceled(), cancellableReconnectTS); } AsyncHelper.ContinueTaskWithState(reconnectTask, cancellableReconnectTS, state: cancellableReconnectTS, - onSuccess: (state) => { ((TaskCompletionSource)state).SetResult(null); } + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); // No need to cancel timer since SqlBulkCopy creates specific task source for reconnection. - AsyncHelper.SetTimeoutException(cancellableReconnectTS, BulkCopyTimeout, - () => { return SQL.BulkLoadInvalidDestinationTable(_destinationTableName, SQL.CR_ReconnectTimeout()); }, CancellationToken.None); - AsyncHelper.ContinueTask(cancellableReconnectTS.Task, source, - onSuccess: () => + AsyncHelper.SetTimeoutExceptionWithState( + completion: cancellableReconnectTS, + timeout: BulkCopyTimeout, + state: _destinationTableName, + onFailure: static (object state) => SQL.BulkLoadInvalidDestinationTable((string)state, SQL.CR_ReconnectTimeout()), + cancellationToken: CancellationToken.None + ); + AsyncHelper.ContinueTaskWithState( + task: cancellableReconnectTS.Task, + completion: source, + state: regReconnectCancel, + onSuccess: (object state) => { - regReconnectCancel.Dispose(); + ((StrongBox)state).Value.Dispose(); if (_parserLock != null) { _parserLock.Release(); @@ -2984,8 +2937,8 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio _parserLock.Wait(canReleaseFromAnyThread: true); WriteToServerInternalRestAsync(cts, source); }, - onFailure: (e) => { regReconnectCancel.Dispose(); }, - onCancellation: () => { regReconnectCancel.Dispose(); }, + onFailure: static (Exception _, object state) => ((StrongBox)state).Value.Dispose(), + onCancellation: static (object state) => ((StrongBox)state).Value.Dispose(), exceptionConverter: (ex) => SQL.BulkLoadInvalidDestinationTable(_destinationTableName, ex)); return; } @@ -2993,7 +2946,7 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio { try { - AsyncHelper.WaitForCompletion(reconnectTask, this.BulkCopyTimeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, BulkCopyTimeout, static () => throw SQL.CR_ReconnectTimeout()); } catch (SqlException ex) { @@ -3034,7 +2987,9 @@ private void WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletio if (internalResultsTask != null) { - AsyncHelper.ContinueTask(internalResultsTask, source, () => WriteToServerInternalRestContinuedAsync(internalResultsTask.Result, cts, source)); + AsyncHelper.ContinueTaskWithState(internalResultsTask, source, this, + onSuccess: (object state) => ((SqlBulkCopy)state).WriteToServerInternalRestContinuedAsync(internalResultsTask.Result, cts, source) + ); } else { @@ -3105,16 +3060,17 @@ private Task WriteToServerInternalAsync(CancellationToken ctoken) else { Debug.Assert(_isAsyncBulkCopy, "Read must not return a Task in the Sync mode"); - AsyncHelper.ContinueTask(readTask, source, - () => + AsyncHelper.ContinueTaskWithState(readTask, source, this, + onSuccess: (object state) => { - if (!_hasMoreRowToCopy) + SqlBulkCopy sqlBulkCopy = (SqlBulkCopy)state; + if (!sqlBulkCopy._hasMoreRowToCopy) { source.SetResult(null); // No rows to copy! } else { - WriteToServerInternalRestAsync(ctoken, source); // Passing the same completion which will be completed by the Callee. + sqlBulkCopy.WriteToServerInternalRestAsync(ctoken, source); // Passing the same completion which will be completed by the Callee. } } ); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs index 7bcd7cffb5..1d955b8c47 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientDiagnosticListenerExtensions.cs @@ -346,5 +346,160 @@ public static void WriteTransactionRollbackError(this SqlDiagnosticListener @thi }); } } + + public static DiagnosticScope CreateCommandScope(this SqlDiagnosticListener @this, SqlCommand command, SqlTransaction transaction, [CallerMemberName] string operationName = "") + { + return DiagnosticScope.CreateCommandScope(@this, command, transaction, operationName); + } + + public static DiagnosticTransactionScope CreateTransactionCommitScope(this SqlDiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operationName = "") + { + return DiagnosticTransactionScope.CreateTransactionCommitScope(@this, isolationLevel, connection, transaction, operationName); + } + + public static DiagnosticTransactionScope CreateTransactionRollbackScope(this SqlDiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName, [CallerMemberName] string operationName = "") + { + return DiagnosticTransactionScope.CreateTransactionRollbackScope(@this, isolationLevel, connection, transaction, transactionName, operationName); + } + } + + internal ref struct DiagnosticScope //: IDisposable //ref structs cannot implement interfaces but the compiler will use pattern matching + { + private const int CommandOperation = 1; + private const int ConnectionOpenOperation = 2; + + private readonly SqlDiagnosticListener _diagnostics; + private readonly int _operation; + private readonly string _operationName; + private readonly Guid _operationId; + private readonly object _context1; + private readonly object _context2; + private Exception _exception; + + private DiagnosticScope(SqlDiagnosticListener diagnostics, int operation, Guid operationsId, string operationName, object context1, object context2) + { + _diagnostics = diagnostics; + _operation = operation; + _operationId = operationsId; + _operationName = operationName; + _context1 = context1; + _context2 = context2; + _exception = null; + } + + public void Dispose() + { + switch (_operation) + { + case CommandOperation: + if (_exception != null) + { + _diagnostics.WriteCommandError(_operationId, (SqlCommand)_context1, (SqlTransaction)_context2, _exception, _operationName); + } + else + { + _diagnostics.WriteCommandAfter(_operationId, (SqlCommand)_context1, (SqlTransaction)_context2, _operationName); + } + break; + + case ConnectionOpenOperation: + if (_exception != null) + { + _diagnostics.WriteConnectionOpenError(_operationId, (SqlConnection)_context1, _exception, _operationName); + } + else + { + _diagnostics.WriteConnectionOpenAfter(_operationId, (SqlConnection)_context1, _operationName); + } + break; + + // ConnectionCloseOperation is not implemented because it is conditionally emitted and that requires manual calls to the write apis + } + } + + public void SetException(Exception ex) + { + _exception = ex; + } + + public static DiagnosticScope CreateCommandScope(SqlDiagnosticListener diagnostics, SqlCommand command, SqlTransaction transaction, [CallerMemberName] string operationName = "") + { + Guid operationId = diagnostics.WriteCommandBefore(command, transaction, operationName); + return new DiagnosticScope(diagnostics, CommandOperation, operationId, operationName, command, transaction); + } + } + + internal ref struct DiagnosticTransactionScope //: IDisposable //ref structs cannot implement interfaces but the compiler will use pattern matching + { + public const int TransactionCommit = 1; + public const int TransactionRollback = 2; + + private readonly SqlDiagnosticListener _diagnostics; + private readonly int _operation; + private readonly Guid _operationId; + private readonly string _operationName; + private readonly IsolationLevel _isolationLevel; + private readonly SqlConnection _connection; + private readonly SqlInternalTransaction _transaction; + private readonly string _transactionName; + private Exception _exception; + + public DiagnosticTransactionScope(SqlDiagnosticListener diagnostics, int operation, Guid operationId, string operationName, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName) + { + _diagnostics = diagnostics; + _operation = operation; + _operationId = operationId; + _operationName = operationName; + _isolationLevel = isolationLevel; + _connection = connection; + _transaction = transaction; + _transactionName = transactionName; + _exception = null; + } + + public void Dispose() + { + switch (_operation) + { + case TransactionCommit: + if (_exception != null) + { + _diagnostics.WriteTransactionCommitError(_operationId, _isolationLevel, _connection, _transaction, _exception, _operationName); + } + else + { + _diagnostics.WriteTransactionCommitAfter(_operationId, _isolationLevel, _connection, _transaction, _operationName); + } + break; + + case TransactionRollback: + if (_exception != null) + { + _diagnostics.WriteTransactionRollbackError(_operationId, _isolationLevel, _connection, _transaction, _exception, _transactionName, _operationName); + } + else + { + _diagnostics.WriteTransactionRollbackAfter(_operationId, _isolationLevel, _connection, _transaction, _transactionName, _operationName); + } + break; + } + } + + public void SetException(Exception ex) + { + _exception = ex; + } + + public static DiagnosticTransactionScope CreateTransactionCommitScope(SqlDiagnosticListener diagnostics, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operationName = "") + { + Guid operationId = diagnostics.WriteTransactionCommitBefore(isolationLevel, connection, transaction, operationName); + return new DiagnosticTransactionScope(diagnostics, TransactionCommit, operationId, operationName, isolationLevel, connection, transaction, null); + } + + public static DiagnosticTransactionScope CreateTransactionRollbackScope(SqlDiagnosticListener diagnostics, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName, [CallerMemberName] string operationName = "") + { + Guid operationId = diagnostics.WriteTransactionRollbackBefore(isolationLevel, connection, transaction, transactionName, operationName); + return new DiagnosticTransactionScope(diagnostics, TransactionCommit, operationId, operationName, isolationLevel, connection, transaction, transactionName); + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs new file mode 100644 index 0000000000..c0312ca219 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientEventSource.NetCoreApp.cs @@ -0,0 +1,396 @@ +// 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.Diagnostics.Tracing; +using System.Threading; + +namespace Microsoft.Data.SqlClient +{ + /// + /// supported frameworks: .Net core 3.1 and .Net standard 2.1 and above + /// + internal partial class SqlClientEventSource : SqlClientEventSourceBase + { + private PollingCounter _activeHardConnections; + private IncrementingPollingCounter _hardConnectsPerSecond; + private IncrementingPollingCounter _hardDisconnectsPerSecond; + + private PollingCounter _activeSoftConnections; + private IncrementingPollingCounter _softConnects; + private IncrementingPollingCounter _softDisconnects; + + private PollingCounter _numberOfNonPooledConnections; + private PollingCounter _numberOfPooledConnections; + + private PollingCounter _numberOfActiveConnectionPoolGroups; + private PollingCounter _numberOfInactiveConnectionPoolGroups; + + private PollingCounter _numberOfActiveConnectionPools; + private PollingCounter _numberOfInactiveConnectionPools; + + private PollingCounter _numberOfActiveConnections; + private PollingCounter _numberOfFreeConnections; + private PollingCounter _numberOfStasisConnections; + private IncrementingPollingCounter _numberOfReclaimedConnections; + + private long _activeHardConnectionsCounter = 0; + private long _hardConnectsCounter = 0; + private long _hardDisconnectsCounter = 0; + + private long _activeSoftConnectionsCounter = 0; + private long _softConnectsCounter = 0; + private long _softDisconnectsCounter = 0; + + private long _nonPooledConnectionsCounter = 0; + private long _pooledConnectionsCounter = 0; + + private long _activeConnectionPoolGroupsCounter = 0; + private long _inactiveConnectionPoolGroupsCounter = 0; + + private long _activeConnectionPoolsCounter = 0; + private long _inactiveConnectionPoolsCounter = 0; + + private long _activeConnectionsCounter = 0; + private long _freeConnectionsCounter = 0; + private long _stasisConnectionsCounter = 0; + private long _reclaimedConnectionsCounter = 0; + + protected override void EventCommandMethodCall(EventCommandEventArgs command) + { + if(command.Command != EventCommand.Enable) + { + return; + } + + _activeHardConnections = _activeHardConnections ?? + new PollingCounter("active-hard-connections", this, () => _activeHardConnectionsCounter) + { + DisplayName = "Actual active connections currently made to servers", + DisplayUnits = "count" + }; + + _hardConnectsPerSecond = _hardConnectsPerSecond ?? + new IncrementingPollingCounter("hard-connects", this, () => _hardConnectsCounter) + { + DisplayName = "Actual connection rate to servers", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _hardDisconnectsPerSecond = _hardDisconnectsPerSecond ?? + new IncrementingPollingCounter("hard-disconnects", this, () => _hardDisconnectsCounter) + { + DisplayName = "Actual disconnection rate from servers", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _activeSoftConnections = _activeSoftConnections ?? + new PollingCounter("active-soft-connects", this, () => _activeSoftConnectionsCounter) + { + DisplayName = "Active connections retrieved from the connection pool", + DisplayUnits = "count" + }; + + _softConnects = _softConnects ?? + new IncrementingPollingCounter("soft-connects", this, () => _softConnectsCounter) + { + DisplayName = "Rate of connections retrieved from the connection pool", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _softDisconnects = _softDisconnects ?? + new IncrementingPollingCounter("soft-disconnects", this, () => _softDisconnectsCounter) + { + DisplayName = "Rate of connections returned to the connection pool", + DisplayUnits = "count / sec", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _numberOfNonPooledConnections = _numberOfNonPooledConnections ?? + new PollingCounter("number-of-non-pooled-connections", this, () => _nonPooledConnectionsCounter) + { + DisplayName = "Number of connections not using connection pooling", + DisplayUnits = "count" + }; + + _numberOfPooledConnections = _numberOfPooledConnections ?? + new PollingCounter("number-of-pooled-connections", this, () => _pooledConnectionsCounter) + { + DisplayName = "Number of connections managed by the connection pool", + DisplayUnits = "count" + }; + + _numberOfActiveConnectionPoolGroups = _numberOfActiveConnectionPoolGroups ?? + new PollingCounter("number-of-active-connection-pool-groups", this, () => _activeConnectionPoolGroupsCounter) + { + DisplayName = "Number of active unique connection strings", + DisplayUnits = "count" + }; + + _numberOfInactiveConnectionPoolGroups = _numberOfInactiveConnectionPoolGroups ?? + new PollingCounter("number-of-inactive-connection-pool-groups", this, () => _inactiveConnectionPoolGroupsCounter) + { + DisplayName = "Number of unique connection strings waiting for pruning", + DisplayUnits = "count" + }; + + _numberOfActiveConnectionPools = _numberOfActiveConnectionPools ?? + new PollingCounter("number-of-active-connection-pools", this, () => _activeConnectionPoolsCounter) + { + DisplayName = "Number of active connection pools", + DisplayUnits = "count" + }; + + _numberOfInactiveConnectionPools = _numberOfInactiveConnectionPools ?? + new PollingCounter("number-of-inactive-connection-pools", this, () => _inactiveConnectionPoolsCounter) + { + DisplayName = "Number of inactive connection pools", + DisplayUnits = "count" + }; + + _numberOfActiveConnections = _numberOfActiveConnections ?? + new PollingCounter("number-of-active-connections", this, () => _activeConnectionsCounter) + { + DisplayName = "Number of active connections", + DisplayUnits = "count" + }; + + _numberOfFreeConnections = _numberOfFreeConnections ?? + new PollingCounter("number-of-free-connections", this, () => _freeConnectionsCounter) + { + DisplayName = "Number of ready connections in the connection pool", + DisplayUnits = "count" + }; + + _numberOfStasisConnections = _numberOfStasisConnections ?? + new PollingCounter("number-of-stasis-connections", this, () => _stasisConnectionsCounter) + { + DisplayName = "Number of connections currently waiting to be ready", + DisplayUnits = "count" + }; + + _numberOfReclaimedConnections = _numberOfReclaimedConnections ?? + new IncrementingPollingCounter("number-of-reclaimed-connections", this, () => _reclaimedConnectionsCounter) + { + DisplayName = "Number of reclaimed connections from GC", + DisplayUnits = "count", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + } + + /// + /// The number of actual connections that are being made to servers + /// + [NonEvent] + internal override void HardConnectRequest() + { + Interlocked.Increment(ref _activeHardConnectionsCounter); + Interlocked.Increment(ref _hardConnectsCounter); + } + + /// + /// The number of actual disconnects that are being made to servers + /// + [NonEvent] + internal override void HardDisconnectRequest() + { + Interlocked.Decrement(ref _activeHardConnectionsCounter); + Interlocked.Increment(ref _hardDisconnectsCounter); + } + + /// + /// The number of connections we get from the pool + /// + [NonEvent] + internal override void SoftConnectRequest() + { + Interlocked.Increment(ref _activeSoftConnectionsCounter); + Interlocked.Increment(ref _softConnectsCounter); + } + + /// + /// The number of connections we return to the pool + /// + [NonEvent] + internal override void SoftDisconnectRequest() + { + Interlocked.Decrement(ref _activeSoftConnectionsCounter); + Interlocked.Increment(ref _softDisconnectsCounter); + } + + /// + /// The number of connections that are not using connection pooling + /// + [NonEvent] + internal override void EnterNonPooledConnection() + { + Interlocked.Increment(ref _nonPooledConnectionsCounter); + } + + /// + /// The number of connections that are not using connection pooling + /// + [NonEvent] + internal override void ExitNonPooledConnection() + { + Interlocked.Decrement(ref _nonPooledConnectionsCounter); + } + + /// + /// The number of connections that are managed by the connection pool + /// + [NonEvent] + internal override void EnterPooledConnection() + { + Interlocked.Increment(ref _pooledConnectionsCounter); + } + + /// + /// The number of connections that are managed by the connection pool + /// + [NonEvent] + internal override void ExitPooledConnection() + { + Interlocked.Decrement(ref _pooledConnectionsCounter); + } + + /// + /// The number of unique connection strings + /// + [NonEvent] + internal override void EnterActiveConnectionPoolGroup() + { + Interlocked.Increment(ref _activeConnectionPoolGroupsCounter); + } + + /// + /// The number of unique connection strings + /// + [NonEvent] + internal override void ExitActiveConnectionPoolGroup() + { + Interlocked.Decrement(ref _activeConnectionPoolGroupsCounter); + } + + /// + /// The number of unique connection strings waiting for pruning + /// + [NonEvent] + internal override void EnterInactiveConnectionPoolGroup() + { + Interlocked.Increment(ref _inactiveConnectionPoolGroupsCounter); + } + + /// + /// The number of unique connection strings waiting for pruning + /// + [NonEvent] + internal override void ExitInactiveConnectionPoolGroup() + { + Interlocked.Decrement(ref _inactiveConnectionPoolGroupsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void EnterActiveConnectionPool() + { + Interlocked.Increment(ref _activeConnectionPoolsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void ExitActiveConnectionPool() + { + Interlocked.Decrement(ref _activeConnectionPoolsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void EnterInactiveConnectionPool() + { + Interlocked.Increment(ref _inactiveConnectionPoolsCounter); + } + + /// + /// The number of connection pools + /// + [NonEvent] + internal override void ExitInactiveConnectionPool() + { + Interlocked.Decrement(ref _inactiveConnectionPoolsCounter); + } + + /// + /// The number of connections currently in-use + /// + [NonEvent] + internal override void EnterActiveConnection() + { + Interlocked.Increment(ref _activeConnectionsCounter); + } + + /// + /// The number of connections currently in-use + /// + [NonEvent] + internal override void ExitActiveConnection() + { + Interlocked.Decrement(ref _activeConnectionsCounter); + } + + /// + /// The number of connections currently available for use + /// + [NonEvent] + internal override void EnterFreeConnection() + { + Interlocked.Increment(ref _freeConnectionsCounter); + } + + /// + /// The number of connections currently available for use + /// + [NonEvent] + internal override void ExitFreeConnection() + { + Interlocked.Decrement(ref _freeConnectionsCounter); + } + + /// + /// The number of connections currently waiting to be made ready for use + /// + [NonEvent] + internal override void EnterStasisConnection() + { + Interlocked.Increment(ref _stasisConnectionsCounter); + } + + /// + /// The number of connections currently waiting to be made ready for use + /// + [NonEvent] + internal override void ExitStasisConnection() + { + Interlocked.Decrement(ref _stasisConnectionsCounter); + } + + /// + /// The number of connections we reclaim from GC'd external connections + /// + [NonEvent] + internal override void ReclaimedConnectionRequest() + { + Interlocked.Increment(ref _reclaimedConnectionsCounter); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientMetaDataCollectionNames.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientMetaDataCollectionNames.cs deleted file mode 100644 index 1c805b53df..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlClientMetaDataCollectionNames.cs +++ /dev/null @@ -1,53 +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.SqlClient -{ - - /// - public static class SqlClientMetaDataCollectionNames - { - /// - public static readonly string Columns = "Columns"; - - /// - public static readonly string Databases = "Databases"; - - /// - public static readonly string ForeignKeys = "ForeignKeys"; - - /// - public static readonly string IndexColumns = "IndexColumns"; - - /// - public static readonly string Indexes = "Indexes"; - - /// - public static readonly string ProcedureParameters = "ProcedureParameters"; - - /// - public static readonly string Procedures = "Procedures"; - - /// - public static readonly string Tables = "Tables"; - - /// - public static readonly string UserDefinedTypes = "UserDefinedTypes"; - - /// - public static readonly string Users = "Users"; - - /// - public static readonly string ViewColumns = "ViewColumns"; - - /// - public static readonly string Views = "Views"; - - /// - public static readonly string AllColumns = "AllColumns"; // supported starting from SQL Server 2008 - - /// - public static readonly string ColumnSetColumns = "ColumnSetColumns"; // supported starting from SQL Server 2008 - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Unix.cs index 9bba9e6fe8..93a7a5577c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Unix.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Unix.cs @@ -33,7 +33,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e /// /// Complete path of a certificate /// Asymmetric Key Encryption Algorithm - /// Plain text column encryption key + /// The plaintext column encryption key /// Encrypted column encryption key public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs index c6f2786583..d644acbea7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.Windows.cs @@ -79,7 +79,15 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e // Parse the path and get the X509 cert X509Certificate2 certificate = GetCertificateByPath(masterKeyPath, isSystemOp: true); - int keySizeInBytes = certificate.PublicKey.Key.KeySize / 8; + + RSA RSAPublicKey = certificate.GetRSAPublicKey(); + int keySizeInBytes; +#if NETCOREAPP || NETSTANDARD2_1 + DSA DSAPublicKey = certificate.GetDSAPublicKey(); + keySizeInBytes = RSAPublicKey is not null ? RSAPublicKey.KeySize / 8 : DSAPublicKey.KeySize / 8; +#else + keySizeInBytes= RSAPublicKey.KeySize / 8; +#endif // Validate and decrypt the EncryptedColumnEncryptionKey // Format is @@ -172,7 +180,15 @@ public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string e // Parse the certificate path and get the X509 cert X509Certificate2 certificate = GetCertificateByPath(masterKeyPath, isSystemOp: false); - int keySizeInBytes = certificate.PublicKey.Key.KeySize / 8; + + RSA RSAPublicKey = certificate.GetRSAPublicKey(); + int keySizeInBytes; +#if NETCOREAPP || NETSTANDARD2_1 + DSA DSAPublicKey = certificate.GetDSAPublicKey(); + keySizeInBytes = RSAPublicKey is not null ? RSAPublicKey.KeySize / 8 : DSAPublicKey.KeySize / 8; +#else + keySizeInBytes= RSAPublicKey.KeySize / 8; +#endif // Construct the encryptedColumnEncryptionKey // Format is diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Unix.cs index 3ba58bde54..a2bafa35a7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Unix.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Unix.cs @@ -45,7 +45,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e /// /// Complete path of an asymmetric key in AKV /// Asymmetric Key Encryption Algorithm - /// Plain text column encryption key + /// The plaintext column encryption key /// Encrypted column encryption key public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Unix.cs index f3af157778..657304842e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Unix.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Unix.cs @@ -46,7 +46,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e /// /// Complete path of an asymmetric key in AKV /// Asymmetric Key Encryption Algorithm - /// Plain text column encryption key + /// The plaintext column encryption key /// Encrypted column encryption key public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index 10d5064a87..b576ef3510 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.SqlTypes; @@ -20,12 +22,19 @@ using Microsoft.Data.Sql; using Microsoft.Data.SqlClient.Server; +// NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. +// New attributes that are designed to work with Microsoft.Data.SqlClient and are publicly documented should be included in future. namespace Microsoft.Data.SqlClient { /// + [DefaultEvent("RecordsAffected")] + [ToolboxItem(true)] + [DesignerCategory("")] + // TODO: Add designer attribute when Microsoft.VSDesigner.Data.VS.SqlCommandDesigner uses Microsoft.Data.SqlClient public sealed partial class SqlCommand : DbCommand, ICloneable { private static int _objectTypeCount; // EventSource Counter + private const int MaxRPCNameLength = 1046; internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText; private static readonly Func s_beginExecuteReaderAsync = BeginExecuteReaderAsyncCallback; @@ -37,7 +46,7 @@ public sealed partial class SqlCommand : DbCommand, ICloneable private static readonly Func s_beginExecuteXmlReaderInternal = BeginExecuteXmlReaderInternalCallback; private static readonly Func s_beginExecuteNonQueryInternal = BeginExecuteNonQueryInternalCallback; - internal sealed class ExecuteReaderAsyncCallContext : AAsyncCallContext + internal sealed class ExecuteReaderAsyncCallContext : AAsyncCallContext { public Guid OperationID; public CommandBehavior CommandBehavior; @@ -45,7 +54,7 @@ internal sealed class ExecuteReaderAsyncCallContext : AAsyncCallContext _owner; public TaskCompletionSource TaskCompletionSource => _source; - public void Set(SqlCommand command, TaskCompletionSource source, IDisposable disposable, CommandBehavior behavior, Guid operationID) + public void Set(SqlCommand command, TaskCompletionSource source, CancellationTokenRegistration disposable, CommandBehavior behavior, Guid operationID) { base.Set(command, source, disposable); CommandBehavior = behavior; @@ -64,6 +73,31 @@ protected override void AfterCleared(SqlCommand owner) } } + internal sealed class ExecuteNonQueryAsyncCallContext : AAsyncCallContext + { + public Guid OperationID; + + public SqlCommand Command => _owner; + + public TaskCompletionSource TaskCompletionSource => _source; + + public void Set(SqlCommand command, TaskCompletionSource source, CancellationTokenRegistration disposable, Guid operationID) + { + base.Set(command, source, disposable); + OperationID = operationID; + } + + protected override void Clear() + { + OperationID = default; + } + + protected override void AfterCleared(SqlCommand owner) + { + + } + } + private CommandType _commandType; private int? _commandTimeout; private UpdateRowSource _updatedRowSource = UpdateRowSource.Both; @@ -103,9 +137,9 @@ protected override void AfterCleared(SqlCommand owner) /// Internal flag for testing purposes that forces all queries to internally end async calls. /// private static bool _forceInternalEndQuery = false; -#endif +#endif - private static readonly SqlDiagnosticListener _diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); + private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); private bool _parentOperationStarted = false; internal static readonly Action s_cancelIgnoreFailure = CancelIgnoreFailureCallback; @@ -149,8 +183,17 @@ private enum EXECTYPE // cached metadata private _SqlMetaDataSet _cachedMetaData; - private Dictionary keysToBeSentToEnclave; - private bool requiresEnclaveComputations = false; + internal ConcurrentDictionary keysToBeSentToEnclave; + internal bool requiresEnclaveComputations = false; + + private bool ShouldCacheEncryptionMetadata + { + get + { + return !requiresEnclaveComputations || _activeConnection.Parser.AreEnclaveRetriesSupported; + } + } + internal EnclavePackage enclavePackage = null; private SqlEnclaveAttestationParameters enclaveAttestationParameters = null; private byte[] customData = null; @@ -172,7 +215,7 @@ internal bool InPrepare /// /// Return if column encryption setting is enabled. - /// The order in the below if is important since _activeConnection.Parser can throw if the + /// The order in the below if is important since _activeConnection.Parser can throw if the /// underlying tds connection is closed and we don't want to change the behavior for folks /// not trying to use transparent parameter encryption i.e. who don't use (SqlCommandColumnEncryptionSetting.Enabled or _activeConnection.IsColumnEncryptionSettingEnabled) here. /// @@ -187,10 +230,17 @@ internal bool IsColumnEncryptionEnabled } } - internal bool ShouldUseEnclaveBasedWorkflow - { - get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } - } + internal bool ShouldUseEnclaveBasedWorkflow => + (!string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) || Connection.AttestationProtocol == SqlConnectionAttestationProtocol.None) && + IsColumnEncryptionEnabled; + + /// + /// Per-command custom providers. It can be provided by the user and can be set more than once. + /// + private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => + _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; // Cached info for async executions private sealed class CachedAsyncState @@ -235,6 +285,8 @@ internal bool IsActiveConnectionValid(SqlConnection activeConnection) internal void ResetAsyncState() { + SqlClientEventSource.Log.TryTraceEvent("CachedAsyncState.ResetAsyncState | API | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}", + _cachedAsyncConnection?.ObjectID, _cachedAsyncConnection?.ClientConnectionId, _cachedAsyncConnection?.AsyncCommandInProgress); _cachedAsyncCloseCount = -1; _cachedAsyncResult = null; if (_cachedAsyncConnection != null) @@ -252,6 +304,7 @@ internal void SetActiveConnectionAndResult(TaskCompletionSource completi { Debug.Assert(activeConnection != null, "Unexpected null connection argument on SetActiveConnectionAndResult!"); TdsParser parser = activeConnection?.Parser; + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.SetActiveConnectionAndResult | API | ObjectId {0}, Client Connection Id {1}, MARS={2}", activeConnection?.ObjectID, activeConnection?.ClientConnectionId, parser?.MARSOn); if ((parser == null) || (parser.State == TdsParserState.Closed) || (parser.State == TdsParserState.Broken)) { throw ADP.ClosedConnectionError(); @@ -312,7 +365,7 @@ private CachedAsyncState cachedAsyncState // Volatile bool used to synchronize with cancel thread the state change of an executing // command going from pre-processing to obtaining a stateObject. The cancel synchronization - // we require in the command is only from entering an Execute* API to obtaining a + // we require in the command is only from entering an Execute* API to obtaining a // stateObj. Once a stateObj is successfully obtained, cancel synchronization is handled // by the stateObject. private volatile bool _pendingCancel; @@ -323,6 +376,7 @@ private CachedAsyncState cachedAsyncState private _SqlRPC[] _sqlRPCParameterEncryptionReqArray; private List _parameterCollectionList; private int _currentlyExecutingBatch; + private SqlRetryLogicBaseProvider _retryLogicProvider; /// /// This variable is used to keep track of which RPC batch's results are being read when reading the results of @@ -331,15 +385,15 @@ private CachedAsyncState cachedAsyncState private int _currentlyExecutingDescribeParameterEncryptionRPC; /// - /// A flag to indicate if EndExecute was already initiated by the Begin call. + /// A flag to indicate if we have in-progress describe parameter encryption RPC requests. + /// Reset to false when completed. /// - private volatile bool _internalEndExecuteInitiated; + internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress { get; private set; } /// - /// A flag to indicate if we have in-progress describe parameter encryption RPC requests. - /// Reset to false when completed. + /// A flag to indicate if EndExecute was already initiated by the Begin call. /// - internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress { get; set; } + private volatile bool _internalEndExecuteInitiated; /// /// A flag to indicate whether we postponed caching the query metadata for this command. @@ -401,6 +455,10 @@ private SqlCommand(SqlCommand from) : this() } /// + /// [ + [DefaultValue(null)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_Connection)] new public SqlConnection Connection { get @@ -438,7 +496,7 @@ private SqlCommand(SqlCommand from) : this() } catch (Exception) { - // we do not really care about errors in unprepare (may be the old connection went bad) + // we do not really care about errors in unprepare (may be the old connection went bad) } finally { @@ -449,12 +507,12 @@ private SqlCommand(SqlCommand from) : this() } } _activeConnection = value; - SqlClientEventSource.Log.TryTraceEvent(" {0}, {1}", ObjectID, value?.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Set_Connection | API | ObjectId {0}, Client Connection Id {1}", ObjectID, value?.ClientConnectionId); } } /// - override protected DbConnection DbConnection + protected override DbConnection DbConnection { get { @@ -474,7 +532,32 @@ private SqlInternalConnectionTds InternalTdsConnection } } + private bool IsProviderRetriable => SqlConfigurableRetryFactory.IsRetriable(RetryLogicProvider); + + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public SqlRetryLogicBaseProvider RetryLogicProvider + { + get + { + if (_retryLogicProvider == null) + { + _retryLogicProvider = SqlConfigurableRetryLogicManager.CommandProvider; + } + return _retryLogicProvider; + } + set + { + _retryLogicProvider = value; + } + } + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] // MDAC 90471 + [ResCategory(StringsHelper.ResourceNames.DataCategory_Notification)] + [ResDescription(StringsHelper.ResourceNames.SqlCommand_Notification)] public SqlNotificationRequest Notification { get @@ -483,9 +566,9 @@ public SqlNotificationRequest Notification } set { - SqlClientEventSource.Log.TryTraceEvent(" {0}", ObjectID); _sqlDep = null; _notification = value; + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Set_Notification | API | Object Id {0}", ObjectID); } } @@ -496,7 +579,7 @@ internal SqlStatistics Statistics if (null != _activeConnection) { if (_activeConnection.StatisticsEnabled || - _diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterExecuteCommand)) + s_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterExecuteCommand)) { return _activeConnection.Statistics; } @@ -506,6 +589,9 @@ internal SqlStatistics Statistics } /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_Transaction)] new public SqlTransaction Transaction { get @@ -527,13 +613,13 @@ internal SqlStatistics Statistics throw SQL.CannotModifyPropertyAsyncOperationInProgress(); } } - SqlClientEventSource.Log.TryTraceEvent(" {0}", ObjectID); _transaction = value; + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Set_Transaction | API | Object Id {0}, Internal Transaction Id {1}, Client Connection Id {2}", ObjectID, value?.InternalTransaction?.TransactionId, Connection?.ClientConnectionId); } } /// - override protected DbTransaction DbTransaction + protected override DbTransaction DbTransaction { get { @@ -542,33 +628,40 @@ override protected DbTransaction DbTransaction set { Transaction = (SqlTransaction)value; + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Set_DbTransaction | API | Object Id {0}, Client Connection Id {1}", ObjectID, Connection?.ClientConnectionId); } } /// - override public string CommandText + [DefaultValue("")] + [RefreshProperties(RefreshProperties.All)] // MDAC 67707 + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_CommandText)] + public override string CommandText { - get - { - string value = _commandText; - return ((null != value) ? value : ADP.StrEmpty); - } + get => _commandText ?? ""; set { - SqlClientEventSource.Log.TryTraceEvent(" {0}, String Value = '{1}'", ObjectID, value); if (_commandText != value) { PropertyChanging(); _commandText = value; } + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Set_CommandText | API | Object Id {0}, String Value = '{1}', Client Connection Id {2}", ObjectID, value, Connection?.ClientConnectionId); } } /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.TCE_SqlCommand_ColumnEncryptionSetting)] public SqlCommandColumnEncryptionSetting ColumnEncryptionSetting => _columnEncryptionSetting; /// - override public int CommandTimeout + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_CommandTimeout)] + public override int CommandTimeout { get { @@ -576,7 +669,6 @@ override public int CommandTimeout } set { - SqlClientEventSource.Log.TryTraceEvent(" {0}, {1}", ObjectID, value); if (value < 0) { throw ADP.InvalidCommandTimeout(value); @@ -586,6 +678,7 @@ override public int CommandTimeout PropertyChanging(); _commandTimeout = value; } + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Set_CommandTimeout | API | ObjectId {0}, Command Timeout value {1}, Client Connection Id {2}", ObjectID, value, Connection?.ClientConnectionId); } } @@ -608,7 +701,11 @@ private int DefaultCommandTimeout } /// - override public CommandType CommandType + [DefaultValue(System.Data.CommandType.Text)] + [RefreshProperties(RefreshProperties.All)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_CommandType)] + public override CommandType CommandType { get { @@ -617,7 +714,6 @@ override public CommandType CommandType } set { - SqlClientEventSource.Log.TryTraceEvent(" {0}, {1}{2}", ObjectID, (int)value, _commandType); if (_commandType != value) { switch (value) @@ -632,6 +728,7 @@ override public CommandType CommandType default: throw ADP.InvalidCommandType(value); } + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Set_CommandType | API | ObjectId {0}, Command type value {1}, Client Connection Id {2}", ObjectID, (int)value, Connection?.ClientConnectionId); } } } @@ -641,6 +738,10 @@ override public CommandType CommandType // when the DataAdapter design wizard generates the insert/update/delete commands it will // set the DesignTimeVisible property to false so that cmds won't appear as individual objects /// + [DefaultValue(true)] + [DesignOnly(true)] + [Browsable(false)] + [EditorBrowsableAttribute(EditorBrowsableState.Never)] public override bool DesignTimeVisible { get @@ -653,7 +754,13 @@ public override bool DesignTimeVisible } } + /// + public bool EnableOptimizedParameterBinding { get; set; } + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_Parameters)] new public SqlParameterCollection Parameters { get @@ -669,7 +776,7 @@ public override bool DesignTimeVisible } /// - override protected DbParameterCollection DbParameterCollection + protected override DbParameterCollection DbParameterCollection { get { @@ -678,7 +785,10 @@ override protected DbParameterCollection DbParameterCollection } /// - override public UpdateRowSource UpdatedRowSource + [DefaultValue(System.Data.UpdateRowSource.Both)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Update)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_UpdatedRowSource)] + public override UpdateRowSource UpdatedRowSource { get { @@ -697,10 +807,13 @@ override public UpdateRowSource UpdatedRowSource default: throw ADP.InvalidUpdateRowSource(value); } + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.UpdatedRowSource | API | ObjectId {0}, Updated row source value {1}, Client Connection Id {2}", ObjectID, (int)value, Connection?.ClientConnectionId); } } /// + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_StatementCompleted)] + [ResDescription(StringsHelper.ResourceNames.DbCommand_StatementCompleted)] public event StatementCompletedEventHandler StatementCompleted { add @@ -722,7 +835,7 @@ internal void OnStatementCompleted(int recordCount) { try { - SqlClientEventSource.Log.TryTraceEvent(" {0}, recordCount={1}", ObjectID, recordCount); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.OnStatementCompleted | Info | ObjectId {0}, Record Count {1}, Client Connection Id {2}", ObjectID, recordCount, Connection?.ClientConnectionId); handler(this, new StatementCompletedEventArgs(recordCount)); } catch (Exception e) @@ -742,79 +855,75 @@ private void PropertyChanging() } /// - override public void Prepare() + public override void Prepare() { // Reset _pendingCancel upon entry into any Execute - used to synchronize state // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - try + using (TryEventScope.Create("SqlCommand.Prepare | API | Object Id {0}", ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); - - // only prepare if batch with parameters - if ( - this.IsPrepared && !this.IsDirty - || (this.CommandType == CommandType.StoredProcedure) - || ( - (System.Data.CommandType.Text == this.CommandType) - && (0 == GetParameterCount(_parameters)) - ) - - ) + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.Prepare | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); + try { - if (null != Statistics) + statistics = SqlStatistics.StartTimer(Statistics); + + // only prepare if batch with parameters + if (this.IsPrepared && !this.IsDirty + || (this.CommandType == CommandType.StoredProcedure) + || ((System.Data.CommandType.Text == this.CommandType) + && (0 == GetParameterCount(_parameters)))) { - Statistics.SafeIncrement(ref Statistics._prepares); + if (null != Statistics) + { + Statistics.SafeIncrement(ref Statistics._prepares); + } + _hiddenPrepare = false; } - _hiddenPrepare = false; - } - else - { - // Validate the command outside of the try/catch to avoid putting the _stateObj on error - ValidateCommand(isAsync: false); - - bool processFinallyBlock = true; - try + else { - // NOTE: The state object isn't actually needed for this, but it is still here for back-compat (since it does a bunch of checks) - GetStateObject(); + // Validate the command outside of the try/catch to avoid putting the _stateObj on error + ValidateCommand(isAsync: false); - // Loop through parameters ensuring that we do not have unspecified types, sizes, scales, or precisions - if (null != _parameters) + bool processFinallyBlock = true; + try { - int count = _parameters.Count; - for (int i = 0; i < count; ++i) + // NOTE: The state object isn't actually needed for this, but it is still here for back-compat (since it does a bunch of checks) + GetStateObject(); + + // Loop through parameters ensuring that we do not have unspecified types, sizes, scales, or precisions + if (null != _parameters) { - _parameters[i].Prepare(this); + int count = _parameters.Count; + for (int i = 0; i < count; ++i) + { + _parameters[i].Prepare(this); + } } - } - InternalPrepare(); - } - catch (Exception e) - { - processFinallyBlock = ADP.IsCatchableExceptionType(e); - throw; - } - finally - { - if (processFinallyBlock) + InternalPrepare(); + } + catch (Exception e) + { + processFinallyBlock = ADP.IsCatchableExceptionType(e); + throw; + } + finally { - _hiddenPrepare = false; // The command is now officially prepared + if (processFinallyBlock) + { + _hiddenPrepare = false; // The command is now officially prepared - ReliablePutStateObject(); + ReliablePutStateObject(); + } } } } - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); + finally + { + SqlStatistics.StopTimer(statistics); + } } } @@ -855,6 +964,9 @@ internal void Unprepare() Debug.Assert(_activeConnection != null, "must have an open connection to UnPrepare"); Debug.Assert(false == _inPrepare, "_inPrepare should be false!"); _execType = EXECTYPE.PREPAREPENDING; + + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.UnPrepare | Info | Object Id {0}, Current Prepared Handle {1}", ObjectID, _prepareHandle); + // Don't zero out the handle because we'll pass it in to sp_prepexec on the next prepare // Unless the close count isn't the same as when we last prepared if ((_activeConnection.CloseCount != _preparedConnectionCloseCount) || (_activeConnection.ReconnectCount != _preparedConnectionReconnectCount)) @@ -864,7 +976,7 @@ internal void Unprepare() } _cachedMetaData = null; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Command unprepared.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.UnPrepare | Info | Object Id {0}, Command unprepared.", ObjectID); } // Cancel is supposed to be multi-thread safe. @@ -872,87 +984,89 @@ internal void Unprepare() // because immediately after checking the connection can be closed or removed via another thread. // /// - override public void Cancel() + public override void Cancel() { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - SqlStatistics statistics = null; - try + using (TryEventScope.Create("SqlCommand.Cancel | API | Object Id {0}", ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.Cancel | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); - // If we are in reconnect phase simply cancel the waiting task - var reconnectCompletionSource = _reconnectionCompletionSource; - if (reconnectCompletionSource != null) + SqlStatistics statistics = null; + try { - if (reconnectCompletionSource.TrySetCanceled()) + statistics = SqlStatistics.StartTimer(Statistics); + + // If we are in reconnect phase simply cancel the waiting task + var reconnectCompletionSource = _reconnectionCompletionSource; + if (reconnectCompletionSource != null) { - return; + if (reconnectCompletionSource.TrySetCanceled()) + { + return; + } } - } - - // the pending data flag means that we are awaiting a response or are in the middle of processing a response - // if we have no pending data, then there is nothing to cancel - // if we have pending data, but it is not a result of this command, then we don't cancel either. Note that - // this model is implementable because we only allow one active command at any one time. This code - // will have to change we allow multiple outstanding batches - if (null == _activeConnection) - { - return; - } - SqlInternalConnectionTds connection = (_activeConnection.InnerConnection as SqlInternalConnectionTds); - if (null == connection) - { // Fail with out locking - return; - } - // The lock here is to protect against the command.cancel / connection.close race condition - // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and - // the command will no longer be cancelable. It might be desirable to be able to cancel the close operation, but this is - // outside of the scope of Whidbey RTM. See (SqlConnection::Close) for other lock. - lock (connection) - { - if (connection != (_activeConnection.InnerConnection as SqlInternalConnectionTds)) - { // make sure the connection held on the active connection is what we have stored in our temp connection variable, if not between getting "connection" and taking the lock, the connection has been closed + // the pending data flag means that we are awaiting a response or are in the middle of processing a response + // if we have no pending data, then there is nothing to cancel + // if we have pending data, but it is not a result of this command, then we don't cancel either. Note that + // this model is implementable because we only allow one active command at any one time. This code + // will have to change we allow multiple outstanding batches + if (null == _activeConnection) + { return; } - - TdsParser parser = connection.Parser; - if (null == parser) - { + SqlInternalConnectionTds connection = (_activeConnection.InnerConnection as SqlInternalConnectionTds); + if (null == connection) + { // Fail with out locking return; } + // The lock here is to protect against the command.cancel / connection.close race condition + // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and + // the command will no longer be cancelable. It might be desirable to be able to cancel the close operation, but this is + // outside of the scope of Whidbey RTM. See (SqlConnection::Close) for other lock. + lock (connection) + { + if (connection != (_activeConnection.InnerConnection as SqlInternalConnectionTds)) + { // make sure the connection held on the active connection is what we have stored in our temp connection variable, if not between getting "connection" and taking the lock, the connection has been closed + return; + } - if (!_pendingCancel) - { // Do nothing if already pending. - // Before attempting actual cancel, set the _pendingCancel flag to false. - // This denotes to other thread before obtaining stateObject from the - // session pool that there is another thread wishing to cancel. - // The period in question is between entering the ExecuteAPI and obtaining - // a stateObject. - _pendingCancel = true; - - TdsParserStateObject stateObj = _stateObj; - if (null != stateObj) + TdsParser parser = connection.Parser; + if (null == parser) { - stateObj.Cancel(this); + return; } - else - { - SqlDataReader reader = connection.FindLiveReader(this); - if (reader != null) + + + if (!_pendingCancel) + { // Do nothing if already pending. + // Before attempting actual cancel, set the _pendingCancel flag to false. + // This denotes to other thread before obtaining stateObject from the + // session pool that there is another thread wishing to cancel. + // The period in question is between entering the ExecuteAPI and obtaining + // a stateObject. + _pendingCancel = true; + + TdsParserStateObject stateObj = _stateObj; + if (null != stateObj) + { + stateObj.Cancel(this); + } + else { - reader.Cancel(this); + SqlDataReader reader = connection.FindLiveReader(this); + if (reader != null) + { + reader.Cancel(this); + } } } } } - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); + finally + { + SqlStatistics.StopTimer(statistics); + } } } @@ -963,72 +1077,67 @@ override public void Cancel() } /// - override protected DbParameter CreateDbParameter() + protected override DbParameter CreateDbParameter() { return CreateParameter(); } /// - override protected void Dispose(bool disposing) + protected override void Dispose(bool disposing) { if (disposing) - { // release managed objects + { + // release managed objects _cachedMetaData = null; + + // reset async cache information to allow a second async execute + _cachedAsyncState?.ResetAsyncState(); } // release unmanaged objects base.Dispose(disposing); } + private SqlDataReader RunExecuteReaderWithRetry(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, [CallerMemberName] string method = "") + => RetryLogicProvider.Execute(this, () => RunExecuteReader(cmdBehavior, runBehavior, returnStream, method)); + /// - override public object ExecuteScalar() + public override object ExecuteScalar() { // Reset _pendingCancel upon entry into any Execute - used to synchronize state // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); - - SqlStatistics statistics = null; - - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID{0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - - Exception e = null; - bool success = false; - int? sqlExceptionNumber = null; - - try + using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction)) + using (TryEventScope.Create("SqlCommand.ExecuteScalar | API | ObjectId {0}", ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); - SqlDataReader ds; - ds = RunExecuteReader(0, RunBehavior.ReturnImmediately, returnStream: true); - success = true; + SqlStatistics statistics = null; + bool success = false; + int? sqlExceptionNumber = null; + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteScalar | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); - return CompleteExecuteScalar(ds, false); - } - catch (Exception ex) - { - if (ex is SqlException) + try { - SqlException exception = (SqlException)ex; - sqlExceptionNumber = exception.Number; + statistics = SqlStatistics.StartTimer(Statistics); + WriteBeginExecuteEvent(); + SqlDataReader ds = IsProviderRetriable ? + RunExecuteReaderWithRetry(0, RunBehavior.ReturnImmediately, returnStream: true) : + RunExecuteReader(0, RunBehavior.ReturnImmediately, returnStream: true, method: nameof(ExecuteScalar)); + success = true; + return CompleteExecuteScalar(ds, false); } - - e = ex; - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); - if (e != null) + catch (Exception ex) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + diagnosticScope.SetException(ex); + if (ex is SqlException sqlException) + { + sqlExceptionNumber = sqlException.Number; + } + throw; } - else + finally { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); } } } @@ -1063,62 +1172,74 @@ private object CompleteExecuteScalar(SqlDataReader ds, bool returnSqlValue) return retResult; } - /// - override public int ExecuteNonQuery() + private Task InternalExecuteNonQueryWithRetry(bool sendToPipe, int timeout, out bool usedCache, bool asyncWrite, bool inRetry, [CallerMemberName] string methodName = "") + { + bool innerUsedCache = false; + Task result = RetryLogicProvider.Execute(this, () => InternalExecuteNonQuery(completion: null, sendToPipe, timeout, out innerUsedCache, asyncWrite, inRetry, methodName)); + usedCache = innerUsedCache; + return result; + } + + /// + public override int ExecuteNonQuery() { // Reset _pendingCancel upon entry into any Execute - used to synchronize state // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); - - SqlStatistics statistics = null; - - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - Exception e = null; - try + using (var diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction)) + using (TryEventScope.Create("SqlCommand.ExecuteNonQuery | API | Object Id {0}", ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); - InternalExecuteNonQuery(completion: null, sendToPipe: false, timeout: CommandTimeout, out _, methodName: nameof(ExecuteNonQuery)); - return _rowsAffected; - } - catch (Exception ex) - { - e = ex; - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - if (e != null) + SqlStatistics statistics = null; + bool success = false; + int? sqlExceptionNumber = null; + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteNonQuery | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}, Command Text {3}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); + + try { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + statistics = SqlStatistics.StartTimer(Statistics); + WriteBeginExecuteEvent(); + if (IsProviderRetriable) + { + InternalExecuteNonQueryWithRetry(sendToPipe: false, timeout: CommandTimeout, out _, asyncWrite: false, inRetry: false); + } + else + { + InternalExecuteNonQuery(completion: null, sendToPipe: false, timeout: CommandTimeout, out _); + } + success = true; + return _rowsAffected; } - else + catch (Exception ex) { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + diagnosticScope.SetException(ex); + if (ex is SqlException sqlException) + { + sqlExceptionNumber = sqlException.Number; + } + throw; + } + finally + { + SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); } } } /// - public IAsyncResult BeginExecuteNonQuery() - { - // BeginExecuteNonQuery will track ExecutionTime for us - return BeginExecuteNonQuery(null, null); - } + public IAsyncResult BeginExecuteNonQuery() => BeginExecuteNonQuery(null, null); // BeginExecuteNonQuery will track ExecutionTime for us /// public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteNonQuery | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false); } private IAsyncResult BeginExecuteNonQueryAsync(AsyncCallback callback, object stateObject) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteNonQueryAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); return BeginExecuteNonQueryInternal(0, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true); } @@ -1191,7 +1312,7 @@ private IAsyncResult BeginExecuteNonQueryInternal(CommandBehavior behavior, Asyn if (callback != null) { globalCompletion.Task.ContinueWith( - (task, state) => ((AsyncCallback)state)(task), + static (task, state) => ((AsyncCallback)state)(task), state: callback ); } @@ -1213,14 +1334,23 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection); _stateObj.ReadSni(completion); } + // Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions, + // trying to call further functions in the catch of either may fail that should be considered on debuging! + catch (System.OutOfMemoryException e) + { + _activeConnection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + _activeConnection.Abort(e); + throw; + } catch (Exception) { // Similarly, if an exception occurs put the stateObj back into the pool. // and reset async cache information to allow a second async execute - if (null != _cachedAsyncState) - { - _cachedAsyncState.ResetAsyncState(); - } + _cachedAsyncState?.ResetAsyncState(); ReliablePutStateObject(); throw; } @@ -1229,7 +1359,9 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource private void VerifyEndExecuteState(Task completionTask, string endMethod, bool fullCheckForColumnEncryption = false) { Debug.Assert(completionTask != null); - + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.VerifyEndExecuteState | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}", + _activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, + _activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress); if (completionTask.IsCanceled) { if (_stateObj != null) @@ -1291,7 +1423,7 @@ private void WaitForAsyncResults(IAsyncResult asyncResult, bool isInternal) } // If this is an internal command we will decrement the count when the End method is actually called by the user. - // If we are using Column Encryption and the previous task failed, the async count should have already been fixed up. + // If we are using Column Encryption and the previous task failed, the async count should have already been fixed up. // There is a generic issue in how we handle the async count because: // a) BeginExecute might or might not clean it up on failure. // b) In EndExecute, we check the task state before waiting and throw if it's failed, whereas if we wait we will always adjust the count. @@ -1310,7 +1442,7 @@ public int EndExecuteNonQuery(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.EndExecuteNonQuery | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); } } @@ -1329,17 +1461,14 @@ private void ThrowIfReconnectionHasBeenCanceled() /// public int EndExecuteNonQueryAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.EndExecuteNonQueryAsync | Info | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; if (asyncException != null) { // Leftover exception from the Begin...InternalReadStage - if (cachedAsyncState != null) - { - cachedAsyncState.ResetAsyncState(); - } + cachedAsyncState?.ResetAsyncState(); ReliablePutStateObject(); throw asyncException.InnerException; } @@ -1380,7 +1509,7 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) sqlExceptionNumber = e.Number; _cachedAsyncState?.ResetAsyncState(); - // SqlException is always catchable + // SqlException is always catchable ReliablePutStateObject(); throw; } @@ -1402,6 +1531,9 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInternal, [CallerMemberName] string endMethod = "") { + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}", + _activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, + _activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress); VerifyEndExecuteState((Task)asyncResult, endMethod); WaitForAsyncResults(asyncResult, isInternal); @@ -1415,7 +1547,7 @@ private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInter bool processFinallyBlock = true; try { - // If this is not for internal usage, notify the dependency. + // If this is not for internal usage, notify the dependency. // If we have already initiated the end internally, the reader should be ready, so just return the rows affected. if (!isInternal) { @@ -1486,6 +1618,8 @@ private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInter private Task InternalExecuteNonQuery(TaskCompletionSource completion, bool sendToPipe, int timeout, out bool usedCache, bool asyncWrite = false, bool inRetry = false, [CallerMemberName] string methodName = "") { + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}", + _activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, _activeConnection?.AsyncCommandInProgress); bool isAsync = (null != completion); usedCache = false; @@ -1507,7 +1641,8 @@ private Task InternalExecuteNonQuery(TaskCompletionSource completion, bo // We skip this block for enclave based always encrypted so that we can make a call to SQL Server to get the encryption information if (!ShouldUseEnclaveBasedWorkflow && !BatchRPCMode && (CommandType.Text == CommandType) && (0 == GetParameterCount(_parameters))) { - Debug.Assert(!sendToPipe, "trying to send non-context command to pipe"); + Debug.Assert(!sendToPipe, "Trying to send non-context command to pipe"); + if (null != statistics) { if (!IsDirty && IsPrepared) @@ -1522,14 +1657,15 @@ private Task InternalExecuteNonQuery(TaskCompletionSource completion, bo // We should never get here for a retry since we only have retries for parameters. Debug.Assert(!inRetry); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | Object Id {0}, RPC execute method name {1}, isAsync {2}, inRetry {3}", ObjectID, methodName, isAsync, inRetry); task = RunExecuteNonQueryTds(methodName, isAsync, timeout, asyncWrite); } else { // otherwise, use a full-fledged execute that can handle params and stored procs - Debug.Assert(!sendToPipe, "trying to send non-context command to pipe"); - SqlClientEventSource.Log.TryTraceEvent(" {0}, Command executed as RPC.", ObjectID); + Debug.Assert(!sendToPipe, "Trying to send non-context command to pipe"); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | Object Id {0}, RPC execute method name {1}, isAsync {2}, inRetry {3}", ObjectID, methodName, isAsync, inRetry); SqlDataReader reader = RunExecuteReader(0, RunBehavior.UntilDone, false, completion, timeout, out task, out usedCache, asyncWrite, inRetry, methodName); if (null != reader) @@ -1558,49 +1694,39 @@ public XmlReader ExecuteXmlReader() // Reset _pendingCancel upon entry into any Execute - used to synchronize state // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - bool success = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); - - SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - int? sqlExceptionNumber = null; - - Exception e = null; - try + using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction)) + using (TryEventScope.Create("SqlCommand.ExecuteXmlReader | API | Object Id {0}", ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); + SqlStatistics statistics = null; + bool success = false; + int? sqlExceptionNumber = null; + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteXmlReader | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); - // use the reader to consume metadata - SqlDataReader ds; - ds = RunExecuteReader(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, returnStream: true); - success = true; - return CompleteXmlReader(ds); - } - catch (Exception ex) - { - e = ex; - if (ex is SqlException) + try { - SqlException exception = (SqlException)ex; - sqlExceptionNumber = exception.Number; + statistics = SqlStatistics.StartTimer(Statistics); + WriteBeginExecuteEvent(); + // use the reader to consume metadata + SqlDataReader ds = IsProviderRetriable ? + RunExecuteReaderWithRetry(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, returnStream: true) : + RunExecuteReader(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, returnStream: true); + success = true; + return CompleteXmlReader(ds); } - - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); - if (e != null) + catch (Exception ex) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + diagnosticScope.SetException(ex); + if (ex is SqlException sqlException) + { + sqlExceptionNumber = sqlException.Number; + } + throw; } - else + finally { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); } } } @@ -1615,12 +1741,13 @@ public IAsyncResult BeginExecuteXmlReader() /// public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteXmlReader | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false); } private IAsyncResult BeginExecuteXmlReaderAsync(AsyncCallback callback, object stateObject) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true); } @@ -1645,6 +1772,7 @@ private IAsyncResult BeginExecuteXmlReaderInternal(CommandBehavior behavior, Asy if (!inRetry) { statistics = SqlStatistics.StartTimer(Statistics); + WriteBeginExecuteEvent(); } bool usedCache; @@ -1694,7 +1822,7 @@ private IAsyncResult BeginExecuteXmlReaderInternal(CommandBehavior behavior, Asy if (callback != null) { localCompletion.Task.ContinueWith( - (task, state) => ((AsyncCallback)state)(task), + static (task, state) => ((AsyncCallback)state)(task), state: callback ); } @@ -1716,14 +1844,25 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource _cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection); _stateObj.ReadSni(completion); } + // Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions, + // trying to call further functions in the catch of either may fail that should be considered on debuging! + catch (System.OutOfMemoryException e) + { + _activeConnection.Abort(e); + completion.TrySetException(e); + throw; + } + catch (System.StackOverflowException e) + { + _activeConnection.Abort(e); + completion.TrySetException(e); + throw; + } catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. // and reset async cache information to allow a second async execute - if (null != _cachedAsyncState) - { - _cachedAsyncState.ResetAsyncState(); - } + _cachedAsyncState?.ResetAsyncState(); ReliablePutStateObject(); completion.TrySetException(e); } @@ -1738,18 +1877,19 @@ public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.EndExecuteXmlReader | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); } } private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.EndExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; if (asyncException != null) { + cachedAsyncState?.ResetAsyncState(); ReliablePutStateObject(); throw asyncException.InnerException; } @@ -1849,14 +1989,14 @@ private XmlReader CompleteXmlReader(SqlDataReader ds, bool isAsync = false) /// public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID{0}, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteReader | API | Correlation | Object Id {0}, Behavior {1}, Activity Id {2}, Client Connection Id {3}, Command Text '{4}'", ObjectID, (int)behavior, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); return BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false); } /// - override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) + protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteDbDataReader | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); return ExecuteReader(behavior); } @@ -1864,8 +2004,7 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) new public SqlDataReader ExecuteReader() { SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteReader | API | Correlation | ObjectID {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -1874,7 +2013,6 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) finally { SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); } } @@ -1885,40 +2023,44 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior) // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); - SqlStatistics statistics = null; bool success = false; int? sqlExceptionNumber = null; Exception e = null; - try - { - statistics = SqlStatistics.StartTimer(Statistics); - return RunExecuteReader(behavior, RunBehavior.ReturnImmediately, returnStream: true); - } - catch (Exception ex) + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); + + using (TryEventScope.Create("SqlCommand.ExecuteReader | API | Object Id {0}", ObjectID)) { - if (ex is SqlException) + try { - SqlException exception = (SqlException)ex; - sqlExceptionNumber = exception.Number; + WriteBeginExecuteEvent(); + statistics = SqlStatistics.StartTimer(Statistics); + return IsProviderRetriable ? + RunExecuteReaderWithRetry(behavior, RunBehavior.ReturnImmediately, returnStream: true) : + RunExecuteReader(behavior, RunBehavior.ReturnImmediately, returnStream: true); } - - e = ex; - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); - - if (e != null) + catch (Exception ex) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + if (ex is SqlException sqlException) + { + sqlExceptionNumber = sqlException.Number; + } + + e = ex; + throw; } - else + finally { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); + if (e != null) + { + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + } + else + { + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + } } } } @@ -1932,18 +2074,19 @@ public SqlDataReader EndExecuteReader(IAsyncResult asyncResult) } finally { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID{0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.EndExecuteReader | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); } } internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID{0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.EndExecuteReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; if (asyncException != null) { + cachedAsyncState?.ResetAsyncState(); ReliablePutStateObject(); throw asyncException.InnerException; } @@ -1967,6 +2110,9 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult) { + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.EndExecuteReaderInternal | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}", + _activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, + _activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress); SqlStatistics statistics = null; bool success = false; int? sqlExceptionNumber = null; @@ -2008,12 +2154,16 @@ private void CleanupExecuteReaderAsync(Task task, TaskCompletionS Exception e = task.Exception.InnerException; if (!_parentOperationStarted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } source.SetException(e); } else { + if (!_parentOperationStarted) + { + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + } if (task.IsCanceled) { source.SetCanceled(); @@ -2022,10 +2172,6 @@ private void CleanupExecuteReaderAsync(Task task, TaskCompletionS { source.SetResult(task.Result); } - if (!_parentOperationStarted) - { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - } } } @@ -2070,7 +2216,7 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC if (!inRetry) { statistics = SqlStatistics.StartTimer(Statistics); - + WriteBeginExecuteEvent(); ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj // back into pool when we should not. } @@ -2122,7 +2268,7 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC if (callback != null) { globalCompletion.Task.ContinueWith( - (task, state) => ((AsyncCallback)state)(task), + static (task, state) => ((AsyncCallback)state)(task), state: callback ); } @@ -2337,21 +2483,33 @@ long firstAttemptStart private void BeginExecuteReaderInternalReadStage(TaskCompletionSource completion) { Debug.Assert(completion != null, "CompletionSource should not be null"); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteReaderInternalReadStage | INFO | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); // Read SNI does not have catches for async exceptions, handle here. try { - // must finish caching information before ReadSni which can activate the callback before returning - cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection); - _stateObj.ReadSni(completion); + // must finish caching information before ReadSni which can activate the callback before returning + cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection); + _stateObj.ReadSni(completion); + } + // Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions, + // trying to call further functions in the catch of either may fail that should be considered on debuging! + catch (System.OutOfMemoryException e) + { + _activeConnection.Abort(e); + completion.TrySetException(e); + throw; + } + catch (System.StackOverflowException e) + { + _activeConnection.Abort(e); + completion.TrySetException(e); + throw; } catch (Exception e) { // Similarly, if an exception occurs put the stateObj back into the pool. // and reset async cache information to allow a second async execute - if (null != _cachedAsyncState) - { - _cachedAsyncState.ResetAsyncState(); - } + _cachedAsyncState?.ResetAsyncState(); ReliablePutStateObject(); completion.TrySetException(e); } @@ -2359,6 +2517,9 @@ private void BeginExecuteReaderInternalReadStage(TaskCompletionSource co private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool isInternal, string endMethod) { + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteReader | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}", + _activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, + _activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress); VerifyEndExecuteState((Task)asyncResult, endMethod); WaitForAsyncResults(asyncResult, isInternal); @@ -2378,9 +2539,17 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool is /// public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) + => IsProviderRetriable ? + InternalExecuteNonQueryWithRetryAsync(cancellationToken) : + InternalExecuteNonQueryAsync(cancellationToken); + + private Task InternalExecuteNonQueryWithRetryAsync(CancellationToken cancellationToken) + => RetryLogicProvider.ExecuteAsync(this, () => InternalExecuteNonQueryAsync(cancellationToken), cancellationToken); + + private Task InternalExecuteNonQueryAsync(CancellationToken cancellationToken) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteNonQueryAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); TaskCompletionSource source = new TaskCompletionSource(); @@ -2396,37 +2565,56 @@ public override Task ExecuteNonQueryAsync(CancellationToken cancellationTok } Task returnedTask = source.Task; + returnedTask = RegisterForConnectionCloseNotification(returnedTask); + + ExecuteNonQueryAsyncCallContext context = new ExecuteNonQueryAsyncCallContext(); + context.Set(this, source, registration, operationId); try { - returnedTask = RegisterForConnectionCloseNotification(returnedTask); - - Task.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) => - { - registration.Dispose(); - if (t.IsFaulted) - { - Exception e = t.Exception.InnerException; - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); - source.SetException(e); - } - else + Task.Factory.FromAsync( + static (AsyncCallback callback, object stateObject) => ((ExecuteNonQueryAsyncCallContext)stateObject).Command.BeginExecuteNonQueryAsync(callback, stateObject), + static (IAsyncResult result) => ((ExecuteNonQueryAsyncCallContext)result.AsyncState).Command.EndExecuteNonQueryAsync(result), + state: context + ).ContinueWith( + static (Task task, object state) => { - if (t.IsCanceled) + ExecuteNonQueryAsyncCallContext context = (ExecuteNonQueryAsyncCallContext)state; + + Guid operationId = context.OperationID; + SqlCommand command = context.Command; + TaskCompletionSource source = context.TaskCompletionSource; + + context.Dispose(); + context = null; + + if (task.IsFaulted) { - source.SetCanceled(); + Exception e = task.Exception.InnerException; + s_diagnosticListener.WriteCommandError(operationId, command, command._transaction, e); + source.SetException(e); } else { - source.SetResult(t.Result); + if (task.IsCanceled) + { + source.SetCanceled(); + } + else + { + source.SetResult(task.Result); + } + s_diagnosticListener.WriteCommandAfter(operationId, command, command._transaction); } - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - } - }, TaskScheduler.Default); + }, + state: context, + scheduler: TaskScheduler.Default + ); } catch (Exception e) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); + context.Dispose(); } return returnedTask; @@ -2435,14 +2623,19 @@ public override Task ExecuteNonQueryAsync(CancellationToken cancellationTok /// protected override Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { - return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith((result) => - { - if (result.IsFaulted) + return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith( + static (Task result) => { - throw result.Exception.InnerException; - } - return result.Result; - }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); + if (result.IsFaulted) + { + throw result.Exception.InnerException; + } + return result.Result; + }, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, + TaskScheduler.Default + ); } /// @@ -2465,12 +2658,21 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b /// new public Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) + => IsProviderRetriable ? + InternalExecuteReaderWithRetryAsync(behavior, cancellationToken) : + InternalExecuteReaderAsync(behavior, cancellationToken); + + private Task InternalExecuteReaderWithRetryAsync(CommandBehavior behavior, CancellationToken cancellationToken) + => RetryLogicProvider.ExecuteAsync(this, () => InternalExecuteReaderAsync(behavior, cancellationToken), cancellationToken); + + private Task InternalExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, behavior={1}, ActivityID {2}", ObjectID, (int)behavior, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteReaderAsync | API | Correlation | Object Id {0}, Behavior {1}, Activity Id {2}, Client Connection Id {3}, Command Text '{4}'", ObjectID, (int)behavior, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteReaderAsync | API> {0}, Client Connection Id {1}, Command Text = '{2}'", ObjectID, Connection?.ClientConnectionId, CommandText); Guid operationId = default(Guid); if (!_parentOperationStarted) { - operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); } TaskCompletionSource source = new TaskCompletionSource(); @@ -2487,11 +2689,11 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b } Task returnedTask = source.Task; + ExecuteReaderAsyncCallContext context = null; try { returnedTask = RegisterForConnectionCloseNotification(returnedTask); - ExecuteReaderAsyncCallContext context = null; if (_activeConnection?.InnerConnection is SqlInternalConnection sqlInternalConnection) { context = Interlocked.Exchange(ref sqlInternalConnection.CachedCommandExecuteReaderAsyncContext, null); @@ -2515,10 +2717,11 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b { if (!_parentOperationStarted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } source.SetException(e); + context.Dispose(); } return returnedTask; @@ -2533,10 +2736,16 @@ private void SetCachedCommandExecuteReaderAsyncContext(ExecuteReaderAsyncCallCon } /// - public override Task ExecuteScalarAsync(CancellationToken cancellationToken) + public override Task ExecuteScalarAsync(CancellationToken cancellationToken) => + // Do not use retry logic here as internal call to ExecuteReaderAsync handles retry logic. + InternalExecuteScalarAsync(cancellationToken); + + private Task InternalExecuteScalarAsync(CancellationToken cancellationToken) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteScalarAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteScalarAsync | API> {0}, Client Connection Id {1}, Command Text = '{2}'", ObjectID, Connection?.ClientConnectionId, CommandText); _parentOperationStarted = true; - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); return ExecuteReaderAsync(cancellationToken).ContinueWith((executeTask) => { @@ -2547,68 +2756,71 @@ public override Task ExecuteScalarAsync(CancellationToken cancellationTo } else if (executeTask.IsFaulted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, executeTask.Exception.InnerException); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, executeTask.Exception.InnerException); source.SetException(executeTask.Exception.InnerException); } else { SqlDataReader reader = executeTask.Result; - reader.ReadAsync(cancellationToken).ContinueWith((readTask) => - { - try + reader.ReadAsync(cancellationToken) + .ContinueWith((Task readTask) => { - if (readTask.IsCanceled) - { - reader.Dispose(); - source.SetCanceled(); - } - else if (readTask.IsFaulted) - { - reader.Dispose(); - _diagnosticListener.WriteCommandError(operationId, this, _transaction, readTask.Exception.InnerException); - source.SetException(readTask.Exception.InnerException); - } - else + try { - Exception exception = null; - object result = null; - try - { - bool more = readTask.Result; - if (more && reader.FieldCount > 0) - { - try - { - result = reader.GetValue(0); - } - catch (Exception e) - { - exception = e; - } - } - } - finally + if (readTask.IsCanceled) { reader.Dispose(); + source.SetCanceled(); } - if (exception != null) + else if (readTask.IsFaulted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, exception); - source.SetException(exception); + reader.Dispose(); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, readTask.Exception.InnerException); + source.SetException(readTask.Exception.InnerException); } else { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - source.SetResult(result); + Exception exception = null; + object result = null; + try + { + bool more = readTask.Result; + if (more && reader.FieldCount > 0) + { + try + { + result = reader.GetValue(0); + } + catch (Exception e) + { + exception = e; + } + } + } + finally + { + reader.Dispose(); + } + if (exception != null) + { + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, exception); + source.SetException(exception); + } + else + { + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + source.SetResult(result); + } } } - } - catch (Exception e) - { - // exception thrown by Dispose... - source.SetException(e); - } - }, TaskScheduler.Default); + catch (Exception e) + { + // exception thrown by Dispose... + source.SetException(e); + } + }, + TaskScheduler.Default + ); } _parentOperationStarted = false; return source.Task; @@ -2623,9 +2835,17 @@ public Task ExecuteXmlReaderAsync() /// public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken) + => IsProviderRetriable ? + InternalExecuteXmlReaderWithRetryAsync(cancellationToken) : + InternalExecuteXmlReaderAsync(cancellationToken); + + private Task InternalExecuteXmlReaderWithRetryAsync(CancellationToken cancellationToken) + => RetryLogicProvider.ExecuteAsync(this, () => InternalExecuteXmlReaderAsync(cancellationToken), cancellationToken); + + private Task InternalExecuteXmlReaderAsync(CancellationToken cancellationToken) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); TaskCompletionSource source = new TaskCompletionSource(); @@ -2645,38 +2865,110 @@ public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken { returnedTask = RegisterForConnectionCloseNotification(returnedTask); - Task.Factory.FromAsync(BeginExecuteXmlReaderAsync, EndExecuteXmlReaderAsync, null).ContinueWith((t) => - { - registration.Dispose(); - if (t.IsFaulted) - { - Exception e = t.Exception.InnerException; - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); - source.SetException(e); - } - else + Task.Factory.FromAsync(BeginExecuteXmlReaderAsync, EndExecuteXmlReaderAsync, null) + .ContinueWith((Task task) => { - if (t.IsCanceled) + registration.Dispose(); + if (task.IsFaulted) { - source.SetCanceled(); + Exception e = task.Exception.InnerException; + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + source.SetException(e); } else { - source.SetResult(t.Result); + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + if (task.IsCanceled) + { + source.SetCanceled(); + } + else + { + source.SetResult(task.Result); + } + } - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - } - }, TaskScheduler.Default); + }, + TaskScheduler.Default + ); } catch (Exception e) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } return returnedTask; } + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnCommand(IDictionary customProviders) + { + ValidateCustomProviders(customProviders); + + // Create a temporary dictionary and then add items from the provided dictionary. + // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs + // in the provided customerProviders dictionary. + Dictionary customColumnEncryptionKeyStoreProviders = + new(customProviders, StringComparer.OrdinalIgnoreCase); + + _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; + } + + private void ValidateCustomProviders(IDictionary customProviders) + { + // Throw when the provided dictionary is null. + if (customProviders is null) + { + throw SQL.NullCustomKeyStoreProviderDictionary(); + } + + // Validate that custom provider list doesn't contain any of system provider list + foreach (string key in customProviders.Keys) + { + // Validate the provider name + // + // Check for null or empty + if (string.IsNullOrWhiteSpace(key)) + { + throw SQL.EmptyProviderName(); + } + + // Check if the name starts with MSSQL_, since this is reserved namespace for system providers. + if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase)) + { + throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix); + } + + // Validate the provider value + if (customProviders[key] is null) + { + throw SQL.NullProviderValue(key); + } + } + } + + /// + /// This function walks through the registered custom column encryption key store providers and returns an object if found. + /// + /// Provider Name to be searched in custom provider dictionary. + /// If the provider is found, initializes the corresponding SqlColumnEncryptionKeyStoreProvider instance. + /// true if the provider is found, else returns false + internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) + { + Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); + return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + } + + /// + /// This function returns a list of the names of the custom providers currently registered. + /// + /// Combined list of provider names + internal List GetColumnEncryptionCustomKeyStoreProvidersNames() + { + return _customColumnEncryptionKeyStoreProviders.Keys.ToList(); + } + // If the user part is quoted, remove first and last brackets and then unquote any right square // brackets in the procedure. This is a very simple parser that performs no validation. As // with the function below, ideally we should have support from the server for this. @@ -2728,8 +3020,8 @@ private enum ProcParamsColIndex { ParameterName = 0, ParameterType, - DataType, // obsolete in katmai, use ManagedDataType instead - ManagedDataType, // new in katmai + DataType, // obsolete in 2008, use ManagedDataType instead + ManagedDataType, // new in 2008 CharacterMaximumLength, NumericPrecision, NumericScale, @@ -2739,16 +3031,16 @@ private enum ProcParamsColIndex XmlSchemaCollectionCatalogName, XmlSchemaCollectionSchemaName, XmlSchemaCollectionName, - UdtTypeName, // obsolete in Katmai. Holds the actual typename if UDT, since TypeName didn't back then. - DateTimeScale // new in Katmai + UdtTypeName, // obsolete in 2008. Holds the actual typename if UDT, since TypeName didn't back then. + DateTimeScale // new in 2008 }; - // Yukon- column ordinals (this array indexed by ProcParamsColIndex - internal static readonly string[] PreKatmaiProcParamsNames = new string[] { + // 2005- column ordinals (this array indexed by ProcParamsColIndex + internal static readonly string[] PreSql2008ProcParamsNames = new string[] { "PARAMETER_NAME", // ParameterName, "PARAMETER_TYPE", // ParameterType, "DATA_TYPE", // DataType - null, // ManagedDataType, introduced in Katmai + null, // ManagedDataType, introduced in 2008 "CHARACTER_MAXIMUM_LENGTH", // CharacterMaximumLength, "NUMERIC_PRECISION", // NumericPrecision, "NUMERIC_SCALE", // NumericScale, @@ -2759,14 +3051,14 @@ private enum ProcParamsColIndex "XML_SCHEMANAME", // XmlSchemaCollectionSchemaName, "XML_SCHEMACOLLECTIONNAME", // XmlSchemaCollectionName "UDT_NAME", // UdtTypeName - null, // Scale for datetime types with scale, introduced in Katmai + null, // Scale for datetime types with scale, introduced in 2008 }; - // Katmai+ column ordinals (this array indexed by ProcParamsColIndex - internal static readonly string[] KatmaiProcParamsNames = new string[] { + // 2008+ column ordinals (this array indexed by ProcParamsColIndex + internal static readonly string[] Sql2008ProcParamsNames = new string[] { "PARAMETER_NAME", // ParameterName, "PARAMETER_TYPE", // ParameterType, - null, // DataType, removed from Katmai+ + null, // DataType, removed from 2008+ "MANAGED_DATA_TYPE", // ManagedDataType, "CHARACTER_MAXIMUM_LENGTH", // CharacterMaximumLength, "NUMERIC_PRECISION", // NumericPrecision, @@ -2777,7 +3069,7 @@ private enum ProcParamsColIndex "XML_CATALOGNAME", // XmlSchemaCollectionCatalogName, "XML_SCHEMANAME", // XmlSchemaCollectionSchemaName, "XML_SCHEMACOLLECTIONNAME", // XmlSchemaCollectionName - null, // UdtTypeName, removed from Katmai+ + null, // UdtTypeName, removed from 2008+ "SS_DATETIME_PRECISION", // Scale for datetime types with scale }; @@ -2812,7 +3104,7 @@ internal void DeriveParameters() StringBuilder cmdText = new StringBuilder(); // Build call for sp_procedure_params_rowset built of unquoted values from user: - // [user server, if provided].[user catalog, else current database].[sys if Yukon, else blank].[sp_procedure_params_rowset] + // [user server, if provided].[user catalog, else current database].[sys if 2005, else blank].[sp_procedure_params_rowset] // Server - pass only if user provided. if (!string.IsNullOrEmpty(parsedSProc[0])) @@ -2829,16 +3121,16 @@ internal void DeriveParameters() SqlCommandSet.BuildStoredProcedureName(cmdText, parsedSProc[1]); cmdText.Append("."); - // Schema - only if Yukon, and then only pass sys. Also - pass managed version of sproc - // for Yukon, else older sproc. + // Schema - only if 2005, and then only pass sys. Also - pass managed version of sproc + // for 2005, else older sproc. string[] colNames; bool useManagedDataType; - if (Connection.IsKatmaiOrNewer) + if (Connection.Is2008OrNewer) { // Procedure - [sp_procedure_params_managed] cmdText.Append("[sys].[").Append(TdsEnums.SP_PARAMS_MGD10).Append("]"); - colNames = KatmaiProcParamsNames; + colNames = Sql2008ProcParamsNames; useManagedDataType = true; } else @@ -2846,7 +3138,7 @@ internal void DeriveParameters() // Procedure - [sp_procedure_params_managed] cmdText.Append("[sys].[").Append(TdsEnums.SP_PARAMS_MANAGED).Append("]"); - colNames = PreKatmaiProcParamsNames; + colNames = PreSql2008ProcParamsNames; useManagedDataType = false; } @@ -2902,7 +3194,7 @@ internal void DeriveParameters() { p.SqlDbType = (SqlDbType)(short)r[colNames[(int)ProcParamsColIndex.ManagedDataType]]; - // Yukon didn't have as accurate of information as we're getting for Katmai, so re-map a couple of + // 2005 didn't have as accurate of information as we're getting for 2008, so re-map a couple of // types for backward compatability. switch (p.SqlDbType) { @@ -2926,8 +3218,7 @@ internal void DeriveParameters() else { p.SqlDbType = MetaType.GetSqlDbTypeFromOleDbType((short)r[colNames[(int)ProcParamsColIndex.DataType]], - ADP.IsNull(r[colNames[(int)ProcParamsColIndex.TypeName]]) ? - ADP.StrEmpty : + ADP.IsNull(r[colNames[(int)ProcParamsColIndex.TypeName]]) ? "" : (string)r[colNames[(int)ProcParamsColIndex.TypeName]]); } @@ -2937,9 +3228,9 @@ internal void DeriveParameters() { int size = (int)a; - // Map MAX sizes correctly. The Katmai server-side proc sends 0 for these instead of -1. - // Should be fixed on the Katmai side, but would likely hold up the RI, and is safer to fix here. - // If we can get the server-side fixed before shipping Katmai, we can remove this mapping. + // Map MAX sizes correctly. The 2008 server-side proc sends 0 for these instead of -1. + // Should be fixed on the 2008 side, but would likely hold up the RI, and is safer to fix here. + // If we can get the server-side fixed before shipping 2008, we can remove this mapping. if (0 == size && (p.SqlDbType == SqlDbType.NVarChar || p.SqlDbType == SqlDbType.VarBinary || @@ -2981,12 +3272,18 @@ internal void DeriveParameters() // type name for Structured types (same as for Udt's except assign p.TypeName instead of p.UdtTypeName if (SqlDbType.Structured == p.SqlDbType) { - Debug.Assert(_activeConnection.IsKatmaiOrNewer, "Invalid datatype token received from pre-katmai server"); + Debug.Assert(_activeConnection.Is2008OrNewer, "Invalid datatype token received from pre-2008 server"); //read the type name p.TypeName = r[colNames[(int)ProcParamsColIndex.TypeCatalogName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeSchemaName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeName]]; + + // the constructed type name above is incorrectly formatted, it should be a 2 part name not 3 + // for compatibility we can't change this because the bug has existed for a long time and been + // worked around by users, so identify that it is present and catch it later in the execution + // process once users can no longer interact with with the parameter type name + p.IsDerivedParameterTypeName = true; } // XmlSchema name for Xml types @@ -3083,7 +3380,7 @@ private void CheckNotificationStateAndAutoEnlist() // ctor is called. But, if we are using default queue, then we do not have this data until // Start(). Due to this, we always delay setting options until execute. - // There is a variance in order between Start(), SqlDependency(), and Execute. This is the + // There is a variance in order between Start(), SqlDependency(), and Execute. This is the // best way to solve that problem. if (null != Notification) { @@ -3147,7 +3444,7 @@ private Task RunExecuteNonQueryTds(string methodName, bool isAsync, int timeout, } else { - AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, timeout, static () => throw SQL.CR_ReconnectTimeout()); timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart); } } @@ -3166,7 +3463,7 @@ private Task RunExecuteNonQueryTds(string methodName, bool isAsync, int timeout, // no parameters are sent over // no data reader is returned // use this overload for "batch SQL" tds token type - SqlClientEventSource.Log.TryTraceEvent(" {0}, Command executed as SQLBATCH.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.RunExecuteNonQueryTds | Info | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command executed as SQLBATCH, Command Text '{3}' ", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); Task executeTask = _stateObj.Parser.TdsExecuteSQLBatch(this.CommandText, timeout, this.Notification, _stateObj, sync: true); Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes"); @@ -3204,7 +3501,7 @@ private Task RunExecuteNonQueryTds(string methodName, bool isAsync, int timeout, private void RunExecuteNonQueryTdsSetupReconnnectContinuation(string methodName, bool isAsync, int timeout, bool asyncWrite, Task reconnectTask, long reconnectionStart, TaskCompletionSource completion) { CancellationTokenSource timeoutCTS = new CancellationTokenSource(); - AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token); + AsyncHelper.SetTimeoutException(completion, timeout, static () => SQL.CR_ReconnectTimeout(), timeoutCTS.Token); AsyncHelper.ContinueTask(reconnectTask, completion, () => { @@ -3223,7 +3520,7 @@ private void RunExecuteNonQueryTdsSetupReconnnectContinuation(string methodName, { AsyncHelper.ContinueTaskWithState(subTask, completion, state: completion, - onSuccess: (state) => ((TaskCompletionSource)state).SetResult(null) + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); } } @@ -3232,7 +3529,7 @@ private void RunExecuteNonQueryTdsSetupReconnnectContinuation(string methodName, /// /// Resets the encryption related state of the command object and each of the parameters. - /// BatchRPC doesn't need special handling to cleanup the state of each RPC object and its parameters since a new RPC object and + /// BatchRPC doesn't need special handling to cleanup the state of each RPC object and its parameters since a new RPC object and /// parameters are generated on every execution. /// private void ResetEncryptionState() @@ -3255,10 +3552,8 @@ private void ResetEncryptionState() _parameters[i].HasReceivedMetadata = false; } } - if (keysToBeSentToEnclave != null) - { - keysToBeSentToEnclave.Clear(); - } + + keysToBeSentToEnclave?.Clear(); enclavePackage = null; requiresEnclaveComputations = false; enclaveAttestationParameters = null; @@ -3468,70 +3763,73 @@ private SqlDataReader GetParameterEncryptionDataReader(out Task returnTask, Task SqlDataReader describeParameterEncryptionDataReader, ReadOnlyDictionary<_SqlRPC, _SqlRPC> describeParameterEncryptionRpcOriginalRpcMap, bool describeParameterEncryptionNeeded) { - returnTask = AsyncHelper.CreateContinuationTask(fetchInputParameterEncryptionInfoTask, () => - { - bool processFinallyBlockAsync = true; - bool decrementAsyncCountInFinallyBlockAsync = true; - - RuntimeHelpers.PrepareConstrainedRegions(); - try + returnTask = AsyncHelper.CreateContinuationTaskWithState(fetchInputParameterEncryptionInfoTask, this, + (object state) => { - // Check for any exceptions on network write, before reading. - CheckThrowSNIException(); - - // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. - // Decrement it when we are about to complete async execute reader. - SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); - if (internalConnectionTds != null) + SqlCommand command = (SqlCommand)state; + bool processFinallyBlockAsync = true; + bool decrementAsyncCountInFinallyBlockAsync = true; +#if !NET6_0_OR_GREATER + RuntimeHelpers.PrepareConstrainedRegions(); +#endif + try { - internalConnectionTds.DecrementAsyncCount(); - decrementAsyncCountInFinallyBlockAsync = false; - } + // Check for any exceptions on network write, before reading. + command.CheckThrowSNIException(); - // Complete executereader. - describeParameterEncryptionDataReader = - CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); - Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); + // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. + // Decrement it when we are about to complete async execute reader. + SqlInternalConnectionTds internalConnectionTds = command._activeConnection.GetOpenTdsConnection(); + if (internalConnectionTds != null) + { + internalConnectionTds.DecrementAsyncCount(); + decrementAsyncCountInFinallyBlockAsync = false; + } - // Read the results of describe parameter encryption. - ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, - describeParameterEncryptionRpcOriginalRpcMap); + // Complete executereader. + describeParameterEncryptionDataReader = command.CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); + Debug.Assert(null == command._stateObj, "non-null state object in PrepareForTransparentEncryption."); + + // Read the results of describe parameter encryption. + command.ReadDescribeEncryptionParameterResults(describeParameterEncryptionDataReader, describeParameterEncryptionRpcOriginalRpcMap); #if DEBUG - // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. - if (_sleepAfterReadDescribeEncryptionParameterResults) + // Failpoint to force the thread to halt to simulate cancellation of SqlCommand. + if (_sleepAfterReadDescribeEncryptionParameterResults) + { + Thread.Sleep(10000); + } +#endif + } + catch (Exception e) { - Thread.Sleep(10000); + processFinallyBlockAsync = ADP.IsCatchableExceptionType(e); + throw; } -#endif - } - catch (Exception e) - { - processFinallyBlockAsync = ADP.IsCatchableExceptionType(e); - throw; - } - finally - { - PrepareTransparentEncryptionFinallyBlock(closeDataReader: processFinallyBlockAsync, - decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync, - clearDataStructures: processFinallyBlockAsync, - wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded, - describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap, - describeParameterEncryptionDataReader: describeParameterEncryptionDataReader); - } - }, - onFailure: ((exception) => - { - if (_cachedAsyncState != null) + finally + { + command.PrepareTransparentEncryptionFinallyBlock(closeDataReader: processFinallyBlockAsync, + decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync, + clearDataStructures: processFinallyBlockAsync, + wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded, + describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap, + describeParameterEncryptionDataReader: describeParameterEncryptionDataReader); + } + }, + onFailure: static (Exception exception, object state) => { - _cachedAsyncState.ResetAsyncState(); - } + SqlCommand command = (SqlCommand)state; + if (command._cachedAsyncState != null) + { + command._cachedAsyncState.ResetAsyncState(); + } - if (exception != null) - { - throw exception; + if (exception != null) + { + throw exception; + } } - })); + ); return describeParameterEncryptionDataReader; } @@ -3545,7 +3843,9 @@ private SqlDataReader GetParameterEncryptionDataReaderAsync(out Task returnTask, bool processFinallyBlockAsync = true; bool decrementAsyncCountInFinallyBlockAsync = true; +#if !NET6_0_OR_GREATER RuntimeHelpers.PrepareConstrainedRegions(); +#endif try { // Check for any exceptions on network write, before reading. @@ -3758,6 +4058,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; tsqlParam.Value = text; tsqlParam.Size = text.Length; + tsqlParam.Direction = ParameterDirection.Input; } else { @@ -3775,6 +4076,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; tsqlParam.Value = text; tsqlParam.Size = text.Length; + tsqlParam.Direction = ParameterDirection.Input; } } @@ -3851,13 +4153,15 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques paramsParam.SqlDbType = ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; paramsParam.Size = parameterList.Length; paramsParam.Value = parameterList; + paramsParam.Direction = ParameterDirection.Input; if (attestationParameters != null) { SqlParameter attestationParametersParam = describeParameterEncryptionRequest.systemParams[2]; - attestationParametersParam.Direction = ParameterDirection.Input; + attestationParametersParam.SqlDbType = SqlDbType.VarBinary; attestationParametersParam.Size = attestationParameters.Length; attestationParametersParam.Value = attestationParameters; + attestationParametersParam.Direction = ParameterDirection.Input; } } @@ -3963,11 +4267,9 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi enclaveMetadataExists = false; } - if (isRequestedByEnclave) { - - if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl) && Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe(this._activeConnection.Parser.EnclaveType); } @@ -3981,8 +4283,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi ds.GetBytes((int)DescribeParameterEncryptionResultSet1.KeySignature, 0, keySignature, 0, keySignatureLength); } - string servername = this._activeConnection.DataSource; - SqlSecurityUtility.VerifyColumnMasterKeySignature(providerName, keyPath, servername, isRequestedByEnclave, keySignature); + SqlSecurityUtility.VerifyColumnMasterKeySignature(providerName, keyPath, isRequestedByEnclave, keySignature, _activeConnection, this); int requestedKey = currentOrdinal; SqlTceCipherInfoEntry cipherInfo; @@ -3992,14 +4293,15 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi { throw SQL.InvalidEncryptionKeyOrdinalEnclaveMetadata(requestedKey, columnEncryptionKeyTable.Count); } + if (keysToBeSentToEnclave == null) { - keysToBeSentToEnclave = new Dictionary(); - keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); + keysToBeSentToEnclave = new ConcurrentDictionary(); + keysToBeSentToEnclave.TryAdd(currentOrdinal, cipherInfo); } else if (!keysToBeSentToEnclave.ContainsKey(currentOrdinal)) { - keysToBeSentToEnclave.Add(currentOrdinal, cipherInfo); + keysToBeSentToEnclave.TryAdd(currentOrdinal, cipherInfo); } requiresEnclaveComputations = true; @@ -4085,7 +4387,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi // Decrypt the symmetric key.(This will also validate and throw if needed). Debug.Assert(_activeConnection != null, @"_activeConnection should not be null"); - SqlSecurityUtility.DecryptSymmetricKey(sqlParameter.CipherMetadata, this._activeConnection.DataSource); + SqlSecurityUtility.DecryptSymmetricKey(sqlParameter.CipherMetadata, _activeConnection, this); // This is effective only for BatchRPCMode even though we set it for non-BatchRPCMode also, // since for non-BatchRPCMode mode, paramoptions gets thrown away and reconstructed in BuildExecuteSql. @@ -4110,7 +4412,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi SqlParameter sqlParameter = rpc.userParams[index]; if (!sqlParameter.HasReceivedMetadata && sqlParameter.Direction != ParameterDirection.ReturnValue) { - // Encryption MD wasn't sent by the server - we expect the metadata to be sent for all the parameters + // Encryption MD wasn't sent by the server - we expect the metadata to be sent for all the parameters // that were sent in the original sp_describe_parameter_encryption but not necessarily for return values, // since there might be multiple return values but server will only send for one of them. // For parameters that don't need encryption, the encryption type is set to plaintext. @@ -4136,7 +4438,6 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi while (ds.Read()) { - if (attestationInfoRead) { throw SQL.MultipleRowsReturnedForAttestationInfo(); @@ -4178,8 +4479,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi } // If we are not in Batch RPC mode, update the query cache with the encryption MD. - // Enclave based Always Encrypted implementation on server side does not support cache at this point. So we should not cache if the query requires keys to be sent to enclave - if (!BatchRPCMode && !requiresEnclaveComputations && (this._parameters != null && this._parameters.Count > 0)) + if (!BatchRPCMode && ShouldCacheEncryptionMetadata && (_parameters is not null && _parameters.Count > 0)) { SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: true); } @@ -4193,7 +4493,7 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior return reader; } - // task is created in case of pending asynchronous write, returned SqlDataReader should not be utilized until that task is complete + // task is created in case of pending asynchronous write, returned SqlDataReader should not be utilized until that task is complete internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, TaskCompletionSource completion, int timeout, out Task task, out bool usedCache, bool asyncWrite = false, bool inRetry = false, [CallerMemberName] string method = "") { bool isAsync = (null != completion); @@ -4347,40 +4647,35 @@ private SqlDataReader RunExecuteReaderTdsWithTransparentParameterEncryption( { long parameterEncryptionStart = ADP.TimerCurrent(); TaskCompletionSource completion = new TaskCompletionSource(); - AsyncHelper.ContinueTask(describeParameterEncryptionTask, completion, - () => + AsyncHelper.ContinueTaskWithState(describeParameterEncryptionTask, completion, this, + (object state) => { + SqlCommand command = (SqlCommand)state; Task subTask = null; - GenerateEnclavePackage(); - RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, isAsync, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds); + command.GenerateEnclavePackage(); + command.RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, isAsync, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds); if (subTask == null) { completion.SetResult(null); } else { - AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null)); + AsyncHelper.ContinueTaskWithState(subTask, completion, completion, static (object state) => ((TaskCompletionSource)state).SetResult(null)); } }, - onFailure: ((exception) => + onFailure: static (Exception exception, object state) => { - if (_cachedAsyncState != null) - { - _cachedAsyncState.ResetAsyncState(); - } + ((SqlCommand)state)._cachedAsyncState?.ResetAsyncState(); if (exception != null) { throw exception; } - }), - onCancellation: (() => + }, + onCancellation: static (object state) => { - if (_cachedAsyncState != null) - { - _cachedAsyncState.ResetAsyncState(); - } - }) - ); + ((SqlCommand)state)._cachedAsyncState?.ResetAsyncState(); + } + ); task = completion.Task; return ds; } @@ -4399,8 +4694,11 @@ private void GenerateEnclavePackage() return; } - if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl) && + Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) + { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage(this._activeConnection.Parser.EnclaveType); + } string enclaveType = this._activeConnection.Parser.EnclaveType; if (string.IsNullOrWhiteSpace(enclaveType)) @@ -4416,7 +4714,7 @@ private void GenerateEnclavePackage() { EnclaveSessionParameters enclaveSessionParameters = new EnclaveSessionParameters(this._activeConnection.DataSource, this._activeConnection.EnclaveAttestationUrl, this._activeConnection.Database); this.enclavePackage = EnclaveDelegate.Instance.GenerateEnclavePackage(attestationProtocol, keysToBeSentToEnclave, - this.CommandText, enclaveType, enclaveSessionParameters); + this.CommandText, enclaveType, enclaveSessionParameters, _activeConnection, this); } catch (EnclaveDelegate.RetryableEnclaveQueryExecutionException) { @@ -4453,7 +4751,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi } else { - AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); }); + AsyncHelper.WaitForCompletion(reconnectTask, timeout, static () => throw SQL.CR_ReconnectTimeout()); timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart); } } @@ -4518,9 +4816,10 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi if (returnStream) { - SqlClientEventSource.Log.TryTraceEvent(" {0}, Command executed as SQLBATCH.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.RunExecuteReaderTds | Info | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command executed as SQLBATCH, Command Text '{3}' ", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); } string text = GetCommandText(cmdBehavior) + GetResetOptionsString(cmdBehavior); + //If the query requires enclave computations, pass the enclavepackage in the SQLBatch TDS stream if (requiresEnclaveComputations) { @@ -4579,7 +4878,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi rpc.options = TdsEnums.RPC_NOMETADATA; if (returnStream) { - SqlClientEventSource.Log.TryTraceEvent(" {0}, Command executed as RPC.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.RunExecuteReaderTds | Info | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command executed as RPC, RPC Name '{3}' ", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, rpc?.rpcName); } Debug.Assert(_rpcArrayOf1[0] == rpc); @@ -4597,7 +4896,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi optionSettings = GetSetOptionsString(cmdBehavior); if (returnStream) { - SqlClientEventSource.Log.TryTraceEvent(" {0}, Command executed as RPC.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.RunExecuteReaderTds | Info | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command executed as RPC, RPC Name '{3}' ", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, rpc?.rpcName); } // turn set options ON @@ -4645,7 +4944,7 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi { SqlInternalConnectionTds innerConnectionTds = (_activeConnection.InnerConnection as SqlInternalConnectionTds); if (null != innerConnectionTds) - { // it may be closed + { // it may be closed innerConnectionTds.DecrementAsyncCount(); } } @@ -4666,25 +4965,28 @@ private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavi private Task RunExecuteReaderTdsSetupContinuation(RunBehavior runBehavior, SqlDataReader ds, string optionSettings, Task writeTask) { - Task task = AsyncHelper.CreateContinuationTask(writeTask, - onSuccess: () => + Task task = AsyncHelper.CreateContinuationTaskWithState( + task: writeTask, + state: _activeConnection, + onSuccess: (object state) => { - _activeConnection.GetOpenTdsConnection(); // it will throw if connection is closed + SqlConnection sqlConnection = (SqlConnection)state; + sqlConnection.GetOpenTdsConnection(); // it will throw if connection is closed cachedAsyncState.SetAsyncReaderState(ds, runBehavior, optionSettings); }, - onFailure: (exc) => + onFailure: static (Exception exc, object state) => { - _activeConnection.GetOpenTdsConnection().DecrementAsyncCount(); + ((SqlConnection)state).GetOpenTdsConnection().DecrementAsyncCount(); } ); return task; } - // This is in its own method to avoid always allocating the lambda in RunExecuteReaderTds + // This is in its own method to avoid always allocating the lambda in RunExecuteReaderTds private void RunExecuteReaderTdsSetupReconnectContinuation(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, bool isAsync, int timeout, bool asyncWrite, bool inRetry, SqlDataReader ds, Task reconnectTask, long reconnectionStart, TaskCompletionSource completion) { CancellationTokenSource timeoutCTS = new CancellationTokenSource(); - AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token); + AsyncHelper.SetTimeoutException(completion, timeout, static () => SQL.CR_ReconnectTimeout(), timeoutCTS.Token); AsyncHelper.ContinueTask(reconnectTask, completion, () => { @@ -4704,7 +5006,7 @@ private void RunExecuteReaderTdsSetupReconnectContinuation(CommandBehavior cmdBe { AsyncHelper.ContinueTaskWithState(subTask, completion, state: completion, - onSuccess: (state) => ((TaskCompletionSource)state).SetResult(null) + onSuccess: static (object state) => ((TaskCompletionSource)state).SetResult(null) ); } } @@ -4813,7 +5115,7 @@ private void FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, stri { //This flag indicates if the datareader's metadata should be cached in this SqlCommand. //Metadata associated with sp_describe_parameter_metadats's datareader should not be cached. - //Ideally, we should be using "forDescribeParameterEncryption" flag for this, but this flag's + //Ideally, we should be using "forDescribeParameterEncryption" flag for this, but this flag's //semantics are overloaded with async workflow and this flag is always false for sync workflow. //Since we are very close to a release and changing the semantics for "forDescribeParameterEncryption" //is risky, we introduced a new parameter to determine whether we should cache a datareader's metadata or not. @@ -5105,8 +5407,8 @@ internal void OnReturnStatus(int status) // If we are not in Batch RPC mode, update the query cache with the encryption MD. // We can do this now that we have distinguished between ReturnValue and ReturnStatus. // Read comment in AddQueryMetadata() for more details. - // Enclave based Always Encrypted implementation on server side does not support cache at this point. So we should not cache if the query requires keys to be sent to enclave - if (!BatchRPCMode && CachingQueryMetadataPostponed && !requiresEnclaveComputations && (this._parameters != null && this._parameters.Count > 0)) + if (!BatchRPCMode && CachingQueryMetadataPostponed && + ShouldCacheEncryptionMetadata && (_parameters is not null && _parameters.Count > 0)) { SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: false); } @@ -5169,7 +5471,7 @@ internal void OnReturnValue(SqlReturnValue rec, TdsParserStateObject stateObj) // Get the key information from the parameter and decrypt the value. rec.cipherMD.EncryptionInfo = thisParam.CipherMetadata.EncryptionInfo; - byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(rec.value.ByteArray, rec.cipherMD, _activeConnection.DataSource); + byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(rec.value.ByteArray, rec.cipherMD, _activeConnection, this); if (unencryptedBytes != null) { @@ -5188,7 +5490,7 @@ internal void OnReturnValue(SqlReturnValue rec, TdsParserStateObject stateObj) { // Create a new SqlBuffer and set it to null // Note: We can't reuse the SqlBuffer in "rec" below since it's already been set (to varbinary) - // in previous call to TryProcessReturnValue(). + // in previous call to TryProcessReturnValue(). // Note 2: We will be coming down this code path only if the Command Setting is set to use TCE. // We pass the command setting as TCE enabled in the below call for this reason. SqlBuffer buff = new SqlBuffer(); @@ -5436,6 +5738,23 @@ private void SetUpRPCParameters(_SqlRPC rpc, bool inSchema, SqlParameterCollecti { options |= TdsEnums.RPC_PARAM_DEFAULT; } + + // detect incorrectly derived type names unchanged by the caller and fix them + if (parameter.IsDerivedParameterTypeName) + { + string[] parts = MultipartIdentifier.ParseMultipartIdentifier(parameter.TypeName, "[\"", "]\"", Strings.SQL_TDSParserTableName, false); + if (parts != null && parts.Length == 4) // will always return int[4] right justified + { + if ( + parts[3] != null && // name must not be null + parts[2] != null && // schema must not be null + parts[1] != null // server should not be null or we don't need to remove it + ) + { + parameter.TypeName = QuoteIdentifier(parts.AsSpan(2, 2)); + } + } + } } rpc.userParamMap[userParamCount] = ((((long)options) << 32) | (long)index); @@ -5476,6 +5795,7 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior) sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Value = paramList; sqlParam.Size = paramList.Length; + sqlParam.Direction = ParameterDirection.Input; //@batch_text string text = GetCommandText(behavior); @@ -5483,6 +5803,7 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior) sqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Size = text.Length; sqlParam.Value = text; + sqlParam.Direction = ParameterDirection.Input; SetUpRPCParameters(rpc, false, _parameters); return rpc; @@ -5544,16 +5865,30 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql GetRPCObject(0, userParameterCount, ref rpc); rpc.ProcID = 0; - rpc.rpcName = this.CommandText; // just get the raw command text + + // TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName + // 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523 + // each char takes 2 bytes. 523 * 2 = 1046 + int commandTextLength = ADP.CharSize * CommandText.Length; + + if (commandTextLength <= MaxRPCNameLength) + { + rpc.rpcName = CommandText; // just get the raw command text + } + else + { + throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength); + } SetUpRPCParameters(rpc, inSchema, parameters); } + // // build the RPC record header for sp_execute // // prototype for sp_execute is: // sp_execute(@handle int,param1value,param2value...) - + // private _SqlRPC BuildExecute(bool inSchema) { Debug.Assert((int)_prepareHandle != -1, "Invalid call to sp_execute without a valid handle!"); @@ -5570,6 +5905,7 @@ private _SqlRPC BuildExecute(bool inSchema) //@handle SqlParameter sqlParam = rpc.systemParams[0]; sqlParam.SqlDbType = SqlDbType.Int; + sqlParam.Size = 4; sqlParam.Value = _prepareHandle; sqlParam.Direction = ParameterDirection.Input; @@ -5622,6 +5958,7 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Size = paramList.Length; sqlParam.Value = paramList; + sqlParam.Direction = ParameterDirection.Input; bool inSchema = (0 != (behavior & CommandBehavior.SchemaOnly)); SetUpRPCParameters(rpc, inSchema, parameters); @@ -5640,11 +5977,22 @@ private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string sto { Debug.Assert(CommandType == CommandType.StoredProcedure, "BuildStoredProcedureStatementForColumnEncryption() should only be called for stored procedures"); Debug.Assert(!string.IsNullOrWhiteSpace(storedProcedureName), "storedProcedureName cannot be null or empty in BuildStoredProcedureStatementForColumnEncryption"); - Debug.Assert(parameters != null, "parameters cannot be null in BuildStoredProcedureStatementForColumnEncryption"); StringBuilder execStatement = new StringBuilder(); execStatement.Append(@"EXEC "); + if (parameters is null) + { + execStatement.Append(ParseAndQuoteIdentifier(storedProcedureName, false)); + return new SqlParameter( + null, + ((execStatement.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, + execStatement.Length) + { + Value = execStatement.ToString() + }; + } + // Find the return value parameter (if any). SqlParameter returnValueParameter = null; foreach (SqlParameter parameter in parameters) @@ -5683,7 +6031,7 @@ private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string sto { // Possibility of a SQL Injection issue through parameter names and how to construct valid identifier for parameters. // Since the parameters comes from application itself, there should not be a security vulnerability. - // Also since the query is not executed, but only analyzed there is no possibility for elevation of privilege, but only for + // Also since the query is not executed, but only analyzed there is no possibility for elevation of privilege, but only for // incorrect results which would only affect the user that attempts the injection. execStatement.AppendFormat(@" {0}={0}", parameters[index].ParameterNameFixed); @@ -5875,7 +6223,30 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete private static string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) { string[] strings = SqlParameter.ParseTypeName(identifier, isUdtTypeName); - return ADP.BuildMultiPartName(strings); + return QuoteIdentifier(strings); + } + + private static string QuoteIdentifier(ReadOnlySpan strings) + { + StringBuilder bld = new StringBuilder(); + + // Stitching back together is a little tricky. Assume we want to build a full multi-part name + // with all parts except trimming separators for leading empty names (null or empty strings, + // but not whitespace). Separators in the middle should be added, even if the name part is + // null/empty, to maintain proper location of the parts. + for (int i = 0; i < strings.Length; i++) + { + if (0 < bld.Length) + { + bld.Append('.'); + } + if (null != strings[i] && 0 != strings[i].Length) + { + ADP.AppendQuotedString(bld, "[", "]", strings[i]); + } + } + + return bld.ToString(); } // returns set option text to turn on format only and key info on and off @@ -6144,7 +6515,6 @@ internal void AddBatchCommand(string commandText, SqlParameterCollection paramet internal int ExecuteBatchRPCCommand() { - Debug.Assert(BatchRPCMode, "Command is not in batch RPC Mode"); Debug.Assert(_RPCList != null, "No batch commands specified"); @@ -6262,10 +6632,15 @@ private void NotifyDependency() public SqlCommand Clone() { SqlCommand clone = new SqlCommand(this); - SqlClientEventSource.Log.TryTraceEvent(" {0}, clone={1}", ObjectID, clone.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlCommand.Clone | API | Object Id {0}, Clone Object Id {1}, Client Connection Id {2}", ObjectID, clone.ObjectID, Connection?.ClientConnectionId); return clone; } + private void WriteBeginExecuteEvent() + { + SqlClientEventSource.Log.TryBeginExecuteEvent(ObjectID, Connection?.DataSource, Connection?.Database, CommandText, Connection?.ClientConnectionId); + } + private void WriteEndExecuteEvent(bool success, int? sqlExceptionNumber, bool synchronous) { if (SqlClientEventSource.Log.IsExecutionTraceEnabled()) @@ -6284,7 +6659,7 @@ private void WriteEndExecuteEvent(bool success, int? sqlExceptionNumber, bool sy int compositeState = successFlag | isSqlExceptionFlag | synchronousFlag; - SqlClientEventSource.Log.EndExecute(GetHashCode(), compositeState, sqlExceptionNumber.GetValueOrDefault()); + SqlClientEventSource.Log.TryEndExecuteEvent(ObjectID, compositeState, sqlExceptionNumber.GetValueOrDefault(), Connection?.ClientConnectionId); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index 082bec5c21..660b5934e0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -7,6 +7,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Data; using System.Data.Common; using System.Diagnostics; @@ -20,11 +21,13 @@ using System.Threading.Tasks; using Microsoft.Data.Common; using Microsoft.Data.ProviderBase; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; namespace Microsoft.Data.SqlClient { /// + [DefaultEvent("InfoMessage")] + [DesignerCategory("")] public sealed partial class SqlConnection : DbConnection, ICloneable { private enum CultureCheckState : uint @@ -60,6 +63,9 @@ private enum CultureCheckState : uint internal bool _suppressStateChangeForReconnection; private int _reconnectCount; + // Retry Logic + private SqlRetryLogicBaseProvider _retryLogicProvider; + // diagnostics listener private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); @@ -68,25 +74,32 @@ private enum CultureCheckState : uint // using SqlConnection.Open() method. internal bool _applyTransientFaultHandling = false; - // System column encryption key store providers are added by default - private static readonly Dictionary _SystemColumnEncryptionKeyStoreProviders - = new Dictionary(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase) - { - {SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()}, - {SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()}, - {SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()} - }; - - // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders - private static readonly object _CustomColumnEncryptionKeyProvidersLock = new object(); // status of invariant culture environment check private static CultureCheckState _cultureCheckState; + // System column encryption key store providers are added by default + private static readonly Dictionary s_systemColumnEncryptionKeyStoreProviders + = new(capacity: 3, comparer: StringComparer.OrdinalIgnoreCase) + { + { SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider() }, + { SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider() }, + { SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider() } + }; + + /// Instance-level list of custom key store providers. It can be set more than once by the user. + private IReadOnlyDictionary _customColumnEncryptionKeyStoreProviders; + + internal bool HasColumnEncryptionKeyStoreProvidersRegistered => + _customColumnEncryptionKeyStoreProviders is not null && _customColumnEncryptionKeyStoreProviders.Count > 0; + + // Lock to control setting of s_globalCustomColumnEncryptionKeyStoreProviders + private static readonly object s_globalCustomColumnEncryptionKeyProvidersLock = new(); + /// - /// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. - /// Custom provider list can only supplied once per application. + /// Global custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary. + /// Global custom provider list can only supplied once per application. /// - private static ReadOnlyDictionary _CustomColumnEncryptionKeyStoreProviders; + private static IReadOnlyDictionary s_globalCustomColumnEncryptionKeyStoreProviders; /// /// Dictionary object holding trusted key paths for various SQL Servers. @@ -94,20 +107,50 @@ private static readonly Dictionary /// IList contains a list of trusted key paths. /// private static readonly ConcurrentDictionary> _ColumnEncryptionTrustedMasterKeyPaths - = new ConcurrentDictionary>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, + = new(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 1, comparer: StringComparer.OrdinalIgnoreCase); private static readonly Action s_openAsyncCancel = OpenAsyncCancel; private static readonly Action, object> s_openAsyncComplete = OpenAsyncComplete; + private bool IsProviderRetriable => SqlConfigurableRetryFactory.IsRetriable(RetryLogicProvider); + + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public SqlRetryLogicBaseProvider RetryLogicProvider + { + get + { + if (_retryLogicProvider == null) + { + _retryLogicProvider = SqlConfigurableRetryLogicManager.ConnectionProvider; + } + return _retryLogicProvider; + } + set + { + _retryLogicProvider = value; + } + } + /// + [DefaultValue(null)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.TCE_SqlConnection_ColumnEncryptionKeyCacheTtl)] public static TimeSpan ColumnEncryptionKeyCacheTtl { get; set; } = TimeSpan.FromHours(2); /// + [DefaultValue(null)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.TCE_SqlConnection_ColumnEncryptionQueryMetadataCacheEnabled)] public static bool ColumnEncryptionQueryMetadataCacheEnabled { get; set; } = true; /// + [DefaultValue(null)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.TCE_SqlConnection_TrustedColumnMasterKeyPaths)] public static IDictionary> ColumnEncryptionTrustedMasterKeyPaths => _ColumnEncryptionTrustedMasterKeyPaths; /// @@ -149,11 +192,15 @@ public SqlConnection(string connectionString, SqlCredential credential) : this() } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveArgument(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } Credential = credential; @@ -179,60 +226,65 @@ private SqlConnection(SqlConnection connection) CacheConnectionStringProperties(); } + internal static bool TryGetSystemColumnEncryptionKeyStoreProvider(string keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider) + { + return s_systemColumnEncryptionKeyStoreProviders.TryGetValue(keyStoreName, out provider); + } + /// - /// This function walks through both system and custom column encryption key store providers and returns an object if found. + /// This function walks through both instance-level and global custom column encryption key store providers and returns an object if found. /// - /// Provider Name to be searched in System Provider diction and Custom provider dictionary. - /// If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance. + /// Provider Name to be searched for. + /// If the provider is found, initializes the corresponding SqlColumnEncryptionKeyStoreProvider instance. /// true if the provider is found, else returns false - static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) + internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) { Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid"); - // Initialize the out parameter - columnKeyStoreProvider = null; - - // Search in the sytem provider list. - if (_SystemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider)) + if (HasColumnEncryptionKeyStoreProvidersRegistered) { - return true; + return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); } - lock (_CustomColumnEncryptionKeyProvidersLock) + lock (s_globalCustomColumnEncryptionKeyProvidersLock) { // If custom provider is not set, then return false - if (_CustomColumnEncryptionKeyStoreProviders == null) + if (s_globalCustomColumnEncryptionKeyStoreProviders is null) { + columnKeyStoreProvider = null; return false; } // Search in the custom provider list - return _CustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); + return s_globalCustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider); } } /// - /// This function returns a list of system provider dictionary currently supported by this driver. + /// This function returns a list of system providers currently supported by this driver. /// /// Combined list of provider names - static internal List GetColumnEncryptionSystemKeyStoreProviders() + internal static List GetColumnEncryptionSystemKeyStoreProvidersNames() { - HashSet providerNames = new HashSet(_SystemColumnEncryptionKeyStoreProviders.Keys); - return providerNames.ToList(); + return s_systemColumnEncryptionKeyStoreProviders.Keys.ToList(); } /// - /// This function returns a list of custom provider dictionary currently registered. + /// This function returns a list of the names of the custom providers currently registered. If the + /// instance-level cache is not empty, that cache is used, else the global cache is used. /// /// Combined list of provider names - static internal List GetColumnEncryptionCustomKeyStoreProviders() + internal List GetColumnEncryptionCustomKeyStoreProvidersNames() { - if (_CustomColumnEncryptionKeyStoreProviders != null) + if (_customColumnEncryptionKeyStoreProviders is not null && + _customColumnEncryptionKeyStoreProviders.Count > 0) { - HashSet providerNames = new HashSet(_CustomColumnEncryptionKeyStoreProviders.Keys); - return providerNames.ToList(); + return _customColumnEncryptionKeyStoreProviders.Keys.ToList(); + } + if (s_globalCustomColumnEncryptionKeyStoreProviders is not null) + { + return s_globalCustomColumnEncryptionKeyStoreProviders.Keys.ToList(); } - return new List(); } @@ -251,9 +303,54 @@ internal bool IsColumnEncryptionSettingEnabled /// public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary customProviders) { + ValidateCustomProviders(customProviders); + + lock (s_globalCustomColumnEncryptionKeyProvidersLock) + { + // Provider list can only be set once + if (s_globalCustomColumnEncryptionKeyStoreProviders is not null) + { + throw SQL.CanOnlyCallOnce(); + } + + // to prevent conflicts between CEK caches, global providers should not use their own CEK caches + foreach (SqlColumnEncryptionKeyStoreProvider provider in customProviders.Values) + { + provider.ColumnEncryptionKeyCacheTtl = new TimeSpan(0); + } + + // Create a temporary dictionary and then add items from the provided dictionary. + // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs + // in the provided customerProviders dictionary. + Dictionary customColumnEncryptionKeyStoreProviders = + new(customProviders, StringComparer.OrdinalIgnoreCase); + + // Set the dictionary to the ReadOnly dictionary. + s_globalCustomColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; + } + } + + /// + public void RegisterColumnEncryptionKeyStoreProvidersOnConnection(IDictionary customProviders) + { + ValidateCustomProviders(customProviders); + + // Create a temporary dictionary and then add items from the provided dictionary. + // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs + // in the provided customerProviders dictionary. + Dictionary customColumnEncryptionKeyStoreProviders = + new(customProviders, StringComparer.OrdinalIgnoreCase); - // Return when the provided dictionary is null. - if (customProviders == null) + // Set the dictionary to the ReadOnly dictionary. + // This method can be called more than once. Re-registering a new collection will replace the + // old collection of providers. + _customColumnEncryptionKeyStoreProviders = customColumnEncryptionKeyStoreProviders; + } + + private static void ValidateCustomProviders(IDictionary customProviders) + { + // Throw when the provided dictionary is null. + if (customProviders is null) { throw SQL.NullCustomKeyStoreProviderDictionary(); } @@ -276,29 +373,11 @@ public static void RegisterColumnEncryptionKeyStoreProviders(IDictionary customColumnEncryptionKeyStoreProviders = - new Dictionary(customProviders, StringComparer.OrdinalIgnoreCase); - - // Set the dictionary to the ReadOnly dictionary. - _CustomColumnEncryptionKeyStoreProviders = new ReadOnlyDictionary(customColumnEncryptionKeyStoreProviders); - } } /// @@ -318,6 +397,14 @@ internal SqlConnectionAttestationProtocol AttestationProtocol } } + /// + /// Get IP address preference + /// + internal SqlConnectionIPAddressPreference iPAddressPreference + { + get => ((SqlConnectionString)ConnectionOptions).IPAddressPreference; + } + // This method will be called once connection string is set or changed. private void CacheConnectionStringProperties() { @@ -325,9 +412,15 @@ private void CacheConnectionStringProperties() if (connString != null) { _connectRetryCount = connString.ConnectRetryCount; + // For Azure Synapse ondemand connections, set _connectRetryCount to 5 instead of 1 to greatly improve recovery + // success rate. Note: Synapse should be detected first as it could be detected as a regular Azure SQL DB endpoint. + if (_connectRetryCount == 1 && ADP.IsAzureSynapseOnDemandEndpoint(connString.DataSource)) + { + _connectRetryCount = 5; + } // For Azure SQL connection, set _connectRetryCount to 2 instead of 1 will greatly improve recovery - // success rate - if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) + // success rate + else if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) { _connectRetryCount = 2; } @@ -348,6 +441,9 @@ private void CacheConnectionStringProperties() // Create a new SqlStatistics object if not already there. // connect the parser to the object. // if there is no parser at this time we need to connect it after creation. + [DefaultValue(false)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_StatisticsEnabled)] public bool StatisticsEnabled { get @@ -365,7 +461,7 @@ public bool StatisticsEnabled if (null == _statistics) { _statistics = new SqlStatistics(); - ADP.TimerCurrent(out _statistics._openTimestamp); + _statistics._openTimestamp = ADP.TimerCurrent(); } // set statistics on the parser // update timestamp; @@ -385,7 +481,7 @@ public bool StatisticsEnabled TdsParser parser = Parser; Debug.Assert(parser != null, "Where's the parser?"); parser.Statistics = null; - ADP.TimerCurrent(out _statistics._closeTimestamp); + _statistics._closeTimestamp = ADP.TimerCurrent(); } } } @@ -425,6 +521,11 @@ private bool UsesActiveDirectoryMSI(SqlConnectionString opt) return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI; } + private bool UsesActiveDirectoryDefault(SqlConnectionString opt) + { + return opt != null && opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault; + } + private bool UsesAuthentication(SqlConnectionString opt) { return opt != null && opt.Authentication != SqlAuthenticationMethod.NotSpecified; @@ -468,6 +569,11 @@ internal int ConnectRetryInterval } /// + [DefaultValue("")] + [SettingsBindableAttribute(true)] + [RefreshProperties(RefreshProperties.All)] + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_ConnectionString)] public override string ConnectionString { get @@ -482,7 +588,7 @@ public override string ConnectionString if (_credential != null) { // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -499,11 +605,15 @@ public override string ConnectionString } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingManagedIdentityWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingNonInteractiveWithCredential(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); @@ -520,6 +630,8 @@ public override string ConnectionString } /// + [ResDescription(StringsHelper.ResourceNames.SqlConnection_ConnectionTimeout)] + [ResCategory(StringsHelper.ResourceNames.SqlConnection_DataSource)] public override int ConnectionTimeout { get @@ -530,6 +642,8 @@ public override int ConnectionTimeout } /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_ConnectionTimeout)] public int CommandTimeout { get @@ -541,6 +655,9 @@ public int CommandTimeout /// // AccessToken: To be used for token based authentication + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_AccessToken)] public string AccessToken { get @@ -572,6 +689,8 @@ public string AccessToken } /// + [ResDescription(StringsHelper.ResourceNames.SqlConnection_Database)] + [ResCategory(StringsHelper.ResourceNames.SqlConnection_DataSource)] public override string Database { // if the connection is open, we need to ask the inner connection what it's @@ -642,6 +761,10 @@ internal string SQLDNSCachingSupportedStateBeforeRedirect } /// + [Browsable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_DataSource)] + [ResCategory(StringsHelper.ResourceNames.SqlConnection_DataSource)] public override string DataSource { get @@ -663,6 +786,9 @@ public override string DataSource } /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_PacketSize)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int PacketSize { // if the connection is open, we need to ask the inner connection what it's @@ -687,6 +813,9 @@ public int PacketSize } /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_ClientConnectionId)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Guid ClientConnectionId { get @@ -712,19 +841,34 @@ public Guid ClientConnectionId } /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_ServerVersion)] public override string ServerVersion { get => GetOpenTdsConnection().ServerVersion; } /// + [Browsable(false)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_ServerProcessId)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ServerProcessId { - get => State.Equals(ConnectionState.Open) | State.Equals(ConnectionState.Executing) | State.Equals(ConnectionState.Fetching) ? - GetOpenTdsConnection().ServerProcessId : 0; + get + { + if ((State & (ConnectionState.Open | ConnectionState.Executing | ConnectionState.Fetching)) > 0) + { + return GetOpenTdsConnection().ServerProcessId; + } + return 0; + } } /// + [Browsable(false)] + [ResDescription(StringsHelper.ResourceNames.DbConnection_State)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override ConnectionState State { get @@ -745,6 +889,9 @@ internal SqlStatistics Statistics } /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Data)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_WorkstationId)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string WorkstationId { get @@ -759,6 +906,9 @@ public string WorkstationId } /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [ResDescription(StringsHelper.ResourceNames.SqlConnection_Credential)] public SqlCredential Credential { get @@ -789,7 +939,7 @@ public SqlCredential Credential { var connectionOptions = (SqlConnectionString)ConnectionOptions; // Check for Credential being used with Authentication=ActiveDirectoryIntegrated | ActiveDirectoryInteractive | - // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI. Since a different error string is used + // ActiveDirectoryDeviceCodeFlow | ActiveDirectoryManagedIdentity/ActiveDirectoryMSI | ActiveDirectoryDefault. Since a different error string is used // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters. if (UsesActiveDirectoryIntegrated(connectionOptions)) @@ -806,11 +956,15 @@ public SqlCredential Credential } else if (UsesActiveDirectoryManagedIdentity(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryManagedIdentityString); } else if (UsesActiveDirectoryMSI(connectionOptions)) { - throw SQL.SettingCredentialWithManagedIdentityInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryMSIString); + } + else if (UsesActiveDirectoryDefault(connectionOptions)) + { + throw SQL.SettingCredentialWithNonInteractiveInvalid(DbConnectionStringBuilderUtil.ActiveDirectoryDefaultString); } CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions); @@ -885,6 +1039,8 @@ protected override DbProviderFactory DbProviderFactory // /// + [ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_InfoMessage)] + [ResDescription(StringsHelper.ResourceNames.DbConnection_InfoMessage)] public event SqlInfoMessageEventHandler InfoMessage; /// @@ -942,8 +1098,7 @@ public SqlTransaction BeginTransaction(string transactionName) [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] override protected DbTransaction BeginDbTransaction(System.Data.IsolationLevel isolationLevel) { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}, isolationLevel={1}", ObjectID, (int)isolationLevel); - try + using (TryEventScope.Create("SqlConnection.BeginDbTransaction | API | Object Id {0}, Isolation Level {1}", ObjectID, (int)isolationLevel)) { DbTransaction transaction = BeginTransaction(isolationLevel); @@ -955,10 +1110,6 @@ override protected DbTransaction BeginDbTransaction(System.Data.IsolationLevel i return transaction; } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } /// @@ -966,32 +1117,33 @@ public SqlTransaction BeginTransaction(System.Data.IsolationLevel iso, string tr { WaitForPendingReconnection(); SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}, iso={1}, transactionName='{2}'", ObjectID, (int)iso, transactionName); - try + using (TryEventScope.Create(SqlClientEventSource.Log.TryScopeEnterEvent("SqlConnection.BeginTransaction | API | Object Id {0}, Iso {1}, Transaction Name '{2}'", ObjectID, (int)iso, transactionName))) { - statistics = SqlStatistics.StartTimer(Statistics); - - SqlTransaction transaction; - bool isFirstAttempt = true; - do + try { - transaction = GetOpenTdsConnection().BeginSqlTransaction(iso, transactionName, isFirstAttempt); // do not reconnect twice - Debug.Assert(isFirstAttempt || !transaction.InternalTransaction.ConnectionHasBeenRestored, "Restored connection on non-first attempt"); - isFirstAttempt = false; - } while (transaction.InternalTransaction.ConnectionHasBeenRestored); + statistics = SqlStatistics.StartTimer(Statistics); + SqlTransaction transaction; + bool isFirstAttempt = true; + do + { + transaction = GetOpenTdsConnection().BeginSqlTransaction(iso, transactionName, isFirstAttempt); // do not reconnect twice + Debug.Assert(isFirstAttempt || !transaction.InternalTransaction.ConnectionHasBeenRestored, "Restored connection on non-first attempt"); + isFirstAttempt = false; + } while (transaction.InternalTransaction.ConnectionHasBeenRestored); - // The GetOpenConnection line above doesn't keep a ref on the outer connection (this), - // and it could be collected before the inner connection can hook it to the transaction, resulting in - // a transaction with a null connection property. Use GC.KeepAlive to ensure this doesn't happen. - GC.KeepAlive(this); - return transaction; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); + // The GetOpenConnection line above doesn't keep a ref on the outer connection (this), + // and it could be collected before the inner connection can hook it to the transaction, resulting in + // a transaction with a null connection property. Use GC.KeepAlive to ensure this doesn't happen. + GC.KeepAlive(this); + + return transaction; + } + finally + { + SqlStatistics.StopTimer(statistics); + } } } @@ -1000,7 +1152,7 @@ public override void ChangeDatabase(string database) { SqlStatistics statistics = null; RepairInnerConnection(); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID{0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.ChangeDatabase | API | Correlation | Object Id {0}, Activity Id {1}, Database {2}", ObjectID, ActivityCorrelator.Current, database); try { statistics = SqlStatistics.StartTimer(Statistics); @@ -1045,10 +1197,10 @@ private void CloseInnerConnection() /// public override void Close() { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - try + using (TryEventScope.Create("SqlConnection.Close | API | Object Id {0}", ObjectID)) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.Close | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, ClientConnectionId); + ConnectionState previousState = State; Guid operationId = default(Guid); Guid clientConnectionId = default(Guid); @@ -1095,7 +1247,7 @@ public override void Close() if (null != Statistics) { - ADP.TimerCurrent(out _statistics._closeTimestamp); + _statistics._closeTimestamp = ADP.TimerCurrent(); } } catch (Exception ex) @@ -1122,10 +1274,6 @@ public override void Close() } } } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } /// @@ -1162,13 +1310,16 @@ public override void Open() Open(SqlConnectionOverrides.None); } + private bool TryOpenWithRetry(TaskCompletionSource retry, SqlConnectionOverrides overrides) + => RetryLogicProvider.Execute(this, () => TryOpen(retry, overrides)); + /// public void Open(SqlConnectionOverrides overrides) { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - try + using (TryEventScope.Create("SqlConnection.Open | API | Correlation | Object Id {0}, Activity Id {1}", ObjectID, ActivityCorrelator.Current)) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.Open | API | Correlation | Object Id {0}, Activity Id {1}", ObjectID, ActivityCorrelator.Current); + Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this); PrepareStatisticsForNewConnection(); @@ -1179,8 +1330,7 @@ public void Open(SqlConnectionOverrides overrides) try { statistics = SqlStatistics.StartTimer(Statistics); - - if (!TryOpen(null, overrides)) + if (!(IsProviderRetriable ? TryOpenWithRetry(null, overrides) : TryOpen(null, overrides))) { throw ADP.InternalError(ADP.InternalErrorCode.SynchronousConnectReturnedPending); } @@ -1204,10 +1354,6 @@ public void Open(SqlConnectionOverrides overrides) } } } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } internal void RegisterWaitingForReconnect(Task waitingTask) @@ -1240,7 +1386,7 @@ private async Task ReconnectAsync(int timeout) { if (ctoken.IsCancellationRequested) { - SqlClientEventSource.Log.TryTraceEvent(" Original ClientConnectionID: {0} - reconnection cancelled.", _originalConnectionId); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.ReconnectAsync | Info | Original Client Connection Id {0}, reconnection cancelled.", _originalConnectionId); return; } try @@ -1259,15 +1405,15 @@ private async Task ReconnectAsync(int timeout) { ForceNewConnection = false; } - SqlClientEventSource.Log.TryTraceEvent(" Reconnection succeeded. ClientConnectionID {0} -> {1}", _originalConnectionId, ClientConnectionId); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.ReconnectAsync | Info | Reconnection succeeded. Client Connection Id {0} -> {1}", _originalConnectionId, ClientConnectionId); return; } catch (SqlException e) { - SqlClientEventSource.Log.TryTraceEvent(" Original ClientConnectionID {0} - reconnection attempt failed error {1}", _originalConnectionId, e.Message); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.ReconnectAsync | Info | Original Client Connection Id {0}, reconnection attempt failed error {1}", _originalConnectionId, e.Message); if (attempt == retryCount - 1) { - SqlClientEventSource.Log.TryTraceEvent(" Original ClientConnectionID {0} - give up reconnection", _originalConnectionId); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.ReconnectAsync | Info | Original Client Connection Id {0}, give up reconnection", _originalConnectionId); throw SQL.CR_AllAttemptsFailed(e, _originalConnectionId); } if (timeout > 0 && ADP.TimerRemaining(commandTimeoutExpiration) < ADP.TimerFromSeconds(ConnectRetryInterval)) @@ -1340,7 +1486,7 @@ internal Task ValidateAndReconnect(Action beforeDisconnect, int timeout) { // could change since the first check, but now is stable since connection is know to be broken _originalConnectionId = ClientConnectionId; - SqlClientEventSource.Log.TryTraceEvent(" Connection ClientConnectionID {0} is invalid, reconnecting", _originalConnectionId); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.ValidateAndReconnect | Info | Connection Client Connection Id {0} is invalid, reconnecting", _originalConnectionId); _recoverySessionData = cData; if (beforeDisconnect != null) { @@ -1430,11 +1576,19 @@ private void CancelOpenAndWait() Debug.Assert(_currentCompletion == null, "After waiting for an async call to complete, there should be no completion source"); } + private Task InternalOpenWithRetryAsync(CancellationToken cancellationToken) + => RetryLogicProvider.ExecuteAsync(this, () => InternalOpenAsync(cancellationToken), cancellationToken); + /// public override Task OpenAsync(CancellationToken cancellationToken) + => IsProviderRetriable ? + InternalOpenWithRetryAsync(cancellationToken) : + InternalOpenAsync(cancellationToken); + + private Task InternalOpenAsync(CancellationToken cancellationToken) { - long scopeID = SqlClientEventSource.Log.TryPoolerScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + long scopeID = SqlClientEventSource.Log.TryPoolerScopeEnterEvent("SqlConnection.InternalOpenAsync | API | Object Id {0}", ObjectID); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.InternalOpenAsync | API | Correlation | Object Id {0}, Activity Id {1}", ObjectID, ActivityCorrelator.Current); try { Guid operationId = s_diagnosticListener.WriteConnectionOpenBefore(this); @@ -1466,7 +1620,6 @@ public override Task OpenAsync(CancellationToken cancellationToken) return result.Task; } - bool completed; try @@ -1521,10 +1674,12 @@ private static void OpenAsyncComplete(Task task, object state) SqlConnection connection = (SqlConnection)task.AsyncState; if (task.Exception != null) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.OpenAsyncComplete | Error | Correlation | Activity Id {0}, Exception {1}", ActivityCorrelator.Current, task.Exception.Message); s_diagnosticListener.WriteConnectionOpenError(operationId, connection, task.Exception); } else { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.OpenAsyncComplete | Info | Correlation | Activity Id {0}, Client Connection Id {1}", ActivityCorrelator.Current, connection?.ClientConnectionId); s_diagnosticListener.WriteConnectionOpenAfter(operationId, connection); } } @@ -1549,6 +1704,7 @@ public override DataTable GetSchema(string collectionName) /// public override DataTable GetSchema(string collectionName, string[] restrictionValues) { + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.GetSchema | Info | Object Id {0}, Collection Name '{1}'", ObjectID, collectionName); return InnerConnection.GetSchema(ConnectionFactory, PoolGroup, this, collectionName, restrictionValues); } @@ -1565,11 +1721,12 @@ public OpenAsyncRetry(SqlConnection parent, TaskCompletionSource retryTask) { - SqlClientEventSource.Log.TryTraceEvent(" {0}", _parent.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.Retry | Info | Object Id {0}", _parent?.ObjectID); _registration.Dispose(); try { @@ -1669,7 +1826,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec (connectionOptions.Authentication == SqlAuthenticationMethod.SqlPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword || connectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) && - (!connectionOptions.HasUserIdKeyword || !connectionOptions.HasPasswordKeyword) && + (!connectionOptions._hasUserIdKeyword || !connectionOptions._hasPasswordKeyword) && _credential == null) { throw SQL.CredentialsNotProvided(connectionOptions.Authentication); @@ -1709,7 +1866,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec if (StatisticsEnabled || (s_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterExecuteCommand) && statistics != null)) { - ADP.TimerCurrent(out _statistics._openTimestamp); + _statistics._openTimestamp = ADP.TimerCurrent(); tdsInnerConnection.Parser.Statistics = _statistics; } else @@ -1748,15 +1905,15 @@ internal bool HasLocalTransactionFromAPI } - internal bool IsKatmaiOrNewer + internal bool Is2008OrNewer { get { if (_currentReconnectionTask != null) { // holds true even if task is completed - return true; // if CR is enabled, connection, if established, will be Katmai+ + return true; // if CR is enabled, connection, if established, will be 2008+ } - return GetOpenTdsConnection().IsKatmaiOrNewer; + return GetOpenTdsConnection().Is2008OrNewer; } } @@ -1832,7 +1989,7 @@ internal void OnError(SqlException exception, bool breakConnection, Action {0}, Connection broken.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.OnError | Info | Object Id {0}, Connection broken.", ObjectID); Close(); } }; @@ -1841,7 +1998,7 @@ internal void OnError(SqlException exception, bool breakConnection, Action {0}, Connection broken.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.OnError | Info | Object Id {0}, Connection broken.", ObjectID); Close(); } } @@ -1892,7 +2049,7 @@ internal void OnInfoMessage(SqlInfoMessageEventArgs imevent) internal void OnInfoMessage(SqlInfoMessageEventArgs imevent, out bool notified) { - SqlClientEventSource.Log.TryTraceEvent(" {0}, Message='{1}'", ObjectID, imevent.Message); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.OnInfoMessage | API | Info | Object Id {0}, Message '{1}'", ObjectID, imevent.Message); SqlInfoMessageEventHandler handler = InfoMessage; if (null != handler) { @@ -1918,10 +2075,10 @@ internal void OnInfoMessage(SqlInfoMessageEventArgs imevent, out bool notified) /// public static void ChangePassword(string connectionString, string newPassword) { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(""); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ActivityID {0}", ActivityCorrelator.Current); - try + using (TryEventScope.Create("SqlConnection.ChangePassword | API | Password change requested.")) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.ChangePassword | API | Correlation | ActivityID {0}", ActivityCorrelator.Current); + if (string.IsNullOrEmpty(connectionString)) { throw SQL.ChangePasswordArgumentMissing(nameof(newPassword)); @@ -1949,19 +2106,15 @@ public static void ChangePassword(string connectionString, string newPassword) ChangePassword(connectionString, connectionOptions, null, newPassword, null); } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } /// public static void ChangePassword(string connectionString, SqlCredential credential, SecureString newSecurePassword) { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(""); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ActivityID {0}", ActivityCorrelator.Current); - try + using (TryEventScope.Create("SqlConnection.ChangePassword | API | Password change requested.")) { + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlConnection.ChangePassword | API | Correlation | ActivityID {0}", ActivityCorrelator.Current); + if (string.IsNullOrEmpty(connectionString)) { throw SQL.ChangePasswordArgumentMissing(nameof(connectionString)); @@ -2010,10 +2163,6 @@ public static void ChangePassword(string connectionString, SqlCredential credent ChangePassword(connectionString, connectionOptions, credential, null, newSecurePassword); } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } private static void ChangePassword(string connectionString, SqlConnectionString connectionOptions, SqlCredential credential, string newPassword, SecureString newSecurePassword) @@ -2047,7 +2196,7 @@ internal Task RegisterForConnectionCloseNotification(Task outerTask, ob { // Connection exists, schedule removal, will be added to ref collection after calling ValidateAndReconnect return outerTask.ContinueWith( - continuationFunction: (task, state) => + continuationFunction: static (task, state) => { Tuple parameters = (Tuple)state; SqlConnection connection = parameters.Item1; @@ -2070,7 +2219,7 @@ public void ResetStatistics() if (ConnectionState.Open == State) { // update timestamp; - ADP.TimerCurrent(out _statistics._openTimestamp); + _statistics._openTimestamp = ADP.TimerCurrent(); } } } @@ -2094,7 +2243,7 @@ private void UpdateStatistics() if (ConnectionState.Open == State) { // update timestamp - ADP.TimerCurrent(out _statistics._closeTimestamp); + _statistics._closeTimestamp = ADP.TimerCurrent(); } // delegate the rest of the work to the SqlStatistics class Statistics.UpdateStatistics(); @@ -2138,7 +2287,7 @@ private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) { if (asmRef.Version != TypeSystemAssemblyVersion && SqlClientEventSource.Log.IsTraceEnabled()) { - SqlClientEventSource.Log.TryTraceEvent(" SQL CLR type version change: Server sent {0}, client will instantiate {1}", asmRef.Version, TypeSystemAssemblyVersion); + SqlClientEventSource.Log.TryTraceEvent("SqlConnection.ResolveTypeAssembly | SQL CLR type version change: Server sent {0}, client will instantiate {1}", asmRef.Version, TypeSystemAssemblyVersion); } asmRef.Version = TypeSystemAssemblyVersion; } @@ -2198,7 +2347,7 @@ internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnD MemoryStream stm = new MemoryStream((byte[])value); - o = SerializationHelperSql9.Deserialize(stm, metaData.udt?.Type); + o = Server.SerializationHelperSql9.Deserialize(stm, metaData.udt?.Type); Debug.Assert(o != null, "object could NOT be created"); return o; @@ -2226,7 +2375,7 @@ internal byte[] GetBytes(object o, out Format format, out int maxSize) using (MemoryStream stm = new MemoryStream(maxSize < 0 ? 0 : maxSize)) { - SerializationHelperSql9.Serialize(stm, o); + Server.SerializationHelperSql9.Serialize(stm, o); retval = stm.ToArray(); } return retval; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.AssemblyLoadContext.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.AssemblyLoadContext.cs new file mode 100644 index 0000000000..7338b8d4d2 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.AssemblyLoadContext.cs @@ -0,0 +1,23 @@ +// 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 System.Runtime.Loader; + +namespace Microsoft.Data.SqlClient +{ + sealed internal partial class SqlConnectionFactory + { + partial void SubscribeToAssemblyLoadContextUnload() + { + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += SqlConnectionFactoryAssemblyLoadContext_Unloading; + } + + private void SqlConnectionFactoryAssemblyLoadContext_Unloading(AssemblyLoadContext obj) + { + Unload(obj, EventArgs.Empty); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs index 42b9e7a6d1..93450aec7b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs @@ -11,12 +11,15 @@ namespace Microsoft.Data.SqlClient { - sealed internal class SqlConnectionFactory : DbConnectionFactory + sealed internal partial class SqlConnectionFactory : DbConnectionFactory { private const string _metaDataXml = "MetaDataXml"; - private SqlConnectionFactory() : base() { } + private SqlConnectionFactory() : base() + { + SubscribeToAssemblyLoadContextUnload(); + } public static readonly SqlConnectionFactory SingletonInstance = new SqlConnectionFactory(); @@ -90,7 +93,7 @@ override protected DbConnectionInternal CreateConnection(DbConnectionOptions opt { // We throw an exception in case of a failure // NOTE: Cloning connection option opt to set 'UserInstance=True' and 'Enlist=False' - // This first connection is established to SqlExpress to get the instance name + // This first connection is established to SqlExpress to get the instance name // of the UserInstance. SqlConnectionString sseopt = new SqlConnectionString(opt, opt.DataSource, userInstance: true, setEnlistValue: false); sseConnection = new SqlInternalConnectionTds(identity, sseopt, key.Credential, null, "", null, false, applyTransientFaultHandling: applyTransientFaultHandling); @@ -182,7 +185,7 @@ override protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions { connectionTimeout *= 10; } - SqlClientEventSource.Log.TryTraceEvent("Set connection pool CreateTimeout={0} when {1} is in use.", connectionTimeout, opt.Authentication); + SqlClientEventSource.Log.TryTraceEvent("SqlConnectionFactory.CreateConnectionPoolGroupOptions | Set connection pool CreateTimeout '{0}' when Authentication mode '{1}' is used.", connectionTimeout, opt.Authentication); } poolingOptions = new DbConnectionPoolGroupOptions( opt.IntegratedSecurity, @@ -306,6 +309,20 @@ protected override DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection.ServerVersion, internalConnection.ServerVersion); } + + private void Unload(object sender, EventArgs e) + { + try + { + Unload(); + } + finally + { + ClearAllPools(); + } + } + + partial void SubscribeToAssemblyLoadContextUnload(); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs index cf9ab34507..a34be614cd 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs @@ -153,8 +153,7 @@ internal void AddWeakReference(object value, int tag) /// override protected DbCommand CreateDbCommand() { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - try + using (TryEventScope.Create(" {0}", ObjectID)) { DbCommand command = null; DbProviderFactory providerFactory = ConnectionFactory.ProviderFactory; @@ -162,10 +161,6 @@ override protected DbCommand CreateDbCommand() command.Connection = this; return command; } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs deleted file mode 100644 index aeaa3fd36e..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs +++ /dev/null @@ -1,91 +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. - -using Microsoft.Data.ProviderBase; - -namespace Microsoft.Data.SqlClient -{ - sealed internal class SqlConnectionPoolGroupProviderInfo : DbConnectionPoolGroupProviderInfo - { - private string _alias; - private string _failoverPartner; - private bool _useFailoverPartner; - - internal SqlConnectionPoolGroupProviderInfo(SqlConnectionString connectionOptions) - { - // This is for the case where the user specified the failover partner - // in the connection string and we have not yet connected to get the - // env change. - _failoverPartner = connectionOptions.FailoverPartner; - - if (string.IsNullOrEmpty(_failoverPartner)) - { - _failoverPartner = null; - } - } - - internal string FailoverPartner - { - get - { - return _failoverPartner; - } - } - - internal bool UseFailoverPartner - { - get - { - return _useFailoverPartner; - } - } - - internal void AliasCheck(string server) - { - if (_alias != server) - { - lock (this) - { - if (null == _alias) - { - _alias = server; - } - else if (_alias != server) - { - SqlClientEventSource.Log.TryTraceEvent(" alias change detected. Clearing PoolGroup"); - base.PoolGroup.Clear(); - _alias = server; - } - } - } - } - - - internal void FailoverCheck(SqlInternalConnection connection, bool actualUseFailoverPartner, SqlConnectionString userConnectionOptions, string actualFailoverPartner) - { - if (UseFailoverPartner != actualUseFailoverPartner) - { - SqlClientEventSource.Log.TryTraceEvent(" Failover detected. failover partner='{0}'. Clearing PoolGroup", actualFailoverPartner); - base.PoolGroup.Clear(); - _useFailoverPartner = actualUseFailoverPartner; - } - // Only construct a new permission set when we're connecting to the - // primary data source, not the failover partner. - if (!_useFailoverPartner && _failoverPartner != actualFailoverPartner) - { - // NOTE: we optimistically generate the permission set to keep - // lock short, but we only do this when we get a new - // failover partner. - - lock (this) - { - if (_failoverPartner != actualFailoverPartner) - { - _failoverPartner = actualFailoverPartner; - } - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs deleted file mode 100644 index f9a43f0c02..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionPoolKey.cs +++ /dev/null @@ -1,97 +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. - -using System; -using System.Diagnostics; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - // SqlConnectionPoolKey: Implementation of a key to connection pool groups for specifically to be used for SqlConnection - // Connection string and SqlCredential are used as a key - internal class SqlConnectionPoolKey : DbConnectionPoolKey - { - private int _hashValue; - private SqlCredential _credential; - private readonly string _accessToken; - - internal SqlConnectionPoolKey(string connectionString, SqlCredential credential, string accessToken) : base(connectionString) - { - Debug.Assert(_credential == null || _accessToken == null, "Credential and AccessToken can't have the value at the same time."); - _credential = credential; - _accessToken = accessToken; - CalculateHashCode(); - } - - private SqlConnectionPoolKey(SqlConnectionPoolKey key) : base(key) - { - _credential = key.Credential; - _accessToken = key.AccessToken; - CalculateHashCode(); - } - - public override object Clone() - { - return new SqlConnectionPoolKey(this); - } - - internal override string ConnectionString - { - get - { - return base.ConnectionString; - } - - set - { - base.ConnectionString = value; - CalculateHashCode(); - } - } - - internal SqlCredential Credential => _credential; - - internal string AccessToken - { - get - { - return _accessToken; - } - } - - public override bool Equals(object obj) - { - SqlConnectionPoolKey key = obj as SqlConnectionPoolKey; - return (key != null - && _credential == key._credential - && ConnectionString == key.ConnectionString - && string.CompareOrdinal(_accessToken, key._accessToken) == 0); - } - - public override int GetHashCode() - { - return _hashValue; - } - - private void CalculateHashCode() - { - _hashValue = base.GetHashCode(); - - if (_credential != null) - { - unchecked - { - _hashValue = _hashValue * 17 + _credential.GetHashCode(); - } - } - else if (_accessToken != null) - { - unchecked - { - _hashValue = _hashValue * 17 + _accessToken.GetHashCode(); - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.NetCoreApp.cs deleted file mode 100644 index d678cbae61..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionString.NetCoreApp.cs +++ /dev/null @@ -1,39 +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. - -using System; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - internal sealed partial class SqlConnectionString : DbConnectionOptions - { - internal static partial class DEFAULT - { - internal const PoolBlockingPeriod PoolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; - } - - private readonly PoolBlockingPeriod _poolBlockingPeriod; - - internal PoolBlockingPeriod PoolBlockingPeriod { get { return _poolBlockingPeriod; } } - - internal Microsoft.Data.SqlClient.PoolBlockingPeriod ConvertValueToPoolBlockingPeriod() - { - string value; - if (!TryGetParsetableValue(KEY.PoolBlockingPeriod, out value)) - { - return DEFAULT.PoolBlockingPeriod; - } - - try - { - return DbConnectionStringBuilderUtil.ConvertToPoolBlockingPeriod(KEY.PoolBlockingPeriod, value); - } - catch (Exception e) when (e is FormatException || e is OverflowException) - { - throw ADP.InvalidConnectionOptionValue(KEY.PoolBlockingPeriod, e); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.NetCoreApp.cs deleted file mode 100644 index cbf55f7a8c..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.NetCoreApp.cs +++ /dev/null @@ -1,42 +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. - -using System.Data.Common; -using System.Diagnostics; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - public sealed partial class SqlConnectionStringBuilder : DbConnectionStringBuilder - { - private PoolBlockingPeriod _poolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; - - private void SetPoolBlockingPeriodValue(PoolBlockingPeriod value) - { - Debug.Assert(DbConnectionStringBuilderUtil.IsValidPoolBlockingPeriodValue(value), "Invalid value for PoolBlockingPeriod"); - base[DbConnectionStringKeywords.PoolBlockingPeriod] = DbConnectionStringBuilderUtil.PoolBlockingPeriodToString(value); - } - - private static PoolBlockingPeriod ConvertToPoolBlockingPeriod(string keyword, object value) - { - return DbConnectionStringBuilderUtil.ConvertToPoolBlockingPeriod(keyword, value); - } - - /// - public PoolBlockingPeriod PoolBlockingPeriod - { - get { return _poolBlockingPeriod; } - set - { - if (!DbConnectionStringBuilderUtil.IsValidPoolBlockingPeriodValue(value)) - { - throw ADP.InvalidEnumerationValue(typeof(PoolBlockingPeriod), (int)value); - } - - SetPoolBlockingPeriodValue(value); - _poolBlockingPeriod = value; - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs deleted file mode 100644 index 0a7a06659a..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ /dev/null @@ -1,1309 +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. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Data.Common; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed partial class SqlConnectionStringBuilder : DbConnectionStringBuilder - { - private enum Keywords - { // specific ordering for ConnectionString output construction - // NamedConnection, - DataSource, - FailoverPartner, - AttachDBFilename, - InitialCatalog, - IntegratedSecurity, - PersistSecurityInfo, - UserID, - Password, - Enlist, - Pooling, - MinPoolSize, - MaxPoolSize, -#if NETCOREAPP - PoolBlockingPeriod, -#endif - MultipleActiveResultSets, - Replication, - ConnectTimeout, - Encrypt, - TrustServerCertificate, - LoadBalanceTimeout, - PacketSize, - TypeSystemVersion, - Authentication, - ApplicationName, - CurrentLanguage, - WorkstationID, - UserInstance, - TransactionBinding, - ApplicationIntent, - MultiSubnetFailover, - ConnectRetryCount, - ConnectRetryInterval, - ColumnEncryptionSetting, - EnclaveAttestationUrl, - AttestationProtocol, - - CommandTimeout, - - // keep the count value last - KeywordsCount - } - - internal const int KeywordsCount = (int)Keywords.KeywordsCount; - internal const int DeprecatedKeywordsCount = 4; - - private static readonly string[] s_validKeywords = CreateValidKeywords(); - private static readonly Dictionary s_keywords = CreateKeywordsDictionary(); - - private ApplicationIntent _applicationIntent = DbConnectionStringDefaults.ApplicationIntent; - private string _applicationName = DbConnectionStringDefaults.ApplicationName; - private string _attachDBFilename = DbConnectionStringDefaults.AttachDBFilename; - private string _currentLanguage = DbConnectionStringDefaults.CurrentLanguage; - private string _dataSource = DbConnectionStringDefaults.DataSource; - private string _failoverPartner = DbConnectionStringDefaults.FailoverPartner; - private string _initialCatalog = DbConnectionStringDefaults.InitialCatalog; - // private string _namedConnection = DbConnectionStringDefaults.NamedConnection; - private string _password = DbConnectionStringDefaults.Password; - private string _transactionBinding = DbConnectionStringDefaults.TransactionBinding; - private string _typeSystemVersion = DbConnectionStringDefaults.TypeSystemVersion; - private string _userID = DbConnectionStringDefaults.UserID; - private string _workstationID = DbConnectionStringDefaults.WorkstationID; - - private int _commandTimeout = DbConnectionStringDefaults.CommandTimeout; - private int _connectTimeout = DbConnectionStringDefaults.ConnectTimeout; - private int _loadBalanceTimeout = DbConnectionStringDefaults.LoadBalanceTimeout; - private int _maxPoolSize = DbConnectionStringDefaults.MaxPoolSize; - private int _minPoolSize = DbConnectionStringDefaults.MinPoolSize; - private int _packetSize = DbConnectionStringDefaults.PacketSize; - private int _connectRetryCount = DbConnectionStringDefaults.ConnectRetryCount; - private int _connectRetryInterval = DbConnectionStringDefaults.ConnectRetryInterval; - - private bool _encrypt = DbConnectionStringDefaults.Encrypt; - private bool _trustServerCertificate = DbConnectionStringDefaults.TrustServerCertificate; - private bool _enlist = DbConnectionStringDefaults.Enlist; - private bool _integratedSecurity = DbConnectionStringDefaults.IntegratedSecurity; - private bool _multipleActiveResultSets = DbConnectionStringDefaults.MultipleActiveResultSets; - private bool _multiSubnetFailover = DbConnectionStringDefaults.MultiSubnetFailover; - private bool _persistSecurityInfo = DbConnectionStringDefaults.PersistSecurityInfo; - private bool _pooling = DbConnectionStringDefaults.Pooling; - private bool _replication = DbConnectionStringDefaults.Replication; - private bool _userInstance = DbConnectionStringDefaults.UserInstance; - private SqlAuthenticationMethod _authentication = DbConnectionStringDefaults.Authentication; - private SqlConnectionColumnEncryptionSetting _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; - private string _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; - private SqlConnectionAttestationProtocol _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; - - private static string[] CreateValidKeywords() - { - string[] validKeywords = new string[KeywordsCount]; - validKeywords[(int)Keywords.ApplicationIntent] = DbConnectionStringKeywords.ApplicationIntent; - validKeywords[(int)Keywords.ApplicationName] = DbConnectionStringKeywords.ApplicationName; - validKeywords[(int)Keywords.AttachDBFilename] = DbConnectionStringKeywords.AttachDBFilename; -#if NETCOREAPP - validKeywords[(int)Keywords.PoolBlockingPeriod] = DbConnectionStringKeywords.PoolBlockingPeriod; -#endif - validKeywords[(int)Keywords.CommandTimeout] = DbConnectionStringKeywords.CommandTimeout; - validKeywords[(int)Keywords.ConnectTimeout] = DbConnectionStringKeywords.ConnectTimeout; - validKeywords[(int)Keywords.CurrentLanguage] = DbConnectionStringKeywords.CurrentLanguage; - validKeywords[(int)Keywords.DataSource] = DbConnectionStringKeywords.DataSource; - validKeywords[(int)Keywords.Encrypt] = DbConnectionStringKeywords.Encrypt; - validKeywords[(int)Keywords.Enlist] = DbConnectionStringKeywords.Enlist; - validKeywords[(int)Keywords.FailoverPartner] = DbConnectionStringKeywords.FailoverPartner; - validKeywords[(int)Keywords.InitialCatalog] = DbConnectionStringKeywords.InitialCatalog; - validKeywords[(int)Keywords.IntegratedSecurity] = DbConnectionStringKeywords.IntegratedSecurity; - validKeywords[(int)Keywords.LoadBalanceTimeout] = DbConnectionStringKeywords.LoadBalanceTimeout; - validKeywords[(int)Keywords.MaxPoolSize] = DbConnectionStringKeywords.MaxPoolSize; - validKeywords[(int)Keywords.MinPoolSize] = DbConnectionStringKeywords.MinPoolSize; - validKeywords[(int)Keywords.MultipleActiveResultSets] = DbConnectionStringKeywords.MultipleActiveResultSets; - validKeywords[(int)Keywords.MultiSubnetFailover] = DbConnectionStringKeywords.MultiSubnetFailover; - // validKeywords[(int)Keywords.NamedConnection] = DbConnectionStringKeywords.NamedConnection; - validKeywords[(int)Keywords.PacketSize] = DbConnectionStringKeywords.PacketSize; - validKeywords[(int)Keywords.Password] = DbConnectionStringKeywords.Password; - validKeywords[(int)Keywords.PersistSecurityInfo] = DbConnectionStringKeywords.PersistSecurityInfo; - validKeywords[(int)Keywords.Pooling] = DbConnectionStringKeywords.Pooling; - validKeywords[(int)Keywords.Replication] = DbConnectionStringKeywords.Replication; - validKeywords[(int)Keywords.TransactionBinding] = DbConnectionStringKeywords.TransactionBinding; - validKeywords[(int)Keywords.TrustServerCertificate] = DbConnectionStringKeywords.TrustServerCertificate; - validKeywords[(int)Keywords.TypeSystemVersion] = DbConnectionStringKeywords.TypeSystemVersion; - validKeywords[(int)Keywords.UserID] = DbConnectionStringKeywords.UserID; - validKeywords[(int)Keywords.UserInstance] = DbConnectionStringKeywords.UserInstance; - validKeywords[(int)Keywords.WorkstationID] = DbConnectionStringKeywords.WorkstationID; - validKeywords[(int)Keywords.ConnectRetryCount] = DbConnectionStringKeywords.ConnectRetryCount; - validKeywords[(int)Keywords.ConnectRetryInterval] = DbConnectionStringKeywords.ConnectRetryInterval; - validKeywords[(int)Keywords.Authentication] = DbConnectionStringKeywords.Authentication; - validKeywords[(int)Keywords.ColumnEncryptionSetting] = DbConnectionStringKeywords.ColumnEncryptionSetting; - validKeywords[(int)Keywords.EnclaveAttestationUrl] = DbConnectionStringKeywords.EnclaveAttestationUrl; - validKeywords[(int)Keywords.AttestationProtocol] = DbConnectionStringKeywords.AttestationProtocol; - return validKeywords; - } - - private static Dictionary CreateKeywordsDictionary() - { - Dictionary hash = new Dictionary(KeywordsCount + SqlConnectionString.SynonymCount, StringComparer.OrdinalIgnoreCase); - hash.Add(DbConnectionStringKeywords.ApplicationIntent, Keywords.ApplicationIntent); - hash.Add(DbConnectionStringKeywords.ApplicationName, Keywords.ApplicationName); - hash.Add(DbConnectionStringKeywords.AttachDBFilename, Keywords.AttachDBFilename); -#if NETCOREAPP - hash.Add(DbConnectionStringKeywords.PoolBlockingPeriod, Keywords.PoolBlockingPeriod); -#endif - hash.Add(DbConnectionStringKeywords.CommandTimeout, Keywords.CommandTimeout); - hash.Add(DbConnectionStringKeywords.ConnectTimeout, Keywords.ConnectTimeout); - hash.Add(DbConnectionStringKeywords.CurrentLanguage, Keywords.CurrentLanguage); - hash.Add(DbConnectionStringKeywords.DataSource, Keywords.DataSource); - hash.Add(DbConnectionStringKeywords.Encrypt, Keywords.Encrypt); - hash.Add(DbConnectionStringKeywords.Enlist, Keywords.Enlist); - hash.Add(DbConnectionStringKeywords.FailoverPartner, Keywords.FailoverPartner); - hash.Add(DbConnectionStringKeywords.InitialCatalog, Keywords.InitialCatalog); - hash.Add(DbConnectionStringKeywords.IntegratedSecurity, Keywords.IntegratedSecurity); - hash.Add(DbConnectionStringKeywords.LoadBalanceTimeout, Keywords.LoadBalanceTimeout); - hash.Add(DbConnectionStringKeywords.MultipleActiveResultSets, Keywords.MultipleActiveResultSets); - hash.Add(DbConnectionStringKeywords.MaxPoolSize, Keywords.MaxPoolSize); - hash.Add(DbConnectionStringKeywords.MinPoolSize, Keywords.MinPoolSize); - hash.Add(DbConnectionStringKeywords.MultiSubnetFailover, Keywords.MultiSubnetFailover); - // hash.Add(DbConnectionStringKeywords.NamedConnection, Keywords.NamedConnection); - hash.Add(DbConnectionStringKeywords.PacketSize, Keywords.PacketSize); - hash.Add(DbConnectionStringKeywords.Password, Keywords.Password); - hash.Add(DbConnectionStringKeywords.PersistSecurityInfo, Keywords.PersistSecurityInfo); - hash.Add(DbConnectionStringKeywords.Pooling, Keywords.Pooling); - hash.Add(DbConnectionStringKeywords.Replication, Keywords.Replication); - hash.Add(DbConnectionStringKeywords.TransactionBinding, Keywords.TransactionBinding); - hash.Add(DbConnectionStringKeywords.TrustServerCertificate, Keywords.TrustServerCertificate); - hash.Add(DbConnectionStringKeywords.TypeSystemVersion, Keywords.TypeSystemVersion); - hash.Add(DbConnectionStringKeywords.UserID, Keywords.UserID); - hash.Add(DbConnectionStringKeywords.UserInstance, Keywords.UserInstance); - hash.Add(DbConnectionStringKeywords.WorkstationID, Keywords.WorkstationID); - hash.Add(DbConnectionStringKeywords.ConnectRetryCount, Keywords.ConnectRetryCount); - hash.Add(DbConnectionStringKeywords.ConnectRetryInterval, Keywords.ConnectRetryInterval); - hash.Add(DbConnectionStringKeywords.Authentication, Keywords.Authentication); - hash.Add(DbConnectionStringKeywords.ColumnEncryptionSetting, Keywords.ColumnEncryptionSetting); - hash.Add(DbConnectionStringKeywords.EnclaveAttestationUrl, Keywords.EnclaveAttestationUrl); - hash.Add(DbConnectionStringKeywords.AttestationProtocol, Keywords.AttestationProtocol); - - hash.Add(DbConnectionStringSynonyms.APP, Keywords.ApplicationName); - hash.Add(DbConnectionStringSynonyms.APPLICATIONINTENT, Keywords.ApplicationIntent); - hash.Add(DbConnectionStringSynonyms.EXTENDEDPROPERTIES, Keywords.AttachDBFilename); - hash.Add(DbConnectionStringSynonyms.INITIALFILENAME, Keywords.AttachDBFilename); - hash.Add(DbConnectionStringSynonyms.CONNECTIONTIMEOUT, Keywords.ConnectTimeout); - hash.Add(DbConnectionStringSynonyms.CONNECTRETRYCOUNT, Keywords.ConnectRetryCount); - hash.Add(DbConnectionStringSynonyms.CONNECTRETRYINTERVAL, Keywords.ConnectRetryInterval); - hash.Add(DbConnectionStringSynonyms.TIMEOUT, Keywords.ConnectTimeout); - hash.Add(DbConnectionStringSynonyms.LANGUAGE, Keywords.CurrentLanguage); - hash.Add(DbConnectionStringSynonyms.ADDR, Keywords.DataSource); - hash.Add(DbConnectionStringSynonyms.ADDRESS, Keywords.DataSource); - hash.Add(DbConnectionStringSynonyms.MULTIPLEACTIVERESULTSETS, Keywords.MultipleActiveResultSets); - hash.Add(DbConnectionStringSynonyms.MULTISUBNETFAILOVER, Keywords.MultiSubnetFailover); - hash.Add(DbConnectionStringSynonyms.NETWORKADDRESS, Keywords.DataSource); -#if NETCOREAPP - hash.Add(DbConnectionStringSynonyms.POOLBLOCKINGPERIOD, Keywords.PoolBlockingPeriod); -#endif - hash.Add(DbConnectionStringSynonyms.SERVER, Keywords.DataSource); - hash.Add(DbConnectionStringSynonyms.DATABASE, Keywords.InitialCatalog); - hash.Add(DbConnectionStringSynonyms.TRUSTEDCONNECTION, Keywords.IntegratedSecurity); - hash.Add(DbConnectionStringSynonyms.TRUSTSERVERCERTIFICATE, Keywords.TrustServerCertificate); - hash.Add(DbConnectionStringSynonyms.ConnectionLifetime, Keywords.LoadBalanceTimeout); - hash.Add(DbConnectionStringSynonyms.Pwd, Keywords.Password); - hash.Add(DbConnectionStringSynonyms.PERSISTSECURITYINFO, Keywords.PersistSecurityInfo); - hash.Add(DbConnectionStringSynonyms.UID, Keywords.UserID); - hash.Add(DbConnectionStringSynonyms.User, Keywords.UserID); - hash.Add(DbConnectionStringSynonyms.WSID, Keywords.WorkstationID); - Debug.Assert((KeywordsCount + SqlConnectionString.SynonymCount) == hash.Count, "initial expected size is incorrect"); - return hash; - } - - /// - public SqlConnectionStringBuilder() : this((string)null) - { - } - - /// - public SqlConnectionStringBuilder(string connectionString) : base() - { - if (!string.IsNullOrEmpty(connectionString)) - { - ConnectionString = connectionString; - } - } - - /// - public override object this[string keyword] - { - get - { - Keywords index = GetIndex(keyword); - return GetAt(index); - } - set - { - if (null != value) - { - Keywords index = GetIndex(keyword); - switch (index) - { - case Keywords.ApplicationIntent: - this.ApplicationIntent = ConvertToApplicationIntent(keyword, value); - break; - case Keywords.ApplicationName: - ApplicationName = ConvertToString(value); - break; - case Keywords.AttachDBFilename: - AttachDBFilename = ConvertToString(value); - break; - case Keywords.CurrentLanguage: - CurrentLanguage = ConvertToString(value); - break; - case Keywords.DataSource: - DataSource = ConvertToString(value); - break; - case Keywords.FailoverPartner: - FailoverPartner = ConvertToString(value); - break; - case Keywords.InitialCatalog: - InitialCatalog = ConvertToString(value); - break; - // case Keywords.NamedConnection: NamedConnection = ConvertToString(value); break; - case Keywords.Password: - Password = ConvertToString(value); - break; - case Keywords.UserID: - UserID = ConvertToString(value); - break; - case Keywords.TransactionBinding: - TransactionBinding = ConvertToString(value); - break; - case Keywords.TypeSystemVersion: - TypeSystemVersion = ConvertToString(value); - break; - case Keywords.WorkstationID: - WorkstationID = ConvertToString(value); - break; - - case Keywords.CommandTimeout: - CommandTimeout = ConvertToInt32(value); - break; - case Keywords.ConnectTimeout: - ConnectTimeout = ConvertToInt32(value); - break; - case Keywords.LoadBalanceTimeout: - LoadBalanceTimeout = ConvertToInt32(value); - break; - case Keywords.MaxPoolSize: - MaxPoolSize = ConvertToInt32(value); - break; - case Keywords.MinPoolSize: - MinPoolSize = ConvertToInt32(value); - break; - case Keywords.PacketSize: - PacketSize = ConvertToInt32(value); - break; - - case Keywords.IntegratedSecurity: - IntegratedSecurity = ConvertToIntegratedSecurity(value); - break; - case Keywords.Authentication: - Authentication = ConvertToAuthenticationType(keyword, value); - break; - case Keywords.ColumnEncryptionSetting: - ColumnEncryptionSetting = ConvertToColumnEncryptionSetting(keyword, value); - break; - case Keywords.EnclaveAttestationUrl: - EnclaveAttestationUrl = ConvertToString(value); - break; - case Keywords.AttestationProtocol: - AttestationProtocol = ConvertToAttestationProtocol(keyword, value); - break; -#if NETCOREAPP - case Keywords.PoolBlockingPeriod: PoolBlockingPeriod = ConvertToPoolBlockingPeriod(keyword, value); break; -#endif - case Keywords.Encrypt: - Encrypt = ConvertToBoolean(value); - break; - case Keywords.TrustServerCertificate: - TrustServerCertificate = ConvertToBoolean(value); - break; - case Keywords.Enlist: - Enlist = ConvertToBoolean(value); - break; - case Keywords.MultipleActiveResultSets: - MultipleActiveResultSets = ConvertToBoolean(value); - break; - case Keywords.MultiSubnetFailover: - MultiSubnetFailover = ConvertToBoolean(value); - break; - case Keywords.PersistSecurityInfo: - PersistSecurityInfo = ConvertToBoolean(value); - break; - case Keywords.Pooling: - Pooling = ConvertToBoolean(value); - break; - case Keywords.Replication: - Replication = ConvertToBoolean(value); - break; - case Keywords.UserInstance: - UserInstance = ConvertToBoolean(value); - break; - case Keywords.ConnectRetryCount: - ConnectRetryCount = ConvertToInt32(value); - break; - case Keywords.ConnectRetryInterval: - ConnectRetryInterval = ConvertToInt32(value); - break; - - default: - Debug.Fail("unexpected keyword"); - throw UnsupportedKeyword(keyword); - } - } - else - { - Remove(keyword); - } - } - } - - /// - public ApplicationIntent ApplicationIntent - { - get { return _applicationIntent; } - set - { - if (!DbConnectionStringBuilderUtil.IsValidApplicationIntentValue(value)) - { - throw ADP.InvalidEnumerationValue(typeof(ApplicationIntent), (int)value); - } - - SetApplicationIntentValue(value); - _applicationIntent = value; - } - } - - /// - public string ApplicationName - { - get { return _applicationName; } - set - { - SetValue(DbConnectionStringKeywords.ApplicationName, value); - _applicationName = value; - } - } - - /// - public string AttachDBFilename - { - get { return _attachDBFilename; } - set - { - SetValue(DbConnectionStringKeywords.AttachDBFilename, value); - _attachDBFilename = value; - } - } - - /// - public int CommandTimeout - { - get { return _commandTimeout; } - set - { - if (value < 0) - { - throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.CommandTimeout); - } - SetValue(DbConnectionStringKeywords.CommandTimeout, value); - _commandTimeout = value; - } - } - - /// - public int ConnectTimeout - { - get { return _connectTimeout; } - set - { - if (value < 0) - { - throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.ConnectTimeout); - } - SetValue(DbConnectionStringKeywords.ConnectTimeout, value); - _connectTimeout = value; - } - } - - /// - public string CurrentLanguage - { - get { return _currentLanguage; } - set - { - SetValue(DbConnectionStringKeywords.CurrentLanguage, value); - _currentLanguage = value; - } - } - - /// - public string DataSource - { - get { return _dataSource; } - set - { - SetValue(DbConnectionStringKeywords.DataSource, value); - _dataSource = value; - } - } - - /// - public bool Encrypt - { - get { return _encrypt; } - set - { - SetValue(DbConnectionStringKeywords.Encrypt, value); - _encrypt = value; - } - } - - /// - public SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting - { - get { return _columnEncryptionSetting; } - set - { - if (!DbConnectionStringBuilderUtil.IsValidColumnEncryptionSetting(value)) - { - throw ADP.InvalidEnumerationValue(typeof(SqlConnectionColumnEncryptionSetting), (int)value); - } - - SetColumnEncryptionSettingValue(value); - _columnEncryptionSetting = value; - } - } - - /// - public string EnclaveAttestationUrl - { - get { return _enclaveAttestationUrl; } - set - { - SetValue(DbConnectionStringKeywords.EnclaveAttestationUrl, value); - _enclaveAttestationUrl = value; - } - } - - /// - public SqlConnectionAttestationProtocol AttestationProtocol - { - get { return _attestationProtocol; } - set - { - if (!DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value)) - { - throw ADP.InvalidEnumerationValue(typeof(SqlConnectionAttestationProtocol), (int)value); - } - - SetAttestationProtocolValue(value); - _attestationProtocol = value; - } - } - - /// - public bool TrustServerCertificate - { - get { return _trustServerCertificate; } - set - { - SetValue(DbConnectionStringKeywords.TrustServerCertificate, value); - _trustServerCertificate = value; - } - } - - /// - public bool Enlist - { - get { return _enlist; } - set - { - SetValue(DbConnectionStringKeywords.Enlist, value); - _enlist = value; - } - } - - /// - public string FailoverPartner - { - get { return _failoverPartner; } - set - { - SetValue(DbConnectionStringKeywords.FailoverPartner, value); - _failoverPartner = value; - } - } - - /// - [TypeConverter(typeof(SqlInitialCatalogConverter))] - public string InitialCatalog - { - get { return _initialCatalog; } - set - { - SetValue(DbConnectionStringKeywords.InitialCatalog, value); - _initialCatalog = value; - } - } - - /// - public bool IntegratedSecurity - { - get { return _integratedSecurity; } - set - { - SetValue(DbConnectionStringKeywords.IntegratedSecurity, value); - _integratedSecurity = value; - } - } - - /// - public SqlAuthenticationMethod Authentication - { - get { return _authentication; } - set - { - if (!DbConnectionStringBuilderUtil.IsValidAuthenticationTypeValue(value)) - { - throw ADP.InvalidEnumerationValue(typeof(SqlAuthenticationMethod), (int)value); - } - - SetAuthenticationValue(value); - _authentication = value; - } - } - - /// - public int LoadBalanceTimeout - { - get { return _loadBalanceTimeout; } - set - { - if (value < 0) - { - throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.LoadBalanceTimeout); - } - SetValue(DbConnectionStringKeywords.LoadBalanceTimeout, value); - _loadBalanceTimeout = value; - } - } - - /// - public int MaxPoolSize - { - get { return _maxPoolSize; } - set - { - if (value < 1) - { - throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.MaxPoolSize); - } - SetValue(DbConnectionStringKeywords.MaxPoolSize, value); - _maxPoolSize = value; - } - } - - /// - public int ConnectRetryCount - { - get { return _connectRetryCount; } - set - { - if ((value < 0) || (value > 255)) - { - throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.ConnectRetryCount); - } - SetValue(DbConnectionStringKeywords.ConnectRetryCount, value); - _connectRetryCount = value; - } - } - - /// - public int ConnectRetryInterval - { - get { return _connectRetryInterval; } - set - { - if ((value < 1) || (value > 60)) - { - throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.ConnectRetryInterval); - } - SetValue(DbConnectionStringKeywords.ConnectRetryInterval, value); - _connectRetryInterval = value; - } - } - - - /// - public int MinPoolSize - { - get { return _minPoolSize; } - set - { - if (value < 0) - { - throw ADP.InvalidConnectionOptionValue(DbConnectionStringKeywords.MinPoolSize); - } - SetValue(DbConnectionStringKeywords.MinPoolSize, value); - _minPoolSize = value; - } - } - - /// - public bool MultipleActiveResultSets - { - get { return _multipleActiveResultSets; } - set - { - SetValue(DbConnectionStringKeywords.MultipleActiveResultSets, value); - _multipleActiveResultSets = value; - } - } - - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Reviewed and Approved by UE")] - public bool MultiSubnetFailover - { - get { return _multiSubnetFailover; } - set - { - SetValue(DbConnectionStringKeywords.MultiSubnetFailover, value); - _multiSubnetFailover = value; - } - } - /* - [DisplayName(DbConnectionStringKeywords.NamedConnection)] - [ResCategoryAttribute(Strings.DataCategory_NamedConnectionString)] - [ResDescriptionAttribute(Strings.DbConnectionString_NamedConnection)] - [RefreshPropertiesAttribute(RefreshProperties.All)] - [TypeConverter(typeof(NamedConnectionStringConverter))] - public string NamedConnection { - get { return _namedConnection; } - set { - SetValue(DbConnectionStringKeywords.NamedConnection, value); - _namedConnection = value; - } - } - */ - /// - public int PacketSize - { - get { return _packetSize; } - set - { - if ((value < TdsEnums.MIN_PACKET_SIZE) || (TdsEnums.MAX_PACKET_SIZE < value)) - { - throw SQL.InvalidPacketSizeValue(); - } - SetValue(DbConnectionStringKeywords.PacketSize, value); - _packetSize = value; - } - } - - /// - public string Password - { - get { return _password; } - set - { - SetValue(DbConnectionStringKeywords.Password, value); - _password = value; - } - } - - /// - public bool PersistSecurityInfo - { - get { return _persistSecurityInfo; } - set - { - SetValue(DbConnectionStringKeywords.PersistSecurityInfo, value); - _persistSecurityInfo = value; - } - } - - /// - public bool Pooling - { - get { return _pooling; } - set - { - SetValue(DbConnectionStringKeywords.Pooling, value); - _pooling = value; - } - } - - /// - public bool Replication - { - get { return _replication; } - set - { - SetValue(DbConnectionStringKeywords.Replication, value); - _replication = value; - } - } - - /// - public string TransactionBinding - { - get { return _transactionBinding; } - set - { - SetValue(DbConnectionStringKeywords.TransactionBinding, value); - _transactionBinding = value; - } - } - - /// - public string TypeSystemVersion - { - get { return _typeSystemVersion; } - set - { - SetValue(DbConnectionStringKeywords.TypeSystemVersion, value); - _typeSystemVersion = value; - } - } - - /// - public string UserID - { - get { return _userID; } - set - { - SetValue(DbConnectionStringKeywords.UserID, value); - _userID = value; - } - } - - /// - public bool UserInstance - { - get { return _userInstance; } - set - { - SetValue(DbConnectionStringKeywords.UserInstance, value); - _userInstance = value; - } - } - - /// - public string WorkstationID - { - get { return _workstationID; } - set - { - SetValue(DbConnectionStringKeywords.WorkstationID, value); - _workstationID = value; - } - } - - /// - public override ICollection Keys - { - get - { - return new System.Collections.ObjectModel.ReadOnlyCollection(s_validKeywords); - } - } - - /// - public override ICollection Values - { - get - { - // written this way so if the ordering of Keywords & _validKeywords changes - // this is one less place to maintain - object[] values = new object[s_validKeywords.Length]; - for (int i = 0; i < values.Length; ++i) - { - values[i] = GetAt((Keywords)i); - } - return new System.Collections.ObjectModel.ReadOnlyCollection(values); - } - } - - /// - public override void Clear() - { - base.Clear(); - for (int i = 0; i < s_validKeywords.Length; ++i) - { - Reset((Keywords)i); - } - } - - /// - public override bool ContainsKey(string keyword) - { - ADP.CheckArgumentNull(keyword, nameof(keyword)); - return s_keywords.ContainsKey(keyword); - } - - private static bool ConvertToBoolean(object value) - { - return DbConnectionStringBuilderUtil.ConvertToBoolean(value); - } - private static int ConvertToInt32(object value) - { - return DbConnectionStringBuilderUtil.ConvertToInt32(value); - } - private static bool ConvertToIntegratedSecurity(object value) - { - return DbConnectionStringBuilderUtil.ConvertToIntegratedSecurity(value); - } - private static SqlAuthenticationMethod ConvertToAuthenticationType(string keyword, object value) - { - return DbConnectionStringBuilderUtil.ConvertToAuthenticationType(keyword, value); - } - private static string ConvertToString(object value) - { - return DbConnectionStringBuilderUtil.ConvertToString(value); - } - private static ApplicationIntent ConvertToApplicationIntent(string keyword, object value) - { - return DbConnectionStringBuilderUtil.ConvertToApplicationIntent(keyword, value); - } - - /// - /// Convert to SqlConnectionColumnEncryptionSetting. - /// - /// - /// - private static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSetting(string keyword, object value) - { - return DbConnectionStringBuilderUtil.ConvertToColumnEncryptionSetting(keyword, value); - } - - /// - /// Convert to SqlConnectionAttestationProtocol - /// - /// - /// - private static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) - { - return DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(keyword, value); - } - - private object GetAt(Keywords index) - { - switch (index) - { - case Keywords.ApplicationIntent: - return this.ApplicationIntent; - case Keywords.ApplicationName: - return ApplicationName; - case Keywords.AttachDBFilename: - return AttachDBFilename; -#if NETCOREAPP - case Keywords.PoolBlockingPeriod: return PoolBlockingPeriod; -#endif - case Keywords.CommandTimeout: - return CommandTimeout; - case Keywords.ConnectTimeout: - return ConnectTimeout; - case Keywords.CurrentLanguage: - return CurrentLanguage; - case Keywords.DataSource: - return DataSource; - case Keywords.Encrypt: - return Encrypt; - case Keywords.Enlist: - return Enlist; - case Keywords.FailoverPartner: - return FailoverPartner; - case Keywords.InitialCatalog: - return InitialCatalog; - case Keywords.IntegratedSecurity: - return IntegratedSecurity; - case Keywords.LoadBalanceTimeout: - return LoadBalanceTimeout; - case Keywords.MultipleActiveResultSets: - return MultipleActiveResultSets; - case Keywords.MaxPoolSize: - return MaxPoolSize; - case Keywords.MinPoolSize: - return MinPoolSize; - case Keywords.MultiSubnetFailover: - return MultiSubnetFailover; - // case Keywords.NamedConnection: return NamedConnection; - case Keywords.PacketSize: - return PacketSize; - case Keywords.Password: - return Password; - case Keywords.PersistSecurityInfo: - return PersistSecurityInfo; - case Keywords.Pooling: - return Pooling; - case Keywords.Replication: - return Replication; - case Keywords.TransactionBinding: - return TransactionBinding; - case Keywords.TrustServerCertificate: - return TrustServerCertificate; - case Keywords.TypeSystemVersion: - return TypeSystemVersion; - case Keywords.UserID: - return UserID; - case Keywords.UserInstance: - return UserInstance; - case Keywords.WorkstationID: - return WorkstationID; - case Keywords.ConnectRetryCount: - return ConnectRetryCount; - case Keywords.ConnectRetryInterval: - return ConnectRetryInterval; - case Keywords.Authentication: - return Authentication; - case Keywords.ColumnEncryptionSetting: - return ColumnEncryptionSetting; - case Keywords.EnclaveAttestationUrl: - return EnclaveAttestationUrl; - case Keywords.AttestationProtocol: - return AttestationProtocol; - default: - Debug.Fail("unexpected keyword"); - throw UnsupportedKeyword(s_validKeywords[(int)index]); - } - } - - private Keywords GetIndex(string keyword) - { - ADP.CheckArgumentNull(keyword, nameof(keyword)); - Keywords index; - if (s_keywords.TryGetValue(keyword, out index)) - { - return index; - } - throw UnsupportedKeyword(keyword); - } - - /// - public override bool Remove(string keyword) - { - ADP.CheckArgumentNull(keyword, nameof(keyword)); - Keywords index; - if (s_keywords.TryGetValue(keyword, out index)) - { - if (base.Remove(s_validKeywords[(int)index])) - { - Reset(index); - return true; - } - } - return false; - } - - private void Reset(Keywords index) - { - switch (index) - { - case Keywords.ApplicationIntent: - _applicationIntent = DbConnectionStringDefaults.ApplicationIntent; - break; - case Keywords.ApplicationName: - _applicationName = DbConnectionStringDefaults.ApplicationName; - break; - case Keywords.AttachDBFilename: - _attachDBFilename = DbConnectionStringDefaults.AttachDBFilename; - break; - case Keywords.Authentication: - _authentication = DbConnectionStringDefaults.Authentication; - break; -#if NETCOREAPP - case Keywords.PoolBlockingPeriod: - _poolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; - break; -#endif - case Keywords.CommandTimeout: - _commandTimeout = DbConnectionStringDefaults.CommandTimeout; - break; - case Keywords.ConnectTimeout: - _connectTimeout = DbConnectionStringDefaults.ConnectTimeout; - break; - case Keywords.CurrentLanguage: - _currentLanguage = DbConnectionStringDefaults.CurrentLanguage; - break; - case Keywords.DataSource: - _dataSource = DbConnectionStringDefaults.DataSource; - break; - case Keywords.Encrypt: - _encrypt = DbConnectionStringDefaults.Encrypt; - break; - case Keywords.Enlist: - _enlist = DbConnectionStringDefaults.Enlist; - break; - case Keywords.FailoverPartner: - _failoverPartner = DbConnectionStringDefaults.FailoverPartner; - break; - case Keywords.InitialCatalog: - _initialCatalog = DbConnectionStringDefaults.InitialCatalog; - break; - case Keywords.IntegratedSecurity: - _integratedSecurity = DbConnectionStringDefaults.IntegratedSecurity; - break; - case Keywords.LoadBalanceTimeout: - _loadBalanceTimeout = DbConnectionStringDefaults.LoadBalanceTimeout; - break; - case Keywords.MultipleActiveResultSets: - _multipleActiveResultSets = DbConnectionStringDefaults.MultipleActiveResultSets; - break; - case Keywords.MaxPoolSize: - _maxPoolSize = DbConnectionStringDefaults.MaxPoolSize; - break; - case Keywords.MinPoolSize: - _minPoolSize = DbConnectionStringDefaults.MinPoolSize; - break; - case Keywords.MultiSubnetFailover: - _multiSubnetFailover = DbConnectionStringDefaults.MultiSubnetFailover; - break; - // case Keywords.NamedConnection: - // _namedConnection = DbConnectionStringDefaults.NamedConnection; - // break; - case Keywords.PacketSize: - _packetSize = DbConnectionStringDefaults.PacketSize; - break; - case Keywords.Password: - _password = DbConnectionStringDefaults.Password; - break; - case Keywords.PersistSecurityInfo: - _persistSecurityInfo = DbConnectionStringDefaults.PersistSecurityInfo; - break; - case Keywords.Pooling: - _pooling = DbConnectionStringDefaults.Pooling; - break; - case Keywords.ConnectRetryCount: - _connectRetryCount = DbConnectionStringDefaults.ConnectRetryCount; - break; - case Keywords.ConnectRetryInterval: - _connectRetryInterval = DbConnectionStringDefaults.ConnectRetryInterval; - break; - case Keywords.Replication: - _replication = DbConnectionStringDefaults.Replication; - break; - case Keywords.TransactionBinding: - _transactionBinding = DbConnectionStringDefaults.TransactionBinding; - break; - case Keywords.TrustServerCertificate: - _trustServerCertificate = DbConnectionStringDefaults.TrustServerCertificate; - break; - case Keywords.TypeSystemVersion: - _typeSystemVersion = DbConnectionStringDefaults.TypeSystemVersion; - break; - case Keywords.UserID: - _userID = DbConnectionStringDefaults.UserID; - break; - case Keywords.UserInstance: - _userInstance = DbConnectionStringDefaults.UserInstance; - break; - case Keywords.WorkstationID: - _workstationID = DbConnectionStringDefaults.WorkstationID; - break; - case Keywords.ColumnEncryptionSetting: - _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; - break; - case Keywords.EnclaveAttestationUrl: - _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; - break; - case Keywords.AttestationProtocol: - _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; - break; - default: - Debug.Fail("unexpected keyword"); - throw UnsupportedKeyword(s_validKeywords[(int)index]); - } - } - - private void SetValue(string keyword, bool value) - { - base[keyword] = value.ToString(); - } - private void SetValue(string keyword, int value) - { - base[keyword] = value.ToString((System.IFormatProvider)null); - } - private void SetValue(string keyword, string value) - { - ADP.CheckArgumentNull(value, keyword); - base[keyword] = value; - } - private void SetApplicationIntentValue(ApplicationIntent value) - { - Debug.Assert(DbConnectionStringBuilderUtil.IsValidApplicationIntentValue(value), "invalid value"); - base[DbConnectionStringKeywords.ApplicationIntent] = DbConnectionStringBuilderUtil.ApplicationIntentToString(value); - } - private void SetColumnEncryptionSettingValue(SqlConnectionColumnEncryptionSetting value) - { - Debug.Assert(DbConnectionStringBuilderUtil.IsValidColumnEncryptionSetting(value), "Invalid value for SqlConnectionColumnEncryptionSetting"); - base[DbConnectionStringKeywords.ColumnEncryptionSetting] = DbConnectionStringBuilderUtil.ColumnEncryptionSettingToString(value); - } - - private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) - { - Debug.Assert(DbConnectionStringBuilderUtil.IsValidAttestationProtocol(value), "Invalid value for SqlConnectionAttestationProtocol"); - base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); - } - - private void SetAuthenticationValue(SqlAuthenticationMethod value) - { - Debug.Assert(DbConnectionStringBuilderUtil.IsValidAuthenticationTypeValue(value), "Invalid value for AuthenticationType"); - base[DbConnectionStringKeywords.Authentication] = DbConnectionStringBuilderUtil.AuthenticationTypeToString(value); - } - - /// - public override bool ShouldSerialize(string keyword) - { - ADP.CheckArgumentNull(keyword, nameof(keyword)); - Keywords index; - return s_keywords.TryGetValue(keyword, out index) && base.ShouldSerialize(s_validKeywords[(int)index]); - } - - /// - public override bool TryGetValue(string keyword, out object value) - { - Keywords index; - if (s_keywords.TryGetValue(keyword, out index)) - { - value = GetAt(index); - return true; - } - value = null; - return false; - } - - private static readonly string[] s_notSupportedKeywords = new string[] { - DbConnectionStringKeywords.AsynchronousProcessing, - DbConnectionStringKeywords.ConnectionReset, - DbConnectionStringKeywords.ContextConnection, - DbConnectionStringKeywords.TransactionBinding, - - DbConnectionStringSynonyms.Async - }; - - private static readonly string[] s_notSupportedNetworkLibraryKeywords = new string[] { - DbConnectionStringKeywords.NetworkLibrary, - - DbConnectionStringSynonyms.NET, - DbConnectionStringSynonyms.NETWORK - }; - - private Exception UnsupportedKeyword(string keyword) - { - if (s_notSupportedKeywords.Contains(keyword, StringComparer.OrdinalIgnoreCase)) - { - return SQL.UnsupportedKeyword(keyword); - } - else if (s_notSupportedNetworkLibraryKeywords.Contains(keyword, StringComparer.OrdinalIgnoreCase)) - { - return SQL.NetworkLibraryKeywordNotSupported(); - } - else - { - return ADP.KeywordNotSupported(keyword); - } - } - - private sealed class SqlInitialCatalogConverter : StringConverter - { - // converter classes should have public ctor - public SqlInitialCatalogConverter() - { - } - - public override bool GetStandardValuesSupported(ITypeDescriptorContext context) - { - return GetStandardValuesSupportedInternal(context); - } - - private bool GetStandardValuesSupportedInternal(ITypeDescriptorContext context) - { - // Only say standard values are supported if the connection string has enough - // information set to instantiate a connection and retrieve a list of databases - bool flag = false; - if (null != context) - { - SqlConnectionStringBuilder constr = (context.Instance as SqlConnectionStringBuilder); - if (null != constr) - { - if ((0 < constr.DataSource.Length) && (constr.IntegratedSecurity || (0 < constr.UserID.Length))) - { - flag = true; - } - } - } - return flag; - } - - public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) - { - // Although theoretically this could be true, some people may want to just type in a name - return false; - } - - public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) - { - // There can only be standard values if the connection string is in a state that might - // be able to instantiate a connection - if (GetStandardValuesSupportedInternal(context)) - { - - // Create an array list to store the database names - List values = new List(); - - try - { - SqlConnectionStringBuilder constr = (SqlConnectionStringBuilder)context.Instance; - - // Create a connection - using (SqlConnection connection = new SqlConnection()) - { - - // Create a basic connection string from current property values - connection.ConnectionString = constr.ConnectionString; - - // Try to open the connection - connection.Open(); - - DataTable databaseTable = connection.GetSchema("DATABASES"); - - foreach (DataRow row in databaseTable.Rows) - { - string dbName = (string)row["database_name"]; - values.Add(dbName); - } - } - } - catch (SqlException e) - { - ADP.TraceExceptionWithoutRethrow(e); - // silently fail - } - - // Return values as a StandardValuesCollection - return new StandardValuesCollection(values); - } - return null; - } - } - } -} - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs deleted file mode 100644 index 3852c375b3..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataAdapter.cs +++ /dev/null @@ -1,286 +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. - -using System; -using System.Data; -using System.Data.Common; -using System.Diagnostics; -using System.Threading; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed class SqlDataAdapter : DbDataAdapter, IDbDataAdapter, ICloneable - { - private static readonly object EventRowUpdated = new object(); - private static readonly object EventRowUpdating = new object(); - - private SqlCommand _deleteCommand, _insertCommand, _selectCommand, _updateCommand; - - private SqlCommandSet _commandSet; - private int _updateBatchSize = 1; - - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); - - internal int ObjectID => _objectID; - - /// - public SqlDataAdapter() : base() - { - GC.SuppressFinalize(this); - } - - /// - public SqlDataAdapter(SqlCommand selectCommand) : this() - { - SelectCommand = selectCommand; - } - - /// - public SqlDataAdapter(string selectCommandText, string selectConnectionString) : this() - { - SqlConnection connection = new SqlConnection(selectConnectionString); - SelectCommand = new SqlCommand(selectCommandText, connection); - } - - /// - public SqlDataAdapter(string selectCommandText, SqlConnection selectConnection) : this() - { - SelectCommand = new SqlCommand(selectCommandText, selectConnection); - } - - private SqlDataAdapter(SqlDataAdapter from) : base(from) - { // Clone - GC.SuppressFinalize(this); - } - - /// - new public SqlCommand DeleteCommand - { - get { return _deleteCommand; } - set { _deleteCommand = value; } - } - - /// - IDbCommand IDbDataAdapter.DeleteCommand - { - get { return _deleteCommand; } - set { _deleteCommand = (SqlCommand)value; } - } - - /// - new public SqlCommand InsertCommand - { - get { return _insertCommand; } - set { _insertCommand = value; } - } - - /// - IDbCommand IDbDataAdapter.InsertCommand - { - get { return _insertCommand; } - set { _insertCommand = (SqlCommand)value; } - } - - /// - new public SqlCommand SelectCommand - { - get { return _selectCommand; } - set { _selectCommand = value; } - } - - /// - IDbCommand IDbDataAdapter.SelectCommand - { - get { return _selectCommand; } - set { _selectCommand = (SqlCommand)value; } - } - - /// - new public SqlCommand UpdateCommand - { - get { return _updateCommand; } - set { _updateCommand = value; } - } - - /// - IDbCommand IDbDataAdapter.UpdateCommand - { - get { return _updateCommand; } - set { _updateCommand = (SqlCommand)value; } - } - - /// - public override int UpdateBatchSize - { - get - { - return _updateBatchSize; - } - set - { - if (0 > value) - { - throw ADP.ArgumentOutOfRange(nameof(UpdateBatchSize)); - } - _updateBatchSize = value; - SqlClientEventSource.Log.TryTraceEvent(" {0}, {1}", ObjectID, value); - } - } - - /// - protected override int AddToBatch(IDbCommand command) - { - int commandIdentifier = _commandSet.CommandCount; - _commandSet.Append((SqlCommand)command); - return commandIdentifier; - } - - /// - protected override void ClearBatch() - { - _commandSet.Clear(); - } - - /// - protected override int ExecuteBatch() - { - Debug.Assert(null != _commandSet && (0 < _commandSet.CommandCount), "no commands"); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - return _commandSet.ExecuteNonQuery(); - } - - /// - protected override IDataParameter GetBatchedParameter(int commandIdentifier, int parameterIndex) - { - Debug.Assert(commandIdentifier < _commandSet.CommandCount, "commandIdentifier out of range"); - Debug.Assert(parameterIndex < _commandSet.GetParameterCount(commandIdentifier), "parameter out of range"); - IDataParameter parameter = _commandSet.GetParameter(commandIdentifier, parameterIndex); - return parameter; - } - - /// - protected override bool GetBatchedRecordsAffected(int commandIdentifier, out int recordsAffected, out Exception error) - { - Debug.Assert(commandIdentifier < _commandSet.CommandCount, "commandIdentifier out of range"); - return _commandSet.GetBatchedAffected(commandIdentifier, out recordsAffected, out error); - } - - /// - protected override void InitializeBatching() - { - SqlClientEventSource.Log.TryTraceEvent(" {0}", ObjectID); - _commandSet = new SqlCommandSet(); - SqlCommand command = SelectCommand; - if (null == command) - { - command = InsertCommand; - if (null == command) - { - command = UpdateCommand; - if (null == command) - { - command = DeleteCommand; - } - } - } - if (null != command) - { - _commandSet.Connection = command.Connection; - _commandSet.Transaction = command.Transaction; - _commandSet.CommandTimeout = command.CommandTimeout; - } - } - - /// - protected override void TerminateBatching() - { - if (null != _commandSet) - { - _commandSet.Dispose(); - _commandSet = null; - } - } - - /// - object ICloneable.Clone() - { - return new SqlDataAdapter(this); - } - - /// - protected override RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) - { - return new SqlRowUpdatedEventArgs(dataRow, command, statementType, tableMapping); - } - - /// - protected override RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) - { - return new SqlRowUpdatingEventArgs(dataRow, command, statementType, tableMapping); - } - - /// - public event SqlRowUpdatedEventHandler RowUpdated - { - add - { - Events.AddHandler(EventRowUpdated, value); - } - remove - { - Events.RemoveHandler(EventRowUpdated, value); - } - } - - /// - public event SqlRowUpdatingEventHandler RowUpdating - { - add - { - SqlRowUpdatingEventHandler handler = (SqlRowUpdatingEventHandler)Events[EventRowUpdating]; - - // Prevent someone from registering two different command builders on the adapter by - // silently removing the old one. - if ((null != handler) && (value.Target is DbCommandBuilder)) - { - SqlRowUpdatingEventHandler d = (SqlRowUpdatingEventHandler)ADP.FindBuilder(handler); - if (null != d) - { - Events.RemoveHandler(EventRowUpdating, d); - } - } - Events.AddHandler(EventRowUpdating, value); - } - remove - { - Events.RemoveHandler(EventRowUpdating, value); - } - } - - /// - override protected void OnRowUpdated(RowUpdatedEventArgs value) - { - SqlRowUpdatedEventHandler handler = (SqlRowUpdatedEventHandler)Events[EventRowUpdated]; - if ((null != handler) && (value is SqlRowUpdatedEventArgs)) - { - handler(this, (SqlRowUpdatedEventArgs)value); - } - base.OnRowUpdated(value); - } - - /// - override protected void OnRowUpdating(RowUpdatingEventArgs value) - { - SqlRowUpdatingEventHandler handler = (SqlRowUpdatingEventHandler)Events[EventRowUpdating]; - if ((null != handler) && (value is SqlRowUpdatingEventArgs)) - { - handler(this, (SqlRowUpdatingEventArgs)value); - } - base.OnRowUpdating(value); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 83fa69a74f..8d43d72685 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -83,18 +83,13 @@ internal class SharedState private long _columnDataBytesRead; // last byte read by user private long _columnDataCharsRead; // last char read by user private char[] _columnDataChars; - private int _columnDataCharsIndex; // Column index that is currently loaded in _columnDataChars + private int _columnDataCharsIndex; // Column index that is currently loaded in _columnDataChars private Task _currentTask; private Snapshot _snapshot; - private CancellationTokenSource _cancelAsyncOnCloseTokenSource; private CancellationToken _cancelAsyncOnCloseToken; - // Used for checking if the Type parameter provided to GetValue is an INullable - internal static readonly Type _typeofINullable = typeof(INullable); - private static readonly Type s_typeofSqlString = typeof(SqlString); - private SqlSequentialStream _currentStream; private SqlSequentialTextReader _currentTextReader; @@ -557,7 +552,7 @@ internal DataTable BuildSchemaTable() schemaRow[nonVersionedProviderType] = (int)(col.cipherMD != null ? col.baseTI.type : col.type); // SqlDbType enum value - does not change with TypeSystem. schemaRow[dataTypeName] = GetDataTypeNameInternal(col); - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.Is2008DateTimeType) { schemaRow[providerType] = SqlDbType.NVarChar; switch (col.type) @@ -600,12 +595,12 @@ internal DataTable BuildSchemaTable() if (col.type == SqlDbType.Udt) { // Additional metadata for UDTs. - Debug.Assert(Connection.IsKatmaiOrNewer, "Invalid Column type received from the server"); + Debug.Assert(Connection.Is2008OrNewer, "Invalid Column type received from the server"); schemaRow[udtAssemblyQualifiedName] = col.udt?.AssemblyQualifiedName; } else if (col.type == SqlDbType.Xml) { // Additional metadata for Xml. - Debug.Assert(Connection.IsKatmaiOrNewer, "Invalid DataType (Xml) for the column"); + Debug.Assert(Connection.Is2008OrNewer, "Invalid DataType (Xml) for the column"); schemaRow[xmlSchemaCollectionDatabase] = col.xmlSchemaCollection?.Database; schemaRow[xmlSchemaCollectionOwningSchema] = col.xmlSchemaCollection?.OwningSchema; schemaRow[xmlSchemaCollectionName] = col.xmlSchemaCollection?.Name; @@ -640,7 +635,7 @@ internal DataTable BuildSchemaTable() schemaRow[precision] = col.metaType.Precision; } - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.Is2008DateTimeType) { schemaRow[scale] = MetaType.MetaNVarChar.Scale; } @@ -759,8 +754,8 @@ private bool TryCleanPartialRead() { AssertReaderState(requireData: true, permitAsync: true); - // VSTS DEVDIV2 380446: It is possible that read attempt we are cleaning after ended with partially - // processed header (if it falls between network packets). In this case the first thing to do is to + // VSTS DEVDIV2 380446: It is possible that read attempt we are cleaning after ended with partially + // processed header (if it falls between network packets). In this case the first thing to do is to // finish reading the header, otherwise code will start treating unread header as TDS payload. if (_stateObj._partialHeaderBytesRead > 0) { @@ -816,7 +811,7 @@ private bool TryCleanPartialRead() Debug.Assert(TdsParser.IsValidTdsToken(token), $"Invalid token after performing CleanPartialRead: {token,-2:X2}"); } -#endif +#endif _sharedState._dataReady = false; return true; @@ -834,100 +829,108 @@ private void CleanPartialReadReliable() /// protected override void Dispose(bool disposing) { - if (disposing) + try { - Close(); + if (disposing) + { + Close(); + } + base.Dispose(disposing); + } + catch (SqlException ex) + { + SqlClientEventSource.Log.TryTraceEvent("SqlDataReader.Dispose | ERR | Error Message: {0}, Stack Trace: {1}", ex.Message, ex.StackTrace); } - base.Dispose(disposing); } /// public override void Close() { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlStatistics statistics = null; - try + using (TryEventScope.Create("SqlDataReader.Close | API | Object Id {0}, Command Object Id {1}", ObjectID, Command?.ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); - TdsParserStateObject stateObj = _stateObj; - - // Request that the current task is stopped - _cancelAsyncOnCloseTokenSource.Cancel(); - var currentTask = _currentTask; - if ((currentTask != null) && (!currentTask.IsCompleted)) + SqlStatistics statistics = null; + try { - try - { - // Wait for the task to complete - ((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne(); + statistics = SqlStatistics.StartTimer(Statistics); + TdsParserStateObject stateObj = _stateObj; - // Ensure that we've finished reading any pending data - var networkPacketTaskSource = stateObj._networkPacketTaskSource; - if (networkPacketTaskSource != null) - { - ((IAsyncResult)networkPacketTaskSource.Task).AsyncWaitHandle.WaitOne(); - } - } - catch (Exception) + // Request that the current task is stopped + _cancelAsyncOnCloseTokenSource.Cancel(); + var currentTask = _currentTask; + if ((currentTask != null) && (!currentTask.IsCompleted)) { - // If we receive any exceptions while waiting, something has gone horribly wrong and we need to doom the connection and fast-fail the reader - _connection.InnerConnection.DoomThisConnection(); - _isClosed = true; - - if (stateObj != null) + try { - lock (stateObj) + // Wait for the task to complete + ((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne(); + + // Ensure that we've finished reading any pending data + var networkPacketTaskSource = stateObj._networkPacketTaskSource; + if (networkPacketTaskSource != null) { - _stateObj = null; - _command = null; - _connection = null; + ((IAsyncResult)networkPacketTaskSource.Task).AsyncWaitHandle.WaitOne(); } } + catch (Exception) + { + // If we receive any exceptions while waiting, something has gone horribly wrong and we need to doom the connection and fast-fail the reader + _connection.InnerConnection.DoomThisConnection(); + _isClosed = true; - throw; + if (stateObj != null) + { + lock (stateObj) + { + _stateObj = null; + _command = null; + _connection = null; + } + } + + throw; + } } - } - // Close down any active Streams and TextReaders (this will also wait for them to finish their async tasks) - // NOTE: This must be done outside of the lock on the stateObj otherwise it will deadlock with CleanupAfterAsyncInvocation - CloseActiveSequentialStreamAndTextReader(); + // Close down any active Streams and TextReaders (this will also wait for them to finish their async tasks) + // NOTE: This must be done outside of the lock on the stateObj otherwise it will deadlock with CleanupAfterAsyncInvocation + CloseActiveSequentialStreamAndTextReader(); - if (stateObj != null) - { - // protect against concurrent close and cancel - lock (stateObj) + if (stateObj != null) { - if (_stateObj != null) - { // reader not closed while we waited for the lock - // TryCloseInternal will clear out the snapshot when it is done - if (_snapshot != null) - { + // protect against concurrent close and cancel + lock (stateObj) + { + if (_stateObj != null) + { // reader not closed while we waited for the lock + // TryCloseInternal will clear out the snapshot when it is done + if (_snapshot != null) + { #if DEBUG - // The stack trace for replays will differ since they weren't captured during close - stateObj._permitReplayStackTraceToDiffer = true; + // The stack trace for replays will differ since they weren't captured during close + stateObj._permitReplayStackTraceToDiffer = true; #endif - PrepareForAsyncContinuation(); - } + PrepareForAsyncContinuation(); + } - SetTimeout(_defaultTimeoutMilliseconds); + SetTimeout(_defaultTimeoutMilliseconds); - // Close can be called from async methods in error cases, - // in which case we need to switch to syncOverAsync - stateObj._syncOverAsync = true; + // Close can be called from async methods in error cases, + // in which case we need to switch to syncOverAsync + stateObj._syncOverAsync = true; - if (!TryCloseInternal(true /*closeReader*/)) - { - throw SQL.SynchronousCallMayNotPend(); + if (!TryCloseInternal(true /*closeReader*/)) + { + throw SQL.SynchronousCallMayNotPend(); + } + // 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 USE stateObj after this point - it has been returned to the TdsParser's session pool and potentially handed out to another thread } } } - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); + finally + { + SqlStatistics.StopTimer(statistics); + } } } @@ -956,7 +959,7 @@ private bool TryCloseInternal(bool closeReader) { _sharedState._dataReady = true; // set _sharedState._dataReady to not confuse CleanPartialRead } - _stateObj._internalTimeout = false; + _stateObj.SetTimeoutStateStopped(); if (_sharedState._dataReady) { cleanDataFailed = true; @@ -1167,7 +1170,7 @@ private string GetDataTypeNameInternal(_SqlMetaData metaData) { string dataTypeName = null; - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { dataTypeName = MetaType.MetaNVarChar.TypeName; } @@ -1249,9 +1252,9 @@ private Type GetFieldTypeInternal(_SqlMetaData metaData) { Type fieldType = null; - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { - // Return katmai types as string + // Return 2008 types as string fieldType = MetaType.MetaNVarChar.ClassType; } else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) @@ -1357,7 +1360,7 @@ private Type GetProviderSpecificFieldTypeInternal(_SqlMetaData metaData) { Type providerSpecificFieldType = null; - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { providerSpecificFieldType = MetaType.MetaNVarChar.SqlType; } @@ -1442,24 +1445,25 @@ override public int GetProviderSpecificValues(object[] values) public override DataTable GetSchemaTable() { SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - try + using (TryEventScope.Create("SqlDataReader.GetSchemaTable | API | Object Id {0}, Command Object Id {1}", ObjectID, Command?.ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); - if (null == _metaData || null == _metaData.schemaTable) + try { - if (null != this.MetaData) + statistics = SqlStatistics.StartTimer(Statistics); + if (null == _metaData || null == _metaData.schemaTable) { - _metaData.schemaTable = BuildSchemaTable(); - Debug.Assert(null != _metaData.schemaTable, "No schema information yet!"); + if (null != this.MetaData) + { + _metaData.schemaTable = BuildSchemaTable(); + Debug.Assert(null != _metaData.schemaTable, "No schema information yet!"); + } } + return _metaData?.schemaTable; + } + finally + { + SqlStatistics.StopTimer(statistics); } - return _metaData?.schemaTable; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); } } @@ -1490,7 +1494,7 @@ virtual public XmlReader GetXmlReader(int i) // Wrap the sequential stream in an XmlReader _currentStream = new SqlSequentialStream(this, i); _lastColumnWithDataChunkRead = i; - return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(_currentStream, closeInput: true); + return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(_currentStream, closeInput: true, async: false); } else { @@ -1500,11 +1504,11 @@ virtual public XmlReader GetXmlReader(int i) if (_data[i].IsNull) { // A 'null' stream - return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(new MemoryStream(Array.Empty(), writable: false), closeInput: true); + return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(new MemoryStream(Array.Empty(), writable: false), closeInput: true, async: false); } else { - // Grab already read data + // Grab already read data return _data[i].SqlXml.CreateReader(); } } @@ -1549,7 +1553,7 @@ override public Stream GetStream(int i) } else { - // Grab already read data + // Grab already read data data = _data[i].SqlBinary.Value; } @@ -1970,7 +1974,7 @@ override public TextReader GetTextReader(int i) } else { - // Grab already read data + // Grab already read data data = _data[i].SqlString.Value; } @@ -2191,7 +2195,7 @@ private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int buffe throw ADP.NonSeqByteAccess(dataIndex, _columnDataCharsRead, nameof(GetChars)); } - // If we start reading the new column, either dataIndex is 0 or + // If we start reading the new column, either dataIndex is 0 or // _columnDataCharsRead is 0 and dataIndex > _columnDataCharsRead is true below. // In both cases we will clean decoder if (dataIndex == 0) @@ -2222,7 +2226,7 @@ private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int buffe // Skip chars // Clean decoder state: we do not reset it, but destroy to ensure - // that we do not start decoding the column with decoder from the old one + // that we do not start decoding the column with decoder from the old one _stateObj._plpdecoder = null; cch = dataIndex - _columnDataCharsRead; cch = isUnicode ? (cch << 1) : cch; @@ -2280,7 +2284,7 @@ override public DateTime GetDateTime(int i) DateTime dt = _data[i].DateTime; // This accessor can be called for regular DateTime column. In this case we should not throw - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].Is2008DateTimeType) { // TypeSystem.SQLServer2005 or less @@ -2378,10 +2382,10 @@ virtual public SqlChars GetSqlChars(int i) { ReadColumn(i); SqlString data; - // Convert Katmai types to string - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) + // Convert 2008 types to string + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].Is2008DateTimeType) { - data = _data[i].KatmaiDateTimeSqlString; + data = _data[i].Sql2008DateTimeSqlString; } else { @@ -2458,9 +2462,9 @@ virtual public SqlString GetSqlString(int i) { ReadColumn(i); - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].Is2008DateTimeType) { - return _data[i].KatmaiDateTimeSqlString; + return _data[i].Sql2008DateTimeSqlString; } return _data[i].SqlString; @@ -2537,10 +2541,10 @@ private object GetSqlValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met { Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); - // Convert Katmai types to string - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) + // Convert 2008 types to string + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { - return data.KatmaiDateTimeSqlString; + return data.Sql2008DateTimeSqlString; } else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) { @@ -2617,10 +2621,10 @@ override public string GetString(int i) { ReadColumn(i); - // Convert katmai value to string if type system knob is 2005 or earlier - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) + // Convert 2008 value to string if type system knob is 2005 or earlier + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].Is2008DateTimeType) { - return _data[i].KatmaiDateTimeString; + return _data[i].Sql2008DateTimeString; } return _data[i].String; @@ -2635,7 +2639,7 @@ override public T GetFieldValue(int i) statistics = SqlStatistics.StartTimer(Statistics); SetTimeout(_defaultTimeoutMilliseconds); - return GetFieldValueInternal(i); + return GetFieldValueInternal(i, isAsync: false); } finally { @@ -2727,7 +2731,7 @@ private object GetValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaDa { Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { if (data.IsNull) { @@ -2735,7 +2739,7 @@ private object GetValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaDa } else { - return data.KatmaiDateTimeString; + return data.Sql2008DateTimeString; } } else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) @@ -2771,7 +2775,7 @@ private object GetValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaDa } } - private T GetFieldValueInternal(int i) + private T GetFieldValueInternal(int i, bool isAsync) { if (_currentTask != null) { @@ -2779,20 +2783,21 @@ private T GetFieldValueInternal(int i) } Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); - bool result = TryReadColumn(i, setTimeout: false); + bool forStreaming = typeof(T) == typeof(XmlReader) || typeof(T) == typeof(TextReader) || typeof(T) == typeof(Stream); + bool result = TryReadColumn(i, setTimeout: false, forStreaming: forStreaming); if (!result) { throw SQL.SynchronousCallMayNotPend(); } - return GetFieldValueFromSqlBufferInternal(_data[i], _metaData[i]); + return GetFieldValueFromSqlBufferInternal(_data[i], _metaData[i], isAsync: isAsync); } - private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData) + private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData, bool isAsync) { // this block of type specific shortcuts uses RyuJIT jit behaviors to achieve fast implementations of the primitive types // RyuJIT will be able to determine at compilation time that the typeof(T)==typeof() options are constant - // and be able to remove all implementations which cannot be reached. this will eliminate non-specialized code for + // and be able to remove all implementations which cannot be reached. this will eliminate non-specialized code for Type dataType = data.GetTypeFromStorageType(false); if (typeof(T) == typeof(int) && dataType == typeof(int)) { @@ -2830,22 +2835,132 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met { return (T)(object)data.Decimal; } - else if (typeof(T) == typeof(DateTimeOffset) && dataType == typeof(DateTimeOffset) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) + else if (typeof(T) == typeof(DateTimeOffset) && dataType == typeof(DateTimeOffset) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { return (T)(object)data.DateTimeOffset; } - else if (typeof(T) == typeof(DateTime) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) + else if (typeof(T) == typeof(DateTime) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { return (T)(object)data.DateTime; } +#if NET6_0_OR_GREATER + else if (typeof(T) == typeof(DateOnly) && dataType == typeof(DateTime) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + { + return (T)(object)data.DateOnly; + } + else if (typeof(T) == typeof(TimeOnly) && dataType == typeof(TimeOnly) && _typeSystem > SqlConnectionString.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) + { + return (T)(object)data.TimeOnly; + } +#endif + else if (typeof(T) == typeof(XmlReader)) + { + // XmlReader only allowed on XML types + if (metaData.metaType.SqlDbType != SqlDbType.Xml) + { + throw SQL.XmlReaderNotSupportOnColumnType(metaData.column); + } + + if (IsCommandBehavior(CommandBehavior.SequentialAccess)) + { + // Wrap the sequential stream in an XmlReader + _currentStream = new SqlSequentialStream(this, metaData.ordinal); + _lastColumnWithDataChunkRead = metaData.ordinal; + return (T)(object)SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(_currentStream, closeInput: true, async: isAsync); + } + else + { + if (data.IsNull) + { + // A 'null' stream + return (T)(object)SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(new MemoryStream(Array.Empty(), writable: false), closeInput: true, async: isAsync); + } + else + { + // Grab already read data + return (T)(object)data.SqlXml.CreateReader(); + } + } + } + else if (typeof(T) == typeof(TextReader)) + { + // Xml type is not supported + MetaType metaType = metaData.metaType; + if (metaData.cipherMD != null) + { + Debug.Assert(metaData.baseTI != null, "_metaData[i].baseTI should not be null."); + metaType = metaData.baseTI.metaType; + } + + if ( + (!metaType.IsCharType && metaType.SqlDbType != SqlDbType.Variant) || + (metaType.SqlDbType == SqlDbType.Xml) + ) + { + throw SQL.TextReaderNotSupportOnColumnType(metaData.column); + } + + // For non-variant types with sequential access, we support proper streaming + if ((metaType.SqlDbType != SqlDbType.Variant) && IsCommandBehavior(CommandBehavior.SequentialAccess)) + { + if (metaData.cipherMD != null) + { + throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(metaData.column); + } + + System.Text.Encoding encoding = SqlUnicodeEncoding.SqlUnicodeEncodingInstance; + if (!metaType.IsNCharType) + { + encoding = metaData.encoding; + } + + _currentTextReader = new SqlSequentialTextReader(this, metaData.ordinal, encoding); + _lastColumnWithDataChunkRead = metaData.ordinal; + return (T)(object)_currentTextReader; + } + else + { + string value = data.IsNull ? string.Empty : data.SqlString.Value; + return (T)(object)new StringReader(value); + } + + } + else if (typeof(T) == typeof(Stream)) + { + if (metaData != null && metaData.cipherMD != null) + { + throw SQL.StreamNotSupportOnEncryptedColumn(metaData.column); + } + + // Stream is only for Binary, Image, VarBinary, Udt, Xml and Timestamp(RowVersion) types + MetaType metaType = metaData.metaType; + if ( + (!metaType.IsBinType || metaType.SqlDbType == SqlDbType.Timestamp) && + metaType.SqlDbType != SqlDbType.Variant + ) + { + throw SQL.StreamNotSupportOnColumnType(metaData.column); + } + + if ((metaType.SqlDbType != SqlDbType.Variant) && (IsCommandBehavior(CommandBehavior.SequentialAccess))) + { + _currentStream = new SqlSequentialStream(this, metaData.ordinal); + _lastColumnWithDataChunkRead = metaData.ordinal; + return (T)(object)_currentStream; + } + else + { + byte[] value = data.IsNull ? Array.Empty() : data.SqlBinary.Value; + return (T)(object)new MemoryStream(value, writable: false); + } + } else { - Type typeofT = typeof(T); - if (_typeofINullable.IsAssignableFrom(typeofT)) + if (typeof(INullable).IsAssignableFrom(typeof(T))) { // If its a SQL Type or Nullable UDT object rawValue = GetSqlValueFromSqlBufferInternal(data, metaData); - if (typeofT == s_typeofSqlString) + if (typeof(T) == typeof(SqlString)) { // Special case: User wants SqlString, but we have a SqlXml // SqlXml can not be typecast into a SqlString, but we need to support SqlString on XML Types - so do a manual conversion @@ -2866,60 +2981,20 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met } else { - if (typeof(XmlReader) == typeofT) + // 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 + // type named to the user so they know what went wrong. Supported types are listed + // in the documentation + try { - if (metaData.metaType.SqlDbType != SqlDbType.Xml) - { - throw SQL.XmlReaderNotSupportOnColumnType(metaData.column); - } - else - { - object clrValue = null; - if (!data.IsNull) - { - clrValue = GetValueFromSqlBufferInternal(data, metaData); - } - if (clrValue is null) // covers IsNull and when there is data which is present but is a clr null somehow - { - return (T)(object)SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader( - new MemoryStream(Array.Empty(), writable: false), - closeInput: true - ); - } - else if (clrValue.GetType() == typeof(string)) - { - return (T)(object)SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader( - new StringReader(clrValue as string), - closeInput: true - ); - } - else - { - // try the type cast to throw the invalid cast exception and inform the user what types they're trying to use and that why it is wrong - return (T)clrValue; - } - } + return (T)GetValueFromSqlBufferInternal(data, metaData); } - else + catch (InvalidCastException) when (data.IsNull) { - try - { - return (T)GetValueFromSqlBufferInternal(data, metaData); - } - catch (InvalidCastException) - { - if (data.IsNull) - { - // If the value was actually null, then we should throw a SqlNullValue instead - throw SQL.SqlNullValue(); - } - else - { - // Legitimate InvalidCast, rethrow - throw; - } - } + // If the value was actually null, then we should throw a SqlNullValue instead + throw SQL.SqlNullValue(); } + } } } @@ -2966,9 +3041,9 @@ override public int GetValues(object[] values) if ((sequentialAccess) && (i < maximumColumn)) { _data[fieldIndex].Clear(); - if (fieldIndex > i && fieldIndex>0) + if (fieldIndex > i && fieldIndex > 0) { - // if we jumped an index forward because of a hidden column see if the buffer before the + // if we jumped an index forward because of a hidden column see if the buffer before the // current one was populated by the seek forward and clear it if it was _data[fieldIndex - 1].Clear(); } @@ -3243,138 +3318,139 @@ override public bool NextResult() private bool TryNextResult(out bool more) { SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - try + using (TryEventScope.Create("SqlDataReader.NextResult | API | Object Id {0}", ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); - - SetTimeout(_defaultTimeoutMilliseconds); - - if (IsClosed) + try { - throw ADP.DataReaderClosed(nameof(NextResult)); - } - _fieldNameLookup = null; + statistics = SqlStatistics.StartTimer(Statistics); - bool success = false; // WebData 100390 - _hasRows = false; // reset HasRows + SetTimeout(_defaultTimeoutMilliseconds); - // if we are specifically only processing a single result, then read all the results off the wire and detach - if (IsCommandBehavior(CommandBehavior.SingleResult)) - { - if (!TryCloseInternal(false /*closeReader*/)) + if (IsClosed) { - more = false; - return false; + throw ADP.DataReaderClosed(nameof(NextResult)); } + _fieldNameLookup = null; - // In the case of not closing the reader, null out the metadata AFTER - // CloseInternal finishes - since CloseInternal may go to the wire - // and use the metadata. - ClearMetaData(); - more = success; - return true; - } + bool success = false; // WebData 100390 + _hasRows = false; // reset HasRows - if (null != _parser) - { - // if there are more rows, then skip them, the user wants the next result - bool moreRows = true; - while (moreRows) + // if we are specifically only processing a single result, then read all the results off the wire and detach + if (IsCommandBehavior(CommandBehavior.SingleResult)) { - if (!TryReadInternal(false, out moreRows)) - { // don't reset set the timeout value + if (!TryCloseInternal(false /*closeReader*/)) + { more = false; return false; } - } - } - // we may be done, so continue only if we have not detached ourselves from the parser - if (null != _parser) - { - bool moreResults; - if (!TryHasMoreResults(out moreResults)) - { - more = false; - return false; + // In the case of not closing the reader, null out the metadata AFTER + // CloseInternal finishes - since CloseInternal may go to the wire + // and use the metadata. + ClearMetaData(); + more = success; + return true; } - if (moreResults) - { - _metaDataConsumed = false; - _browseModeInfoConsumed = false; - switch (_altRowStatus) + if (null != _parser) + { + // if there are more rows, then skip them, the user wants the next result + bool moreRows = true; + while (moreRows) { - case ALTROWSTATUS.AltRow: - int altRowId; - if (!_parser.TryGetAltRowId(_stateObj, out altRowId)) - { - more = false; - return false; - } - _SqlMetaDataSet altMetaDataSet = _altMetaDataSetCollection.GetAltMetaData(altRowId); - if (altMetaDataSet != null) - { - _metaData = altMetaDataSet; - } - Debug.Assert((_metaData != null), "Can't match up altrowmetadata"); - break; - case ALTROWSTATUS.Done: - // restore the row-metaData - _metaData = _altMetaDataSetCollection.metaDataSet; - Debug.Assert(_altRowStatus == ALTROWSTATUS.Done, "invalid AltRowStatus"); - _altRowStatus = ALTROWSTATUS.Null; - break; - default: - if (!TryConsumeMetaData()) - { - more = false; - return false; - } - if (_metaData == null) - { - more = false; - return true; - } - break; + if (!TryReadInternal(false, out moreRows)) + { // don't reset set the timeout value + more = false; + return false; + } } - - success = true; } - else + + // we may be done, so continue only if we have not detached ourselves from the parser + if (null != _parser) { - // detach the parser from this reader now - if (!TryCloseInternal(false /*closeReader*/)) + bool moreResults; + if (!TryHasMoreResults(out moreResults)) { more = false; return false; } + if (moreResults) + { + _metaDataConsumed = false; + _browseModeInfoConsumed = false; - // In the case of not closing the reader, null out the metadata AFTER - // CloseInternal finishes - since CloseInternal may go to the wire - // and use the metadata. - if (!TrySetMetaData(null, false)) + switch (_altRowStatus) + { + case ALTROWSTATUS.AltRow: + int altRowId; + if (!_parser.TryGetAltRowId(_stateObj, out altRowId)) + { + more = false; + return false; + } + _SqlMetaDataSet altMetaDataSet = _altMetaDataSetCollection.GetAltMetaData(altRowId); + if (altMetaDataSet != null) + { + _metaData = altMetaDataSet; + } + Debug.Assert((_metaData != null), "Can't match up altrowmetadata"); + break; + case ALTROWSTATUS.Done: + // restore the row-metaData + _metaData = _altMetaDataSetCollection.metaDataSet; + Debug.Assert(_altRowStatus == ALTROWSTATUS.Done, "invalid AltRowStatus"); + _altRowStatus = ALTROWSTATUS.Null; + break; + default: + if (!TryConsumeMetaData()) + { + more = false; + return false; + } + if (_metaData == null) + { + more = false; + return true; + } + break; + } + + success = true; + } + else { - more = false; - return false; + // detach the parser from this reader now + if (!TryCloseInternal(false /*closeReader*/)) + { + more = false; + return false; + } + + // In the case of not closing the reader, null out the metadata AFTER + // CloseInternal finishes - since CloseInternal may go to the wire + // and use the metadata. + if (!TrySetMetaData(null, false)) + { + more = false; + return false; + } } } + else + { + // Clear state in case of Read calling CloseInternal() then user calls NextResult() + // and the case where the Read() above will do essentially the same thing. + ClearMetaData(); + } + + more = success; + return true; } - else + finally { - // Clear state in case of Read calling CloseInternal() then user calls NextResult() - // and the case where the Read() above will do essentially the same thing. - ClearMetaData(); + SqlStatistics.StopTimer(statistics); } - - more = success; - return true; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); } } @@ -3404,160 +3480,198 @@ override public bool Read() private bool TryReadInternal(bool setTimeout, out bool more) { SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - try + using (TryEventScope.Create("SqlDataReader.TryReadInternal | API | Object Id {0}", ObjectID)) { - statistics = SqlStatistics.StartTimer(Statistics); +#if !NET6_0_OR_GREATER + RuntimeHelpers.PrepareConstrainedRegions(); +#endif - if (null != _parser) + try { - if (setTimeout) - { - SetTimeout(_defaultTimeoutMilliseconds); - } - if (_sharedState._dataReady) + statistics = SqlStatistics.StartTimer(Statistics); + + if (null != _parser) { - if (!TryCleanPartialRead()) + if (setTimeout) { - more = false; - return false; + SetTimeout(_defaultTimeoutMilliseconds); + } + if (_sharedState._dataReady) + { + if (!TryCleanPartialRead()) + { + more = false; + return false; + } } - } - // clear out our buffers - SqlBuffer.Clear(_data); + // clear out our buffers + SqlBuffer.Clear(_data); - _sharedState._nextColumnHeaderToRead = 0; - _sharedState._nextColumnDataToRead = 0; - _sharedState._columnDataBytesRemaining = -1; // unknown - _lastColumnWithDataChunkRead = -1; + _sharedState._nextColumnHeaderToRead = 0; + _sharedState._nextColumnDataToRead = 0; + _sharedState._columnDataBytesRemaining = -1; // unknown + _lastColumnWithDataChunkRead = -1; - if (!_haltRead) - { - bool moreRows; - if (!TryHasMoreRows(out moreRows)) + if (!_haltRead) { - more = false; - return false; - } - if (moreRows) - { - // read the row from the backend (unless it's an altrow were the marker is already inside the altrow ...) - while (_stateObj.HasPendingData) + bool moreRows; + if (!TryHasMoreRows(out moreRows)) { - if (_altRowStatus != ALTROWSTATUS.AltRow) + more = false; + return false; + } + if (moreRows) + { + // read the row from the backend (unless it's an altrow were the marker is already inside the altrow ...) + while (_stateObj.HasPendingData) { - // if this is an ordinary row we let the run method consume the ROW token - if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out _sharedState._dataReady)) + if (_altRowStatus != ALTROWSTATUS.AltRow) { - more = false; - return false; + // if this is an ordinary row we let the run method consume the ROW token + if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out _sharedState._dataReady)) + { + more = false; + return false; + } + if (_sharedState._dataReady) + { + break; + } } - if (_sharedState._dataReady) + else { + // ALTROW token and AltrowId are already consumed ... + Debug.Assert(_altRowStatus == ALTROWSTATUS.AltRow, "invalid AltRowStatus"); + _altRowStatus = ALTROWSTATUS.Done; + _sharedState._dataReady = true; break; } } - else + if (_sharedState._dataReady) { - // ALTROW token and AltrowId are already consumed ... - Debug.Assert(_altRowStatus == ALTROWSTATUS.AltRow, "invalid AltRowStatus"); - _altRowStatus = ALTROWSTATUS.Done; - _sharedState._dataReady = true; - break; + _haltRead = IsCommandBehavior(CommandBehavior.SingleRow); + more = true; + return true; } } - if (_sharedState._dataReady) + + if (!_stateObj.HasPendingData) { - _haltRead = IsCommandBehavior(CommandBehavior.SingleRow); - more = true; - return true; + if (!TryCloseInternal(false /*closeReader*/)) + { + more = false; + return false; + } } } - - if (!_stateObj.HasPendingData) + else { - if (!TryCloseInternal(false /*closeReader*/)) + // if we did not get a row and halt is true, clean off rows of result + // success must be false - or else we could have just read off row and set + // halt to true + bool moreRows; + if (!TryHasMoreRows(out moreRows)) { more = false; return false; } - } - } - else - { - // if we did not get a row and halt is true, clean off rows of result - // success must be false - or else we could have just read off row and set - // halt to true - bool moreRows; - if (!TryHasMoreRows(out moreRows)) - { - more = false; - return false; - } - while (moreRows) - { - // if we are in SingleRow mode, and we've read the first row, - // read the rest of the rows, if any - while (_stateObj.HasPendingData && !_sharedState._dataReady) + while (moreRows) { - if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out _sharedState._dataReady)) + // if we are in SingleRow mode, and we've read the first row, + // read the rest of the rows, if any + while (_stateObj.HasPendingData && !_sharedState._dataReady) { - more = false; - return false; + if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out _sharedState._dataReady)) + { + more = false; + return false; + } } - } - if (_sharedState._dataReady) - { - if (!TryCleanPartialRead()) + if (_sharedState._dataReady) + { + if (!TryCleanPartialRead()) + { + more = false; + return false; + } + } + + // clear out our buffers + SqlBuffer.Clear(_data); + + _sharedState._nextColumnHeaderToRead = 0; + + if (!TryHasMoreRows(out moreRows)) { more = false; return false; } } - // clear out our buffers - SqlBuffer.Clear(_data); - - _sharedState._nextColumnHeaderToRead = 0; + // reset haltRead + _haltRead = false; + } + } + else if (IsClosed) + { + throw ADP.DataReaderClosed(nameof(Read)); + } + more = false; - if (!TryHasMoreRows(out moreRows)) - { - more = false; - return false; - } +#if DEBUG + if ((!_sharedState._dataReady) && (_stateObj.HasPendingData)) + { + byte token; + if (!_stateObj.TryPeekByte(out token)) + { + return false; } - // reset haltRead - _haltRead = false; + Debug.Assert(TdsParser.IsValidTdsToken(token), $"DataReady is false, but next token is invalid: {token,-2:X2}"); } +#endif + + return true; } - else if (IsClosed) + catch (OutOfMemoryException e) { - throw ADP.DataReaderClosed(nameof(Read)); + _isClosed = true; + SqlConnection con = _connection; + if (con != null) + { + con.Abort(e); + } + throw; } - more = false; - -#if DEBUG - if ((!_sharedState._dataReady) && (_stateObj.HasPendingData)) + catch (StackOverflowException e) { - byte token; - if (!_stateObj.TryPeekByte(out token)) + _isClosed = true; + SqlConnection con = _connection; + if (con != null) { - return false; + con.Abort(e); } - - Debug.Assert(TdsParser.IsValidTdsToken(token), $"DataReady is false, but next token is invalid: {token,-2:X2}"); + throw; + } + /* Even though ThreadAbortException exists in .NET Core, + * since Abort is not supported, the common language runtime won't ever throw ThreadAbortException. + * just to keep a common codebase!*/ + catch (System.Threading.ThreadAbortException e) + { + _isClosed = true; + SqlConnection con = _connection; + if (con != null) + { + con.Abort(e); + } + throw; + } + finally + { + SqlStatistics.StopTimer(statistics); } -#endif - - return true; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); } } @@ -3576,7 +3690,7 @@ private void ReadColumn(int i, bool setTimeout = true, bool allowPartiallyReadCo } } - private bool TryReadColumn(int i, bool setTimeout, bool allowPartiallyReadColumn = false) + private bool TryReadColumn(int i, bool setTimeout, bool allowPartiallyReadColumn = false, bool forStreaming = false) { CheckDataIsReady(columnIndex: i, permitAsync: true, allowPartiallyReadColumn: allowPartiallyReadColumn, methodName: null); @@ -3588,7 +3702,7 @@ private bool TryReadColumn(int i, bool setTimeout, bool allowPartiallyReadColumn SetTimeout(_defaultTimeoutMilliseconds); } - if (!TryReadColumnInternal(i, readHeaderOnly: false)) + if (!TryReadColumnInternal(i, readHeaderOnly: false, forStreaming: forStreaming)) { return false; } @@ -3637,7 +3751,7 @@ private bool TryReadColumnHeader(int i) return TryReadColumnInternal(i, readHeaderOnly: true); } - private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) + internal bool TryReadColumnInternal(int i, bool readHeaderOnly = false, bool forStreaming = false) { AssertReaderState(requireData: true, permitAsync: true, columnIndex: i); @@ -3701,17 +3815,69 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) { _SqlMetaData columnMetaData = _metaData[_sharedState._nextColumnHeaderToRead]; - if ((isSequentialAccess) && (_sharedState._nextColumnHeaderToRead < i)) + if (isSequentialAccess) { - // SkipValue is no-op if the column appears in NBC bitmask - // if not, it skips regular and PLP types - if (!_parser.TrySkipValue(columnMetaData, _sharedState._nextColumnHeaderToRead, _stateObj)) + if (_sharedState._nextColumnHeaderToRead < i) { - return false; + // SkipValue is no-op if the column appears in NBC bitmask + // if not, it skips regular and PLP types + if (!_parser.TrySkipValue(columnMetaData, _sharedState._nextColumnHeaderToRead, _stateObj)) + { + return false; + } + + _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead; + _sharedState._nextColumnHeaderToRead++; } + else if (_sharedState._nextColumnHeaderToRead == i) + { + bool isNull; + ulong dataLength; + if (!_parser.TryProcessColumnHeader(columnMetaData, _stateObj, _sharedState._nextColumnHeaderToRead, out isNull, out dataLength)) + { + return false; + } - _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead; - _sharedState._nextColumnHeaderToRead++; + _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead; + _sharedState._nextColumnHeaderToRead++; // We read this one + _sharedState._columnDataBytesRemaining = (long)dataLength; + + if (isNull) + { + if (columnMetaData.type != SqlDbType.Timestamp) + { + TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], + columnMetaData, + _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, + _parser.Connection); + } + } + else + { + if (!readHeaderOnly && !forStreaming) + { + // If we're in sequential mode try to read the data and then if it succeeds update shared + // state so there are no remaining bytes and advance the next column to read + if (!_parser.TryReadSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, (int)dataLength, _stateObj, + _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, + columnMetaData.column)) + { // will read UDTs as VARBINARY. + return false; + } + _sharedState._columnDataBytesRemaining = 0; + _sharedState._nextColumnDataToRead++; + } + else + { + _sharedState._columnDataBytesRemaining = (long)dataLength; + } + } + } + else + { + // we have read past the column somehow, this is an error + Debug.Assert(false, "We have read past the column somehow, this is an error"); + } } else { @@ -3725,12 +3891,14 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead; _sharedState._nextColumnHeaderToRead++; // We read this one - if (isNull && columnMetaData.type != SqlDbType.Timestamp) + // Trigger new behavior for RowVersion to send DBNull.Value by allowing entry for Timestamp or discard entry for Timestamp for legacy support. + // if LegacyRowVersionNullBehavior is enabled, Timestamp type must enter "else" block. + if (isNull && (!LocalAppContextSwitches.LegacyRowVersionNullBehavior || columnMetaData.type != SqlDbType.Timestamp)) { TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], - columnMetaData, - _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, - _parser.Connection); + columnMetaData, + _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, + _parser.Connection); if (!readHeaderOnly) { @@ -3746,7 +3914,7 @@ private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) // can read it out of order if (!_parser.TryReadSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, (int)dataLength, _stateObj, _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, - columnMetaData.column)) + columnMetaData.column, _command)) { // will read UDTs as VARBINARY. return false; } @@ -3935,7 +4103,7 @@ private void RestoreServerSettings(TdsParser parser, TdsParserStateObject stateO // broken connection, so check state first. if (parser.State == TdsParserState.OpenLoggedIn) { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID '{1}'", ObjectID, ActivityCorrelator.Current); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlDataReader.RestoreServerSettings | Info | Correlation | Object Id {0}, Activity Id '{1}'", ObjectID, ActivityCorrelator.Current); Task executeTask = parser.TdsExecuteSQLBatch(_resetOptionsString, (_command != null) ? _command.CommandTimeout : 0, null, stateObj, sync: true); Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes"); @@ -4046,8 +4214,8 @@ internal bool TrySetMetaData(_SqlMetaDataSet metaData, bool moreInfo) if (_parser != null) { // There is a valid case where parser is null - // Peek, and if row token present, set _hasRows true since there is a - // row in the result + // Peek, and if row token present, set _hasRows true since there is a + // row in the result byte b; if (!_stateObj.TryPeekByte(out b)) { @@ -4240,8 +4408,8 @@ private void AssertReaderState(bool requireData, bool permitAsync, int? columnIn /// public override Task NextResultAsync(CancellationToken cancellationToken) { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - try + using (TryEventScope.Create("SqlDataReader.NextResultAsync | API | Object Id {0}", ObjectID)) + using (var registrationHolder = new DisposableTemporaryOnStack()) { TaskCompletionSource source = new TaskCompletionSource(); @@ -4251,7 +4419,6 @@ public override Task NextResultAsync(CancellationToken cancellationToken) return source.Task; } - IDisposable registration = null; if (cancellationToken.CanBeCanceled) { if (cancellationToken.IsCancellationRequested) @@ -4259,7 +4426,7 @@ public override Task NextResultAsync(CancellationToken cancellationToken) source.SetCanceled(); return source.Task; } - registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); + registrationHolder.Set(cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command)); } Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); @@ -4277,11 +4444,7 @@ public override Task NextResultAsync(CancellationToken cancellationToken) return source.Task; } - return InvokeAsyncCall(new HasNextResultAsyncCallContext(this, source, registration)); - } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); + return InvokeAsyncCall(new HasNextResultAsyncCallContext(this, source, registrationHolder.Take())); } } @@ -4290,17 +4453,17 @@ private static Task NextResultAsyncExecute(Task task, object state) HasNextResultAsyncCallContext context = (HasNextResultAsyncCallContext)state; if (task != null) { - SqlClientEventSource.Log.TryTraceEvent(" attempt retry {0}", ObjectID); - context._reader.PrepareForAsyncContinuation(); + SqlClientEventSource.Log.TryTraceEvent("SqlDataReader.NextResultAsyncExecute | attempt retry {0}", ObjectID); + context.Reader.PrepareForAsyncContinuation(); } - if (context._reader.TryNextResult(out bool more)) + if (context.Reader.TryNextResult(out bool more)) { - // completed + // completed return more ? ADP.TrueTask : ADP.FalseTask; } - return context._reader.ExecuteAsyncCall(context); + return context.Reader.ExecuteAsyncCall(context); } // NOTE: This will return null if it completed sequentially @@ -4331,12 +4494,12 @@ internal Task GetBytesAsync(int columnIndex, byte[] buffer, int index, int var context = new GetBytesAsyncCallContext(this) { - columnIndex = columnIndex, - buffer = buffer, - index = index, - length = length, - timeout = timeout, - cancellationToken = cancellationToken, + _columnIndex = columnIndex, + _buffer = buffer, + _index = index, + _length = length, + _timeout = timeout, + _cancellationToken = cancellationToken, }; // Check if we need to skip columns @@ -4361,18 +4524,18 @@ internal Task GetBytesAsync(int columnIndex, byte[] buffer, int index, int timeoutToken = timeoutCancellationSource.Token; } - context._disposable = timeoutCancellationSource; - context.timeoutToken = timeoutToken; - context._source = source; PrepareAsyncInvocation(useSnapshot: true); + context.Set(this, source, timeoutCancellationSource); + context._timeoutToken = timeoutToken; + return InvokeAsyncCall(context); } else { // We're already at the correct column, just read the data - context.mode = GetBytesAsyncCallContext.OperationMode.Read; + context._mode = GetBytesAsyncCallContext.OperationMode.Read; // Switch to async PrepareAsyncInvocation(useSnapshot: false); @@ -4392,9 +4555,9 @@ internal Task GetBytesAsync(int columnIndex, byte[] buffer, int index, int private static Task GetBytesAsyncSeekExecute(Task task, object state) { GetBytesAsyncCallContext context = (GetBytesAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; - Debug.Assert(context.mode == GetBytesAsyncCallContext.OperationMode.Seek, "context.mode must be Seek to check if seeking can resume"); + Debug.Assert(context._mode == GetBytesAsyncCallContext.OperationMode.Seek, "context.mode must be Seek to check if seeking can resume"); if (task != null) { @@ -4404,16 +4567,16 @@ private static Task GetBytesAsyncSeekExecute(Task task, object state) // Prepare for stateObj timeout reader.SetTimeout(reader._defaultTimeoutMilliseconds); - if (reader.TryReadColumnHeader(context.columnIndex)) + if (reader.TryReadColumnHeader(context._columnIndex)) { // Only once we have read up to where we need to be can we check the cancellation tokens (otherwise we will be in an unknown state) - if (context.cancellationToken.IsCancellationRequested) + if (context._cancellationToken.IsCancellationRequested) { // User requested cancellation - return Task.FromCanceled(context.cancellationToken); + return Task.FromCanceled(context._cancellationToken); } - else if (context.timeoutToken.IsCancellationRequested) + else if (context._timeoutToken.IsCancellationRequested) { // Timeout return Task.FromException(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout()))); @@ -4421,7 +4584,7 @@ private static Task GetBytesAsyncSeekExecute(Task task, object state) else { // Up to the correct column - continue to read - context.mode = GetBytesAsyncCallContext.OperationMode.Read; + context._mode = GetBytesAsyncCallContext.OperationMode.Read; reader.SwitchToAsyncWithoutSnapshot(); int totalBytesRead; var readTask = reader.GetBytesAsyncReadDataStage(context, true, out totalBytesRead); @@ -4445,18 +4608,18 @@ private static Task GetBytesAsyncSeekExecute(Task task, object state) private static Task GetBytesAsyncReadExecute(Task task, object state) { var context = (GetBytesAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; - Debug.Assert(context.mode == GetBytesAsyncCallContext.OperationMode.Read, "context.mode must be Read to check if read can resume"); + Debug.Assert(context._mode == GetBytesAsyncCallContext.OperationMode.Read, "context.mode must be Read to check if read can resume"); reader.PrepareForAsyncContinuation(); - if (context.cancellationToken.IsCancellationRequested) + if (context._cancellationToken.IsCancellationRequested) { // User requested cancellation - return Task.FromCanceled(context.cancellationToken); + return Task.FromCanceled(context._cancellationToken); } - else if (context.timeoutToken.IsCancellationRequested) + else if (context._timeoutToken.IsCancellationRequested) { // Timeout return Task.FromException(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout()))); @@ -4468,18 +4631,18 @@ private static Task GetBytesAsyncReadExecute(Task task, object state) int bytesReadThisIteration; bool result = reader.TryGetBytesInternalSequential( - context.columnIndex, - context.buffer, - context.index + context.totalBytesRead, - context.length - context.totalBytesRead, + context._columnIndex, + context._buffer, + context._index + context._totalBytesRead, + context._length - context._totalBytesRead, out bytesReadThisIteration ); - context.totalBytesRead += bytesReadThisIteration; - Debug.Assert(context.totalBytesRead <= context.length, "Read more bytes than required"); + context._totalBytesRead += bytesReadThisIteration; + Debug.Assert(context._totalBytesRead <= context._length, "Read more bytes than required"); if (result) { - return Task.FromResult(context.totalBytesRead); + return Task.FromResult(context._totalBytesRead); } else { @@ -4490,24 +4653,24 @@ out bytesReadThisIteration private Task GetBytesAsyncReadDataStage(GetBytesAsyncCallContext context, bool isContinuation, out int bytesRead) { - Debug.Assert(context.mode == GetBytesAsyncCallContext.OperationMode.Read, "context.Mode must be Read to read data"); + Debug.Assert(context._mode == GetBytesAsyncCallContext.OperationMode.Read, "context.Mode must be Read to read data"); - _lastColumnWithDataChunkRead = context.columnIndex; + _lastColumnWithDataChunkRead = context._columnIndex; TaskCompletionSource source = null; // Prepare for stateObj timeout SetTimeout(_defaultTimeoutMilliseconds); // Try to read without any continuations (all the data may already be in the stateObj's buffer) - bool filledBuffer = context._reader.TryGetBytesInternalSequential( - context.columnIndex, - context.buffer, - context.index + context.totalBytesRead, - context.length - context.totalBytesRead, + bool filledBuffer = context.Reader.TryGetBytesInternalSequential( + context._columnIndex, + context._buffer, + context._index + context._totalBytesRead, + context._length - context._totalBytesRead, out bytesRead ); - context.totalBytesRead += bytesRead; - Debug.Assert(context.totalBytesRead <= context.length, "Read more bytes than required"); + context._totalBytesRead += bytesRead; + Debug.Assert(context._totalBytesRead <= context._length, "Read more bytes than required"); if (!filledBuffer) { @@ -4515,7 +4678,7 @@ out bytesRead if (!isContinuation) { // This is the first async operation which is happening - setup the _currentTask and timeout - Debug.Assert(context._source==null, "context._source should not be non-null when trying to change to async"); + Debug.Assert(context.Source == null, "context._source should not be non-null when trying to change to async"); source = new TaskCompletionSource(); Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); if (original != null) @@ -4523,7 +4686,7 @@ out bytesRead source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); return source.Task; } - context._source = source; + context.Source = source; // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) if (_cancelAsyncOnCloseToken.IsCancellationRequested) { @@ -4533,14 +4696,14 @@ out bytesRead } // Timeout - Debug.Assert(context.timeoutToken == CancellationToken.None, "TimeoutToken is set when GetBytesAsyncReadDataStage is not a continuation"); - if (context.timeout > 0) + Debug.Assert(context._timeoutToken == CancellationToken.None, "TimeoutToken is set when GetBytesAsyncReadDataStage is not a continuation"); + if (context._timeout > 0) { CancellationTokenSource timeoutCancellationSource = new CancellationTokenSource(); - timeoutCancellationSource.CancelAfter(context.timeout); - Debug.Assert(context._disposable is null, "setting context.disposable would lose the previous disposable"); - context._disposable = timeoutCancellationSource; - context.timeoutToken = timeoutCancellationSource.Token; + timeoutCancellationSource.CancelAfter(context._timeout); + Debug.Assert(context.Disposable is null, "setting context.disposable would lose the previous disposable"); + context.Disposable = timeoutCancellationSource; + context._timeoutToken = timeoutCancellationSource.Token; } } @@ -4552,10 +4715,10 @@ out bytesRead } else { - Debug.Assert(context._source != null, "context._source should not be null when continuing"); + Debug.Assert(context.Source != null, "context._source should not be null when continuing"); // setup for cleanup/completing retryTask.ContinueWith( - continuationAction: AAsyncCallContext.s_completeCallback, + continuationAction: SqlDataReaderBaseAsyncCallContext.s_completeCallback, state: context, TaskScheduler.Default ); @@ -4575,14 +4738,20 @@ out bytesRead /// public override Task ReadAsync(CancellationToken cancellationToken) { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - try + using (TryEventScope.Create("SqlDataReader.ReadAsync | API | Object Id {0}", ObjectID)) + using (var registrationHolder = new DisposableTemporaryOnStack()) { if (IsClosed) { return Task.FromException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed())); } + // Register first to catch any already expired tokens to be able to trigger cancellation event. + if (cancellationToken.CanBeCanceled) + { + registrationHolder.Set(cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command)); + } + // If user's token is canceled, return a canceled task if (cancellationToken.IsCancellationRequested) { @@ -4681,12 +4850,6 @@ public override Task ReadAsync(CancellationToken cancellationToken) return source.Task; } - IDisposable registration = null; - if (cancellationToken.CanBeCanceled) - { - registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); - } - ReadAsyncCallContext context = null; if (_connection?.InnerConnection is SqlInternalConnection sqlInternalConnection) { @@ -4697,9 +4860,9 @@ public override Task ReadAsync(CancellationToken cancellationToken) context = new ReadAsyncCallContext(); } - Debug.Assert(context._reader == null && context._source == null && context._disposable == null, "cached ReadAsyncCallContext was not properly disposed"); + Debug.Assert(context.Reader == null && context.Source == null && context.Disposable == default, "cached ReadAsyncCallContext was not properly disposed"); - context.Set(this, source, registration); + context.Set(this, source, registrationHolder.Take()); context._hasMoreData = more; context._hasReadRowToken = rowTokenRead; @@ -4707,16 +4870,12 @@ public override Task ReadAsync(CancellationToken cancellationToken) return InvokeAsyncCall(context); } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } } private static Task ReadAsyncExecute(Task task, object state) { var context = (ReadAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; ref bool hasMoreData = ref context._hasMoreData; ref bool hasReadRowToken = ref context._hasReadRowToken; @@ -4730,7 +4889,7 @@ private static Task ReadAsyncExecute(Task task, object state) // If there are no more rows, or this is Sequential Access, then we are done if (!hasMoreData || (reader._commandBehavior & CommandBehavior.SequentialAccess) == CommandBehavior.SequentialAccess) { - // completed + // completed return hasMoreData ? ADP.TrueTask : ADP.FalseTask; } else @@ -4750,7 +4909,7 @@ private static Task ReadAsyncExecute(Task task, object state) // if non-sequentialaccess then read entire row before returning if (reader.TryReadColumn(reader._metaData.Length - 1, true)) { - // completed + // completed return ADP.TrueTask; } } @@ -4841,56 +5000,58 @@ override public Task IsDBNullAsync(int i, CancellationToken cancellationTo return Task.FromException(ex); } - // Setup and check for pending task - TaskCompletionSource source = new TaskCompletionSource(); - Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); - if (original != null) + using (var registrationHolder = new DisposableTemporaryOnStack()) { - source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); - return source.Task; - } + // Setup and check for pending task + TaskCompletionSource source = new TaskCompletionSource(); + Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); + if (original != null) + { + source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); + return source.Task; + } - // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) - if (_cancelAsyncOnCloseToken.IsCancellationRequested) - { - source.SetCanceled(); - _currentTask = null; - return source.Task; - } + // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) + if (_cancelAsyncOnCloseToken.IsCancellationRequested) + { + source.SetCanceled(); + _currentTask = null; + return source.Task; + } - // Setup cancellations - IDisposable registration = null; - if (cancellationToken.CanBeCanceled) - { - registration = cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command); - } + // Setup cancellations + if (cancellationToken.CanBeCanceled) + { + registrationHolder.Set(cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command)); + } - IsDBNullAsyncCallContext context = null; - if (_connection?.InnerConnection is SqlInternalConnection sqlInternalConnection) - { - context = Interlocked.Exchange(ref sqlInternalConnection.CachedDataReaderIsDBNullContext, null); - } - if (context is null) - { - context = new IsDBNullAsyncCallContext(); - } + IsDBNullAsyncCallContext context = null; + if (_connection?.InnerConnection is SqlInternalConnection sqlInternalConnection) + { + context = Interlocked.Exchange(ref sqlInternalConnection.CachedDataReaderIsDBNullContext, null); + } + if (context is null) + { + context = new IsDBNullAsyncCallContext(); + } - Debug.Assert(context._reader == null && context._source == null && context._disposable == null, "cached ISDBNullAsync context not properly disposed"); + Debug.Assert(context.Reader == null && context.Source == null && context.Disposable == default, "cached ISDBNullAsync context not properly disposed"); - context.Set(this, source, registration); - context._columnIndex = i; + context.Set(this, source, registrationHolder.Take()); + context._columnIndex = i; - // Setup async - PrepareAsyncInvocation(useSnapshot: true); + // Setup async + PrepareAsyncInvocation(useSnapshot: true); - return InvokeAsyncCall(context); + return InvokeAsyncCall(context); + } } } private static Task IsDBNullAsyncExecute(Task task, object state) { IsDBNullAsyncCallContext context = (IsDBNullAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; if (task != null) { @@ -4929,7 +5090,7 @@ override public Task GetFieldValueAsync(int i, CancellationToken cancellat var metaData = _metaData; if ((data != null) && (metaData != null)) { - return Task.FromResult(GetFieldValueFromSqlBufferInternal(data[i], metaData[i])); + return Task.FromResult(GetFieldValueFromSqlBufferInternal(data[i], metaData[i], isAsync:false)); } else { @@ -4969,7 +5130,7 @@ override public Task GetFieldValueAsync(int i, CancellationToken cancellat { _stateObj._shouldHaveEnoughData = true; #endif - return Task.FromResult(GetFieldValueInternal(i)); + return Task.FromResult(GetFieldValueInternal(i, isAsync:true)); #if DEBUG } finally @@ -4988,49 +5149,62 @@ override public Task GetFieldValueAsync(int i, CancellationToken cancellat return Task.FromException(ex); } - // Setup and check for pending task - TaskCompletionSource source = new TaskCompletionSource(); - Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); - if (original != null) + using (var registrationHolder = new DisposableTemporaryOnStack()) { - source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); - return source.Task; - } + // Setup and check for pending task + TaskCompletionSource source = new TaskCompletionSource(); + Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null); + if (original != null) + { + source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); + return source.Task; + } - // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) - if (_cancelAsyncOnCloseToken.IsCancellationRequested) - { - source.SetCanceled(); - _currentTask = null; - return source.Task; - } + // Check if cancellation due to close is requested (this needs to be done after setting _currentTask) + if (_cancelAsyncOnCloseToken.IsCancellationRequested) + { + source.SetCanceled(); + _currentTask = null; + return source.Task; + } - // Setup cancellations - IDisposable registration = null; - if (cancellationToken.CanBeCanceled) - { - registration = cancellationToken.Register(s => ((SqlCommand)s).CancelIgnoreFailure(), _command); - } + // Setup cancellations + if (cancellationToken.CanBeCanceled) + { + registrationHolder.Set(cancellationToken.Register(SqlCommand.s_cancelIgnoreFailure, _command)); + } - // Setup async - PrepareAsyncInvocation(useSnapshot: true); + // Setup async + PrepareAsyncInvocation(useSnapshot: true); - return InvokeAsyncCall(new GetFieldValueAsyncCallContext(this, source, registration, i)); + GetFieldValueAsyncCallContext context = new GetFieldValueAsyncCallContext(this, source, registrationHolder.Take()); + context._columnIndex = i; + + return InvokeAsyncCall(context); + } } private static Task GetFieldValueAsyncExecute(Task task, object state) { GetFieldValueAsyncCallContext context = (GetFieldValueAsyncCallContext)state; - SqlDataReader reader = context._reader; + SqlDataReader reader = context.Reader; int columnIndex = context._columnIndex; if (task != null) { reader.PrepareForAsyncContinuation(); } + if (typeof(T) == typeof(Stream) || typeof(T) == typeof(TextReader) || typeof(T) == typeof(XmlReader)) + { + if (reader.IsCommandBehavior(CommandBehavior.SequentialAccess) && reader._sharedState._dataReady && reader.TryReadColumnInternal(context._columnIndex, readHeaderOnly: true)) + { + return Task.FromResult(reader.GetFieldValueFromSqlBufferInternal(reader._data[columnIndex], reader._metaData[columnIndex], isAsync: true)); + } + } + if (reader.TryReadColumn(columnIndex, setTimeout: false)) { - return Task.FromResult(reader.GetFieldValueFromSqlBufferInternal(reader._data[columnIndex], reader._metaData[columnIndex])); + return Task.FromResult(reader.GetFieldValueFromSqlBufferInternal(reader._data[columnIndex], reader._metaData[columnIndex], isAsync:false)); } else { @@ -5059,72 +5233,63 @@ internal void CompletePendingReadWithFailure(int errorCode, bool resetForcePendi } #endif - - internal class Snapshot + + internal abstract class SqlDataReaderBaseAsyncCallContext : AAsyncBaseCallContext { - public bool _dataReady; - public bool _haltRead; - public bool _metaDataConsumed; - public bool _browseModeInfoConsumed; - public bool _hasRows; - public ALTROWSTATUS _altRowStatus; - public int _nextColumnDataToRead; - public int _nextColumnHeaderToRead; - public long _columnDataBytesRead; - public long _columnDataBytesRemaining; + internal static readonly Action, object> s_completeCallback = CompleteAsyncCallCallback; - public _SqlMetaDataSet _metadata; - public _SqlMetaDataSetCollection _altMetaDataSetCollection; - public MultiPartTableName[] _tableNames; + internal static readonly Func> s_executeCallback = ExecuteAsyncCallCallback; - public SqlSequentialStream _currentStream; - public SqlSequentialTextReader _currentTextReader; - } + protected SqlDataReaderBaseAsyncCallContext() + { + } - internal abstract class AAsyncCallContext : IDisposable - { - internal static readonly Action, object> s_completeCallback = SqlDataReader.CompleteAsyncCallCallback; + protected SqlDataReaderBaseAsyncCallContext(SqlDataReader owner, TaskCompletionSource source) + { + Set(owner, source); + } - internal static readonly Func> s_executeCallback = SqlDataReader.ExecuteAsyncCallCallback; + internal abstract Func> Execute { get; } - internal SqlDataReader _reader; - internal TaskCompletionSource _source; - internal IDisposable _disposable; + internal SqlDataReader Reader { get => _owner; set => _owner = value; } - protected AAsyncCallContext() - { - } + public TaskCompletionSource Source { get => _source; set => _source = value; } - protected AAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable = null) + private static Task ExecuteAsyncCallCallback(Task task, object state) { - Set(reader, source, disposable); + SqlDataReaderBaseAsyncCallContext context = (SqlDataReaderBaseAsyncCallContext)state; + return context.Reader.ContinueAsyncCall(task, context); } - internal void Set(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable = null) + private static void CompleteAsyncCallCallback(Task task, object state) { - this._reader = reader ?? throw new ArgumentNullException(nameof(reader)); - this._source = source ?? throw new ArgumentNullException(nameof(source)); - this._disposable = disposable; + SqlDataReaderBaseAsyncCallContext context = (SqlDataReaderBaseAsyncCallContext)state; + context.Reader.CompleteAsyncCall(task, context); } + } + + internal abstract class SqlDataReaderAsyncCallContext : SqlDataReaderBaseAsyncCallContext + where TDisposable : IDisposable + { + private TDisposable _disposable; - internal void Clear() + public TDisposable Disposable { get => _disposable; set => _disposable = value; } + + public void Set(SqlDataReader owner, TaskCompletionSource source, TDisposable disposable) { - _source = null; - _reader = null; - IDisposable copyDisposable = _disposable; - _disposable = null; - copyDisposable?.Dispose(); + base.Set(owner, source); + _disposable = disposable; } - internal abstract Func> Execute { get; } - - public virtual void Dispose() + protected override void DisposeCore() { - Clear(); + TDisposable copy = _disposable; + _disposable = default; + copy.Dispose(); } } - internal sealed class ReadAsyncCallContext : AAsyncCallContext + internal sealed class ReadAsyncCallContext : SqlDataReaderAsyncCallContext { internal static readonly Func> s_execute = SqlDataReader.ReadAsyncExecute; @@ -5137,15 +5302,13 @@ internal ReadAsyncCallContext() internal override Func> Execute => s_execute; - public override void Dispose() + protected override void AfterCleared(SqlDataReader owner) { - SqlDataReader reader = this._reader; - base.Dispose(); - reader.SetCachedReadAsyncCallContext(this); + owner.SetCachedReadAsyncCallContext(this); } } - internal sealed class IsDBNullAsyncCallContext : AAsyncCallContext + internal sealed class IsDBNullAsyncCallContext : SqlDataReaderAsyncCallContext { internal static readonly Func> s_execute = SqlDataReader.IsDBNullAsyncExecute; @@ -5155,27 +5318,25 @@ internal IsDBNullAsyncCallContext() { } internal override Func> Execute => s_execute; - public override void Dispose() + protected override void AfterCleared(SqlDataReader owner) { - SqlDataReader reader = this._reader; - base.Dispose(); - reader.SetCachedIDBNullAsyncCallContext(this); + owner.SetCachedIDBNullAsyncCallContext(this); } } - private sealed class HasNextResultAsyncCallContext : AAsyncCallContext + private sealed class HasNextResultAsyncCallContext : SqlDataReaderAsyncCallContext { private static readonly Func> s_execute = SqlDataReader.NextResultAsyncExecute; - public HasNextResultAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable) - : base(reader, source, disposable) + public HasNextResultAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, CancellationTokenRegistration disposable) { + Set(reader, source, disposable); } internal override Func> Execute => s_execute; } - private sealed class GetBytesAsyncCallContext : AAsyncCallContext + private sealed class GetBytesAsyncCallContext : SqlDataReaderAsyncCallContext { internal enum OperationMode { @@ -5186,63 +5347,66 @@ internal enum OperationMode private static readonly Func> s_executeSeek = SqlDataReader.GetBytesAsyncSeekExecute; private static readonly Func> s_executeRead = SqlDataReader.GetBytesAsyncReadExecute; - internal int columnIndex; - internal byte[] buffer; - internal int index; - internal int length; - internal int timeout; - internal CancellationToken cancellationToken; - internal CancellationToken timeoutToken; - internal int totalBytesRead; + internal int _columnIndex; + internal byte[] _buffer; + internal int _index; + internal int _length; + internal int _timeout; + internal CancellationToken _cancellationToken; + internal CancellationToken _timeoutToken; + internal int _totalBytesRead; - internal OperationMode mode; + internal OperationMode _mode; internal GetBytesAsyncCallContext(SqlDataReader reader) { - this._reader = reader ?? throw new ArgumentNullException(nameof(reader)); + Reader = reader ?? throw new ArgumentNullException(nameof(reader)); } - internal override Func> Execute => mode == OperationMode.Seek ? s_executeSeek : s_executeRead; + internal override Func> Execute => _mode == OperationMode.Seek ? s_executeSeek : s_executeRead; - public override void Dispose() + protected override void Clear() { - buffer = null; - cancellationToken = default; - timeoutToken = default; - base.Dispose(); + _buffer = null; + _cancellationToken = default; + _timeoutToken = default; + base.Clear(); } } - private sealed class GetFieldValueAsyncCallContext : AAsyncCallContext + private sealed class GetFieldValueAsyncCallContext : SqlDataReaderAsyncCallContext { private static readonly Func> s_execute = SqlDataReader.GetFieldValueAsyncExecute; - internal readonly int _columnIndex; + internal int _columnIndex; + + internal GetFieldValueAsyncCallContext() { } - internal GetFieldValueAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, IDisposable disposable, int columnIndex) - : base(reader, source, disposable) + internal GetFieldValueAsyncCallContext(SqlDataReader reader, TaskCompletionSource source, CancellationTokenRegistration disposable) { - _columnIndex = columnIndex; + Set(reader, source, disposable); } - internal override Func> Execute => s_execute; - } - - private static Task ExecuteAsyncCallCallback(Task task, object state) - { - AAsyncCallContext context = (AAsyncCallContext)state; - return context._reader.ExecuteAsyncCall(task, context); - } + protected override void Clear() + { + _columnIndex = -1; + base.Clear(); + } - private static void CompleteAsyncCallCallback(Task task, object state) - { - AAsyncCallContext context = (AAsyncCallContext)state; - context._reader.CompleteAsyncCall(task, context); + internal override Func> Execute => s_execute; } - private Task InvokeAsyncCall(AAsyncCallContext context) + /// + /// Starts the process of executing an async call using an SqlDataReaderAsyncCallContext derived context object. + /// After this call the context lifetime is handled by BeginAsyncCall ContinueAsyncCall and CompleteAsyncCall AsyncCall methods + /// + /// + /// + /// + /// + private Task InvokeAsyncCall(SqlDataReaderBaseAsyncCallContext context) { - TaskCompletionSource source = context._source; + TaskCompletionSource source = context.Source; try { Task task; @@ -5262,7 +5426,7 @@ private Task InvokeAsyncCall(AAsyncCallContext context) else { task.ContinueWith( - continuationAction: AAsyncCallContext.s_completeCallback, + continuationAction: SqlDataReaderBaseAsyncCallContext.s_completeCallback, state: context, TaskScheduler.Default ); @@ -5281,7 +5445,13 @@ private Task InvokeAsyncCall(AAsyncCallContext context) return source.Task; } - private Task ExecuteAsyncCall(AAsyncCallContext context) + /// + /// Begins an async call checking for cancellation and then setting up the callback for when data is available + /// + /// + /// + /// + private Task ExecuteAsyncCall(AAsyncBaseCallContext context) { // _networkPacketTaskSource could be null if the connection was closed // while an async invocation was outstanding. @@ -5294,14 +5464,23 @@ private Task ExecuteAsyncCall(AAsyncCallContext context) else { return completionSource.Task.ContinueWith( - continuationFunction: AAsyncCallContext.s_executeCallback, + continuationFunction: SqlDataReaderBaseAsyncCallContext.s_executeCallback, state: context, TaskScheduler.Default ).Unwrap(); } } - private Task ExecuteAsyncCall(Task task, AAsyncCallContext context) + /// + /// When data has become available for an async call it is woken and this method is called. + /// It will call the async execution func and if a Task is returned indicating more data + /// is needed it will wait until it is called again when more is available + /// + /// + /// + /// + /// + private Task ContinueAsyncCall(Task task, SqlDataReaderBaseAsyncCallContext context) { // this function must be an instance function called from the static callback because otherwise a compiler error // is caused by accessing the _cancelAsyncOnCloseToken field of a MarshalByRefObject derived class @@ -5354,9 +5533,16 @@ private Task ExecuteAsyncCall(Task task, AAsyncCallContext context) return Task.FromException(ADP.ExceptionWithStackTrace(ADP.ClosedConnectionError())); } - private void CompleteAsyncCall(Task task, AAsyncCallContext context) + /// + /// When data has been successfully processed for an async call the async func will call this + /// function to set the result into the task and cleanup the async state ready for another call + /// + /// + /// + /// + private void CompleteAsyncCall(Task task, SqlDataReaderBaseAsyncCallContext context) { - TaskCompletionSource source = context._source; + TaskCompletionSource source = context.Source; context.Dispose(); // If something has forced us to switch to SyncOverAsync mode while in an async task then we need to guarantee that we do the cleanup @@ -5383,6 +5569,28 @@ private void CompleteAsyncCall(Task task, AAsyncCallContext context) } } + + internal class Snapshot + { + public bool _dataReady; + public bool _haltRead; + public bool _metaDataConsumed; + public bool _browseModeInfoConsumed; + public bool _hasRows; + public ALTROWSTATUS _altRowStatus; + public int _nextColumnDataToRead; + public int _nextColumnHeaderToRead; + public long _columnDataBytesRead; + public long _columnDataBytesRemaining; + + public _SqlMetaDataSet _metadata; + public _SqlMetaDataSetCollection _altMetaDataSetCollection; + public MultiPartTableName[] _tableNames; + + public SqlSequentialStream _currentStream; + public SqlSequentialTextReader _currentTextReader; + } + private void PrepareAsyncInvocation(bool useSnapshot) { // if there is already a snapshot, then the previous async command @@ -5459,7 +5667,7 @@ private void CleanupAfterAsyncInvocation(bool ignoreCloseToken = false) } // This function is called directly if calling function already closed the reader, so _stateObj is null, - // in other cases parameterless version should be called + // in other cases parameterless version should be called private void CleanupAfterAsyncInvocationInternal(TdsParserStateObject stateObj, bool resetNetworkPacketTaskSource = true) { if (resetNetworkPacketTaskSource) @@ -5563,7 +5771,7 @@ private ReadOnlyCollection BuildColumnSchema() _SqlMetaData col = md[i]; SqlDbColumn dbColumn = new SqlDbColumn(md[i]); - if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType) + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.Is2008DateTimeType) { dbColumn.SqlNumericScale = MetaType.MetaNVarChar.Scale; } @@ -5593,5 +5801,5 @@ private ReadOnlyCollection BuildColumnSchema() return new ReadOnlyCollection(columnSchema); } - }// SqlDataReader -}// namespace + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index d806dc814a..680b34079d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -80,13 +80,15 @@ public void Initialize() // transaction. SqlInternalConnection connection = _connection; SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection {1}, delegating transaction.", ObjectID, connection.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Initialize | RES | CPOOL | Object Id {0}, Client Connection Id {1}, delegating transaction.", ObjectID, usersConnection?.ClientConnectionId); +#if !NET6_0_OR_GREATER RuntimeHelpers.PrepareConstrainedRegions(); +#endif try { if (connection.IsEnlistedInTransaction) { - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection {1}, was enlisted, now defecting.", ObjectID, connection.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Initialize | RES | CPOOL | {0}, Client Connection Id {1}, was enlisted, now defecting.", ObjectID, usersConnection?.ClientConnectionId); // defect first connection.EnlistNull(); @@ -143,8 +145,10 @@ public byte[] Promote() if (null != connection) { SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection {1}, promoting transaction.", ObjectID, connection.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Promote | RES | CPOOL | Object Id {0}, Client Connection Id {1}, promoting transaction.", ObjectID, usersConnection?.ClientConnectionId); +#if !NET6_0_OR_GREATER RuntimeHelpers.PrepareConstrainedRegions(); +#endif try { lock (connection) @@ -179,6 +183,8 @@ public byte[] Promote() { promoteException = e; + ADP.TraceExceptionWithoutRethrow(e); + // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event @@ -187,6 +193,7 @@ public byte[] Promote() catch (InvalidOperationException e) { promoteException = e; + ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } @@ -208,20 +215,33 @@ public byte[] Promote() } //Throw exception only if Transaction is still active and not yet aborted. - if (promoteException != null && Transaction.TransactionInformation.Status != TransactionStatus.Aborted) + if (promoteException != null) { - throw SQL.PromotionFailed(promoteException); + try + { + // Safely access Transaction status - as it's possible Transaction is not in right state. + if (Transaction?.TransactionInformation?.Status != TransactionStatus.Aborted) + { + throw SQL.PromotionFailed(promoteException); + } + } + catch (TransactionException te) + { + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Promote | RES | CPOOL | Object Id {0}, Client Connection Id {1}, Transaction exception occurred: {2}.", ObjectID, usersConnection?.ClientConnectionId, te.Message); + // Throw promote exception if transaction state is unknown. + throw SQL.PromotionFailed(promoteException); + } } else { // The transaction was aborted externally, since it's already doomed above, we only log the same. - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection {1}, aborted during promotion.", ObjectID, connection.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Promote | RES | CPOOL | Object Id {0}, Client Connection Id {1}, Aborted during promotion.", ObjectID, usersConnection?.ClientConnectionId); } } else { // The transaction was aborted externally, doom the connection to make sure it's eventually rolled back and log the same. - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection null, aborted before promoting.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Promote | RES | CPOOL | {0}, Connection null, aborted before promoting.", ObjectID); } return returnValue; } @@ -235,8 +255,10 @@ public void Rollback(SinglePhaseEnlistment enlistment) if (null != connection) { SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection {1}, rolling back transaction.", ObjectID, connection.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Rollback | RES | CPOOL | Object Id {0}, Client Connection Id {1}, rolling back transaction.", ObjectID, usersConnection?.ClientConnectionId); +#if !NET6_0_OR_GREATER RuntimeHelpers.PrepareConstrainedRegions(); +#endif try { lock (connection) @@ -254,27 +276,30 @@ public void Rollback(SinglePhaseEnlistment enlistment) connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); } } - catch (SqlException) + catch (SqlException e) { + ADP.TraceExceptionWithoutRethrow(e); + // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event connection.DoomThisConnection(); - // Unlike SinglePhaseCommit, a rollback is a rollback, regardless + // Unlike SinglePhaseCommit, a rollback is a rollback, regardless // of how it happens, so SysTx won't throw an exception, and we - // don't want to throw an exception either, because SysTx isn't + // don't want to throw an exception either, because SysTx isn't // handling it and it may create a fail fast scenario. In the end, // there is no way for us to communicate to the consumer that this // failed for more serious reasons than usual. - // + // // This is a bit like "should you throw if Close fails", however, - // it only matters when you really need to know. In that case, + // it only matters when you really need to know. In that case, // we have the tracing that we're doing to fallback on for the // investigation. } - catch (InvalidOperationException) + catch (InvalidOperationException e) { + ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } } @@ -304,7 +329,7 @@ public void Rollback(SinglePhaseEnlistment enlistment) { // The transaction was aborted, report that to SysTx and log the same. enlistment.Aborted(); - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection null, aborted before rollback.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.Rollback | RES | CPOOL | Object Id {0}, Connection null, aborted before rollback.", ObjectID); } } @@ -317,27 +342,27 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) if (null != connection) { SqlConnection usersConnection = connection.Connection; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection {1}, committing transaction.", ObjectID, connection.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.SinglePhaseCommit | RES | CPOOL | Object Id {0}, Client Connection Id {1}, committing transaction.", ObjectID, usersConnection?.ClientConnectionId); +#if !NET6_0_OR_GREATER RuntimeHelpers.PrepareConstrainedRegions(); +#endif try { - // If the connection is doomed, we can be certain that the - // transaction will eventually be rolled back, and we shouldn't - // attempt to commit it. - if (connection.IsConnectionDoomed) + Exception commitException = null; + + lock (connection) { - lock (connection) + // If the connection is doomed, we can be certain that the + // transaction will eventually be rolled back or has already been aborted externally, and we shouldn't + // attempt to commit it. + if (connection.IsConnectionDoomed) { _active = false; // set to inactive first, doesn't matter how the rest completes, this transaction is done. _connection = null; - } - enlistment.Aborted(SQL.ConnectionDoomed()); - } - else - { - Exception commitException; - lock (connection) + enlistment.Aborted(SQL.ConnectionDoomed()); + } + else { try { @@ -348,12 +373,13 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) _connection = null; // Set prior to ExecuteTransaction call in case this initiates a TransactionEnd event connection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, System.Data.IsolationLevel.Unspecified, _internalTransaction, true); - commitException = null; } catch (SqlException e) { commitException = e; + ADP.TraceExceptionWithoutRethrow(e); + // Doom the connection, to make sure that the transaction is // eventually rolled back. // VSTS 144562: doom the connection while having the lock on it to prevent race condition with "Transaction Ended" Event @@ -362,43 +388,45 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) catch (InvalidOperationException e) { commitException = e; + ADP.TraceExceptionWithoutRethrow(e); connection.DoomThisConnection(); } - } - if (commitException != null) - { - // connection.ExecuteTransaction failed with exception - if (_internalTransaction.IsCommitted) + if (commitException != null) { - // Even though we got an exception, the transaction - // was committed by the server. - enlistment.Committed(); - } - else if (_internalTransaction.IsAborted) - { - // The transaction was aborted, report that to - // SysTx. - enlistment.Aborted(commitException); - } - else - { - // The transaction is still active, we cannot - // know the state of the transaction. - enlistment.InDoubt(commitException); + // connection.ExecuteTransaction failed with exception + if (_internalTransaction.IsCommitted) + { + // Even though we got an exception, the transaction + // was committed by the server. + enlistment.Committed(); + } + else if (_internalTransaction.IsAborted) + { + // The transaction was aborted, report that to + // SysTx. + enlistment.Aborted(commitException); + } + else + { + // The transaction is still active, we cannot + // know the state of the transaction. + enlistment.InDoubt(commitException); + } + + // We eat the exception. This is called on the SysTx + // thread, not the applications thread. If we don't + // eat the exception an UnhandledException will occur, + // causing the process to FailFast. } - // We eat the exception. This is called on the SysTx - // thread, not the applications thread. If we don't - // eat the exception an UnhandledException will occur, - // causing the process to FailFast. + connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); } + } - connection.CleanupConnectionOnTransactionCompletion(_atomicTransaction); - if (commitException == null) - { - // connection.ExecuteTransaction succeeded - enlistment.Committed(); - } + if (commitException == null) + { + // connection.ExecuteTransaction succeeded + enlistment.Committed(); } } catch (System.OutOfMemoryException e) @@ -421,7 +449,7 @@ public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) { // The transaction was aborted before we could commit, report that to SysTx and log the same. enlistment.Aborted(); - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection null, aborted before commit.", ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.SinglePhaseCommit | RES | CPOOL | Object Id {0}, Connection null, aborted before commit.", ObjectID); } } @@ -435,7 +463,7 @@ internal void TransactionEnded(Transaction transaction) if (connection != null) { - SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection {1}, transaction completed externally.", ObjectID, connection.ObjectID); + SqlClientEventSource.Log.TryTraceEvent("SqlDelegatedTransaction.TransactionEnded | RES | CPOOL | Object Id {0}, Connection Id {1}, transaction completed externally.", ObjectID, connection?._objectID); lock (connection) { if (_atomicTransaction.Equals(transaction)) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs deleted file mode 100644 index 0ebb461cc7..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs +++ /dev/null @@ -1,38 +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. - -using System; - -namespace Microsoft.Data.SqlClient -{ - // these members were moved to a separate file in order - // to be able to skip them on platforms where AppDomain members are not supported - // for example, some mobile profiles on mono - partial class SqlDependencyPerAppDomainDispatcher - { - private void SubscribeToAppDomainUnload() - { - // If rude abort - we'll leak. This is acceptable for now. - AppDomain.CurrentDomain.DomainUnload += new EventHandler(UnloadEventHandler); - } - - private void UnloadEventHandler(object sender, EventArgs e) - { - long scopeID = SqlClientEventSource.Log.TryNotificationScopeEnterEvent(" {0}", ObjectID); - try - { - // Make non-blocking call to ProcessDispatcher to ThreadPool.QueueUserWorkItem to complete - // stopping of all start calls in this AppDomain. For containers shared among various AppDomains, - // this will just be a ref-count subtract. For non-shared containers, we will close the container - // and clean-up. - var dispatcher = SqlDependency.ProcessDispatcher; - dispatcher?.QueueAppDomainUnloading(SqlDependency.AppDomainKey); - } - finally - { - SqlClientEventSource.Log.TryNotificationScopeLeaveEvent(scopeID); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs index ec3b6549b3..8035b460db 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDiagnosticListener.NetCoreApp.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Reflection; +using System.Runtime.Loader; namespace Microsoft.Data.SqlClient { @@ -10,6 +12,12 @@ internal sealed class SqlDiagnosticListener : DiagnosticListener { public SqlDiagnosticListener(string name) : base(name) { + AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += SqlDiagnosticListener_Unloading; + } + + private void SqlDiagnosticListener_Unloading(AssemblyLoadContext obj) + { + Dispose(); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs deleted file mode 100644 index 739187a7e9..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.NetCoreApp.cs +++ /dev/null @@ -1,27 +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. - -using System.Security.Cryptography; - -namespace Microsoft.Data.SqlClient -{ - /// - internal partial class SqlEnclaveAttestationParameters - { - private static readonly string _clientDiffieHellmanKeyName = "ClientDiffieHellmanKey"; - private static readonly string _inputName = "input"; - private static readonly string _className = "EnclaveAttestationParameters"; - - /// - internal ECDiffieHellman ClientDiffieHellmanKey { get; } - - /// - internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHellman clientDiffieHellmanKey) - { - _input = input ?? throw SQL.NullArgumentInConstructorInternal(_inputName, _className); - Protocol = protocol; - ClientDiffieHellmanKey = clientDiffieHellmanKey ?? throw SQL.NullArgumentInConstructorInternal(_clientDiffieHellmanKeyName, _className); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs deleted file mode 100644 index 25f2737c70..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.cs +++ /dev/null @@ -1,44 +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.SqlClient -{ - /// - internal partial class SqlEnclaveAttestationParameters - { - private readonly byte[] _input = null; - - /// - internal int Protocol { get; } - - /// - internal byte[] GetInput() - { - return Clone(_input); - } - - /// - /// Deep copy the array into a new array - /// - /// - /// - private byte[] Clone(byte[] arrayToClone) - { - - if (null == arrayToClone) - { - return null; - } - - byte[] returnValue = new byte[arrayToClone.Length]; - - for (int i = 0; i < arrayToClone.Length; i++) - { - returnValue[i] = arrayToClone[i]; - } - - return returnValue; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs deleted file mode 100644 index 5d9f130578..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs +++ /dev/null @@ -1,72 +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. - -using System; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed class SqlError - { - internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber, uint win32ErrorCode, Exception exception = null) - : this(infoNumber, errorState, errorClass, server, errorMessage, procedure, lineNumber, exception) - { - Win32ErrorCode = (int)win32ErrorCode; - } - - internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber, Exception exception = null) - { - Number = infoNumber; - State = errorState; - Class = errorClass; - Server = server; - Message = errorMessage; - Procedure = procedure; - LineNumber = lineNumber; - Win32ErrorCode = 0; - Exception = exception; - if (errorClass != 0) - { - SqlClientEventSource.Log.TryTraceEvent(" infoNumber={0}, errorState={1}, errorClass={2}, errorMessage='{3}', procedure='{4}', lineNumber={5}", infoNumber, (int)errorState, (int)errorClass, errorMessage, procedure ?? "None", (int)lineNumber); - } - } - - /// - // There is no exception stack included because the correct exception stack is only available - // on SqlException, and to obtain that the SqlError would have to have backpointers all the - // way back to SqlException. If the user needs a call stack, they can obtain it on SqlException. - public override string ToString() - { - return typeof(SqlError).ToString() + ": " + Message; // since this is sealed so we can change GetType to typeof - } - - /// - public string Source { get; private set; } = TdsEnums.SQL_PROVIDER_NAME; - - /// - public int Number { get; private set; } - - /// - public byte State { get; private set; } - - /// - public byte Class { get; private set; } - - /// - public string Server { get; private set; } - - /// - public string Message { get; private set; } - - /// - public string Procedure { get; private set; } - - /// - public int LineNumber { get; private set; } - - internal int Win32ErrorCode { get; private set; } - - internal Exception Exception { get; private set; } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs deleted file mode 100644 index 80d625a38b..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs +++ /dev/null @@ -1,46 +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. - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed class SqlErrorCollection : ICollection - { - // Ideally this would be typed as List, but that would make the non-generic - // CopyTo behave differently than the full framework (which uses ArrayList), throwing - // ArgumentException instead of the expected InvalidCastException for incompatible types. - // Instead, we use List, which makes the non-generic CopyTo behave like - // ArrayList.CopyTo. - private readonly List _errors = new List(); - - internal SqlErrorCollection() { } - - /// - public void CopyTo(Array array, int index) => ((ICollection)_errors).CopyTo(array, index); - - /// - public void CopyTo(SqlError[] array, int index) => _errors.CopyTo(array, index); - - /// - public int Count => _errors.Count; - - /// - object ICollection.SyncRoot => this; - - /// - bool ICollection.IsSynchronized => false; - - /// - public SqlError this[int index] => (SqlError)_errors[index]; - - /// - public IEnumerator GetEnumerator() => _errors.GetEnumerator(); - - internal void Add(SqlError error) => _errors.Add(error); - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInfoMessageEvent.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInfoMessageEvent.cs deleted file mode 100644 index f508331743..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInfoMessageEvent.cs +++ /dev/null @@ -1,46 +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.SqlClient -{ - /// - public sealed class SqlInfoMessageEventArgs : System.EventArgs - { - private SqlException _exception; - - internal SqlInfoMessageEventArgs(SqlException exception) - { - _exception = exception; - } - - /// - public SqlErrorCollection Errors - { - get { return _exception.Errors; } - } - - private bool ShouldSerializeErrors() - { - return (null != _exception) && (0 < _exception.Errors.Count); - } - - /// - public string Message - { - get { return _exception.Message; } - } - - /// - public string Source - { - get { return _exception.Source; } - } - - /// - override public string ToString() - { - return Message; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index bc8c0655ff..e977641175 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -21,7 +21,7 @@ namespace Microsoft.Data.SqlClient { - internal class SessionStateRecord + internal sealed class SessionStateRecord { internal bool _recoverable; internal uint _version; @@ -29,7 +29,7 @@ internal class SessionStateRecord internal byte[] _data; } - internal class SessionData + internal sealed class SessionData { internal const int _maxNumberOfSessionStates = 256; internal uint _tdsVersion; @@ -82,7 +82,7 @@ public void Reset() _language = null; if (_deltaDirty) { - _delta = new SessionStateRecord[_maxNumberOfSessionStates]; + Array.Clear(_delta, 0, _delta.Length); _deltaDirty = false; } _unrecoverableStatesCount = 0; @@ -101,14 +101,17 @@ public void AssertUnrecoverableStateCountIsCorrect() } } - sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposable + internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposable { + // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600 + internal const int MsalHttpRetryStatusCode = 429; + // CONNECTION AND STATE VARIABLES private readonly SqlConnectionPoolGroupProviderInfo _poolGroupProviderInfo; // will only be null when called for ChangePassword, or creating SSE User Instance private TdsParser _parser; private SqlLoginAck _loginAck; private SqlCredential _credential; - private FederatedAuthenticationFeatureExtensionData? _fedAuthFeatureExtensionData; + private FederatedAuthenticationFeatureExtensionData _fedAuthFeatureExtensionData; // Connection Resiliency private bool _sessionRecoveryRequested; @@ -269,7 +272,13 @@ internal bool IsDNSCachingBeforeRedirectSupported // Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. // If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'. - 40613 + 40613, + + // Can not connect to the SQL pool since it is paused. Please resume the SQL pool and try again. + 42108, + + // The SQL pool is warming up. Please try again. + 42109 }; internal SessionData CurrentSessionData @@ -630,15 +639,15 @@ internal protected override bool IsNonPoolableTransactionRoot { get { - return IsTransactionRoot && (!IsKatmaiOrNewer || null == Pool); + return IsTransactionRoot && (!Is2008OrNewer || null == Pool); } } - internal override bool IsKatmaiOrNewer + internal override bool Is2008OrNewer { get { - return _parser.IsKatmaiOrNewer; + return _parser.Is2008OrNewer; } } @@ -928,7 +937,7 @@ private void ResetConnection() if (_fResetConnection) { - // Ensure we are either going against shiloh, or we are not enlisted in a + // Ensure we are either going against 2000, or we are not enlisted in a // distributed transaction - otherwise don't reset! // Prepare the parser for the connection reset - the next time a trip // to the server is made. @@ -995,11 +1004,11 @@ internal override void ExecuteTransaction(TransactionRequest transactionRequest, string transactionName = (null == name) ? string.Empty : name; - ExecuteTransactionYukon(transactionRequest, transactionName, iso, internalTransaction, isDelegateControlRequest); + ExecuteTransaction2005(transactionRequest, transactionName, iso, internalTransaction, isDelegateControlRequest); } - internal void ExecuteTransactionYukon( + internal void ExecuteTransaction2005( TransactionRequest transactionRequest, string transactionName, System.Data.IsolationLevel iso, @@ -1062,7 +1071,7 @@ bool isDelegateControlRequest requestType = TdsEnums.TransactionManagerRequestType.Commit; break; case TransactionRequest.IfRollback: - // Map IfRollback to Rollback since with Yukon and beyond we should never need + // Map IfRollback to Rollback since with 2005 and beyond we should never need // the if since the server will inform us when transactions have completed // as a result of an error on the server. case TransactionRequest.Rollback: @@ -1116,9 +1125,13 @@ bool isDelegateControlRequest stateObj = _parser.GetSession(this); mustPutSession = true; } - else if (internalTransaction.OpenResultsCount != 0) + if (internalTransaction.OpenResultsCount != 0) { - //throw SQL.CannotCompleteDelegatedTransactionWithOpenResults(this); + SqlClientEventSource.Log.TryTraceEvent(" {0}, Connection is marked to be doomed when closed. Transaction ended with OpenResultsCount {1} > 0, MARSOn {2}", + ObjectID, + internalTransaction.OpenResultsCount, + _parser.MARSOn); + throw SQL.CannotCompleteDelegatedTransactionWithOpenResults(this, _parser.MARSOn); } } @@ -1232,7 +1245,7 @@ private void CompleteLogin(bool enlistOK) _parser._physicalStateObj.SniContext = SniContext.Snix_Login; } - private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, SecureString newSecurePassword) + private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, SecureString newSecurePassword, SqlConnectionEncryptOption encrypt) { // create a new login record SqlLogin login = new SqlLogin(); @@ -1304,6 +1317,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault // Since AD Integrated may be acting like Windows integrated, additionally check _fedAuthRequired || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { @@ -1337,7 +1351,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // The SQLDNSCaching feature is implicitly set requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; - _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData); + _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt); } private void LoginFailure() @@ -1538,7 +1552,7 @@ private void LoginNoFailover(ServerInfo serverInfo, throw SQL.ROR_TimeoutAfterRoutingInfo(this); } - serverInfo = new ServerInfo(ConnectionOptions, RoutingInfo, serverInfo.ResolvedServerName); + serverInfo = new ServerInfo(ConnectionOptions, RoutingInfo, serverInfo.ResolvedServerName, serverInfo.ServerSPN); _timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination); _originalClientConnectionId = _clientConnectionId; _routingDestination = serverInfo.UserServerName; @@ -1624,7 +1638,7 @@ private void LoginNoFailover(ServerInfo serverInfo, // We must wait for CompleteLogin to finish for to have the // env change from the server to know its designated failover // partner; save this information in _currentFailoverPartner. - PoolGroupProviderInfo.FailoverCheck(this, false, connectionOptions, ServerProvidedFailOverPartner); + PoolGroupProviderInfo.FailoverCheck(false, connectionOptions, ServerProvidedFailOverPartner); } CurrentDataSource = originalServerInfo.UserServerName; } @@ -1682,7 +1696,7 @@ TimeoutTimer timeout int sleepInterval = 100; //milliseconds to sleep (back off) between attempts. long timeoutUnitInterval; - ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost); + ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost, connectionOptions.FailoverPartnerSPN); ResolveExtendedServerName(primaryServerInfo, !redirectedUserInstance, connectionOptions); if (null == ServerProvidedFailOverPartner) @@ -1830,7 +1844,7 @@ TimeoutTimer timeout // We must wait for CompleteLogin to finish for to have the // env change from the server to know its designated failover // partner; save this information in _currentFailoverPartner. - PoolGroupProviderInfo.FailoverCheck(this, useFailoverHost, connectionOptions, ServerProvidedFailOverPartner); + PoolGroupProviderInfo.FailoverCheck(useFailoverHost, connectionOptions, ServerProvidedFailOverPartner); } CurrentDataSource = (useFailoverHost ? failoverHost : primaryServerInfo.UserServerName); } @@ -1842,11 +1856,36 @@ private void ResolveExtendedServerName(ServerInfo serverInfo, bool aliasLookup, string host = serverInfo.UserServerName; string protocol = serverInfo.UserProtocol; - //TODO: fix local host enforcement with datadirectory and failover - if (options.EnforceLocalHost) - { - // verify LocalHost for |DataDirectory| usage - SqlConnectionString.VerifyLocalHostAndFixup(ref host, true, true /*fix-up to "."*/); + if (aliasLookup) + { // We skip this for UserInstances... + // Perform registry lookup to see if host is an alias. It will appropriately set host and protocol, if an Alias. + // Check if it was already resolved, during CR reconnection _currentSessionData values will be copied from + // _reconnectSessonData of the previous connection + if (_currentSessionData != null && !string.IsNullOrEmpty(host)) + { + Tuple hostPortPair; + if (_currentSessionData._resolvedAliases.TryGetValue(host, out hostPortPair)) + { + host = hostPortPair.Item1; + protocol = hostPortPair.Item2; + } + else + { + TdsParserStaticMethods.AliasRegistryLookup(ref host, ref protocol); + _currentSessionData._resolvedAliases.Add(serverInfo.UserServerName, new Tuple(host, protocol)); + } + } + else + { + TdsParserStaticMethods.AliasRegistryLookup(ref host, ref protocol); + } + + //TODO: fix local host enforcement with datadirectory and failover + if (options.EnforceLocalHost) + { + // verify LocalHost for |DataDirectory| usage + SqlConnectionString.VerifyLocalHostAndFixup(ref host, true, true /*fix-up to "."*/); + } } serverInfo.SetDerivedNames(protocol, host); @@ -1871,17 +1910,14 @@ private void AttemptOneLogin( this, ignoreSniOpenTimeout, timeout.LegacyTimerExpire, - ConnectionOptions.Encrypt, - ConnectionOptions.TrustServerCertificate, - ConnectionOptions.IntegratedSecurity, - withFailover, - ConnectionOptions.Authentication); + ConnectionOptions, + withFailover); _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); _timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.LoginBegin); _parser._physicalStateObj.SniContext = SniContext.Snix_Login; - this.Login(serverInfo, timeout, newPassword, newSecurePassword); + this.Login(serverInfo, timeout, newPassword, newSecurePassword, ConnectionOptions.Encrypt); _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth); _timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.PostLogin); @@ -1983,36 +2019,36 @@ internal bool IgnoreEnvChange internal void OnEnvChange(SqlEnvChange rec) { Debug.Assert(!IgnoreEnvChange, "This function should not be called if IgnoreEnvChange is set!"); - switch (rec.type) + switch (rec._type) { case TdsEnums.ENV_DATABASE: // If connection is not open and recovery is not in progress, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { - _originalDatabase = rec.newValue; + _originalDatabase = rec._newValue; } - CurrentDatabase = rec.newValue; + CurrentDatabase = rec._newValue; break; case TdsEnums.ENV_LANG: // If connection is not open and recovery is not in progress, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { - _originalLanguage = rec.newValue; + _originalLanguage = rec._newValue; } - _currentLanguage = rec.newValue; + _currentLanguage = rec._newValue; break; case TdsEnums.ENV_PACKETSIZE: - _currentPacketSize = int.Parse(rec.newValue, CultureInfo.InvariantCulture); + _currentPacketSize = int.Parse(rec._newValue, CultureInfo.InvariantCulture); break; case TdsEnums.ENV_COLLATION: if (_currentSessionData != null) { - _currentSessionData._collation = rec.newCollation; + _currentSessionData._collation = rec._newCollation; } break; @@ -2032,20 +2068,20 @@ internal void OnEnvChange(SqlEnvChange rec) { throw SQL.ROR_FailoverNotSupportedServer(this); } - _currentFailoverPartner = rec.newValue; + _currentFailoverPartner = rec._newValue; break; case TdsEnums.ENV_PROMOTETRANSACTION: - byte[] dtcToken = null; - if (rec.newBinRented) + byte[] dtcToken; + if (rec._newBinRented) { - dtcToken = new byte[rec.newLength]; - Buffer.BlockCopy(rec.newBinValue, 0, dtcToken, 0, dtcToken.Length); + dtcToken = new byte[rec._newLength]; + Buffer.BlockCopy(rec._newBinValue, 0, dtcToken, 0, dtcToken.Length); } else { - dtcToken = rec.newBinValue; - rec.newBinValue = null; + dtcToken = rec._newBinValue; + rec._newBinValue = null; } PromotedDTCToken = dtcToken; break; @@ -2054,7 +2090,7 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS: - // For now we skip these Yukon only env change notifications + // For now we skip these 2005 only env change notifications break; case TdsEnums.ENV_SPRESETCONNECTIONACK: @@ -2066,16 +2102,16 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_USERINSTANCE: - _instanceName = rec.newValue; + _instanceName = rec._newValue; break; case TdsEnums.ENV_ROUTING: SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received routing info", ObjectID); - if (string.IsNullOrEmpty(rec.newRoutingInfo.ServerName) || rec.newRoutingInfo.Protocol != 0 || rec.newRoutingInfo.Port == 0) + if (string.IsNullOrEmpty(rec._newRoutingInfo.ServerName) || rec._newRoutingInfo.Protocol != 0 || rec._newRoutingInfo.Port == 0) { throw SQL.ROR_InvalidRoutingInfo(this); } - RoutingInfo = rec.newRoutingInfo; + RoutingInfo = rec._newRoutingInfo; break; default: @@ -2106,12 +2142,13 @@ internal void OnLoginAck(SqlLoginAck rec) /// Federated Authentication Info. internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) { - Debug.Assert((ConnectionOptions.HasUserIdKeyword && ConnectionOptions.HasPasswordKeyword) + Debug.Assert((ConnectionOptions._hasUserIdKeyword && ConnectionOptions._hasPasswordKeyword) || _credential != null || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryMSI + || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryDefault || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); Debug.Assert(fedAuthInfo != null, "info should not be null."); @@ -2246,7 +2283,9 @@ internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionP bool authenticationContextLocked = false; // Prepare CER to ensure the lock on authentication context is released. +#if !NET6_0_OR_GREATER RuntimeHelpers.PrepareConstrainedRegions(); +#endif try { // Try to obtain a lock on the context. If acquired, this thread got the opportunity to update. @@ -2292,7 +2331,6 @@ internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionP /// SqlFedAuthToken internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { - Debug.Assert(fedAuthInfo != null, "fedAuthInfo should not be null."); // No:of milliseconds to sleep for the inital back off. @@ -2324,7 +2362,8 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) authority: fedAuthInfo.stsurl, serverName: ConnectionOptions.DataSource, databaseName: ConnectionOptions.InitialCatalog) - .WithConnectionId(_clientConnectionId); + .WithConnectionId(_clientConnectionId) + .WithConnectionTimeout(ConnectionOptions.ConnectTimeout); switch (ConnectionOptions.Authentication) { case SqlAuthenticationMethod.ActiveDirectoryIntegrated: @@ -2346,7 +2385,9 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) } else { - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + // We use Task.Run here in all places to execute task synchronously in the same context. + // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209 + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; @@ -2354,6 +2395,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: case SqlAuthenticationMethod.ActiveDirectoryMSI: + case SqlAuthenticationMethod.ActiveDirectoryDefault: if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying) { _fedAuthToken = _activeDirectoryAuthTimeoutRetryHelper.CachedToken; @@ -2361,7 +2403,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) else { authParamsBuilder.WithUserId(ConnectionOptions.UserID); - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } break; @@ -2377,13 +2419,13 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { username = _credential.UserId; authParamsBuilder.WithUserId(username).WithPassword(_credential.Password); - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); } else { username = ConnectionOptions.UserID; authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password); - Task.Run(() => _fedAuthToken = authProvider.AcquireTokenAsync(authParamsBuilder).Result.ToSqlFedAuthToken()).Wait(); + _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken(); } _activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken; } @@ -2406,7 +2448,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // Deal with Msal service exceptions first, retry if 429 received. catch (MsalServiceException serviceException) { - if (429 == serviceException.StatusCode) + if (serviceException.StatusCode == MsalHttpRetryStatusCode) { RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter; if (retryAfter.Delta.HasValue) @@ -2425,9 +2467,15 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) } else { - break; + SqlClientEventSource.Log.TryTraceEvent(" Timeout: {0}", serviceException.ErrorCode); + throw SQL.ActiveDirectoryTokenRetrievingTimeout(Enum.GetName(typeof(SqlAuthenticationMethod), ConnectionOptions.Authentication), serviceException.ErrorCode, serviceException); } } + else + { + SqlClientEventSource.Log.TryTraceEvent(" {0}", serviceException.ErrorCode); + throw ADP.CreateSqlException(serviceException, ConnectionOptions, this, username); + } } // Deal with normal MsalExceptions. catch (MsalException msalException) @@ -2437,21 +2485,8 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) || _timeout.MillisecondsRemaining <= sleepInterval) { SqlClientEventSource.Log.TryTraceEvent(" {0}", msalException.ErrorCode); - // Error[0] - SqlErrorCollection sqlErs = new SqlErrorCollection(); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, StringsHelper.GetString(Strings.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - - // Error[1] - string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, errorMessage1, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - // Error[2] - if (!string.IsNullOrEmpty(msalException.Message)) - { - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, msalException.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - } - SqlException exc = SqlException.CreateException(sqlErs, "", this); - throw exc; + throw ADP.CreateSqlException(msalException, ConnectionOptions, this, username); } SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, sleeping {1}[Milliseconds]", ObjectID, sleepInterval); @@ -2460,6 +2495,11 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) Thread.Sleep(sleepInterval); sleepInterval *= 2; } + // All other exceptions from MSAL/Azure Identity APIs + catch (Exception e) + { + throw SqlException.CreateException(new() { new(0, (byte)0x00, (byte)TdsEnums.FATAL_ERROR_CLASS, ConnectionOptions.DataSource, e.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0) }, "", this, e); + } } Debug.Assert(_fedAuthToken != null, "fedAuthToken should not be null."); @@ -2477,12 +2517,9 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) internal void OnFeatureExtAck(int featureId, byte[] data) { - if (RoutingInfo != null) + if (RoutingInfo != null && TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) { - if (TdsEnums.FEATUREEXT_SQLDNSCACHING != featureId) - { - return; - } + return; } switch (featureId) @@ -2564,7 +2601,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) Debug.Assert(_fedAuthFeatureExtensionData != null, "_fedAuthFeatureExtensionData must not be null when _federatedAuthenticationRequested == true"); - switch (_fedAuthFeatureExtensionData.Value.libraryType) + switch (_fedAuthFeatureExtensionData.libraryType) { case TdsEnums.FedAuthLibrary.MSAL: case TdsEnums.FedAuthLibrary.SecurityToken: @@ -2579,7 +2616,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) default: Debug.Fail("Unknown _fedAuthLibrary type"); SqlClientEventSource.Log.TryTraceEvent(" {0}, Attempting to use unknown federated authentication library", ObjectID); - throw SQL.ParsingErrorLibraryType(ParsingErrorState.FedAuthFeatureAckUnknownLibraryType, (int)_fedAuthFeatureExtensionData.Value.libraryType); + throw SQL.ParsingErrorLibraryType(ParsingErrorState.FedAuthFeatureAckUnknownLibraryType, (int)_fedAuthFeatureExtensionData.libraryType); } _federatedAuthenticationAcknowledged = true; @@ -2628,6 +2665,7 @@ internal void OnFeatureExtAck(int featureId, byte[] data) Debug.Assert(_tceVersionSupported <= TdsEnums.MAX_SUPPORTED_TCE_VERSION, "Client support TCE version 2"); _parser.IsColumnEncryptionSupported = true; _parser.TceVersionSupported = _tceVersionSupported; + _parser.AreEnclaveRetriesSupported = _tceVersionSupported == 3; if (data.Length > 1) { @@ -2757,6 +2795,7 @@ internal sealed class ServerInfo internal string ResolvedServerName { get; private set; } // the resolved servername only internal string ResolvedDatabaseName { get; private set; } // name of target database after resolution internal string UserProtocol { get; private set; } // the user specified protocol + internal string ServerSPN { get; private set; } // the server SPN // The original user-supplied server name from the connection string. // If connection string has no Data Source, the value is set to string.Empty. @@ -2777,10 +2816,16 @@ private set internal readonly string PreRoutingServerName; // Initialize server info from connection options, - internal ServerInfo(SqlConnectionString userOptions) : this(userOptions, userOptions.DataSource) { } + internal ServerInfo(SqlConnectionString userOptions) : this(userOptions, userOptions.DataSource, userOptions.ServerSPN) { } + + // Initialize server info from connection options, but override DataSource and ServerSPN with given server name and server SPN + internal ServerInfo(SqlConnectionString userOptions, string serverName, string serverSPN) : this(userOptions, serverName) + { + ServerSPN = serverSPN; + } // Initialize server info from connection options, but override DataSource with given server name - internal ServerInfo(SqlConnectionString userOptions, string serverName) + private ServerInfo(SqlConnectionString userOptions, string serverName) { //----------------- // Preconditions @@ -2799,7 +2844,7 @@ internal ServerInfo(SqlConnectionString userOptions, string serverName) // Initialize server info from connection options, but override DataSource with given server name - internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string preRoutingServerName) + internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string preRoutingServerName, string serverSPN) { //----------------- // Preconditions @@ -2820,6 +2865,7 @@ internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string UserProtocol = TdsEnums.TCP; SetDerivedNames(UserProtocol, UserServerName); ResolvedDatabaseName = userOptions.InitialCatalog; + ServerSPN = serverSPN; } internal void SetDerivedNames(string protocol, string serverName) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs deleted file mode 100644 index 77030bd20e..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs +++ /dev/null @@ -1,553 +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. - -using System; -using System.Data; -using System.Diagnostics; -using System.Threading; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - internal enum TransactionState - { - Pending = 0, - Active = 1, - Aborted = 2, - Committed = 3, - Unknown = 4, - } - - internal enum TransactionType - { - LocalFromTSQL = 1, - LocalFromAPI = 2, - Delegated = 3, - Distributed = 4, - Context = 5, // only valid in proc. - }; - - sealed internal class SqlInternalTransaction - { - internal const long NullTransactionId = 0; - - private TransactionState _transactionState; - private TransactionType _transactionType; - private long _transactionId; // passed in the MARS headers - private int _openResultCount; // passed in the MARS headers - private SqlInternalConnection _innerConnection; - private bool _disposing; // used to prevent us from throwing exceptions while we're disposing - private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. - - private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); - - internal bool RestoreBrokenConnection { get; set; } - internal bool ConnectionHasBeenRestored { get; set; } - - internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction) : this(innerConnection, type, outerTransaction, NullTransactionId) - { - } - - internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) - { - SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Created for connection {1}, outer transaction {2}, Type {3}", ObjectID, innerConnection.ObjectID, outerTransaction?.ObjectID, (int)type); - _innerConnection = innerConnection; - _transactionType = type; - - if (null != outerTransaction) - { - _parent = new WeakReference(outerTransaction); - } - - _transactionId = transactionId; - RestoreBrokenConnection = false; - ConnectionHasBeenRestored = false; - } - - internal bool HasParentTransaction - { - get - { - // Return true if we are an API started local transaction, or if we were a TSQL - // started local transaction and were then wrapped with a parent transaction as - // a result of a later API begin transaction. - bool result = ((TransactionType.LocalFromAPI == _transactionType) || - (TransactionType.LocalFromTSQL == _transactionType && _parent != null)); - return result; - } - } - - internal bool IsAborted - { - get - { - return (TransactionState.Aborted == _transactionState); - } - } - - internal bool IsActive - { - get - { - return (TransactionState.Active == _transactionState); - } - } - - internal bool IsCommitted - { - get - { - return (TransactionState.Committed == _transactionState); - } - } - - internal bool IsCompleted - { - get - { - return (TransactionState.Aborted == _transactionState - || TransactionState.Committed == _transactionState - || TransactionState.Unknown == _transactionState); - } - } - - internal bool IsDelegated - { - get - { - bool result = (TransactionType.Delegated == _transactionType); - return result; - } - } - - internal bool IsDistributed - { - get - { - bool result = (TransactionType.Distributed == _transactionType); - return result; - } - } - - internal bool IsLocal - { - get - { - bool result = (TransactionType.LocalFromTSQL == _transactionType - || TransactionType.LocalFromAPI == _transactionType - ); - return result; - } - } - - internal bool IsOrphaned - { - get - { - // An internal transaction is orphaned when its parent has been - // reclaimed by GC. - bool result; - if (null == _parent) - { - // No parent, so we better be LocalFromTSQL. Should we even return in this case - - // since it could be argued this is invalid? - Debug.Fail("Why are we calling IsOrphaned with no parent?"); - Debug.Assert(_transactionType == TransactionType.LocalFromTSQL, "invalid state"); - result = false; - } - else if (null == _parent.Target) - { - // We have an parent, but parent was GC'ed. - result = true; - } - else - { - // We have an parent, and parent is alive. - result = false; - } - - return result; - } - } - - internal bool IsZombied - { - get - { - return (null == _innerConnection); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal int OpenResultsCount - { - get - { - return _openResultCount; - } - } - - internal SqlTransaction Parent - { - get - { - SqlTransaction result = null; - // Should we protect against this, since this probably is an invalid state? - Debug.Assert(null != _parent, "Why are we calling Parent with no parent?"); - if (null != _parent) - { - result = (SqlTransaction)_parent.Target; - } - return result; - } - } - - internal long TransactionId - { - get - { - return _transactionId; - } - set - { - Debug.Assert(NullTransactionId == _transactionId, "setting transaction cookie while one is active?"); - _transactionId = value; - } - } - - internal void Activate() - { - _transactionState = TransactionState.Active; - } - - private void CheckTransactionLevelAndZombie() - { - try - { - if (!IsZombied && GetServerTransactionLevel() == 0) - { - // If not zombied, not closed, and not in transaction, zombie. - Zombie(); - } - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - Zombie(); // If exception caught when trying to check level, zombie. - } - } - - internal void CloseFromConnection() - { - SqlInternalConnection innerConnection = _innerConnection; - - Debug.Assert(innerConnection != null, "How can we be here if the connection is null?"); - SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Closing", ObjectID); - bool processFinallyBlock = true; - try - { - innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); - } - catch (Exception e) - { - processFinallyBlock = ADP.IsCatchableExceptionType(e); - throw; - } - finally - { - if (processFinallyBlock) - { - // Always ensure we're zombied; Yukon will send an EnvChange that - // will cause the zombie, but only if we actually go to the wire; - // Sphinx and Shiloh won't send the env change, so we have to handle - // them ourselves. - Zombie(); - } - } - } - - internal void Commit() - { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - - if (_innerConnection.IsLockedForBulkCopy) - { - throw SQL.ConnectionLockedForBcpEvent(); - } - - _innerConnection.ValidateConnectionForExecute(null); - - // If this transaction has been completed, throw exception since it is unusable. - try - { - // COMMIT ignores transaction names, and so there is no reason to pass it anything. COMMIT - // simply commits the transaction from the most recent BEGIN, nested or otherwise. - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, null, false); - { - ZombieParent(); - } - } - catch (Exception e) - { - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - } - - throw; - } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } - } - - internal void Completed(TransactionState transactionState) - { - Debug.Assert(TransactionState.Active < transactionState, "invalid transaction completion state?"); - _transactionState = transactionState; - Zombie(); - } - - internal int DecrementAndObtainOpenResultCount() - { - int openResultCount = Interlocked.Decrement(ref _openResultCount); - if (openResultCount < 0) - { - throw SQL.OpenResultCountExceeded(); - } - return openResultCount; - } - - internal void Dispose() - { - this.Dispose(true); - System.GC.SuppressFinalize(this); - } - - private /*protected override*/ void Dispose(bool disposing) - { - SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Disposing", ObjectID); - if (disposing) - { - if (null != _innerConnection) - { - // implicitly rollback if transaction still valid - _disposing = true; - this.Rollback(); - } - } - } - - private int GetServerTransactionLevel() - { - // This function is needed for those times when it is impossible to determine the server's - // transaction level, unless the user's arguments were parsed - which is something we don't want - // to do. An example when it is impossible to determine the level is after a rollback. - - using (SqlCommand transactionLevelCommand = new SqlCommand("set @out = @@trancount", (SqlConnection)(_innerConnection.Owner))) - { - transactionLevelCommand.Transaction = Parent; - - SqlParameter parameter = new SqlParameter("@out", SqlDbType.Int); - parameter.Direction = ParameterDirection.Output; - transactionLevelCommand.Parameters.Add(parameter); - - transactionLevelCommand.RunExecuteReader(CommandBehavior.Default, RunBehavior.UntilDone, returnStream: false); - - return (int)parameter.Value; - } - } - - internal int IncrementAndObtainOpenResultCount() - { - int openResultCount = Interlocked.Increment(ref _openResultCount); - - if (openResultCount < 0) - { - throw SQL.OpenResultCountExceeded(); - } - return openResultCount; - } - - internal void InitParent(SqlTransaction transaction) - { - Debug.Assert(_parent == null, "Why do we have a parent on InitParent?"); - _parent = new WeakReference(transaction); - } - - internal void Rollback() - { - var scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - if (_innerConnection.IsLockedForBulkCopy) - { - throw SQL.ConnectionLockedForBcpEvent(); - } - - _innerConnection.ValidateConnectionForExecute(null); - - try - { - // If no arg is given to ROLLBACK it will rollback to the outermost begin - rolling back - // all nested transactions as well as the outermost transaction. - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); - - // Since Rollback will rollback to outermost begin, no need to check - // server transaction level. This transaction has been completed. - Zombie(); - } - catch (Exception e) - { - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - - if (!_disposing) - { - throw; - } - } - else - { - throw; - } - } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } - } - - internal void Rollback(string transactionName) - { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}, transactionName={1}", ObjectID, transactionName); - if (_innerConnection.IsLockedForBulkCopy) - { - throw SQL.ConnectionLockedForBcpEvent(); - } - - _innerConnection.ValidateConnectionForExecute(null); - - // ROLLBACK takes either a save point name or a transaction name. It will rollback the - // transaction to either the save point with the save point name or begin with the - // transaction name. NOTE: for simplicity it is possible to give all save point names - // the same name, and ROLLBACK will simply rollback to the most recent save point with the - // save point name. - if (string.IsNullOrEmpty(transactionName)) - throw SQL.NullEmptyTransactionName(); - - try - { - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, transactionName, IsolationLevel.Unspecified, null, false); - } - catch (Exception e) - { - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - } - throw; - } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } - } - - internal void Save(string savePointName) - { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}, savePointName={1}", ObjectID, savePointName); - _innerConnection.ValidateConnectionForExecute(null); - - // ROLLBACK takes either a save point name or a transaction name. It will rollback the - // transaction to either the save point with the save point name or begin with the - // transaction name. So, to rollback a nested transaction you must have a save point. - // SAVE TRANSACTION MUST HAVE AN ARGUMENT!!! Save Transaction without an arg throws an - // exception from the server. So, an overload for SaveTransaction without an arg doesn't make - // sense to have. Save Transaction does not affect the transaction level. - if (string.IsNullOrEmpty(savePointName)) - throw SQL.NullEmptyTransactionName(); - - try - { - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Save, savePointName, IsolationLevel.Unspecified, null, false); - } - catch (Exception e) - { - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - } - - throw; - } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } - } - - internal void Zombie() - { - // Called by several places in the code to ensure that the outer - // transaction object has been zombied and the parser has broken - // it's reference to us. - - // NOTE: we'll be called from the TdsParser when it gets appropriate - // ENVCHANGE events that indicate the transaction has completed, however - // we cannot rely upon those events occurring in the case of pre-Yukon - // servers (and when we don't go to the wire because the connection - // is broken) so we can also be called from the Commit/Rollback/Save - // methods to handle that case as well. - - // There are two parts to a full zombie: - // 1) Zombie parent and disconnect outer transaction from internal transaction - // 2) Disconnect internal transaction from connection and parser - // Number 1 needs to be done whenever a SqlTransaction object is completed. Number - // 2 is only done when a transaction is actually completed. Since users can begin - // transactions both in and outside of the API, and since nested begins are not actual - // transactions we need to distinguish between #1 and #2. - - ZombieParent(); - - SqlInternalConnection innerConnection = _innerConnection; - _innerConnection = null; - - if (null != innerConnection) - { - innerConnection.DisconnectTransaction(this); - } - } - - private void ZombieParent() - { - if (null != _parent) - { - SqlTransaction parent = (SqlTransaction)_parent.Target; - if (null != parent) - { - parent.Zombie(); - } - _parent = null; - } - } - - internal string TraceString() - { - return String.Format(/*IFormatProvider*/ null, "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", - ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlNotificationEventArgs.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlNotificationEventArgs.cs deleted file mode 100644 index 0e37f820ea..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlNotificationEventArgs.cs +++ /dev/null @@ -1,35 +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. - -using System; - -namespace Microsoft.Data.SqlClient -{ - /// - public class SqlNotificationEventArgs : EventArgs - { - private SqlNotificationType _type; - private SqlNotificationInfo _info; - private SqlNotificationSource _source; - - /// - public SqlNotificationEventArgs(SqlNotificationType type, SqlNotificationInfo info, SqlNotificationSource source) - { - _info = info; - _source = source; - _type = type; - } - - /// - public SqlNotificationType Type => _type; - - /// - public SqlNotificationInfo Info => _info; - - /// - public SqlNotificationSource Source => _source; - - internal static SqlNotificationEventArgs s_notifyError = new SqlNotificationEventArgs(SqlNotificationType.Subscribe, SqlNotificationInfo.Error, SqlNotificationSource.Object); - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterCollection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterCollection.cs deleted file mode 100644 index 5dc09cd4c2..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterCollection.cs +++ /dev/null @@ -1,140 +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. - -using System; -using System.Data; -using System.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed partial class SqlParameterCollection : DbParameterCollection - { - private bool _isDirty; - private static Type s_itemType = typeof(SqlParameter); - - internal SqlParameterCollection() : base() - { - } - - internal bool IsDirty - { - get - { - return _isDirty; - } - set - { - _isDirty = value; - } - } - /// - public override bool IsFixedSize => ((System.Collections.IList)InnerList).IsFixedSize; - /// - public override bool IsReadOnly => ((System.Collections.IList)InnerList).IsReadOnly; - /// - new public SqlParameter this[int index] - { - get - { - return (SqlParameter)GetParameter(index); - } - set - { - SetParameter(index, value); - } - } - - /// - new public SqlParameter this[string parameterName] - { - get - { - return (SqlParameter)GetParameter(parameterName); - } - set - { - SetParameter(parameterName, value); - } - } - - /// - public SqlParameter Add(SqlParameter value) - { - Add((object)value); - return value; - } - - /// - public SqlParameter AddWithValue(string parameterName, object value) - { // 79027 - return Add(new SqlParameter(parameterName, value)); - } - - /// - public SqlParameter Add(string parameterName, SqlDbType sqlDbType) - { - return Add(new SqlParameter(parameterName, sqlDbType)); - } - - /// - public SqlParameter Add(string parameterName, SqlDbType sqlDbType, int size) - { - return Add(new SqlParameter(parameterName, sqlDbType, size)); - } - - /// - public SqlParameter Add(string parameterName, SqlDbType sqlDbType, int size, string sourceColumn) - { - return Add(new SqlParameter(parameterName, sqlDbType, size, sourceColumn)); - } - - /// - public void AddRange(SqlParameter[] values) - { - AddRange((Array)values); - } - - /// - override public bool Contains(string value) - { // WebData 97349 - return (-1 != IndexOf(value)); - } - - /// - public bool Contains(SqlParameter value) - { - return (-1 != IndexOf(value)); - } - - /// - public void CopyTo(SqlParameter[] array, int index) - { - CopyTo((Array)array, index); - } - - /// - public int IndexOf(SqlParameter value) - { - return IndexOf((object)value); - } - - /// - public void Insert(int index, SqlParameter value) - { - Insert(index, (object)value); - } - - private void OnChange() - { - IsDirty = true; - } - - /// - public void Remove(SqlParameter value) - { - Remove((object)value); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterCollectionHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterCollectionHelper.cs deleted file mode 100644 index 134a436119..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterCollectionHelper.cs +++ /dev/null @@ -1,336 +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. - -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Diagnostics; -using System.Globalization; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - public sealed partial class SqlParameterCollection : DbParameterCollection - { - private List _items; - - /// - override public int Count - { - get - { - return ((null != _items) ? _items.Count : 0); - } - } - - private List InnerList - { - get - { - List items = _items; - - if (null == items) - { - items = new List(); - _items = items; - } - return items; - } - } - - /// - override public object SyncRoot - { - get - { - return ((System.Collections.ICollection)InnerList).SyncRoot; - } - } - - /// - override public int Add(object value) - { - OnChange(); - ValidateType(value); - Validate(-1, value); - InnerList.Add((SqlParameter)value); - return Count - 1; - } - - /// - override public void AddRange(System.Array values) - { - OnChange(); - if (null == values) - { - throw ADP.ArgumentNull(nameof(values)); - } - foreach (object value in values) - { - ValidateType(value); - } - foreach (SqlParameter value in values) - { - Validate(-1, value); - InnerList.Add((SqlParameter)value); - } - } - - private int CheckName(string parameterName) - { - int index = IndexOf(parameterName); - if (index < 0) - { - throw ADP.ParametersSourceIndex(parameterName, this, s_itemType); - } - return index; - } - - /// - override public void Clear() - { - OnChange(); - List items = InnerList; - - if (null != items) - { - foreach (SqlParameter item in items) - { - item.ResetParent(); - } - items.Clear(); - } - } - - /// - override public bool Contains(object value) - { - return (-1 != IndexOf(value)); - } - - /// - override public void CopyTo(Array array, int index) - { - ((System.Collections.ICollection)InnerList).CopyTo(array, index); - } - - /// - override public System.Collections.IEnumerator GetEnumerator() - { - return ((System.Collections.ICollection)InnerList).GetEnumerator(); - } - - /// - override protected DbParameter GetParameter(int index) - { - RangeCheck(index); - return InnerList[index]; - } - - /// - override protected DbParameter GetParameter(string parameterName) - { - int index = IndexOf(parameterName); - if (index < 0) - { - throw ADP.ParametersSourceIndex(parameterName, this, s_itemType); - } - return InnerList[index]; - } - - private static int IndexOf(System.Collections.IEnumerable items, string parameterName) - { - if (null != items) - { - int i = 0; - - foreach (SqlParameter parameter in items) - { - if (parameterName == parameter.ParameterName) - { - return i; - } - ++i; - } - i = 0; - - foreach (SqlParameter parameter in items) - { - if (0 == ADP.DstCompare(parameterName, parameter.ParameterName)) - { - return i; - } - ++i; - } - } - return -1; - } - - /// - override public int IndexOf(string parameterName) - { - return IndexOf(InnerList, parameterName); - } - - /// - override public int IndexOf(object value) - { - if (null != value) - { - ValidateType(value); - - List items = InnerList; - - if (null != items) - { - int count = items.Count; - - for (int i = 0; i < count; i++) - { - if (value == items[i]) - { - return i; - } - } - } - } - return -1; - } - - /// - override public void Insert(int index, object value) - { - OnChange(); - ValidateType(value); - Validate(-1, (SqlParameter)value); - InnerList.Insert(index, (SqlParameter)value); - } - - private void RangeCheck(int index) - { - if ((index < 0) || (Count <= index)) - { - throw ADP.ParametersMappingIndex(index, this); - } - } - - /// - override public void Remove(object value) - { - OnChange(); - ValidateType(value); - int index = IndexOf(value); - if (-1 != index) - { - RemoveIndex(index); - } - else if (this != ((SqlParameter)value).CompareExchangeParent(null, this)) - { - throw ADP.CollectionRemoveInvalidObject(s_itemType, this); - } - } - - /// - override public void RemoveAt(int index) - { - OnChange(); - RangeCheck(index); - RemoveIndex(index); - } - - /// - override public void RemoveAt(string parameterName) - { - OnChange(); - int index = CheckName(parameterName); - RemoveIndex(index); - } - - private void RemoveIndex(int index) - { - List items = InnerList; - Debug.Assert((null != items) && (0 <= index) && (index < Count), "RemoveIndex, invalid"); - SqlParameter item = items[index]; - items.RemoveAt(index); - item.ResetParent(); - } - - private void Replace(int index, object newValue) - { - List items = InnerList; - Debug.Assert((null != items) && (0 <= index) && (index < Count), "Replace Index invalid"); - ValidateType(newValue); - Validate(index, newValue); - SqlParameter item = items[index]; - items[index] = (SqlParameter)newValue; - item.ResetParent(); - } - - /// - override protected void SetParameter(int index, DbParameter value) - { - OnChange(); - RangeCheck(index); - Replace(index, value); - } - - /// - override protected void SetParameter(string parameterName, DbParameter value) - { - OnChange(); - int index = IndexOf(parameterName); - if (index < 0) - { - throw ADP.ParametersSourceIndex(parameterName, this, s_itemType); - } - Replace(index, value); - } - - private void Validate(int index, object value) - { - if (null == value) - { - throw ADP.ParameterNull(nameof(value), this, s_itemType); - } - - object parent = ((SqlParameter)value).CompareExchangeParent(this, null); - if (null != parent) - { - if (this != parent) - { - throw ADP.ParametersIsNotParent(s_itemType, this); - } - if (index != IndexOf(value)) - { - throw ADP.ParametersIsParent(s_itemType, this); - } - } - - string name = ((SqlParameter)value).ParameterName; - if (0 == name.Length) - { - index = 1; - do - { - name = ADP.Parameter + index.ToString(CultureInfo.CurrentCulture); - index++; - } while (-1 != IndexOf(name)); - ((SqlParameter)value).ParameterName = name; - } - } - - private void ValidateType(object value) - { - if (null == value) - { - throw ADP.ParameterNull(nameof(value), this, s_itemType); - } - else if (!s_itemType.IsInstanceOfType(value)) - { - throw ADP.InvalidParameterType(this, s_itemType, value); - } - } - }; -} - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterHelper.cs deleted file mode 100644 index 454fe07385..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameterHelper.cs +++ /dev/null @@ -1,233 +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. - -using System; -using System.Data; -using System.Data.Common; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - public sealed partial class SqlParameter : DbParameter - { - private object _value; - - private object _parent; - - private ParameterDirection _direction; - private int _size; - - private int _offset; - private string _sourceColumn; - private bool _sourceColumnNullMapping; - - private bool _isNullable; - - private object _coercedValue; - - - private object CoercedValue - { - get - { - return _coercedValue; - } - set - { - _coercedValue = value; - } - } - - /// - override public ParameterDirection Direction - { - get - { - ParameterDirection direction = _direction; - return ((0 != direction) ? direction : ParameterDirection.Input); - } - set - { - if (_direction != value) - { - switch (value) - { - case ParameterDirection.Input: - case ParameterDirection.Output: - case ParameterDirection.InputOutput: - case ParameterDirection.ReturnValue: - PropertyChanging(); - _direction = value; - break; - default: - throw ADP.InvalidParameterDirection(value); - } - } - } - } - - /// - override public bool IsNullable - { - get - { - return _isNullable; - } - set - { - _isNullable = value; - } - } - - /// - public int Offset - { - get - { - return _offset; - } - set - { - if (value < 0) - { - throw ADP.InvalidOffsetValue(value); - } - _offset = value; - } - } - - /// - override public int Size - { - get - { - int size = _size; - if (0 == size) - { - size = ValueSize(Value); - } - return size; - } - set - { - if (_size != value) - { - if (value < -1) - { - throw ADP.InvalidSizeValue(value); - } - PropertyChanging(); - _size = value; - } - } - } - - - private bool ShouldSerializeSize() - { - return (0 != _size); - } - - /// - override public string SourceColumn - { - get - { - string sourceColumn = _sourceColumn; - return ((null != sourceColumn) ? sourceColumn : ADP.StrEmpty); - } - set - { - _sourceColumn = value; - } - } - - /// - public override bool SourceColumnNullMapping - { - get - { - return _sourceColumnNullMapping; - } - set - { - _sourceColumnNullMapping = value; - } - } - - - internal object CompareExchangeParent(object value, object comparand) - { - object parent = _parent; - if (comparand == parent) - { - _parent = value; - } - return parent; - } - - internal void ResetParent() - { - _parent = null; - } - - /// - override public string ToString() - { - return ParameterName; - } - - private byte ValuePrecisionCore(object value) - { - if (value is decimal) - { - return ((System.Data.SqlTypes.SqlDecimal)(Decimal)value).Precision; - } - return 0; - } - - private byte ValueScaleCore(object value) - { - if (value is decimal) - { - return (byte)((decimal.GetBits((decimal)value)[3] & 0x00ff0000) >> 0x10); - } - return 0; - } - - private int ValueSizeCore(object value) - { - if (!ADP.IsNull(value)) - { - string svalue = (value as string); - if (null != svalue) - { - return svalue.Length; - } - byte[] bvalue = (value as byte[]); - if (null != bvalue) - { - return bvalue.Length; - } - char[] cvalue = (value as char[]); - if (null != cvalue) - { - return cvalue.Length; - } - if ((value is byte) || (value is char)) - { - return 1; - } - } - return 0; - } - - internal void CopyTo(SqlParameter destination) - { - ADP.CheckArgumentNull(destination, nameof(destination)); - CloneHelper(destination); - } - } -} - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlSequentialStream.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlSequentialStream.cs deleted file mode 100644 index f8a3a086ed..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlSequentialStream.cs +++ /dev/null @@ -1,317 +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. - -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - sealed internal class SqlSequentialStream : System.IO.Stream - { - private SqlDataReader _reader; // The SqlDataReader that we are reading data from - private int _columnIndex; // The index of out column in the table - private Task _currentTask; // Holds the current task being processed - private int _readTimeout; // Read timeout for this stream in ms (for Stream.ReadTimeout) - private CancellationTokenSource _disposalTokenSource; // Used to indicate that a cancellation is requested due to disposal - - internal SqlSequentialStream(SqlDataReader reader, int columnIndex) - { - Debug.Assert(reader != null, "Null reader when creating sequential stream"); - Debug.Assert(columnIndex >= 0, "Invalid column index when creating sequential stream"); - - _reader = reader; - _columnIndex = columnIndex; - _currentTask = null; - _disposalTokenSource = new CancellationTokenSource(); - - // Safely convert the CommandTimeout from seconds to milliseconds - if ((reader.Command != null) && (reader.Command.CommandTimeout != 0)) - { - _readTimeout = (int)Math.Min((long)reader.Command.CommandTimeout * 1000L, (long)int.MaxValue); - } - else - { - _readTimeout = Timeout.Infinite; - } - } - - public override bool CanRead - { - get { return ((_reader != null) && (!_reader.IsClosed)); } - } - - public override bool CanSeek - { - get { return false; } - } - - public override bool CanTimeout - { - get { return true; } - } - - public override bool CanWrite - { - get { return false; } - } - - public override void Flush() - { } - - public override long Length - { - get { throw ADP.NotSupported(); } - } - - public override long Position - { - get { throw ADP.NotSupported(); } - set { throw ADP.NotSupported(); } - } - - public override int ReadTimeout - { - get { return _readTimeout; } - set - { - if ((value > 0) || (value == Timeout.Infinite)) - { - _readTimeout = value; - } - else - { - throw ADP.ArgumentOutOfRange(nameof(value)); - } - } - } - - internal int ColumnIndex - { - get { return _columnIndex; } - } - - public override int Read(byte[] buffer, int offset, int count) - { - ValidateReadParameters(buffer, offset, count); - if (!CanRead) - { - throw ADP.ObjectDisposed(this); - } - if (_currentTask != null) - { - throw ADP.AsyncOperationPending(); - } - - try - { - return _reader.GetBytesInternalSequential(_columnIndex, buffer, offset, count, _readTimeout); - } - catch (SqlException ex) - { - // Stream.Read() can't throw a SqlException - so wrap it in an IOException - throw ADP.ErrorReadingFromStream(ex); - } - } - - - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - ValidateReadParameters(buffer, offset, count); - - TaskCompletionSource completion = new TaskCompletionSource(); - if (!CanRead) - { - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ObjectDisposed(this))); - } - else - { - try - { - Task original = Interlocked.CompareExchange(ref _currentTask, completion.Task, null); - if (original != null) - { - completion.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); - } - else - { - // Set up a combined cancellation token for both the user's and our disposal tokens - CancellationTokenSource combinedTokenSource; - if (!cancellationToken.CanBeCanceled) - { - // Users token is not cancellable - just use ours - combinedTokenSource = _disposalTokenSource; - } - else - { - // Setup registrations from user and disposal token to cancel the combined token - combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _disposalTokenSource.Token); - } - - int bytesRead = 0; - Task getBytesTask = null; - var reader = _reader; - if ((reader != null) && (!cancellationToken.IsCancellationRequested) && (!_disposalTokenSource.Token.IsCancellationRequested)) - { - getBytesTask = reader.GetBytesAsync(_columnIndex, buffer, offset, count, _readTimeout, combinedTokenSource.Token, out bytesRead); - } - - if (getBytesTask == null) - { - _currentTask = null; - if (cancellationToken.IsCancellationRequested) - { - completion.SetCanceled(); - } - else if (!CanRead) - { - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ObjectDisposed(this))); - } - else - { - completion.SetResult(bytesRead); - } - - if (combinedTokenSource != _disposalTokenSource) - { - combinedTokenSource.Dispose(); - } - } - else - { - getBytesTask.ContinueWith((t) => - { - _currentTask = null; - // If we completed, but _reader is null (i.e. the stream is closed), then report cancellation - if ((t.Status == TaskStatus.RanToCompletion) && (CanRead)) - { - completion.SetResult((int)t.Result); - } - else if (t.Status == TaskStatus.Faulted) - { - if (t.Exception.InnerException is SqlException) - { - // Stream.ReadAsync() can't throw a SqlException - so wrap it in an IOException - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ErrorReadingFromStream(t.Exception.InnerException))); - } - else - { - completion.SetException(t.Exception.InnerException); - } - } - else if (!CanRead) - { - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ObjectDisposed(this))); - } - else - { - completion.SetCanceled(); - } - - if (combinedTokenSource != _disposalTokenSource) - { - combinedTokenSource.Dispose(); - } - }, TaskScheduler.Default); - } - } - } - catch (Exception ex) - { - // In case of any errors, ensure that the completion is completed and the task is set back to null if we switched it - completion.TrySetException(ex); - Interlocked.CompareExchange(ref _currentTask, null, completion.Task); - throw; - } - } - - return completion.Task; - } - - public override IAsyncResult BeginRead(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) => - TaskToApm.Begin(ReadAsync(array, offset, count, CancellationToken.None), asyncCallback, asyncState); - - public override int EndRead(IAsyncResult asyncResult) => - TaskToApm.End(asyncResult); - - public override long Seek(long offset, System.IO.SeekOrigin origin) - { - throw ADP.NotSupported(); - } - - public override void SetLength(long value) - { - throw ADP.NotSupported(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw ADP.NotSupported(); - } - - /// - /// Forces the stream to act as if it was closed (i.e. CanRead=false and Read() throws) - /// This does not actually close the stream, read off the rest of the data or dispose this - /// - internal void SetClosed() - { - _disposalTokenSource.Cancel(); - _reader = null; - - // Wait for pending task - var currentTask = _currentTask; - if (currentTask != null) - { - ((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne(); - } - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - // Set the stream as closed - SetClosed(); - } - - base.Dispose(disposing); - } - - /// - /// Checks the parameters passed into a Read() method are valid - /// - /// - /// - /// - internal static void ValidateReadParameters(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw ADP.ArgumentNull(nameof(buffer)); - } - if (offset < 0) - { - throw ADP.ArgumentOutOfRange(nameof(offset)); - } - if (count < 0) - { - throw ADP.ArgumentOutOfRange(nameof(count)); - } - try - { - if (checked(offset + count) > buffer.Length) - { - throw ExceptionBuilder.InvalidOffsetLength(); - } - } - catch (OverflowException) - { - // If we've overflowed when adding offset and count, then they never would have fit into buffer anyway - throw ExceptionBuilder.InvalidOffsetLength(); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 620b56917e..07472db42b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -4,185 +4,62 @@ using System; using System.ComponentModel; -using System.Data; using System.Data.Common; -using System.Diagnostics; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { /// - public sealed class SqlTransaction : DbTransaction + public sealed partial class SqlTransaction : DbTransaction { - private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; - - private SqlInternalTransaction _internalTransaction; - private SqlConnection _connection; - - private bool _isFromAPI; - - internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, - IsolationLevel iso, SqlInternalTransaction internalTransaction) - { - _isolationLevel = iso; - _connection = con; - - if (internalTransaction == null) - { - _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); - } - else - { - Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); - _internalTransaction = internalTransaction; - _internalTransaction.InitParent(this); - } - } + private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); //////////////////////////////////////////////////////////////////////////////////////// - // PROPERTIES + // PUBLIC METHODS //////////////////////////////////////////////////////////////////////////////////////// - /// - new public SqlConnection Connection - { - get - { - if (IsZombied) - { - return null; - } - else - { - return _connection; - } - } - } - - /// - override protected DbConnection DbConnection - { - get - { - return Connection; - } - } - - internal SqlInternalTransaction InternalTransaction - { - get - { - return _internalTransaction; - } - } - - /// - override public IsolationLevel IsolationLevel + /// + public override void Commit() { - get + using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionCommitScope(_isolationLevel, _connection, InternalTransaction)) { ZombieCheck(); - return _isolationLevel; - } - } - - private bool IsYukonPartialZombie - { - get - { - return (null != _internalTransaction && _internalTransaction.IsCompleted); - } - } - - internal bool IsZombied - { - get - { - return (null == _internalTransaction || _internalTransaction.IsCompleted); - } - } - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal SqlStatistics Statistics - { - get - { - if (null != _connection) + using (TryEventScope.Create("SqlTransaction.Commit | API | Object Id {0}", ObjectID)) { - if (_connection.StatisticsEnabled) + SqlStatistics statistics = null; + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Commit | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); + try { - return _connection.Statistics; - } - } - return null; - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PUBLIC METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - /// - override public void Commit() - { - Exception e = null; - Guid operationId = s_diagnosticListener.WriteTransactionCommitBefore(_isolationLevel, _connection, InternalTransaction); - - ZombieCheck(); - - SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - try - { - statistics = SqlStatistics.StartTimer(Statistics); + statistics = SqlStatistics.StartTimer(Statistics); - _isFromAPI = true; + _isFromAPI = true; - _internalTransaction.Commit(); - } - catch (SqlException ex) - { - // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, - // this connection may not be in reusable state. - // We will abort this connection and make sure it does not go back to the pool. - var innerException = ex.InnerException as Win32Exception; - if (innerException != null && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) - { - _connection.Abort(ex); - } - e = ex; - throw; - } - catch (Exception ex) - { - e = ex; - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - if (e != null) - { - s_diagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, _connection, InternalTransaction, e); - } - else - { - s_diagnosticListener.WriteTransactionCommitAfter(operationId, _isolationLevel, _connection, InternalTransaction); + _internalTransaction.Commit(); + } + catch (SqlException ex) + { + diagnosticScope.SetException(ex); + // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, + // this connection may not be in reusable state. + // We will abort this connection and make sure it does not go back to the pool. + if (ex.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) + { + _connection.Abort(ex); + } + throw; + } + catch (Exception ex) + { + diagnosticScope.SetException(ex); + throw; + } + finally + { + SqlStatistics.StopTimer(statistics); + _isFromAPI = false; + } } - - _isFromAPI = false; } } @@ -191,7 +68,7 @@ protected override void Dispose(bool disposing) { if (disposing) { - if (!IsZombied && !IsYukonPartialZombie) + if (!IsZombied && !Is2005PartialZombie) { _internalTransaction.Dispose(); } @@ -200,151 +77,105 @@ protected override void Dispose(bool disposing) } /// - override public void Rollback() + public override void Rollback() { - Exception e = null; - Guid operationId = s_diagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connection, InternalTransaction); - - if (IsYukonPartialZombie) + using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, null)) { - // Put something in the trace in case a customer has an issue - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} partial zombie no rollback required", ObjectID); - _internalTransaction = null; // yukon zombification - } - else - { - ZombieCheck(); - - SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0}", ObjectID); - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - try - { - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; - - _internalTransaction.Rollback(); - } - catch (Exception ex) + if (Is2005PartialZombie) { - e = ex; - throw; + // Put something in the trace in case a customer has an issue + SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Rollback | ADV | Object Id {0}, partial zombie no rollback required", ObjectID); + _internalTransaction = null; // 2005 zombification } - finally + else { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - if (e != null) - { - s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, InternalTransaction, e); - } - else + ZombieCheck(); + + SqlStatistics statistics = null; + using (TryEventScope.Create("SqlTransaction.Rollback | API | Object Id {0}", ObjectID)) { - s_diagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connection, InternalTransaction); + SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Rollback | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); + try + { + statistics = SqlStatistics.StartTimer(Statistics); + + _isFromAPI = true; + + _internalTransaction.Rollback(); + } + catch (Exception ex) + { + diagnosticScope.SetException(ex); + throw; + } + finally + { + SqlStatistics.StopTimer(statistics); + _isFromAPI = false; + } } - _isFromAPI = false; } } } /// +#if NET6_0_OR_GREATER + public override void Rollback(string transactionName) +#else public void Rollback(string transactionName) +#endif { - Exception e = null; - Guid operationId = s_diagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connection, InternalTransaction, transactionName); - - ZombieCheck(); - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0} transactionName='{1}'", ObjectID, transactionName); - SqlStatistics statistics = null; - try + using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, transactionName)) { - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; + ZombieCheck(); - _internalTransaction.Rollback(transactionName); - } - catch (Exception ex) - { - e = ex; - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - if (e != null) + using (TryEventScope.Create(SqlClientEventSource.Log.TryScopeEnterEvent("SqlTransaction.Rollback | API | Object Id {0}, Transaction Name='{1}', ActivityID {2}, Client Connection Id {3}", ObjectID, transactionName, ActivityCorrelator.Current, Connection?.ClientConnectionId))) { - s_diagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connection, InternalTransaction, e, transactionName); - } - else - { - s_diagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connection, InternalTransaction, transactionName); - } + SqlStatistics statistics = null; + try + { + statistics = SqlStatistics.StartTimer(Statistics); - _isFromAPI = false; + _isFromAPI = true; + _internalTransaction.Rollback(transactionName); + } + catch (Exception ex) + { + diagnosticScope.SetException(ex); + throw; + } + finally + { + SqlStatistics.StopTimer(statistics); + _isFromAPI = false; + } + } } } /// +#if NET6_0_OR_GREATER + public override void Save(string savePointName) +#else public void Save(string savePointName) +#endif { ZombieCheck(); SqlStatistics statistics = null; - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(" {0} savePointName='{1}'", ObjectID, savePointName); - try + using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectID, savePointName)) { - statistics = SqlStatistics.StartTimer(Statistics); - - _internalTransaction.Save(savePointName); - } - finally - { - SqlStatistics.StopTimer(statistics); - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // INTERNAL METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - internal void Zombie() - { - // For Yukon, we have to defer "zombification" until - // we get past the users' next rollback, else we'll - // throw an exception there that is a breaking change. - // Of course, if the connection is already closed, - // then we're free to zombify... - SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); - if (null != internalConnection && !_isFromAPI) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} yukon deferred zombie", ObjectID); - } - else - { - _internalTransaction = null; // pre-yukon zombification - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PRIVATE METHODS - //////////////////////////////////////////////////////////////////////////////////////// + try + { + statistics = SqlStatistics.StartTimer(Statistics); - private void ZombieCheck() - { - // If this transaction has been completed, throw exception since it is unusable. - if (IsZombied) - { - if (IsYukonPartialZombie) + _internalTransaction.Save(savePointName); + } + finally { - _internalTransaction = null; // yukon zombification + SqlStatistics.StopTimer(statistics); } - - throw ADP.TransactionZombied(this); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index bf21c8db3a..4aebe4b518 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -34,7 +34,7 @@ internal static Task CreateContinuationTask(Task task, Action onSuccess, Action< TaskCompletionSource completion = new TaskCompletionSource(); ContinueTaskWithState(task, completion, state: Tuple.Create(onSuccess, onFailure, completion), - onSuccess: (state) => + onSuccess: static (object state) => { var parameters = (Tuple, TaskCompletionSource>)state; Action success = parameters.Item1; @@ -42,7 +42,7 @@ internal static Task CreateContinuationTask(Task task, Action onSuccess, Action< success(); taskCompletionSource.SetResult(null); }, - onFailure: (exception, state) => + onFailure: static (Exception exception, object state) => { var parameters = (Tuple, TaskCompletionSource>)state; Action failure = parameters.Item2; @@ -64,7 +64,7 @@ internal static Task CreateContinuationTaskWithState(Task task, object state, Ac { var completion = new TaskCompletionSource(); ContinueTaskWithState(task, completion, state, - onSuccess: (continueState) => + onSuccess: (object continueState) => { onSuccess(continueState); completion.SetResult(null); @@ -81,12 +81,12 @@ internal static Task CreateContinuationTask(Task task, Action on } internal static void ContinueTask(Task task, - TaskCompletionSource completion, - Action onSuccess, - Action onFailure = null, - Action onCancellation = null, - Func exceptionConverter = null - ) + TaskCompletionSource completion, + Action onSuccess, + Action onFailure = null, + Action onCancellation = null, + Func exceptionConverter = null + ) { task.ContinueWith( tsk => @@ -145,7 +145,7 @@ internal static void ContinueTaskWithState(Task task, ) { task.ContinueWith( - tsk => + (Task tsk, object state2) => { if (tsk.Exception != null) { @@ -156,7 +156,7 @@ internal static void ContinueTaskWithState(Task task, } try { - onFailure?.Invoke(exc, state); + onFailure?.Invoke(exc, state2); } finally { @@ -167,7 +167,7 @@ internal static void ContinueTaskWithState(Task task, { try { - onCancellation?.Invoke(state); + onCancellation?.Invoke(state2); } finally { @@ -178,14 +178,16 @@ internal static void ContinueTaskWithState(Task task, { try { - onSuccess(state); + onSuccess(state2); } catch (Exception e) { completion.SetException(e); } } - }, TaskScheduler.Default + }, + state: state, + scheduler: TaskScheduler.Default ); } @@ -205,25 +207,42 @@ internal static void WaitForCompletion(Task task, int timeout, Action onTimeout } if (!task.IsCompleted) { - task.ContinueWith(t => { var ignored = t.Exception; }); //Ensure the task does not leave an unobserved exception - if (onTimeout != null) - { - onTimeout(); - } + task.ContinueWith(static t => { var ignored = t.Exception; }); //Ensure the task does not leave an unobserved exception + onTimeout?.Invoke(); } } - internal static void SetTimeoutException(TaskCompletionSource completion, int timeout, Func exc, CancellationToken ctoken) + internal static void SetTimeoutException(TaskCompletionSource completion, int timeout, Func onFailure, CancellationToken ctoken) { if (timeout > 0) { - Task.Delay(timeout * 1000, ctoken).ContinueWith((tsk) => - { - if (!tsk.IsCanceled && !completion.Task.IsCompleted) + Task.Delay(timeout * 1000, ctoken).ContinueWith( + (Task task) => { - completion.TrySetException(exc()); + if (!task.IsCanceled && !completion.Task.IsCompleted) + { + completion.TrySetException(onFailure()); + } } - }); + ); + } + } + + internal static void SetTimeoutExceptionWithState(TaskCompletionSource completion, int timeout, object state, Func onFailure, CancellationToken cancellationToken) + { + if (timeout > 0) + { + Task.Delay(timeout * 1000, cancellationToken).ContinueWith( + (Task task, object state) => + { + if (!task.IsCanceled && !completion.Task.IsCompleted) + { + completion.TrySetException(onFailure(state)); + } + }, + state: state, + cancellationToken: CancellationToken.None + ); } } } @@ -248,7 +267,7 @@ internal static class SQL // internal static Exception CannotGetDTCAddress() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_CannotGetDTCAddress)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_CannotGetDTCAddress)); } internal static Exception InvalidInternalPacketSize(string str) @@ -257,135 +276,135 @@ internal static Exception InvalidInternalPacketSize(string str) } internal static Exception InvalidPacketSize() { - return ADP.ArgumentOutOfRange(System.StringsHelper.GetString(Strings.SQL_InvalidTDSPacketSize)); + return ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.SQL_InvalidTDSPacketSize)); } internal static Exception InvalidPacketSizeValue() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_InvalidPacketSizeValue)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_InvalidPacketSizeValue)); } internal static Exception InvalidSSPIPacketSize() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_InvalidSSPIPacketSize)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_InvalidSSPIPacketSize)); } internal static Exception AuthenticationAndIntegratedSecurity() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_AuthenticationAndIntegratedSecurity)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_AuthenticationAndIntegratedSecurity)); } internal static Exception IntegratedWithPassword() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_IntegratedWithPassword)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_IntegratedWithPassword)); } internal static Exception InteractiveWithPassword() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_InteractiveWithPassword)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_InteractiveWithPassword)); } internal static Exception DeviceFlowWithUsernamePassword() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_DeviceFlowWithUsernamePassword)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_DeviceFlowWithUsernamePassword)); } - internal static Exception ManagedIdentityWithPassword(string authenticationMode) + internal static Exception NonInteractiveWithPassword(string authenticationMode) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_ManagedIdentityWithPassword, authenticationMode)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_NonInteractiveWithPassword, authenticationMode)); } static internal Exception SettingIntegratedWithCredential() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingIntegratedWithCredential)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingIntegratedWithCredential)); } static internal Exception SettingInteractiveWithCredential() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingInteractiveWithCredential)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingInteractiveWithCredential)); } static internal Exception SettingDeviceFlowWithCredential() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingDeviceFlowWithCredential)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingDeviceFlowWithCredential)); } - static internal Exception SettingManagedIdentityWithCredential(string authenticationMode) + static internal Exception SettingNonInteractiveWithCredential(string authenticationMode) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingManagedIdentityWithCredential, authenticationMode)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingNonInteractiveWithCredential, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedArgument() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithIntegrated)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithIntegrated)); } static internal Exception SettingCredentialWithInteractiveArgument() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithInteractive)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithInteractive)); } static internal Exception SettingCredentialWithDeviceFlowArgument() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityArgument(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveArgument(string authenticationMode) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } static internal Exception SettingCredentialWithIntegratedInvalid() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithIntegrated)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithIntegrated)); } static internal Exception SettingCredentialWithInteractiveInvalid() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithInteractive)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithInteractive)); } static internal Exception SettingCredentialWithDeviceFlowInvalid() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithDeviceFlow)); } - static internal Exception SettingCredentialWithManagedIdentityInvalid(string authenticationMode) + static internal Exception SettingCredentialWithNonInteractiveInvalid(string authenticationMode) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SettingCredentialWithManagedIdentity, authenticationMode)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SettingCredentialWithNonInteractive, authenticationMode)); } internal static Exception NullEmptyTransactionName() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_NullEmptyTransactionName)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_NullEmptyTransactionName)); } internal static Exception UserInstanceFailoverNotCompatible() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_UserInstanceFailoverNotCompatible)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_UserInstanceFailoverNotCompatible)); } internal static Exception CredentialsNotProvided(SqlAuthenticationMethod auth) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_CredentialsNotProvided, DbConnectionStringBuilderUtil.AuthenticationTypeToString(auth))); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_CredentialsNotProvided, DbConnectionStringBuilderUtil.AuthenticationTypeToString(auth))); } internal static Exception ParsingErrorLibraryType(ParsingErrorState state, int libraryType) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorAuthLibraryType, ((int)state).ToString(CultureInfo.InvariantCulture), libraryType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorAuthLibraryType, ((int)state).ToString(CultureInfo.InvariantCulture), libraryType)); } internal static Exception InvalidSQLServerVersionUnknown() { - return ADP.DataAdapter(System.StringsHelper.GetString(Strings.SQL_InvalidSQLServerVersionUnknown)); + return ADP.DataAdapter(StringsHelper.GetString(Strings.SQL_InvalidSQLServerVersionUnknown)); } internal static Exception SynchronousCallMayNotPend() { - return new Exception(System.StringsHelper.GetString(Strings.Sql_InternalError)); + return new Exception(StringsHelper.GetString(Strings.Sql_InternalError)); } internal static Exception ConnectionLockedForBcpEvent() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ConnectionLockedForBcpEvent)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ConnectionLockedForBcpEvent)); } internal static Exception InstanceFailure() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_InstanceFailure)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_InstanceFailure)); } internal static Exception ChangePasswordArgumentMissing(string argumentName) { - return ADP.ArgumentNull(System.StringsHelper.GetString(Strings.SQL_ChangePasswordArgumentMissing, argumentName)); + return ADP.ArgumentNull(StringsHelper.GetString(Strings.SQL_ChangePasswordArgumentMissing, argumentName)); } internal static Exception ChangePasswordConflictsWithSSPI() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_ChangePasswordConflictsWithSSPI)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_ChangePasswordConflictsWithSSPI)); } - internal static Exception ChangePasswordRequiresYukon() + internal static Exception ChangePasswordRequires2005() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ChangePasswordRequiresYukon)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ChangePasswordRequiresYukon)); } internal static Exception ChangePasswordUseOfUnallowedKey(string key) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ChangePasswordUseOfUnallowedKey, key)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ChangePasswordUseOfUnallowedKey, key)); } internal static Exception GlobalizationInvariantModeNotSupported() { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_GlobalizationInvariantModeNotSupported)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_GlobalizationInvariantModeNotSupported)); } // @@ -393,87 +412,92 @@ internal static Exception GlobalizationInvariantModeNotSupported() // internal static Exception GlobalTransactionsNotEnabled() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.GT_Disabled)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.GT_Disabled)); } internal static Exception UnknownSysTxIsolationLevel(System.Transactions.IsolationLevel isolationLevel) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_UnknownSysTxIsolationLevel, isolationLevel.ToString())); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_UnknownSysTxIsolationLevel, isolationLevel.ToString())); } internal static Exception InvalidPartnerConfiguration(string server, string database) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_InvalidPartnerConfiguration, server, database)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_InvalidPartnerConfiguration, server, database)); } internal static Exception BatchedUpdateColumnEncryptionSettingMismatch() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_BatchedUpdateColumnEncryptionSettingMismatch, "SqlCommandColumnEncryptionSetting", "SelectCommand", "InsertCommand", "UpdateCommand", "DeleteCommand")); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_BatchedUpdateColumnEncryptionSettingMismatch, "SqlCommandColumnEncryptionSetting", "SelectCommand", "InsertCommand", "UpdateCommand", "DeleteCommand")); } internal static Exception MARSUnsupportedOnConnection() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_MarsUnsupportedOnConnection)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_MarsUnsupportedOnConnection)); } internal static Exception CannotModifyPropertyAsyncOperationInProgress([CallerMemberName] string property = "") { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_CannotModifyPropertyAsyncOperationInProgress, property)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_CannotModifyPropertyAsyncOperationInProgress, property)); } internal static Exception NonLocalSSEInstance() { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_NonLocalSSEInstance)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_NonLocalSSEInstance)); } // SQL.ActiveDirectoryAuth // internal static Exception UnsupportedAuthentication(string authentication) { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_UnsupportedAuthentication, authentication)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_UnsupportedAuthentication, authentication)); } internal static Exception UnsupportedSqlAuthenticationMethod(SqlAuthenticationMethod authentication) { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_UnsupportedSqlAuthenticationMethod, authentication)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_UnsupportedSqlAuthenticationMethod, authentication)); } internal static Exception UnsupportedAuthenticationSpecified(SqlAuthenticationMethod authentication) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_UnsupportedAuthenticationSpecified, authentication)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_UnsupportedAuthenticationSpecified, authentication)); } internal static Exception CannotCreateAuthProvider(string authentication, string type, Exception e) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_CannotCreateAuthProvider, authentication, type), e); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_CannotCreateAuthProvider, authentication, type), e); } internal static Exception CannotCreateSqlAuthInitializer(string type, Exception e) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_CannotCreateAuthInitializer, type), e); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_CannotCreateAuthInitializer, type), e); } internal static Exception CannotInitializeAuthProvider(string type, Exception e) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_CannotInitializeAuthProvider, type), e); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_CannotInitializeAuthProvider, type), e); } internal static Exception UnsupportedAuthenticationByProvider(string authentication, string type) { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_UnsupportedAuthenticationByProvider, type, authentication)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_UnsupportedAuthenticationByProvider, type, authentication)); } internal static Exception CannotFindAuthProvider(string authentication) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_CannotFindAuthProvider, authentication)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_CannotFindAuthProvider, authentication)); } internal static Exception CannotGetAuthProviderConfig(Exception e) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_CannotGetAuthProviderConfig), e); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_CannotGetAuthProviderConfig), e); } internal static Exception ParameterCannotBeEmpty(string paramName) { - return ADP.ArgumentNull(System.StringsHelper.GetString(Strings.SQL_ParameterCannotBeEmpty, paramName)); + return ADP.ArgumentNull(StringsHelper.GetString(Strings.SQL_ParameterCannotBeEmpty, paramName)); + } + + internal static Exception ParameterDirectionInvalidForOptimizedBinding(string paramName) + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParameterDirectionInvalidForOptimizedBinding, paramName)); } internal static Exception ActiveDirectoryInteractiveTimeout() @@ -486,6 +510,10 @@ internal static Exception ActiveDirectoryDeviceFlowTimeout() return ADP.TimeoutException(Strings.SQL_Timeout_Active_Directory_DeviceFlow_Authentication); } + internal static Exception ActiveDirectoryTokenRetrievingTimeout(string authenticaton, string errorCode, Exception exception) + { + return ADP.TimeoutException(StringsHelper.GetString(Strings.AAD_Token_Retrieving_Timeout, authenticaton, errorCode, exception?.Message), exception); + } // // SQL.DataCommand @@ -493,7 +521,7 @@ internal static Exception ActiveDirectoryDeviceFlowTimeout() internal static ArgumentOutOfRangeException NotSupportedEnumerationValue(Type type, int value) { - return ADP.ArgumentOutOfRange(System.StringsHelper.GetString(Strings.SQL_NotSupportedEnumerationValue, type.Name, value.ToString(System.Globalization.CultureInfo.InvariantCulture)), type.Name); + return ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.SQL_NotSupportedEnumerationValue, type.Name, value.ToString(System.Globalization.CultureInfo.InvariantCulture)), type.Name); } internal static ArgumentOutOfRangeException NotSupportedCommandType(CommandType value) @@ -539,23 +567,23 @@ internal static ArgumentOutOfRangeException NotSupportedIsolationLevel(System.Da internal static Exception OperationCancelled() { - Exception exception = ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_OperationCancelled)); + Exception exception = ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_OperationCancelled)); return exception; } internal static Exception PendingBeginXXXExists() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_PendingBeginXXXExists)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_PendingBeginXXXExists)); } internal static ArgumentOutOfRangeException InvalidSqlDependencyTimeout(string param) { - return ADP.ArgumentOutOfRange(System.StringsHelper.GetString(Strings.SqlDependency_InvalidTimeout), param); + return ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.SqlDependency_InvalidTimeout), param); } internal static Exception NonXmlResult() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_NonXmlResult)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_NonXmlResult)); } // @@ -563,27 +591,27 @@ internal static Exception NonXmlResult() // internal static Exception InvalidUdt3PartNameFormat() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_InvalidUdt3PartNameFormat)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_InvalidUdt3PartNameFormat)); } internal static Exception InvalidParameterTypeNameFormat() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_InvalidParameterTypeNameFormat)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_InvalidParameterTypeNameFormat)); } internal static Exception InvalidParameterNameLength(string value) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_InvalidParameterNameLength, value)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_InvalidParameterNameLength, value)); } internal static Exception PrecisionValueOutOfRange(byte precision) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_PrecisionValueOutOfRange, precision.ToString(CultureInfo.InvariantCulture))); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_PrecisionValueOutOfRange, precision.ToString(CultureInfo.InvariantCulture))); } internal static Exception ScaleValueOutOfRange(byte scale) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_ScaleValueOutOfRange, scale.ToString(CultureInfo.InvariantCulture))); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_ScaleValueOutOfRange, scale.ToString(CultureInfo.InvariantCulture))); } internal static Exception TimeScaleValueOutOfRange(byte scale) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_TimeScaleValueOutOfRange, scale.ToString(CultureInfo.InvariantCulture))); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_TimeScaleValueOutOfRange, scale.ToString(CultureInfo.InvariantCulture))); } internal static Exception InvalidSqlDbType(SqlDbType value) { @@ -591,41 +619,41 @@ internal static Exception InvalidSqlDbType(SqlDbType value) } internal static Exception UnsupportedTVPOutputParameter(ParameterDirection direction, string paramName) { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SqlParameter_UnsupportedTVPOutputParameter, + return ADP.NotSupported(StringsHelper.GetString(Strings.SqlParameter_UnsupportedTVPOutputParameter, direction.ToString(), paramName)); } internal static Exception DBNullNotSupportedForTVPValues(string paramName) { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SqlParameter_DBNullNotSupportedForTVP, paramName)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SqlParameter_DBNullNotSupportedForTVP, paramName)); } internal static Exception UnexpectedTypeNameForNonStructParams(string paramName) { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SqlParameter_UnexpectedTypeNameForNonStruct, paramName)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SqlParameter_UnexpectedTypeNameForNonStruct, paramName)); } internal static Exception ParameterInvalidVariant(string paramName) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParameterInvalidVariant, paramName)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParameterInvalidVariant, paramName)); } internal static Exception MustSetTypeNameForParam(string paramType, string paramName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_ParameterTypeNameRequired, paramType, paramName)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_ParameterTypeNameRequired, paramType, paramName)); } internal static Exception NullSchemaTableDataTypeNotSupported(string columnName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.NullSchemaTableDataTypeNotSupported, columnName)); + return ADP.Argument(StringsHelper.GetString(Strings.NullSchemaTableDataTypeNotSupported, columnName)); } internal static Exception InvalidSchemaTableOrdinals() { - return ADP.Argument(System.StringsHelper.GetString(Strings.InvalidSchemaTableOrdinals)); + return ADP.Argument(StringsHelper.GetString(Strings.InvalidSchemaTableOrdinals)); } internal static Exception EnumeratedRecordMetaDataChanged(string fieldName, int recordNumber) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_EnumeratedRecordMetaDataChanged, fieldName, recordNumber)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_EnumeratedRecordMetaDataChanged, fieldName, recordNumber)); } internal static Exception EnumeratedRecordFieldCountChanged(int recordNumber) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_EnumeratedRecordFieldCountChanged, recordNumber)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_EnumeratedRecordFieldCountChanged, recordNumber)); } // @@ -637,59 +665,59 @@ internal static Exception EnumeratedRecordFieldCountChanged(int recordNumber) // internal static Exception InvalidTDSVersion() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_InvalidTDSVersion)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_InvalidTDSVersion)); } internal static Exception ParsingError() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingError)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingError)); } internal static Exception ParsingError(ParsingErrorState state) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorWithState, ((int)state).ToString(CultureInfo.InvariantCulture))); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorWithState, ((int)state).ToString(CultureInfo.InvariantCulture))); } internal static Exception ParsingError(ParsingErrorState state, Exception innerException) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorWithState, ((int)state).ToString(CultureInfo.InvariantCulture)), innerException); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorWithState, ((int)state).ToString(CultureInfo.InvariantCulture)), innerException); } internal static Exception ParsingErrorValue(ParsingErrorState state, int value) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorValue, ((int)state).ToString(CultureInfo.InvariantCulture), value)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorValue, ((int)state).ToString(CultureInfo.InvariantCulture), value)); } internal static Exception ParsingErrorFeatureId(ParsingErrorState state, int featureId) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorFeatureId, ((int)state).ToString(CultureInfo.InvariantCulture), featureId)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorFeatureId, ((int)state).ToString(CultureInfo.InvariantCulture), featureId)); } internal static Exception ParsingErrorToken(ParsingErrorState state, int token) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorToken, ((int)state).ToString(CultureInfo.InvariantCulture), token)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorToken, ((int)state).ToString(CultureInfo.InvariantCulture), token)); } internal static Exception ParsingErrorLength(ParsingErrorState state, int length) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorLength, ((int)state).ToString(CultureInfo.InvariantCulture), length)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorLength, ((int)state).ToString(CultureInfo.InvariantCulture), length)); } internal static Exception ParsingErrorStatus(ParsingErrorState state, int status) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorStatus, ((int)state).ToString(CultureInfo.InvariantCulture), status)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorStatus, ((int)state).ToString(CultureInfo.InvariantCulture), status)); } internal static Exception ParsingErrorOffset(ParsingErrorState state, int offset) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParsingErrorOffset, ((int)state).ToString(CultureInfo.InvariantCulture), offset)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ParsingErrorOffset, ((int)state).ToString(CultureInfo.InvariantCulture), offset)); } internal static Exception MoneyOverflow(string moneyValue) { - return ADP.Overflow(System.StringsHelper.GetString(Strings.SQL_MoneyOverflow, moneyValue)); + return ADP.Overflow(StringsHelper.GetString(Strings.SQL_MoneyOverflow, moneyValue)); } internal static Exception SmallDateTimeOverflow(string datetime) { - return ADP.Overflow(System.StringsHelper.GetString(Strings.SQL_SmallDateTimeOverflow, datetime)); + return ADP.Overflow(StringsHelper.GetString(Strings.SQL_SmallDateTimeOverflow, datetime)); } internal static Exception SNIPacketAllocationFailure() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_SNIPacketAllocationFailure)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_SNIPacketAllocationFailure)); } internal static Exception TimeOverflow(string time) { - return ADP.Overflow(System.StringsHelper.GetString(Strings.SQL_TimeOverflow, time)); + return ADP.Overflow(StringsHelper.GetString(Strings.SQL_TimeOverflow, time)); } // @@ -697,47 +725,52 @@ internal static Exception TimeOverflow(string time) // internal static Exception InvalidRead() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_InvalidRead)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_InvalidRead)); } internal static Exception NonBlobColumn(string columnName) { - return ADP.InvalidCast(System.StringsHelper.GetString(Strings.SQL_NonBlobColumn, columnName)); + return ADP.InvalidCast(StringsHelper.GetString(Strings.SQL_NonBlobColumn, columnName)); } internal static Exception NonCharColumn(string columnName) { - return ADP.InvalidCast(System.StringsHelper.GetString(Strings.SQL_NonCharColumn, columnName)); + return ADP.InvalidCast(StringsHelper.GetString(Strings.SQL_NonCharColumn, columnName)); } internal static Exception StreamNotSupportOnColumnType(string columnName) { - return ADP.InvalidCast(System.StringsHelper.GetString(Strings.SQL_StreamNotSupportOnColumnType, columnName)); + return ADP.InvalidCast(StringsHelper.GetString(Strings.SQL_StreamNotSupportOnColumnType, columnName)); } internal static Exception StreamNotSupportOnEncryptedColumn(string columnName) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_StreamNotSupportOnEncryptedColumn, columnName, "Stream")); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_StreamNotSupportOnEncryptedColumn, columnName, "Stream")); } internal static Exception SequentialAccessNotSupportedOnEncryptedColumn(string columnName) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_SequentialAccessNotSupportedOnEncryptedColumn, columnName, "CommandBehavior=SequentialAccess")); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_SequentialAccessNotSupportedOnEncryptedColumn, columnName, "CommandBehavior=SequentialAccess")); } internal static Exception TextReaderNotSupportOnColumnType(string columnName) { - return ADP.InvalidCast(System.StringsHelper.GetString(Strings.SQL_TextReaderNotSupportOnColumnType, columnName)); + return ADP.InvalidCast(StringsHelper.GetString(Strings.SQL_TextReaderNotSupportOnColumnType, columnName)); } internal static Exception XmlReaderNotSupportOnColumnType(string columnName) { - return ADP.InvalidCast(System.StringsHelper.GetString(Strings.SQL_XmlReaderNotSupportOnColumnType, columnName)); + return ADP.InvalidCast(StringsHelper.GetString(Strings.SQL_XmlReaderNotSupportOnColumnType, columnName)); } internal static Exception UDTUnexpectedResult(string exceptionText) { - return ADP.TypeLoad(System.StringsHelper.GetString(Strings.SQLUDT_Unexpected, exceptionText)); + return ADP.TypeLoad(StringsHelper.GetString(Strings.SQLUDT_Unexpected, exceptionText)); + } + + internal static Exception DateTimeOverflow() + { + return new OverflowException(SqlTypes.SQLResource.DateTimeOverflowMessage); } // @@ -745,50 +778,57 @@ internal static Exception UDTUnexpectedResult(string exceptionText) // internal static Exception SqlCommandHasExistingSqlNotificationRequest() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQLNotify_AlreadyHasCommand)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQLNotify_AlreadyHasCommand)); } internal static Exception SqlDepDefaultOptionsButNoStart() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlDependency_DefaultOptionsButNoStart)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlDependency_DefaultOptionsButNoStart)); } internal static Exception SqlDependencyDatabaseBrokerDisabled() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlDependency_DatabaseBrokerDisabled)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlDependency_DatabaseBrokerDisabled)); } internal static Exception SqlDependencyEventNoDuplicate() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlDependency_EventNoDuplicate)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlDependency_EventNoDuplicate)); } internal static Exception SqlDependencyDuplicateStart() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlDependency_DuplicateStart)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlDependency_DuplicateStart)); } internal static Exception SqlDependencyIdMismatch() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlDependency_IdMismatch)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlDependency_IdMismatch)); } internal static Exception SqlDependencyNoMatchingServerStart() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlDependency_NoMatchingServerStart)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlDependency_NoMatchingServerStart)); } internal static Exception SqlDependencyNoMatchingServerDatabaseStart() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlDependency_NoMatchingServerDatabaseStart)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlDependency_NoMatchingServerDatabaseStart)); } // // SQL.SqlDelegatedTransaction // + static internal Exception CannotCompleteDelegatedTransactionWithOpenResults(SqlInternalConnectionTds internalConnection, bool marsOn) + { + SqlErrorCollection errors = new SqlErrorCollection(); + errors.Add(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, null, (StringsHelper.GetString(Strings.ADP_OpenReaderExists, marsOn ? ADP.Command : ADP.Connection)), "", 0, TdsEnums.SNI_WAIT_TIMEOUT)); + return SqlException.CreateException(errors, null, internalConnection); + } + internal static TransactionPromotionException PromotionFailed(Exception inner) { - TransactionPromotionException e = new TransactionPromotionException(System.StringsHelper.GetString(Strings.SqlDelegatedTransaction_PromotionFailed), inner); + TransactionPromotionException e = new TransactionPromotionException(StringsHelper.GetString(Strings.SqlDelegatedTransaction_PromotionFailed), inner); ADP.TraceExceptionAsReturnValue(e); return e; } @@ -799,30 +839,30 @@ internal static TransactionPromotionException PromotionFailed(Exception inner) // internal static Exception UnexpectedUdtTypeNameForNonUdtParams() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQLUDT_UnexpectedUdtTypeName)); + return ADP.Argument(StringsHelper.GetString(Strings.SQLUDT_UnexpectedUdtTypeName)); } internal static Exception MustSetUdtTypeNameForUdtParams() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQLUDT_InvalidUdtTypeName)); + return ADP.Argument(StringsHelper.GetString(Strings.SQLUDT_InvalidUdtTypeName)); } internal static Exception UDTInvalidSqlType(string typeName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQLUDT_InvalidSqlType, typeName)); + return ADP.Argument(StringsHelper.GetString(Strings.SQLUDT_InvalidSqlType, typeName)); } internal static Exception UDTInvalidSize(int maxSize, int maxSupportedSize) { - throw ADP.ArgumentOutOfRange(System.StringsHelper.GetString(Strings.SQLUDT_InvalidSize, maxSize, maxSupportedSize)); + throw ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.SQLUDT_InvalidSize, maxSize, maxSupportedSize)); } internal static Exception InvalidSqlDbTypeForConstructor(SqlDbType type) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SqlMetaData_InvalidSqlDbTypeForConstructorFormat, type.ToString())); + return ADP.Argument(StringsHelper.GetString(Strings.SqlMetaData_InvalidSqlDbTypeForConstructorFormat, type.ToString())); } internal static Exception NameTooLong(string parameterName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SqlMetaData_NameTooLong), parameterName); + return ADP.Argument(StringsHelper.GetString(Strings.SqlMetaData_NameTooLong), parameterName); } internal static Exception InvalidSortOrder(SortOrder order) @@ -832,40 +872,40 @@ internal static Exception InvalidSortOrder(SortOrder order) internal static Exception MustSpecifyBothSortOrderAndOrdinal(SortOrder order, int ordinal) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlMetaData_SpecifyBothSortOrderAndOrdinal, order.ToString(), ordinal)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlMetaData_SpecifyBothSortOrderAndOrdinal, order.ToString(), ordinal)); } internal static Exception UnsupportedColumnTypeForSqlProvider(string columnName, string typeName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SqlProvider_InvalidDataColumnType, columnName, typeName)); + return ADP.Argument(StringsHelper.GetString(Strings.SqlProvider_InvalidDataColumnType, columnName, typeName)); } internal static Exception InvalidColumnMaxLength(string columnName, long maxLength) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SqlProvider_InvalidDataColumnMaxLength, columnName, maxLength)); + return ADP.Argument(StringsHelper.GetString(Strings.SqlProvider_InvalidDataColumnMaxLength, columnName, maxLength)); } internal static Exception InvalidColumnPrecScale() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SqlMisc_InvalidPrecScaleMessage)); + return ADP.Argument(StringsHelper.GetString(Strings.SqlMisc_InvalidPrecScaleMessage)); } internal static Exception NotEnoughColumnsInStructuredType() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SqlProvider_NotEnoughColumnsInStructuredType)); + return ADP.Argument(StringsHelper.GetString(Strings.SqlProvider_NotEnoughColumnsInStructuredType)); } internal static Exception DuplicateSortOrdinal(int sortOrdinal) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlProvider_DuplicateSortOrdinal, sortOrdinal)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlProvider_DuplicateSortOrdinal, sortOrdinal)); } internal static Exception MissingSortOrdinal(int sortOrdinal) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlProvider_MissingSortOrdinal, sortOrdinal)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlProvider_MissingSortOrdinal, sortOrdinal)); } internal static Exception SortOrdinalGreaterThanFieldCount(int columnOrdinal, int sortOrdinal) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlProvider_SortOrdinalGreaterThanFieldCount, sortOrdinal, columnOrdinal)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlProvider_SortOrdinalGreaterThanFieldCount, sortOrdinal, columnOrdinal)); } internal static Exception IEnumerableOfSqlDataRecordHasNoRows() { - return ADP.Argument(System.StringsHelper.GetString(Strings.IEnumerableOfSqlDataRecordHasNoRows)); + return ADP.Argument(StringsHelper.GetString(Strings.IEnumerableOfSqlDataRecordHasNoRows)); } @@ -876,11 +916,11 @@ internal static Exception IEnumerableOfSqlDataRecordHasNoRows() // internal static Exception BulkLoadMappingInaccessible() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadMappingInaccessible)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadMappingInaccessible)); } internal static Exception BulkLoadMappingsNamesOrOrdinalsOnly() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadMappingsNamesOrOrdinalsOnly)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadMappingsNamesOrOrdinalsOnly)); } internal static Exception BulkLoadCannotConvertValue(Type sourcetype, MetaType metatype, int ordinal, int rowNumber, bool isEncrypted, string columnName, string value, Exception e) { @@ -891,16 +931,16 @@ internal static Exception BulkLoadCannotConvertValue(Type sourcetype, MetaType m } if (rowNumber == -1) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadCannotConvertValueWithoutRowNo, quotedValue, sourcetype.Name, metatype.TypeName, ordinal, columnName), e); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadCannotConvertValueWithoutRowNo, quotedValue, sourcetype.Name, metatype.TypeName, ordinal, columnName), e); } else { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadCannotConvertValue, quotedValue, sourcetype.Name, metatype.TypeName, ordinal, columnName, rowNumber), e); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadCannotConvertValue, quotedValue, sourcetype.Name, metatype.TypeName, ordinal, columnName, rowNumber), e); } } internal static Exception BulkLoadNonMatchingColumnMapping() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadNonMatchingColumnMapping)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadNonMatchingColumnMapping)); } internal static Exception BulkLoadNonMatchingColumnName(string columnName) { @@ -908,79 +948,79 @@ internal static Exception BulkLoadNonMatchingColumnName(string columnName) } internal static Exception BulkLoadNonMatchingColumnName(string columnName, Exception e) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadNonMatchingColumnName, columnName), e); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadNonMatchingColumnName, columnName), e); } internal static Exception BulkLoadNullEmptyColumnName(string paramName) { - return ADP.Argument(string.Format(System.StringsHelper.GetString(Strings.SQL_ParameterCannotBeEmpty), paramName)); + return ADP.Argument(string.Format(StringsHelper.GetString(Strings.SQL_ParameterCannotBeEmpty), paramName)); } internal static Exception BulkLoadUnspecifiedSortOrder() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_BulkLoadUnspecifiedSortOrder)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadUnspecifiedSortOrder)); } internal static Exception BulkLoadInvalidOrderHint() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOrderHint)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOrderHint)); } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { - return ADP.InvalidOperation(string.Format(System.StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInvalidColumn), columnName)); + return ADP.InvalidOperation(string.Format(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInvalidColumn), columnName)); } internal static Exception BulkLoadOrderHintDuplicateColumn(string columnName) { - return ADP.InvalidOperation(string.Format(System.StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintDuplicateColumn), columnName)); + return ADP.InvalidOperation(string.Format(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintDuplicateColumn), columnName)); } internal static Exception BulkLoadStringTooLong(string tableName, string columnName, string truncatedValue) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadStringTooLong, tableName, columnName, truncatedValue)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadStringTooLong, tableName, columnName, truncatedValue)); } internal static Exception BulkLoadInvalidVariantValue() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadInvalidVariantValue)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidVariantValue)); } internal static Exception BulkLoadInvalidTimeout(int timeout) { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_BulkLoadInvalidTimeout, timeout.ToString(CultureInfo.InvariantCulture))); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidTimeout, timeout.ToString(CultureInfo.InvariantCulture))); } internal static Exception BulkLoadExistingTransaction() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadExistingTransaction)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadExistingTransaction)); } internal static Exception BulkLoadNoCollation() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadNoCollation)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadNoCollation)); } internal static Exception BulkLoadConflictingTransactionOption() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQL_BulkLoadConflictingTransactionOption)); + return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadConflictingTransactionOption)); } internal static Exception BulkLoadLcidMismatch(int sourceLcid, string sourceColumnName, int destinationLcid, string destinationColumnName) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.Sql_BulkLoadLcidMismatch, sourceLcid, sourceColumnName, destinationLcid, destinationColumnName)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.Sql_BulkLoadLcidMismatch, sourceLcid, sourceColumnName, destinationLcid, destinationColumnName)); } internal static Exception InvalidOperationInsideEvent() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOperationInsideEvent)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOperationInsideEvent)); } internal static Exception BulkLoadMissingDestinationTable() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadMissingDestinationTable)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadMissingDestinationTable)); } internal static Exception BulkLoadInvalidDestinationTable(string tableName, Exception inner) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadInvalidDestinationTable, tableName), inner); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidDestinationTable, tableName), inner); } internal static Exception BulkLoadBulkLoadNotAllowDBNull(string columnName) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadNotAllowDBNull, columnName)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadNotAllowDBNull, columnName)); } internal static Exception BulkLoadPendingOperation() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BulkLoadPendingOperation)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadPendingOperation)); } internal static Exception InvalidTableDerivedPrecisionForTvp(string columnName, byte precision) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlParameter_InvalidTableDerivedPrecisionForTvp, precision, columnName, System.Data.SqlTypes.SqlDecimal.MaxPrecision)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlParameter_InvalidTableDerivedPrecisionForTvp, precision, columnName, System.Data.SqlTypes.SqlDecimal.MaxPrecision)); } // @@ -988,17 +1028,17 @@ internal static Exception InvalidTableDerivedPrecisionForTvp(string columnName, // internal static Exception ConnectionDoomed() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ConnectionDoomed)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ConnectionDoomed)); } internal static Exception OpenResultCountExceeded() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_OpenResultCountExceeded)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_OpenResultCountExceeded)); } internal static Exception UnsupportedSysTxForGlobalTransactions() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_UnsupportedSysTxVersion)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_UnsupportedSysTxVersion)); } internal static readonly byte[] AttentionHeader = new byte[] { @@ -1023,7 +1063,7 @@ internal static Exception UnsupportedSysTxForGlobalTransactions() /// internal static Exception MultiSubnetFailoverWithFailoverPartner(bool serverProvidedFailoverPartner, SqlInternalConnectionTds internalConnection) { - string msg = System.StringsHelper.GetString(Strings.SQLMSF_FailoverPartnerNotSupported); + string msg = StringsHelper.GetString(Strings.SQLMSF_FailoverPartnerNotSupported); if (serverProvidedFailoverPartner) { // Replacing InvalidOperation with SQL exception @@ -1063,13 +1103,13 @@ internal static Exception MultiSubnetFailoverWithNonTcpProtocol() internal static Exception ROR_FailoverNotSupportedConnString() { - return ADP.Argument(System.StringsHelper.GetString(Strings.SQLROR_FailoverNotSupported)); + return ADP.Argument(StringsHelper.GetString(Strings.SQLROR_FailoverNotSupported)); } internal static Exception ROR_FailoverNotSupportedServer(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (System.StringsHelper.GetString(Strings.SQLROR_FailoverNotSupported)), "", 0)); + errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (StringsHelper.GetString(Strings.SQLROR_FailoverNotSupported)), "", 0)); SqlException exc = SqlException.CreateException(errors, null, internalConnection); exc._doNotReconnect = true; return exc; @@ -1078,7 +1118,7 @@ internal static Exception ROR_FailoverNotSupportedServer(SqlInternalConnectionTd internal static Exception ROR_RecursiveRoutingNotSupported(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (System.StringsHelper.GetString(Strings.SQLROR_RecursiveRoutingNotSupported)), "", 0)); + errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (StringsHelper.GetString(Strings.SQLROR_RecursiveRoutingNotSupported)), "", 0)); SqlException exc = SqlException.CreateException(errors, null, internalConnection); exc._doNotReconnect = true; return exc; @@ -1087,7 +1127,7 @@ internal static Exception ROR_RecursiveRoutingNotSupported(SqlInternalConnection internal static Exception ROR_UnexpectedRoutingInfo(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (System.StringsHelper.GetString(Strings.SQLROR_UnexpectedRoutingInfo)), "", 0)); + errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (StringsHelper.GetString(Strings.SQLROR_UnexpectedRoutingInfo)), "", 0)); SqlException exc = SqlException.CreateException(errors, null, internalConnection); exc._doNotReconnect = true; return exc; @@ -1096,7 +1136,7 @@ internal static Exception ROR_UnexpectedRoutingInfo(SqlInternalConnectionTds int internal static Exception ROR_InvalidRoutingInfo(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (System.StringsHelper.GetString(Strings.SQLROR_InvalidRoutingInfo)), "", 0)); + errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (StringsHelper.GetString(Strings.SQLROR_InvalidRoutingInfo)), "", 0)); SqlException exc = SqlException.CreateException(errors, null, internalConnection); exc._doNotReconnect = true; return exc; @@ -1105,7 +1145,7 @@ internal static Exception ROR_InvalidRoutingInfo(SqlInternalConnectionTds intern internal static Exception ROR_TimeoutAfterRoutingInfo(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (System.StringsHelper.GetString(Strings.SQLROR_TimeoutAfterRoutingInfo)), "", 0)); + errors.Add(new SqlError(0, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, null, (StringsHelper.GetString(Strings.SQLROR_TimeoutAfterRoutingInfo)), "", 0)); SqlException exc = SqlException.CreateException(errors, null, internalConnection); exc._doNotReconnect = true; return exc; @@ -1133,7 +1173,7 @@ internal static SqlException CR_ReconnectionCancelled() internal static Exception CR_NextAttemptWillExceedQueryTimeout(SqlException innerException, Guid connectionId) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQLCR_NextAttemptWillExceedQueryTimeout), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQLCR_NextAttemptWillExceedQueryTimeout), "", 0)); SqlException exc = SqlException.CreateException(errors, "", connectionId, innerException); return exc; } @@ -1141,7 +1181,7 @@ internal static Exception CR_NextAttemptWillExceedQueryTimeout(SqlException inne internal static Exception CR_EncryptionChanged(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQLCR_EncryptionChanged), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQLCR_EncryptionChanged), "", 0)); SqlException exc = SqlException.CreateException(errors, "", internalConnection); return exc; } @@ -1149,7 +1189,7 @@ internal static Exception CR_EncryptionChanged(SqlInternalConnectionTds internal internal static SqlException CR_AllAttemptsFailed(SqlException innerException, Guid connectionId) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQLCR_AllAttemptsFailed), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQLCR_AllAttemptsFailed), "", 0)); SqlException exc = SqlException.CreateException(errors, "", connectionId, innerException); return exc; } @@ -1157,7 +1197,7 @@ internal static SqlException CR_AllAttemptsFailed(SqlException innerException, G internal static SqlException CR_NoCRAckAtReconnection(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQLCR_NoCRAckAtReconnection), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQLCR_NoCRAckAtReconnection), "", 0)); SqlException exc = SqlException.CreateException(errors, "", internalConnection); return exc; } @@ -1165,7 +1205,7 @@ internal static SqlException CR_NoCRAckAtReconnection(SqlInternalConnectionTds i internal static SqlException CR_TDSVersionNotPreserved(SqlInternalConnectionTds internalConnection) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQLCR_TDSVestionNotPreserved), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQLCR_TDSVestionNotPreserved), "", 0)); SqlException exc = SqlException.CreateException(errors, "", internalConnection); return exc; } @@ -1173,7 +1213,7 @@ internal static SqlException CR_TDSVersionNotPreserved(SqlInternalConnectionTds internal static SqlException CR_UnrecoverableServer(Guid connectionId) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQLCR_UnrecoverableServer), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQLCR_UnrecoverableServer), "", 0)); SqlException exc = SqlException.CreateException(errors, "", connectionId); return exc; } @@ -1181,22 +1221,22 @@ internal static SqlException CR_UnrecoverableServer(Guid connectionId) internal static SqlException CR_UnrecoverableClient(Guid connectionId) { SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQLCR_UnrecoverableClient), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQLCR_UnrecoverableClient), "", 0)); SqlException exc = SqlException.CreateException(errors, "", connectionId); return exc; } internal static Exception StreamWriteNotSupported() { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_StreamWriteNotSupported)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_StreamWriteNotSupported)); } internal static Exception StreamReadNotSupported() { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_StreamReadNotSupported)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_StreamReadNotSupported)); } internal static Exception StreamSeekNotSupported() { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_StreamSeekNotSupported)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_StreamSeekNotSupported)); } internal static System.Data.SqlTypes.SqlNullValueException SqlNullValue() { @@ -1205,31 +1245,31 @@ internal static System.Data.SqlTypes.SqlNullValueException SqlNullValue() } internal static Exception SubclassMustOverride() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SqlMisc_SubclassMustOverride)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SqlMisc_SubclassMustOverride)); } // ProjectK\CoreCLR specific errors internal static Exception UnsupportedKeyword(string keyword) { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_UnsupportedKeyword, keyword)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_UnsupportedKeyword, keyword)); } internal static Exception NetworkLibraryKeywordNotSupported() { - return ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_NetworkLibraryNotSupported)); + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_NetworkLibraryNotSupported)); } internal static Exception UnsupportedFeatureAndToken(SqlInternalConnectionTds internalConnection, string token) { - var innerException = ADP.NotSupported(System.StringsHelper.GetString(Strings.SQL_UnsupportedToken, token)); + var innerException = ADP.NotSupported(StringsHelper.GetString(Strings.SQL_UnsupportedToken, token)); SqlErrorCollection errors = new SqlErrorCollection(); - errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, System.StringsHelper.GetString(Strings.SQL_UnsupportedFeature), "", 0)); + errors.Add(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, null, StringsHelper.GetString(Strings.SQL_UnsupportedFeature), "", 0)); SqlException exc = SqlException.CreateException(errors, "", internalConnection, innerException); return exc; } internal static Exception BatchedUpdatesNotAvailableOnContextConnection() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_BatchedUpdatesNotAvailableOnContextConnection)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BatchedUpdatesNotAvailableOnContextConnection)); } internal static Exception Azure_ManagedIdentityException(string msg) { @@ -1248,39 +1288,39 @@ internal static Exception Azure_ManagedIdentityException(string msg) internal static Exception InvalidKeyEncryptionAlgorithm(string encryptionAlgorithm, string validEncryptionAlgorithm, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidKeyEncryptionAlgorithmSysErr : Strings.TCE_InvalidKeyEncryptionAlgorithm; - return ADP.Argument(System.StringsHelper.GetString(message, encryptionAlgorithm, validEncryptionAlgorithm), TdsEnums.TCE_PARAM_ENCRYPTION_ALGORITHM); + return ADP.Argument(StringsHelper.GetString(message, encryptionAlgorithm, validEncryptionAlgorithm), TdsEnums.TCE_PARAM_ENCRYPTION_ALGORITHM); } internal static Exception NullKeyEncryptionAlgorithm(bool isSystemOp) { string message = isSystemOp ? Strings.TCE_NullKeyEncryptionAlgorithmSysErr : Strings.TCE_NullKeyEncryptionAlgorithm; - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTION_ALGORITHM, System.StringsHelper.GetString(message)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTION_ALGORITHM, StringsHelper.GetString(message)); } internal static Exception EmptyColumnEncryptionKey() { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_EmptyColumnEncryptionKey), TdsEnums.TCE_PARAM_COLUMNENCRYPTION_KEY); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_EmptyColumnEncryptionKey), TdsEnums.TCE_PARAM_COLUMNENCRYPTION_KEY); } internal static Exception NullColumnEncryptionKey() { - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_COLUMNENCRYPTION_KEY, System.StringsHelper.GetString(Strings.TCE_NullColumnEncryptionKey)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_COLUMNENCRYPTION_KEY, StringsHelper.GetString(Strings.TCE_NullColumnEncryptionKey)); } internal static Exception EmptyEncryptedColumnEncryptionKey() { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_EmptyEncryptedColumnEncryptionKey), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_EmptyEncryptedColumnEncryptionKey), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception NullEncryptedColumnEncryptionKey() { - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTED_CEK, System.StringsHelper.GetString(Strings.TCE_NullEncryptedColumnEncryptionKey)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTED_CEK, StringsHelper.GetString(Strings.TCE_NullEncryptedColumnEncryptionKey)); } internal static Exception LargeCertificatePathLength(int actualLength, int maxLength, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_LargeCertificatePathLengthSysErr : Strings.TCE_LargeCertificatePathLength; - return ADP.Argument(System.StringsHelper.GetString(message, actualLength, maxLength), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, actualLength, maxLength), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } @@ -1288,250 +1328,250 @@ internal static Exception NullCertificatePath(string[] validLocations, bool isSy { Debug.Assert(2 == validLocations.Length); string message = isSystemOp ? Strings.TCE_NullCertificatePathSysErr : Strings.TCE_NullCertificatePath; - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_MASTERKEY_PATH, System.StringsHelper.GetString(message, validLocations[0], validLocations[1], @"/")); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_MASTERKEY_PATH, StringsHelper.GetString(message, validLocations[0], validLocations[1], @"/")); } internal static Exception NullCspKeyPath(bool isSystemOp) { string message = isSystemOp ? Strings.TCE_NullCspPathSysErr : Strings.TCE_NullCspPath; - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_MASTERKEY_PATH, System.StringsHelper.GetString(message, @"/")); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_MASTERKEY_PATH, StringsHelper.GetString(message, @"/")); } internal static Exception NullCngKeyPath(bool isSystemOp) { string message = isSystemOp ? Strings.TCE_NullCngPathSysErr : Strings.TCE_NullCngPath; - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_MASTERKEY_PATH, System.StringsHelper.GetString(message, @"/")); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_MASTERKEY_PATH, StringsHelper.GetString(message, @"/")); } internal static Exception InvalidCertificatePath(string actualCertificatePath, string[] validLocations, bool isSystemOp) { Debug.Assert(2 == validLocations.Length); string message = isSystemOp ? Strings.TCE_InvalidCertificatePathSysErr : Strings.TCE_InvalidCertificatePath; - return ADP.Argument(System.StringsHelper.GetString(message, actualCertificatePath, validLocations[0], validLocations[1], @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, actualCertificatePath, validLocations[0], validLocations[1], @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidCspPath(string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidCspPathSysErr : Strings.TCE_InvalidCspPath; - return ADP.Argument(System.StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidCngPath(string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidCngPathSysErr : Strings.TCE_InvalidCngPath; - return ADP.Argument(System.StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception EmptyCspName(string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_EmptyCspNameSysErr : Strings.TCE_EmptyCspName; - return ADP.Argument(System.StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception EmptyCngName(string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_EmptyCngNameSysErr : Strings.TCE_EmptyCngName; - return ADP.Argument(System.StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception EmptyCspKeyId(string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_EmptyCspKeyIdSysErr : Strings.TCE_EmptyCspKeyId; - return ADP.Argument(System.StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception EmptyCngKeyId(string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_EmptyCngKeyIdSysErr : Strings.TCE_EmptyCngKeyId; - return ADP.Argument(System.StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, masterKeyPath, @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidCspName(string cspName, string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidCspNameSysErr : Strings.TCE_InvalidCspName; - return ADP.Argument(System.StringsHelper.GetString(message, cspName, masterKeyPath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, cspName, masterKeyPath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidCspKeyIdentifier(string keyIdentifier, string masterKeyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidCspKeyIdSysErr : Strings.TCE_InvalidCspKeyId; - return ADP.Argument(System.StringsHelper.GetString(message, keyIdentifier, masterKeyPath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, keyIdentifier, masterKeyPath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidCngKey(string masterKeyPath, string cngProviderName, string keyIdentifier, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidCngKeySysErr : Strings.TCE_InvalidCngKey; - return ADP.Argument(System.StringsHelper.GetString(message, masterKeyPath, cngProviderName, keyIdentifier), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, masterKeyPath, cngProviderName, keyIdentifier), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidCertificateLocation(string certificateLocation, string certificatePath, string[] validLocations, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidCertificateLocationSysErr : Strings.TCE_InvalidCertificateLocation; - return ADP.Argument(System.StringsHelper.GetString(message, certificateLocation, certificatePath, validLocations[0], validLocations[1], @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, certificateLocation, certificatePath, validLocations[0], validLocations[1], @"/"), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidCertificateStore(string certificateStore, string certificatePath, string validCertificateStore, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_InvalidCertificateStoreSysErr : Strings.TCE_InvalidCertificateStore; - return ADP.Argument(System.StringsHelper.GetString(message, certificateStore, certificatePath, validCertificateStore), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, certificateStore, certificatePath, validCertificateStore), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception EmptyCertificateThumbprint(string certificatePath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_EmptyCertificateThumbprintSysErr : Strings.TCE_EmptyCertificateThumbprint; - return ADP.Argument(System.StringsHelper.GetString(message, certificatePath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, certificatePath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception CertificateNotFound(string thumbprint, string certificateLocation, string certificateStore, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_CertificateNotFoundSysErr : Strings.TCE_CertificateNotFound; - return ADP.Argument(System.StringsHelper.GetString(message, thumbprint, certificateLocation, certificateStore), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, thumbprint, certificateLocation, certificateStore), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } internal static Exception InvalidAlgorithmVersionInEncryptedCEK(byte actual, byte expected) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidAlgorithmVersionInEncryptedCEK, actual.ToString(@"X2"), expected.ToString(@"X2")), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidAlgorithmVersionInEncryptedCEK, actual.ToString(@"X2"), expected.ToString(@"X2")), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidCiphertextLengthInEncryptedCEK(int actual, int expected, string certificateName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEK, actual, expected, certificateName), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEK, actual, expected, certificateName), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidCiphertextLengthInEncryptedCEKCsp(int actual, int expected, string masterKeyPath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEKCsp, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEKCsp, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidCiphertextLengthInEncryptedCEKCng(int actual, int expected, string masterKeyPath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEKCng, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEKCng, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidSignatureInEncryptedCEK(int actual, int expected, string masterKeyPath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEK, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEK, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidSignatureInEncryptedCEKCsp(int actual, int expected, string masterKeyPath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEKCsp, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEKCsp, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidSignatureInEncryptedCEKCng(int actual, int expected, string masterKeyPath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEKCng, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEKCng, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidCertificateSignature(string certificatePath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidCertificateSignature, certificatePath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCertificateSignature, certificatePath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception InvalidSignature(string masterKeyPath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidSignature, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidSignature, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK); } internal static Exception CertificateWithNoPrivateKey(string keyPath, bool isSystemOp) { string message = isSystemOp ? Strings.TCE_CertificateWithNoPrivateKeySysErr : Strings.TCE_CertificateWithNoPrivateKey; - return ADP.Argument(System.StringsHelper.GetString(message, keyPath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); + return ADP.Argument(StringsHelper.GetString(message, keyPath), TdsEnums.TCE_PARAM_MASTERKEY_PATH); } #endregion Always Encrypted - Certificate Store Provider Errors #region Always Encrypted - Cryptographic Algorithms Error messages internal static Exception NullPlainText() { - return ADP.ArgumentNull(System.StringsHelper.GetString(Strings.TCE_NullPlainText)); + return ADP.ArgumentNull(StringsHelper.GetString(Strings.TCE_NullPlainText)); } internal static Exception NullCipherText() { - return ADP.ArgumentNull(System.StringsHelper.GetString(Strings.TCE_NullCipherText)); + return ADP.ArgumentNull(StringsHelper.GetString(Strings.TCE_NullCipherText)); } internal static Exception NullColumnEncryptionAlgorithm(string supportedAlgorithms) { - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTION_ALGORITHM, System.StringsHelper.GetString(Strings.TCE_NullColumnEncryptionAlgorithm, supportedAlgorithms)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTION_ALGORITHM, StringsHelper.GetString(Strings.TCE_NullColumnEncryptionAlgorithm, supportedAlgorithms)); } internal static Exception NullColumnEncryptionKeySysErr() { - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTIONKEY, System.StringsHelper.GetString(Strings.TCE_NullColumnEncryptionKeySysErr)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_ENCRYPTIONKEY, StringsHelper.GetString(Strings.TCE_NullColumnEncryptionKeySysErr)); } internal static Exception InvalidKeySize(string algorithmName, int actualKeylength, int expectedLength) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidKeySize, algorithmName, actualKeylength, expectedLength), TdsEnums.TCE_PARAM_ENCRYPTIONKEY); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidKeySize, algorithmName, actualKeylength, expectedLength), TdsEnums.TCE_PARAM_ENCRYPTIONKEY); } internal static Exception InvalidEncryptionType(string algorithmName, SqlClientEncryptionType encryptionType, params SqlClientEncryptionType[] validEncryptionTypes) { const string valueSeparator = @", "; - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidEncryptionType, algorithmName, encryptionType.ToString(), string.Join(valueSeparator, validEncryptionTypes.Select((validEncryptionType => @"'" + validEncryptionType + @"'")))), TdsEnums.TCE_PARAM_ENCRYPTIONTYPE); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidEncryptionType, algorithmName, encryptionType.ToString(), string.Join(valueSeparator, validEncryptionTypes.Select((validEncryptionType => @"'" + validEncryptionType + @"'")))), TdsEnums.TCE_PARAM_ENCRYPTIONTYPE); } internal static Exception InvalidCipherTextSize(int actualSize, int minimumSize) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidCipherTextSize, actualSize, minimumSize), TdsEnums.TCE_PARAM_CIPHERTEXT); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCipherTextSize, actualSize, minimumSize), TdsEnums.TCE_PARAM_CIPHERTEXT); } internal static Exception InvalidAlgorithmVersion(byte actual, byte expected) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidAlgorithmVersion, actual.ToString(@"X2"), expected.ToString(@"X2")), TdsEnums.TCE_PARAM_CIPHERTEXT); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidAlgorithmVersion, actual.ToString(@"X2"), expected.ToString(@"X2")), TdsEnums.TCE_PARAM_CIPHERTEXT); } internal static Exception InvalidAuthenticationTag() { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidAuthenticationTag), TdsEnums.TCE_PARAM_CIPHERTEXT); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidAuthenticationTag), TdsEnums.TCE_PARAM_CIPHERTEXT); } #endregion Always Encrypted - Cryptographic Algorithms Error messages #region Always Encrypted - Errors from sp_describe_parameter_encryption internal static Exception UnexpectedDescribeParamFormatParameterMetadata() { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UnexpectedDescribeParamFormatParameterMetadata, "sp_describe_parameter_encryption")); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UnexpectedDescribeParamFormatParameterMetadata, "sp_describe_parameter_encryption")); } internal static Exception UnexpectedDescribeParamFormatAttestationInfo(string enclaveType) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UnexpectedDescribeParamFormatAttestationInfo, "sp_describe_parameter_encryption", enclaveType)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UnexpectedDescribeParamFormatAttestationInfo, "sp_describe_parameter_encryption", enclaveType)); } internal static Exception InvalidEncryptionKeyOrdinalEnclaveMetadata(int ordinal, int maxOrdinal) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata, ordinal, maxOrdinal)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_InvalidEncryptionKeyOrdinalEnclaveMetadata, ordinal, maxOrdinal)); } internal static Exception InvalidEncryptionKeyOrdinalParameterMetadata(int ordinal, int maxOrdinal) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_InvalidEncryptionKeyOrdinalParameterMetadata, ordinal, maxOrdinal)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_InvalidEncryptionKeyOrdinalParameterMetadata, ordinal, maxOrdinal)); } public static Exception MultipleRowsReturnedForAttestationInfo() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_MultipleRowsReturnedForAttestationInfo, "sp_describe_parameter_encryption")); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_MultipleRowsReturnedForAttestationInfo, "sp_describe_parameter_encryption")); } internal static Exception ParamEncryptionMetadataMissing(string paramName, string procedureName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_ParamEncryptionMetaDataMissing, "sp_describe_parameter_encryption", paramName, procedureName)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_ParamEncryptionMetaDataMissing, "sp_describe_parameter_encryption", paramName, procedureName)); } internal static Exception ProcEncryptionMetadataMissing(string procedureName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_ProcEncryptionMetaDataMissing, "sp_describe_parameter_encryption", procedureName)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_ProcEncryptionMetaDataMissing, "sp_describe_parameter_encryption", procedureName)); } internal static Exception UnableToVerifyColumnMasterKeySignature(Exception innerException) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_UnableToVerifyColumnMasterKeySignature, innerException.Message), innerException); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_UnableToVerifyColumnMasterKeySignature, innerException.Message), innerException); } internal static Exception ColumnMasterKeySignatureVerificationFailed(string cmkPath) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_ColumnMasterKeySignatureVerificationFailed, cmkPath)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_ColumnMasterKeySignatureVerificationFailed, cmkPath)); } internal static Exception InvalidKeyStoreProviderName(string providerName, List systemProviders, List customProviders) @@ -1539,22 +1579,22 @@ internal static Exception InvalidKeyStoreProviderName(string providerName, List< const string valueSeparator = @", "; string systemProviderStr = string.Join(valueSeparator, systemProviders.Select(provider => $"'{provider}'")); string customProviderStr = string.Join(valueSeparator, customProviders.Select(provider => $"'{provider}'")); - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidKeyStoreProviderName, providerName, systemProviderStr, customProviderStr)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidKeyStoreProviderName, providerName, systemProviderStr, customProviderStr)); } internal static Exception ParamInvalidForceColumnEncryptionSetting(string paramName, string procedureName) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_ParamInvalidForceColumnEncryptionSetting, TdsEnums.TCE_PARAM_FORCE_COLUMN_ENCRYPTION, paramName, procedureName, "SqlParameter")); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_ParamInvalidForceColumnEncryptionSetting, TdsEnums.TCE_PARAM_FORCE_COLUMN_ENCRYPTION, paramName, procedureName, "SqlParameter")); } internal static Exception ParamUnExpectedEncryptionMetadata(string paramName, string procedureName) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_ParamUnExpectedEncryptionMetadata, paramName, procedureName, TdsEnums.TCE_PARAM_FORCE_COLUMN_ENCRYPTION, "SqlParameter")); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_ParamUnExpectedEncryptionMetadata, paramName, procedureName, TdsEnums.TCE_PARAM_FORCE_COLUMN_ENCRYPTION, "SqlParameter")); } internal static Exception ColumnMasterKeySignatureNotFound(string cmkPath) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_ColumnMasterKeySignatureNotFound, cmkPath)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_ColumnMasterKeySignatureNotFound, cmkPath)); } #endregion Always Encrypted - Errors from sp_describe_parameter_encryption @@ -1562,42 +1602,42 @@ internal static Exception ColumnMasterKeySignatureNotFound(string cmkPath) internal static Exception ExceptionWhenGeneratingEnclavePackage(Exception innerException) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_ExceptionWhenGeneratingEnclavePackage, innerException.Message), innerException); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_ExceptionWhenGeneratingEnclavePackage, innerException.Message), innerException); } internal static Exception FailedToEncryptRegisterRulesBytePackage(Exception innerException) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_FailedToEncryptRegisterRulesBytePackage, innerException.Message), innerException); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_FailedToEncryptRegisterRulesBytePackage, innerException.Message), innerException); } internal static Exception InvalidKeyIdUnableToCastToUnsignedShort(int keyId, Exception innerException) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidKeyIdUnableToCastToUnsignedShort, keyId, innerException.Message), innerException); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidKeyIdUnableToCastToUnsignedShort, keyId, innerException.Message), innerException); } internal static Exception InvalidDatabaseIdUnableToCastToUnsignedInt(int databaseId, Exception innerException) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidDatabaseIdUnableToCastToUnsignedInt, databaseId, innerException.Message), innerException); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidDatabaseIdUnableToCastToUnsignedInt, databaseId, innerException.Message), innerException); } internal static Exception InvalidAttestationParameterUnableToConvertToUnsignedInt(string variableName, int intValue, string enclaveType, Exception innerException) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt, enclaveType, intValue, variableName, innerException.Message), innerException); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidAttestationParameterUnableToConvertToUnsignedInt, enclaveType, intValue, variableName, innerException.Message), innerException); } internal static Exception OffsetOutOfBounds(string argument, string type, string method) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_OffsetOutOfBounds, type, method)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_OffsetOutOfBounds, type, method)); } internal static Exception InsufficientBuffer(string argument, string type, string method) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InsufficientBuffer, argument, type, method)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InsufficientBuffer, argument, type, method)); } internal static Exception ColumnEncryptionKeysNotFound() { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_ColumnEncryptionKeysNotFound)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_ColumnEncryptionKeysNotFound)); } #endregion Always Encrypted - Errors from secure channel Communication @@ -1605,29 +1645,44 @@ internal static Exception ColumnEncryptionKeysNotFound() #region Always Encrypted - Errors when performing attestation internal static Exception AttestationInfoNotReturnedFromSqlServer(string enclaveType, string enclaveAttestationUrl) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_AttestationInfoNotReturnedFromSQLServer, enclaveType, enclaveAttestationUrl)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_AttestationInfoNotReturnedFromSQLServer, enclaveType, enclaveAttestationUrl)); } + + internal static SqlException AttestationFailed(string errorMessage, Exception innerException = null) + { + SqlErrorCollection errors = new(); + errors.Add(new SqlError( + infoNumber: 0, + errorState: 0, + errorClass: 0, + server: null, + errorMessage, + procedure: string.Empty, + lineNumber: 0)); + return SqlException.CreateException(errors, serverVersion: string.Empty, Guid.Empty, innerException); + } + #endregion Always Encrypted - Errors when performing attestation #region Always Encrypted - Errors when establishing secure channel internal static Exception NullArgumentInConstructorInternal(string argumentName, string objectUnderConstruction) { - return ADP.ArgumentNull(argumentName, System.StringsHelper.GetString(Strings.TCE_NullArgumentInConstructorInternal, argumentName, objectUnderConstruction)); + return ADP.ArgumentNull(argumentName, StringsHelper.GetString(Strings.TCE_NullArgumentInConstructorInternal, argumentName, objectUnderConstruction)); } internal static Exception EmptyArgumentInConstructorInternal(string argumentName, string objectUnderConstruction) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_EmptyArgumentInConstructorInternal, argumentName, objectUnderConstruction)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_EmptyArgumentInConstructorInternal, argumentName, objectUnderConstruction)); } internal static Exception NullArgumentInternal(string argumentName, string type, string method) { - return ADP.ArgumentNull(argumentName, System.StringsHelper.GetString(Strings.TCE_NullArgumentInternal, argumentName, type, method)); + return ADP.ArgumentNull(argumentName, StringsHelper.GetString(Strings.TCE_NullArgumentInternal, argumentName, type, method)); } internal static Exception EmptyArgumentInternal(string argumentName, string type, string method) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_EmptyArgumentInternal, argumentName, type, method)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_EmptyArgumentInternal, argumentName, type, method)); } #endregion Always Encrypted - Errors when establishing secure channel @@ -1635,47 +1690,47 @@ internal static Exception EmptyArgumentInternal(string argumentName, string type internal static Exception CannotGetSqlColumnEncryptionEnclaveProviderConfig(Exception innerException) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig, innerException.Message), innerException); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_CannotGetSqlColumnEncryptionEnclaveProviderConfig, innerException.Message), innerException); } internal static Exception CannotCreateSqlColumnEncryptionEnclaveProvider(string providerName, string type, Exception innerException) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_CannotCreateSqlColumnEncryptionEnclaveProvider, providerName, type, innerException.Message), innerException); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_CannotCreateSqlColumnEncryptionEnclaveProvider, providerName, type, innerException.Message), innerException); } internal static Exception SqlColumnEncryptionEnclaveProviderNameCannotBeEmpty() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_SqlColumnEncryptionEnclaveProviderNameCannotBeEmpty)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_SqlColumnEncryptionEnclaveProviderNameCannotBeEmpty)); } internal static Exception NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe(string enclaveType) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe, "sp_describe_parameter_encryption", enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe, "sp_describe_parameter_encryption", enclaveType)); } internal static Exception NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage(string enclaveType) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage, enclaveType)); } internal static Exception EnclaveTypeNullForEnclaveBasedQuery() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_EnclaveTypeNullForEnclaveBasedQuery)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveTypeNullForEnclaveBasedQuery)); } internal static Exception EnclaveProvidersNotConfiguredForEnclaveBasedQuery() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveProvidersNotConfiguredForEnclaveBasedQuery)); } internal static Exception EnclaveProviderNotFound(string enclaveType) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_EnclaveProviderNotFound, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveProviderNotFound, enclaveType)); } internal static Exception NullEnclaveSessionReturnedFromProvider(string enclaveType, string attestationUrl) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_NullEnclaveSessionReturnedFromProvider, enclaveType, attestationUrl)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_NullEnclaveSessionReturnedFromProvider, enclaveType, attestationUrl)); } #endregion Always Encrypted - Enclave provider/configuration errors @@ -1708,17 +1763,17 @@ internal static Exception GetExceptionArray(string serverName, string errorMessa internal static Exception ColumnDecryptionFailed(string columnName, string serverName, Exception e) { - return GetExceptionArray(serverName, System.StringsHelper.GetString(Strings.TCE_ColumnDecryptionFailed, columnName), e); + return GetExceptionArray(serverName, StringsHelper.GetString(Strings.TCE_ColumnDecryptionFailed, columnName), e); } internal static Exception ParamEncryptionFailed(string paramName, string serverName, Exception e) { - return GetExceptionArray(serverName, System.StringsHelper.GetString(Strings.TCE_ParamEncryptionFailed, paramName), e); + return GetExceptionArray(serverName, StringsHelper.GetString(Strings.TCE_ParamEncryptionFailed, paramName), e); } internal static Exception ParamDecryptionFailed(string paramName, string serverName, Exception e) { - return GetExceptionArray(serverName, System.StringsHelper.GetString(Strings.TCE_ParamDecryptionFailed, paramName), e); + return GetExceptionArray(serverName, StringsHelper.GetString(Strings.TCE_ParamDecryptionFailed, paramName), e); } #endregion Always Encrypted - Generic toplevel failures @@ -1726,17 +1781,17 @@ internal static Exception ParamDecryptionFailed(string paramName, string serverN internal static Exception UnknownColumnEncryptionAlgorithm(string algorithmName, string supportedAlgorithms) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UnknownColumnEncryptionAlgorithm, algorithmName, supportedAlgorithms)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UnknownColumnEncryptionAlgorithm, algorithmName, supportedAlgorithms)); } internal static Exception UnknownColumnEncryptionAlgorithmId(int algoId, string supportAlgorithmIds) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UnknownColumnEncryptionAlgorithmId, algoId, supportAlgorithmIds), TdsEnums.TCE_PARAM_CIPHER_ALGORITHM_ID); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UnknownColumnEncryptionAlgorithmId, algoId, supportAlgorithmIds), TdsEnums.TCE_PARAM_CIPHER_ALGORITHM_ID); } internal static Exception UnsupportedNormalizationVersion(byte version) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UnsupportedNormalizationVersion, version, "'1'", "SQL Server")); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UnsupportedNormalizationVersion, version, "'1'", "SQL Server")); } internal static Exception UnrecognizedKeyStoreProviderName(string providerName, List systemProviders, List customProviders) @@ -1744,12 +1799,12 @@ internal static Exception UnrecognizedKeyStoreProviderName(string providerName, const string valueSeparator = @", "; string systemProviderStr = string.Join(valueSeparator, systemProviders.Select(provider => @"'" + provider + @"'")); string customProviderStr = string.Join(valueSeparator, customProviders.Select(provider => @"'" + provider + @"'")); - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UnrecognizedKeyStoreProviderName, providerName, systemProviderStr, customProviderStr)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UnrecognizedKeyStoreProviderName, providerName, systemProviderStr, customProviderStr)); } internal static Exception InvalidDataTypeForEncryptedParameter(string parameterName, int actualDataType, int expectedDataType) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_NullProviderValue, parameterName, actualDataType, expectedDataType)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_NullProviderValue, parameterName, actualDataType, expectedDataType)); } internal static Exception KeyDecryptionFailed(string providerName, string keyHex, Exception e) @@ -1757,57 +1812,57 @@ internal static Exception KeyDecryptionFailed(string providerName, string keyHex if (providerName.Equals(SqlColumnEncryptionCertificateStoreProvider.ProviderName)) { - return GetExceptionArray(null, System.StringsHelper.GetString(Strings.TCE_KeyDecryptionFailedCertStore, providerName, keyHex), e); + return GetExceptionArray(null, StringsHelper.GetString(Strings.TCE_KeyDecryptionFailedCertStore, providerName, keyHex), e); } else { - return GetExceptionArray(null, System.StringsHelper.GetString(Strings.TCE_KeyDecryptionFailed, providerName, keyHex), e); + return GetExceptionArray(null, StringsHelper.GetString(Strings.TCE_KeyDecryptionFailed, providerName, keyHex), e); } } internal static Exception UntrustedKeyPath(string keyPath, string serverName) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UntrustedKeyPath, keyPath, serverName)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UntrustedKeyPath, keyPath, serverName)); } internal static Exception UnsupportedDatatypeEncryption(string dataType) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_UnsupportedDatatype, dataType)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_UnsupportedDatatype, dataType)); } internal static Exception ThrowDecryptionFailed(string keyStr, string valStr, Exception e) { - return GetExceptionArray(null, System.StringsHelper.GetString(Strings.TCE_DecryptionFailed, keyStr, valStr), e); + return GetExceptionArray(null, StringsHelper.GetString(Strings.TCE_DecryptionFailed, keyStr, valStr), e); } internal static Exception NullEnclaveSessionDuringQueryExecution(string enclaveType, string enclaveAttestationUrl) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_NullEnclaveSessionDuringQueryExecution, enclaveType, enclaveAttestationUrl)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_NullEnclaveSessionDuringQueryExecution, enclaveType, enclaveAttestationUrl)); } internal static Exception NullEnclavePackageForEnclaveBasedQuery(string enclaveType, string enclaveAttestationUrl) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_NullEnclavePackageForEnclaveBasedQuery, enclaveType, enclaveAttestationUrl)); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_NullEnclavePackageForEnclaveBasedQuery, enclaveType, enclaveAttestationUrl)); } internal static Exception EnclaveProviderNotFound(string enclaveType, string attestationProtocol) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_EnclaveProviderNotFound, enclaveType, attestationProtocol)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveProviderNotFound, enclaveType, attestationProtocol)); } internal static Exception EnclaveTypeNotSupported(string enclaveType) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_EnclaveTypeNotSupported, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveTypeNotSupported, enclaveType)); } internal static Exception AttestationProtocolNotSupportEnclaveType(string attestationProtocolStr, string enclaveType) { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupportEnclaveType, attestationProtocolStr, enclaveType)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupportEnclaveType, attestationProtocolStr, enclaveType)); } internal static Exception AttestationProtocolNotSpecifiedForGeneratingEnclavePackage() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePackage)); } #endregion Always Encrypted - Client side query processing errors @@ -1816,27 +1871,27 @@ internal static Exception AttestationProtocolNotSpecifiedForGeneratingEnclavePac internal static Exception TceNotSupported() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_NotSupportedByServer, "SQL Server")); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_NotSupportedByServer, "SQL Server")); } internal static Exception EnclaveComputationsNotSupported() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_EnclaveComputationsNotSupported)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveComputationsNotSupported)); } internal static Exception AttestationURLNotSupported() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_AttestationURLNotSupported)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationURLNotSupported)); } internal static Exception AttestationProtocolNotSupported() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupported)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupported)); } internal static Exception EnclaveTypeNotReturned() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_EnclaveTypeNotReturned)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveTypeNotReturned)); } #endregion Always Encrypted - SQL connection related error messages @@ -1844,27 +1899,27 @@ internal static Exception EnclaveTypeNotReturned() internal static Exception CanOnlyCallOnce() { - return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.TCE_CanOnlyCallOnce)); + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_CanOnlyCallOnce)); } internal static Exception NullCustomKeyStoreProviderDictionary() { - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS, System.StringsHelper.GetString(Strings.TCE_NullCustomKeyStoreProviderDictionary)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS, StringsHelper.GetString(Strings.TCE_NullCustomKeyStoreProviderDictionary)); } internal static Exception InvalidCustomKeyStoreProviderName(string providerName, string prefix) { - return ADP.Argument(System.StringsHelper.GetString(Strings.TCE_InvalidCustomKeyStoreProviderName, providerName, prefix), TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS); + return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCustomKeyStoreProviderName, providerName, prefix), TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS); } internal static Exception NullProviderValue(string providerName) { - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS, System.StringsHelper.GetString(Strings.TCE_NullProviderValue, providerName)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS, StringsHelper.GetString(Strings.TCE_NullProviderValue, providerName)); } internal static Exception EmptyProviderName() { - return ADP.ArgumentNull(TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS, System.StringsHelper.GetString(Strings.TCE_EmptyProviderName)); + return ADP.ArgumentNull(TdsEnums.TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS, StringsHelper.GetString(Strings.TCE_EmptyProviderName)); } #endregion Always Encrypted - Extensibility related error messages @@ -1878,7 +1933,7 @@ internal static string GetSNIErrorMessage(int sniError) Debug.Assert(sniError > 0 && sniError <= (int)SNINativeMethodWrapper.SniSpecialErrors.MaxErrorValue, "SNI error is out of range"); string errorMessageId = string.Format("SNI_ERROR_{0}", sniError); - return System.StringsHelper.GetResourceString(errorMessageId); + return StringsHelper.GetResourceString(errorMessageId); } // Default values for SqlDependency and SqlNotificationRequest @@ -1903,123 +1958,123 @@ private SQLMessage() { /* prevent utility class from being instantiated*/ } internal static string CultureIdError() { - return System.StringsHelper.GetString(Strings.SQL_CultureIdError); + return StringsHelper.GetString(Strings.SQL_CultureIdError); } internal static string EncryptionNotSupportedByClient() { - return System.StringsHelper.GetString(Strings.SQL_EncryptionNotSupportedByClient); + return StringsHelper.GetString(Strings.SQL_EncryptionNotSupportedByClient); } internal static string EncryptionNotSupportedByServer() { - return System.StringsHelper.GetString(Strings.SQL_EncryptionNotSupportedByServer); + return StringsHelper.GetString(Strings.SQL_EncryptionNotSupportedByServer); } internal static string OperationCancelled() { - return System.StringsHelper.GetString(Strings.SQL_OperationCancelled); + return StringsHelper.GetString(Strings.SQL_OperationCancelled); } internal static string SevereError() { - return System.StringsHelper.GetString(Strings.SQL_SevereError); + return StringsHelper.GetString(Strings.SQL_SevereError); } internal static string SSPIInitializeError() { - return System.StringsHelper.GetString(Strings.SQL_SSPIInitializeError); + return StringsHelper.GetString(Strings.SQL_SSPIInitializeError); } internal static string SSPIGenerateError() { - return System.StringsHelper.GetString(Strings.SQL_SSPIGenerateError); + return StringsHelper.GetString(Strings.SQL_SSPIGenerateError); } internal static string SqlServerBrowserNotAccessible() { - return System.StringsHelper.GetString(Strings.SQL_SqlServerBrowserNotAccessible); + return StringsHelper.GetString(Strings.SQL_SqlServerBrowserNotAccessible); } internal static string KerberosTicketMissingError() { - return System.StringsHelper.GetString(Strings.SQL_KerberosTicketMissingError); + return StringsHelper.GetString(Strings.SQL_KerberosTicketMissingError); } internal static string Timeout() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_Execution); + return StringsHelper.GetString(Strings.SQL_Timeout_Execution); } internal static string Timeout_PreLogin_Begin() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_Begin); + return StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_Begin); } internal static string Timeout_PreLogin_InitializeConnection() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_InitializeConnection); + return StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_InitializeConnection); } internal static string Timeout_PreLogin_SendHandshake() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_SendHandshake); + return StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_SendHandshake); } internal static string Timeout_PreLogin_ConsumeHandshake() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_ConsumeHandshake); + return StringsHelper.GetString(Strings.SQL_Timeout_PreLogin_ConsumeHandshake); } internal static string Timeout_Login_Begin() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_Login_Begin); + return StringsHelper.GetString(Strings.SQL_Timeout_Login_Begin); } internal static string Timeout_Login_ProcessConnectionAuth() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_Login_ProcessConnectionAuth); + return StringsHelper.GetString(Strings.SQL_Timeout_Login_ProcessConnectionAuth); } internal static string Timeout_PostLogin() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_PostLogin); + return StringsHelper.GetString(Strings.SQL_Timeout_PostLogin); } internal static string Timeout_FailoverInfo() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_FailoverInfo); + return StringsHelper.GetString(Strings.SQL_Timeout_FailoverInfo); } internal static string Timeout_RoutingDestination() { - return System.StringsHelper.GetString(Strings.SQL_Timeout_RoutingDestinationInfo); + return StringsHelper.GetString(Strings.SQL_Timeout_RoutingDestinationInfo); } internal static string Duration_PreLogin_Begin(long PreLoginBeginDuration) { - return System.StringsHelper.GetString(Strings.SQL_Duration_PreLogin_Begin, PreLoginBeginDuration); + return StringsHelper.GetString(Strings.SQL_Duration_PreLogin_Begin, PreLoginBeginDuration); } internal static string Duration_PreLoginHandshake(long PreLoginBeginDuration, long PreLoginHandshakeDuration) { - return System.StringsHelper.GetString(Strings.SQL_Duration_PreLoginHandshake, PreLoginBeginDuration, PreLoginHandshakeDuration); + return StringsHelper.GetString(Strings.SQL_Duration_PreLoginHandshake, PreLoginBeginDuration, PreLoginHandshakeDuration); } internal static string Duration_Login_Begin(long PreLoginBeginDuration, long PreLoginHandshakeDuration, long LoginBeginDuration) { - return System.StringsHelper.GetString(Strings.SQL_Duration_Login_Begin, PreLoginBeginDuration, PreLoginHandshakeDuration, LoginBeginDuration); + return StringsHelper.GetString(Strings.SQL_Duration_Login_Begin, PreLoginBeginDuration, PreLoginHandshakeDuration, LoginBeginDuration); } internal static string Duration_Login_ProcessConnectionAuth(long PreLoginBeginDuration, long PreLoginHandshakeDuration, long LoginBeginDuration, long LoginAuthDuration) { - return System.StringsHelper.GetString(Strings.SQL_Duration_Login_ProcessConnectionAuth, PreLoginBeginDuration, PreLoginHandshakeDuration, LoginBeginDuration, LoginAuthDuration); + return StringsHelper.GetString(Strings.SQL_Duration_Login_ProcessConnectionAuth, PreLoginBeginDuration, PreLoginHandshakeDuration, LoginBeginDuration, LoginAuthDuration); } internal static string Duration_PostLogin(long PreLoginBeginDuration, long PreLoginHandshakeDuration, long LoginBeginDuration, long LoginAuthDuration, long PostLoginDuration) { - return System.StringsHelper.GetString(Strings.SQL_Duration_PostLogin, PreLoginBeginDuration, PreLoginHandshakeDuration, LoginBeginDuration, LoginAuthDuration, PostLoginDuration); + return StringsHelper.GetString(Strings.SQL_Duration_PostLogin, PreLoginBeginDuration, PreLoginHandshakeDuration, LoginBeginDuration, LoginAuthDuration, PostLoginDuration); } internal static string UserInstanceFailure() { - return System.StringsHelper.GetString(Strings.SQL_UserInstanceFailure); + return StringsHelper.GetString(Strings.SQL_UserInstanceFailure); } internal static string PreloginError() { - return System.StringsHelper.GetString(Strings.Snix_PreLogin); + return StringsHelper.GetString(Strings.Snix_PreLogin); } internal static string ExClientConnectionId() { - return System.StringsHelper.GetString(Strings.SQL_ExClientConnectionId); + return StringsHelper.GetString(Strings.SQL_ExClientConnectionId); } internal static string ExErrorNumberStateClass() { - return System.StringsHelper.GetString(Strings.SQL_ExErrorNumberStateClass); + return StringsHelper.GetString(Strings.SQL_ExErrorNumberStateClass); } internal static string ExOriginalClientConnectionId() { - return System.StringsHelper.GetString(Strings.SQL_ExOriginalClientConnectionId); + return StringsHelper.GetString(Strings.SQL_ExOriginalClientConnectionId); } internal static string ExRoutingDestination() { - return System.StringsHelper.GetString(Strings.SQL_ExRoutingDestination); + return StringsHelper.GetString(Strings.SQL_ExRoutingDestination); } } @@ -2133,37 +2188,4 @@ public static MethodInfo GetPromotedToken } } - /// - /// This class implements a FIFO Queue with SemaphoreSlim for ordered execution of parallel tasks. - /// Currently used in Managed SNI (SNISslStream) to override SslStream's WriteAsync implementation. - /// - internal class ConcurrentQueueSemaphore - { - private readonly SemaphoreSlim _semaphore; - private readonly ConcurrentQueue> _queue = - new ConcurrentQueue>(); - - public ConcurrentQueueSemaphore(int initialCount) - { - _semaphore = new SemaphoreSlim(initialCount); - } - - public Task WaitAsync(CancellationToken cancellationToken) - { - var tcs = new TaskCompletionSource(); - _queue.Enqueue(tcs); - _semaphore.WaitAsync().ContinueWith(t => - { - if (_queue.TryDequeue(out TaskCompletionSource popped)) - popped.SetResult(true); - }, cancellationToken); - return tcs.Task; - } - - public void Release() - { - _semaphore.Release(); - } - } - -}//namespace +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs index 692633efd6..2f242a083f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Unix.cs @@ -26,7 +26,7 @@ private void WaitForSSLHandShakeToComplete(ref uint error, ref int protocolVersi private SNIErrorDetails GetSniErrorDetails() { SNIErrorDetails details; - SNIError sniError = SNIProxy.GetInstance().GetLastError(); + SNIError sniError = SNIProxy.Instance.GetLastError(); details.sniErrorNumber = sniError.sniError; details.errorMessage = sniError.errorMessage; details.nativeError = sniError.nativeError; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs index 07846127d7..f7a0363f27 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs @@ -95,7 +95,7 @@ private SNIErrorDetails GetSniErrorDetails() if (TdsParserStateObjectFactory.UseManagedSNI) { - SNIError sniError = SNIProxy.GetInstance().GetLastError(); + SNIError sniError = SNIProxy.Instance.GetLastError(); details.sniErrorNumber = sniError.sniError; details.errorMessage = sniError.errorMessage; details.nativeError = sniError.nativeError; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 9ce5c7a8d2..8217604c72 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -10,7 +10,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; -using System.Reflection; using System.Security.Authentication; using System.Text; using System.Threading; @@ -42,11 +41,15 @@ internal sealed partial class TdsParser { private static int _objectTypeCount; // EventSource counter private readonly SqlClientLogger _logger = new SqlClientLogger(); - private readonly string _typeName; internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); internal int ObjectID => _objectID; + /// + /// Verify client encryption possibility. + /// + private bool ClientOSEncryptionSupport => TdsParserStateObjectFactory.Singleton.ClientOSEncryptionSupport; + // Default state object for parser internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI. @@ -85,7 +88,7 @@ internal sealed partial class TdsParser private EncryptionOptions _encryptionOption = s_sniSupportedEncryptionOption; private SqlInternalTransaction _currentTransaction; - private SqlInternalTransaction _pendingTransaction; // pending transaction for Yukon and beyond. + private SqlInternalTransaction _pendingTransaction; // pending transaction for 2005 and beyond. // need to hold on to the transaction id if distributed transaction merely rolls back without defecting. private long _retainedTransactionId = SqlInternalTransaction.NullTransactionId; @@ -108,13 +111,15 @@ internal sealed partial class TdsParser // Version variables - private bool _isYukon = false; // set to true if speaking to Yukon or later + private bool _is2005 = false; // set to true if speaking to 2005 or later - private bool _isKatmai = false; + private bool _is2008 = false; - private bool _isDenali = false; + private bool _is2012 = false; - private byte[] _sniSpnBuffer = null; + private bool _is2022 = false; + + private byte[][] _sniSpnBuffer = null; // SqlStatistics private SqlStatistics _statistics = null; @@ -157,13 +162,18 @@ internal sealed partial class TdsParser /// internal byte TceVersionSupported { get; set; } + /// + /// Server supports retrying when the enclave CEKs sent by the client do not match what is needed for the query to run. + /// + internal bool AreEnclaveRetriesSupported { get; set; } + /// /// Type of enclave being used by the server /// internal string EnclaveType { get; set; } internal bool isTcpProtocol { get; set; } - internal string FQDNforDNSCahce { get; set; } + internal string FQDNforDNSCache { get; set; } /// /// Get if data classification is enabled by the server. @@ -176,13 +186,14 @@ internal sealed partial class TdsParser /// internal int DataClassificationVersion { get; set; } + private SqlCollation _cachedCollation; + internal TdsParser(bool MARS, bool fAsynchronous) { - _fMARS = MARS; // may change during Connect to pre Yukon servers + _fMARS = MARS; // may change during Connect to pre 2005 servers _physicalStateObj = TdsParserStateObjectFactory.Singleton.CreateTdsParserStateObject(this); DataClassificationVersion = TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED; - _typeName = GetType().Name; } internal SqlInternalConnectionTds Connection @@ -248,11 +259,13 @@ internal EncryptionOptions EncryptionOptions } } - internal bool IsKatmaiOrNewer + internal bool Is2005OrNewer => true; + + internal bool Is2008OrNewer { get { - return _isKatmai; + return _is2008; } } @@ -313,7 +326,7 @@ private bool IncludeTraceHeader { get { - return (_isDenali && SqlClientEventSource.Log.IsEnabled()); + return (_is2012 && SqlClientEventSource.Log.IsEnabled()); } } @@ -340,6 +353,7 @@ internal void ProcessPendingAck(TdsParserStateObject stateObj) { if (stateObj._attentionSent) { + SqlClientEventSource.Log.TryTraceEvent("TdsParser.ProcessPendingAck | INFO | Connection Object Id {0}, State Obj Id {1}, Processing Attention.", _connHandler._objectID, stateObj.ObjectID); ProcessAttention(stateObj); } } @@ -349,12 +363,17 @@ internal void Connect( SqlInternalConnectionTds connHandler, bool ignoreSniOpenTimeout, long timerExpire, - bool encrypt, - bool trustServerCert, - bool integratedSecurity, - bool withFailover, - SqlAuthenticationMethod authType) + SqlConnectionString connectionOptions, + bool withFailover) { + SqlConnectionEncryptOption encrypt = connectionOptions.Encrypt; + bool isTlsFirst = (encrypt == SqlConnectionEncryptOption.Strict); + bool trustServerCert = connectionOptions.TrustServerCertificate; + bool integratedSecurity = connectionOptions.IntegratedSecurity; + SqlAuthenticationMethod authType = connectionOptions.Authentication; + string hostNameInCertificate = connectionOptions.HostNameInCertificate; + string serverCertificateFilename = connectionOptions.ServerCertificate; + if (_state != TdsParserState.Closed) { Debug.Fail("TdsParser.Connect called on non-closed connection!"); @@ -379,36 +398,15 @@ internal void Connect( else { _sniSpnBuffer = null; - switch (authType) - { - case SqlAuthenticationMethod.ActiveDirectoryPassword: - SqlClientEventSource.Log.TryTraceEvent(" Active Directory Password authentication"); - break; - case SqlAuthenticationMethod.ActiveDirectoryIntegrated: - SqlClientEventSource.Log.TryTraceEvent(" Active Directory Integrated authentication"); - break; - case SqlAuthenticationMethod.ActiveDirectoryInteractive: - SqlClientEventSource.Log.TryTraceEvent(" Active Directory Interactive authentication"); - break; - case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal: - SqlClientEventSource.Log.TryTraceEvent(" Active Directory Service Principal authentication"); - break; - case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow: - SqlClientEventSource.Log.TryTraceEvent(" Active Directory Device Code Flow authentication"); - break; - case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity: - SqlClientEventSource.Log.TryTraceEvent(" Active Directory Managed Identity authentication"); - break; - case SqlAuthenticationMethod.ActiveDirectoryMSI: - SqlClientEventSource.Log.TryTraceEvent(" Active Directory MSI authentication"); - break; - case SqlAuthenticationMethod.SqlPassword: - SqlClientEventSource.Log.TryTraceEvent(" SQL Password authentication"); - break; - default: - SqlClientEventSource.Log.TryTraceEvent(" SQL authentication"); - break; - } + SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | Connection Object Id {0}, Authentication Mode: {1}", _connHandler._objectID, + authType == SqlAuthenticationMethod.NotSpecified ? SqlAuthenticationMethod.SqlPassword.ToString() : authType.ToString()); + } + + // Encryption is not supported on SQL Local DB - disable it if they have only specified Mandatory + if (connHandler.ConnectionOptions.LocalDBInstance != null && encrypt == SqlConnectionEncryptOption.Mandatory) + { + encrypt = SqlConnectionEncryptOption.Optional; + SqlClientEventSource.Log.TryTraceEvent(" Encryption will be disabled as target server is a SQL Local DB instance."); } _sniSpnBuffer = null; @@ -417,7 +415,13 @@ internal void Connect( if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated) { LoadSSPILibrary(); - SqlClientEventSource.Log.TryTraceEvent(" SSPI or Active Directory Authentication Library for SQL Server based integrated authentication"); + SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | SSPI or Active Directory Authentication Library loaded for SQL Server based integrated authentication"); + } + + // if Strict encryption (i.e. isTlsFirst) is chosen trust server certificate should always be false. + if (isTlsFirst) + { + trustServerCert = false; } byte[] instanceName = null; @@ -428,19 +432,34 @@ internal void Connect( bool fParallel = _connHandler.ConnectionOptions.MultiSubnetFailover; - FQDNforDNSCahce = serverInfo.ResolvedServerName; + FQDNforDNSCache = serverInfo.ResolvedServerName; - int commaPos = FQDNforDNSCahce.IndexOf(","); + int commaPos = FQDNforDNSCache.IndexOf(","); if (commaPos != -1) { - FQDNforDNSCahce = FQDNforDNSCahce.Substring(0, commaPos); + FQDNforDNSCache = FQDNforDNSCache.Substring(0, commaPos); } _connHandler.pendingSQLDNSObject = null; // AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, - out instanceName, ref _sniSpnBuffer, false, true, fParallel, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated); + _physicalStateObj.CreatePhysicalSNIHandle( + serverInfo.ExtendedServerName, + ignoreSniOpenTimeout, + timerExpire, + out instanceName, + ref _sniSpnBuffer, + false, + true, + fParallel, + _connHandler.ConnectionOptions.IPAddressPreference, + FQDNforDNSCache, + ref _connHandler.pendingSQLDNSObject, + serverInfo.ServerSPN, + integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated, + isTlsFirst, + hostNameInCertificate, + serverCertificateFilename); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -483,18 +502,37 @@ internal void Connect( if (null == _connHandler.pendingSQLDNSObject) { // for DNS Caching phase 1 - _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject); + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject); + } + + if (!ClientOSEncryptionSupport) + { + //If encryption is required, an error will be thrown. + if (encrypt != SqlConnectionEncryptOption.Optional) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + _encryptionOption = EncryptionOptions.NOT_SUP; } SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake"); - SendPreLoginHandshake(instanceName, encrypt); + SendPreLoginHandshake(instanceName, encrypt, integratedSecurity, serverCertificateFilename); _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake); _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); _physicalStateObj.SniContext = SniContext.Snix_PreLogin; SqlClientEventSource.Log.TryTraceEvent(" Consuming prelogin handshake"); - PreLoginHandshakeStatus status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired); + PreLoginHandshakeStatus status = ConsumePreLoginHandshake( + encrypt, + trustServerCert, + integratedSecurity, + out marsCapable, + out _connHandler._fedAuthRequired, + isTlsFirst, + serverCertificateFilename); if (status == PreLoginHandshakeStatus.InstanceFailure) { @@ -504,7 +542,22 @@ internal void Connect( // On Instance failure re-connect and flush SNI named instance cache. _physicalStateObj.SniContext = SniContext.Snix_Connect; - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, true, true, fParallel, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity); + _physicalStateObj.CreatePhysicalSNIHandle( + serverInfo.ExtendedServerName, + ignoreSniOpenTimeout, + timerExpire, out instanceName, + ref _sniSpnBuffer, + true, + true, + fParallel, + _connHandler.ConnectionOptions.IPAddressPreference, + FQDNforDNSCache, + ref _connHandler.pendingSQLDNSObject, + serverInfo.ServerSPN, + integratedSecurity, + isTlsFirst, + hostNameInCertificate, + serverCertificateFilename); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -521,20 +574,28 @@ internal void Connect( if (null == _connHandler.pendingSQLDNSObject) { // for DNS Caching phase 1 - _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject); + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject); } - SendPreLoginHandshake(instanceName, encrypt); - status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired); + SendPreLoginHandshake(instanceName, encrypt, integratedSecurity, serverCertificateFilename); + status = ConsumePreLoginHandshake( + encrypt, + trustServerCert, + integratedSecurity, + out marsCapable, + out _connHandler._fedAuthRequired, + isTlsFirst, + serverCertificateFilename); - // Don't need to check for Sphinx failure, since we've already consumed - // one pre-login packet and know we are connecting to Shiloh. + // Don't need to check for 7.0 failure, since we've already consumed + // one pre-login packet and know we are connecting to 2000. if (status == PreLoginHandshakeStatus.InstanceFailure) { SqlClientEventSource.Log.TryTraceEvent(" Prelogin handshake unsuccessful. Login failure"); throw SQL.InstanceFailure(); } } + SqlClientEventSource.Log.TryTraceEvent(" Prelogin handshake successful"); if (_fMARS && marsCapable) { @@ -641,9 +702,24 @@ internal void PutSession(TdsParserStateObject session) } } - - private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) + private void SendPreLoginHandshake( + byte[] instanceName, + SqlConnectionEncryptOption encrypt, + bool integratedSecurity, + string serverCertificateFilename) { + if (encrypt == SqlConnectionEncryptOption.Strict) + { + //Always validate the certificate when in strict encryption mode + uint info = TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE | TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE | TdsEnums.SNI_SSL_SEND_ALPN_EXTENSION; + + EnableSsl(info, encrypt, integratedSecurity, serverCertificateFilename); + + // Since encryption has already been negotiated, we need to set encryption not supported in + // prelogin so that we don't try to negotiate encryption again during ConsumePreLoginHandshake. + _encryptionOption = EncryptionOptions.NOT_SUP; + } + // PreLoginHandshake buffer consists of: // 1) Standard header, with type = MT_PRELOGIN // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length) @@ -695,13 +771,13 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) case (int)PreLoginOptions.ENCRYPT: if (_encryptionOption == EncryptionOptions.NOT_SUP) { - // If OS doesn't support encryption, inform server not supported. + //If OS doesn't support encryption and encryption is not required, inform server "not supported" by client. payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP; } else { // Else, inform server of user request. - if (encrypt) + if (encrypt == SqlConnectionEncryptOption.Mandatory) { payload[payloadLength] = (byte)EncryptionOptions.ON; _encryptionOption = EncryptionOptions.ON; @@ -798,11 +874,61 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); } - private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trustServerCert, bool integratedSecurity, out bool marsCapable, out bool fedAuthRequired) + private void EnableSsl(uint info, SqlConnectionEncryptOption encrypt, bool integratedSecurity, string serverCertificateFilename) + { + uint error = 0; + + if (encrypt && !integratedSecurity) + { + // optimization: in case of SQL Authentication and encryption in TDS, set SNI_SSL_IGNORE_CHANNEL_BINDINGS + // to let SNI know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. + // This applies to Native SNI + info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; + } + + error = _physicalStateObj.EnableSsl(ref info, encrypt == SqlConnectionEncryptOption.Strict, serverCertificateFilename); + + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + int protocolVersion = 0; + WaitForSSLHandShakeToComplete(ref error, ref protocolVersion); + + SslProtocols protocol = (SslProtocols)protocolVersion; + string warningMessage = protocol.GetProtocolWarning(); + if (!string.IsNullOrEmpty(warningMessage)) + { + if (!encrypt && LocalAppContextSwitches.SuppressInsecureTLSWarning) + { + // Skip console warning + SqlClientEventSource.Log.TryTraceEvent("{3}", nameof(TdsParser), nameof(EnableSsl), SqlClientLogger.LogLevel.Warning, warningMessage); + } + else + { + // This logs console warning of insecure protocol in use. + _logger.LogWarning(nameof(TdsParser), nameof(EnableSsl), warningMessage); + } + } + + // create a new packet encryption changes the internal packet size + _physicalStateObj.ClearAllWritePackets(); + } + + private PreLoginHandshakeStatus ConsumePreLoginHandshake( + SqlConnectionEncryptOption encrypt, + bool trustServerCert, + bool integratedSecurity, + out bool marsCapable, + out bool fedAuthRequired, + bool tlsFirst, + string serverCert) { marsCapable = _fMARS; // Assign default value fedAuthRequired = false; - bool isYukonOrLater = false; + bool is2005OrLater = false; Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); bool result = _physicalStateObj.TryReadNetworkPacket(); if (!result) @@ -861,10 +987,10 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus int level = (payload[payloadOffset + 2] << 8) | payload[payloadOffset + 3]; - isYukonOrLater = majorVersion >= 9; - if (!isYukonOrLater) + is2005OrLater = majorVersion >= 9; + if (!is2005OrLater) { - marsCapable = false; // If pre-Yukon, MARS not supported. + marsCapable = false; // If pre-2005, MARS not supported. } break; @@ -892,9 +1018,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus _physicalStateObj.Dispose(); ThrowExceptionAndWarning(_physicalStateObj); } - break; - case (EncryptionOptions.OFF): if (serverOption == EncryptionOptions.OFF) { @@ -906,11 +1030,11 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus // Encrypt all. _encryptionOption = EncryptionOptions.ON; } - + // NOT_SUP: No encryption. break; case (EncryptionOptions.NOT_SUP): - if (serverOption == EncryptionOptions.REQ) + if (!tlsFirst && serverOption == EncryptionOptions.REQ) { _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); @@ -918,7 +1042,6 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus } break; - default: Debug.Fail("Invalid client encryption option detected"); break; @@ -927,42 +1050,13 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus if (_encryptionOption == EncryptionOptions.ON || _encryptionOption == EncryptionOptions.LOGIN) { - uint error = 0; - // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || (_connHandler._accessTokenInBytes != null && !trustServerCert); + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || + (_connHandler._accessTokenInBytes != null && !trustServerCert); uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) - | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - - if (encrypt && !integratedSecurity) - { - // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI - // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. - // This applies to Native SNI - info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; - } + | (is2005OrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - error = _physicalStateObj.EnableSsl(ref info); - - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - int protocolVersion = 0; - WaitForSSLHandShakeToComplete(ref error, ref protocolVersion); - - SslProtocols protocol = (SslProtocols)protocolVersion; - string warningMessage = protocol.GetProtocolWarning(); - if (!string.IsNullOrEmpty(warningMessage)) - { - // This logs console warning of insecure protocol in use. - _logger.LogWarning(_typeName, MethodBase.GetCurrentMethod().Name, warningMessage); - } - - // create a new packet encryption changes the internal packet size - _physicalStateObj.ClearAllWritePackets(); + EnableSsl(info, encrypt, integratedSecurity, serverCert); } break; @@ -1265,7 +1359,7 @@ internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool calle // report exception to pending async operation // before OnConnectionClosed overrides the exception // due to connection close notification through references - var taskSource = stateObj._networkPacketTaskSource; + TaskCompletionSource taskSource = stateObj._networkPacketTaskSource; if (taskSource != null) { taskSource.TrySetException(ADP.ExceptionWithStackTrace(exception)); @@ -1275,7 +1369,7 @@ internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool calle if (asyncClose) { // Wait until we have the parser lock, then try to close - var connHandler = _connHandler; + SqlInternalConnectionTds connHandler = _connHandler; Action wrapCloseAction = closeAction => { Task.Factory.StartNew(() => @@ -1322,13 +1416,12 @@ internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool calle internal SqlError ProcessSNIError(TdsParserStateObject stateObj) { - long scopeID = SqlClientEventSource.Log.TryScopeEnterEvent(""); - try + using (TryEventScope.Create("")) { #if DEBUG // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error Debug.Assert(SniContext.Undefined != stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None"); - SqlClientEventSource.Log.TrySNITraceEvent(" SNIContext must not be None = {0}, _fMARS = {1}, TDS Parser State = {2}", stateObj.DebugOnlyCopyOfSniContext, _fMARS, _state); + SqlClientEventSource.Log.TryTraceEvent(" SNIContext must not be None = {0}, _fMARS = {1}, TDS Parser State = {2}", stateObj.DebugOnlyCopyOfSniContext, _fMARS, _state); #endif SNIErrorDetails details = GetSniErrorDetails(); @@ -1453,12 +1546,8 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj) SqlClientEventSource.Log.TryAdvancedTraceErrorEvent(" SNI Error Message. Native Error = {0}, Line Number ={1}, Function ={2}, Exception ={3}, Server = {4}", (int)details.nativeError, (int)details.lineNumber, details.function, details.exception, _server); - return new SqlError((int)details.nativeError, 0x00, TdsEnums.FATAL_ERROR_CLASS, - _server, errorMessage, details.function, (int)details.lineNumber, details.nativeError, details.exception); - } - finally - { - SqlClientEventSource.Log.TryScopeLeaveEvent(scopeID); + return new SqlError(infoNumber: (int)details.nativeError, errorState: 0x00, TdsEnums.FATAL_ERROR_CLASS, _server, + errorMessage, details.function, (int)details.lineNumber, win32ErrorCode: details.nativeError, details.exception); } } @@ -1529,7 +1618,7 @@ internal void CheckResetConnection(TdsParserStateObject stateObj) { Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state on WritePacket ResetConnection"); - // Otherwise if Yukon and we grabbed the event, free it. Another execute grabbed the event and + // Otherwise if 2005 and we grabbed the event, free it. Another execute grabbed the event and // took care of sending the reset. stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); @@ -1539,7 +1628,7 @@ internal void CheckResetConnection(TdsParserStateObject stateObj) { if (_fMARS && stateObj._fResetEventOwned) { - // If exception thrown, and we are on Yukon and own the event, release it! + // If exception thrown, and we are on 2005 and own the event, release it! stateObj._fResetConnectionSent = false; stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); @@ -1812,7 +1901,7 @@ internal void WriteDouble(double v, TdsParserStateObject stateObj) internal void PrepareResetConnection(bool preserveTransaction) { - // Set flag to reset connection upon next use - only for use on shiloh! + // Set flag to reset connection upon next use - only for use on 2000! _fResetConnection = true; _fPreserveTransaction = preserveTransaction; } @@ -1896,7 +1985,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead // If there is data ready, but we didn't exit the loop, then something is wrong Debug.Assert(!dataReady, "dataReady not expected - did we forget to skip the row?"); - if (stateObj._internalTimeout) + if (stateObj.IsTimeoutStateExpired) { runBehavior = RunBehavior.Attention; } @@ -2174,7 +2263,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead { if (!this.Connection.IgnoreEnvChange) { - switch (env.type) + switch (env._type) { case TdsEnums.ENV_BEGINTRAN: case TdsEnums.ENV_ENLISTDTC: @@ -2189,12 +2278,12 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead if (null != _currentTransaction) { - _currentTransaction.TransactionId = env.newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. + _currentTransaction.TransactionId = env._newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. } else { - TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env.type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed; - _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env.newLongValue); + TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env._type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed; + _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env._newLongValue); } if (null != _statistics && !_statisticsIsInTransaction) { @@ -2219,22 +2308,22 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead // Check null for case where Begin and Rollback obtained in the same message. if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) { - Debug.Assert(_currentTransaction.TransactionId != env.newLongValue, "transaction id's are not equal!"); + Debug.Assert(_currentTransaction.TransactionId != env._newLongValue, "transaction id's are not equal!"); } #endif - if (TdsEnums.ENV_COMMITTRAN == env.type) + if (TdsEnums.ENV_COMMITTRAN == env._type) { _currentTransaction.Completed(TransactionState.Committed); } - else if (TdsEnums.ENV_ROLLBACKTRAN == env.type) + else if (TdsEnums.ENV_ROLLBACKTRAN == env._type) { // Hold onto transaction id if distributed tran is rolled back. This must // be sent to the server on subsequent executions even though the transaction // is considered to be rolled back. if (_currentTransaction.IsDistributed && _currentTransaction.IsActive) { - _retainedTransactionId = env.oldLongValue; + _retainedTransactionId = env._oldLongValue; } _currentTransaction.Completed(TransactionState.Aborted); } @@ -2253,7 +2342,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead } } SqlEnvChange head = env; - env = env.Next; + env = env._next; head.Clear(); head = null; } @@ -2520,7 +2609,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead stateObj._attentionSent = false; stateObj.HasReceivedAttention = false; - if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior) && !stateObj._internalTimeout) + if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior) && !stateObj.IsTimeoutStateExpired) { // Add attention error to collection - if not RunBehavior.Clean! stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.OperationCancelled(), "", 0)); @@ -2550,8 +2639,8 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, while (tokenLength > processedLength) { - var env = new SqlEnvChange(); - if (!stateObj.TryReadByte(out env.type)) + SqlEnvChange env = new SqlEnvChange(); + if (!stateObj.TryReadByte(out env._type)) { return false; } @@ -2563,11 +2652,11 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, } else { - tail.Next = env; + tail._next = env; tail = env; } - switch (env.type) + switch (env._type) { case TdsEnums.ENV_DATABASE: case TdsEnums.ENV_LANG: @@ -2584,16 +2673,16 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - if (env.newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) + if (env._newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) { _defaultCodePage = TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE; _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); } else { - Debug.Assert(env.newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10"); + Debug.Assert(env._newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10"); - string stringCodePage = env.newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET); + string stringCodePage = env._newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET); _defaultCodePage = int.Parse(stringCodePage, NumberStyles.Integer, CultureInfo.InvariantCulture); _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); @@ -2609,9 +2698,10 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, // Changing packet size does not support retry, should not pend" throw SQL.SynchronousCallMayNotPend(); } + // Only set on physical state object - this should only occur on LoginAck prior // to MARS initialization! - int packetSize = int.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); + int packetSize = int.Parse(env._newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); if (_physicalStateObj.SetPacketSize(packetSize)) { @@ -2621,7 +2711,6 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, // Update SNI ConsumerInfo value to be resulting packet size uint unsignedPacketSize = (uint)packetSize; - uint result = _physicalStateObj.SetConnectionBufferSize(ref unsignedPacketSize); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SNISetInfo"); } @@ -2633,7 +2722,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - _defaultLCID = int.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); + _defaultLCID = int.Parse(env._newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); break; case TdsEnums.ENV_COMPFLAGS: @@ -2644,54 +2733,54 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, break; case TdsEnums.ENV_COLLATION: - Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!"); + Debug.Assert(env._newLength == 5 || env._newLength == 0, "Improper length in new collation!"); if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.newLength = byteLength; - if (env.newLength == 5) + env._newLength = byteLength; + if (env._newLength == 5) { - if (!TryProcessCollation(stateObj, out env.newCollation)) + if (!TryProcessCollation(stateObj, out env._newCollation)) { return false; } // Give the parser the new collation values in case parameters don't specify one - _defaultCollation = env.newCollation; + _defaultCollation = env._newCollation; // UTF8 collation - if ((env.newCollation.info & TdsEnums.UTF8_IN_TDSCOLLATION) == TdsEnums.UTF8_IN_TDSCOLLATION) + if (env._newCollation.IsUTF8) { _defaultEncoding = Encoding.UTF8; } else { - int newCodePage = GetCodePage(env.newCollation, stateObj); + int newCodePage = GetCodePage(env._newCollation, stateObj); if (newCodePage != _defaultCodePage) { _defaultCodePage = newCodePage; _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); } } - _defaultLCID = env.newCollation.LCID; + _defaultLCID = env._newCollation.LCID; } if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.oldLength = byteLength; - Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!"); - if (env.oldLength == 5) + env._oldLength = byteLength; + Debug.Assert(env._oldLength == 5 || env._oldLength == 0, "Improper length in old collation!"); + if (env._oldLength == 5) { - if (!TryProcessCollation(stateObj, out env.oldCollation)) + if (!TryProcessCollation(stateObj, out env._oldCollation)) { return false; } } - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; break; case TdsEnums.ENV_BEGINTRAN: @@ -2704,44 +2793,44 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newLength = byteLength; - Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!"); + env._newLength = byteLength; + Debug.Assert(env._newLength == 0 || env._newLength == 8, "Improper length for new transaction id!"); - if (env.newLength > 0) + if (env._newLength > 0) { - if (!stateObj.TryReadInt64(out env.newLongValue)) + if (!stateObj.TryReadInt64(out env._newLongValue)) { return false; } - Debug.Assert(env.newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id. + Debug.Assert(env._newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id. } else { - env.newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. + env._newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. } if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.oldLength = byteLength; - Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!"); + env._oldLength = byteLength; + Debug.Assert(env._oldLength == 0 || env._oldLength == 8, "Improper length for old transaction id!"); - if (env.oldLength > 0) + if (env._oldLength > 0) { - if (!stateObj.TryReadInt64(out env.oldLongValue)) + if (!stateObj.TryReadInt64(out env._oldLongValue)) { return false; } - Debug.Assert(env.oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id. + Debug.Assert(env._oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id. } else { - env.oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. + env._oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. } // env.length includes 1 byte type token - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; break; case TdsEnums.ENV_LOGSHIPNODE: @@ -2754,12 +2843,12 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, break; case TdsEnums.ENV_PROMOTETRANSACTION: - if (!stateObj.TryReadInt32(out env.newLength)) + if (!stateObj.TryReadInt32(out env._newLength)) { // new value has 4 byte length return false; } - env.newBinValue = new byte[env.newLength]; - if (!stateObj.TryReadByteArray(env.newBinValue, env.newLength)) + env._newBinValue = new byte[env._newLength]; + if (!stateObj.TryReadByteArray(env._newBinValue, env._newLength)) { // read new value with 4 byte length return false; } @@ -2768,11 +2857,11 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.oldLength = byteLength; - Debug.Assert(0 == env.oldLength, "old length should be zero"); + env._oldLength = byteLength; + Debug.Assert(0 == env._oldLength, "old length should be zero"); // env.length includes 1 byte for type token - env.length = 5 + env.newLength; + env._length = 5 + env._newLength; break; case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS: @@ -2796,7 +2885,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newLength = newLength; + env._newLength = newLength; byte protocol; if (!stateObj.TryReadByte(out protocol)) { @@ -2817,7 +2906,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newRoutingInfo = new RoutingInfo(protocol, port, serverName); + env._newRoutingInfo = new RoutingInfo(protocol, port, serverName); ushort oldLength; if (!stateObj.TryReadUInt16(out oldLength)) { @@ -2827,14 +2916,14 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.length = env.newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength] + env._length = env._newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength] break; default: - Debug.Fail("Unknown environment change token: " + env.type); + Debug.Fail("Unknown environment change token: " + env._type); break; } - processedLength += env.length; + processedLength += env._length; } sqlEnvChange = head; @@ -2849,10 +2938,10 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state { return false; } - env.newLength = byteLength; - env.newBinValue = ArrayPool.Shared.Rent(env.newLength); - env.newBinRented = true; - if (!stateObj.TryReadByteArray(env.newBinValue, env.newLength)) + env._newLength = byteLength; + env._newBinValue = ArrayPool.Shared.Rent(env._newLength); + env._newBinRented = true; + if (!stateObj.TryReadByteArray(env._newBinValue, env._newLength)) { return false; } @@ -2860,16 +2949,16 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state { return false; } - env.oldLength = byteLength; - env.oldBinValue = ArrayPool.Shared.Rent(env.oldLength); - env.oldBinRented = true; - if (!stateObj.TryReadByteArray(env.oldBinValue, env.oldLength)) + env._oldLength = byteLength; + env._oldBinValue = ArrayPool.Shared.Rent(env._oldLength); + env._oldBinRented = true; + if (!stateObj.TryReadByteArray(env._oldBinValue, env._oldLength)) { return false; } // env.length includes 1 byte type token - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; return true; } @@ -2895,13 +2984,13 @@ private bool TryReadTwoStringFields(SqlEnvChange env, TdsParserStateObject state return false; } - env.newLength = newLength; - env.newValue = newValue; - env.oldLength = oldLength; - env.oldValue = oldValue; + env._newLength = newLength; + env._newValue = newValue; + env._oldLength = oldLength; + env._oldValue = oldValue; // env.length includes 1 byte type token - env.length = 3 + env.newLength * 2 + env.oldLength * 2; + env._length = 3 + env._newLength * 2 + env._oldLength * 2; return true; } @@ -2911,11 +3000,11 @@ private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavio ushort status; int count; - // This is added back since removing it from here introduces regressions in Managed SNI. - // It forces SqlDataReader.ReadAsync() method to run synchronously, - // and will block the calling thread until data is fed from SQL Server. - // TODO Investigate better solution to support non-blocking ReadAsync(). - stateObj._syncOverAsync = true; + if (LocalAppContextSwitches.MakeReadAsyncBlocking) + { + // Don't retry TryProcessDone + stateObj._syncOverAsync = true; + } // status // command @@ -3143,7 +3232,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) bool ret = false; if (_connHandler._cleanSQLDNSCaching) { - ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); + ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCache); } if (_connHandler.IsSQLDNSCachingSupported && _connHandler.pendingSQLDNSObject != null @@ -3169,7 +3258,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) if (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT) { // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && attestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.EnclaveComputationsNotSupported(); } @@ -3177,14 +3266,14 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) { throw SQL.AttestationURLNotSupported(); } - else if (SqlConnectionAttestationProtocol.NotSpecified != _connHandler.ConnectionOptions.AttestationProtocol) + else if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.AttestationProtocolNotSupported(); } } // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) || attestationProtocol == SqlConnectionAttestationProtocol.None) { if (string.IsNullOrWhiteSpace(EnclaveType)) { @@ -3195,7 +3284,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) // Check if the attestation protocol is specified and supports the enclave type. if (SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) { - throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + throw SQL.AttestationProtocolNotSupportEnclaveType(attestationProtocol.ToString(), EnclaveType); } } } @@ -3210,10 +3299,8 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta { case TdsEnums.ENCLAVE_TYPE_VBS: if (attestationProtocol != SqlConnectionAttestationProtocol.AAS -#if ENCLAVE_SIMULATOR - && attestationProtocol != SqlConnectionAttestationProtocol.SIM -#endif - && attestationProtocol != SqlConnectionAttestationProtocol.HGS) + && attestationProtocol != SqlConnectionAttestationProtocol.HGS + && attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3222,7 +3309,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta case TdsEnums.ENCLAVE_TYPE_SGX: #if ENCLAVE_SIMULATOR if (attestationProtocol != SqlConnectionAttestationProtocol.AAS - && attestationProtocol != SqlConnectionAttestationProtocol.SIM) + && attestationProtocol != SqlConnectionAttestationProtocol.None) #else if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) #endif @@ -3233,7 +3320,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta #if ENCLAVE_SIMULATOR case TdsEnums.ENCLAVE_TYPE_SIMULATOR: - if (attestationProtocol != SqlConnectionAttestationProtocol.SIM) + if (attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3247,26 +3334,6 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta return true; } - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } - private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; @@ -3335,7 +3402,7 @@ private bool TryProcessDataClassification(TdsParserStateObject stateObj, out Sen { return false; } - var labels = new List