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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<docs>
<members name="SqlClientApp">
<SqlClientApp>
<summary>
Specifies the known application identifiers that Microsoft.Data.SqlClient reports for user agent telemetry.
</summary>
<remarks>
<para>
Production applications that meet the bar are welcome to reserve an identifier here.
</para>
<para>
Identifier reservations are as follows:
</para>
<list type="bullet">
<item>
<description>0x0001-0x7FFF: Microsoft-defined large-scale applications.</description>
</item>
<item>
<description>0x8000-0xBFFF: Reserved for small-scale use.</description>
</item>
<item>
<description>0xC000-0xFFFF: Public and developer use.</description>
</item>
</list>
<para>
An unregistered identifier may still be reported by casting a value to this type. Identifiers are limited to
the 16-bit space the protocol allows, so a value outside 0x0000 to 0xFFFF is rejected when it is assigned to
<see cref="P:Microsoft.Data.SqlClient.SqlConnection.SqlClientAppId" />.
</para>
</remarks>
</SqlClientApp>
<Unknown>
<summary>
No application identity is reported. This is the default.
</summary>
<value>
0
</value>
</Unknown>
<EntityFramework>
<summary>
The Microsoft Entity Framework Core SQL Server provider.
</summary>
<value>
1
</value>
</EntityFramework>
<SemanticKernel>
<summary>
Microsoft Semantic Kernel.
</summary>
<value>
2
</value>
</SemanticKernel>
<ManagementStudio>
<summary>
Microsoft SQL Server Management Studio.
</summary>
<value>
3
</value>
</ManagementStudio>
<SqlManagementObjects>
<summary>
Microsoft SQL Server Management Objects.
</summary>
<value>
4
</value>
</SqlManagementObjects>
<DataTierApplicationFramework>
<summary>
Microsoft SQL Server Data-Tier Application Framework.
</summary>
<value>
5
</value>
</DataTierApplicationFramework>
<SqlToolsService>
<summary>
Microsoft SQL Tools Service.
</summary>
<value>
6
</value>
</SqlToolsService>
<AspNetCoreDistributedSqlServerCache>
<summary>
Microsoft ASP.NET Core distributed SQL Server cache.
</summary>
<value>
7
</value>
</AspNetCoreDistributedSqlServerCache>
<EntityFramework6>
<summary>
Microsoft Entity Framework 6 SQL Server provider.
</summary>
<value>
8
</value>
</EntityFramework6>
<AzureFunctionsSqlExtension>
<summary>
Microsoft Azure Functions SQL extension.
</summary>
<value>
9
</value>
</AzureFunctionsSqlExtension>
<OrleansAdoNet>
<summary>
Microsoft Orleans ADO.NET providers.
</summary>
<value>
10
</value>
</OrleansAdoNet>
<DurableTaskSqlServer>
<summary>
Microsoft Durable Task SQL Server provider.
</summary>
<value>
11
</value>
</DurableTaskSqlServer>
<SqlPackage>
<summary>
The <c>sqlpackage</c> command-line tool.
</summary>
<value>
12
</value>
<remarks>
<c>sqlpackage</c> is built on the Data-Tier Application Framework, but reports its own identifier so that
command-line use can be told apart from other callers of that framework.
</remarks>
</SqlPackage>
</members>
</docs>
51 changes: 51 additions & 0 deletions doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2294,6 +2294,57 @@ The following sample tries to open a connection to an invalid database to simula
</para>
</remarks>
</System.ICloneable.Clone>
<SqlClientAppId>
<summary>
Gets or sets the middleware application identity reported to the server for this connection.
</summary>
<value>
A <see cref="T:Microsoft.Data.SqlClient.SqlClientApp" /> value. The default is
<see cref="F:Microsoft.Data.SqlClient.SqlClientApp.Unknown" />.
</value>
<example>
<para>
Set the identity before opening the connection:
</para>
<code language="c#">
using Microsoft.Data.SqlClient;

using var connection = new SqlConnection(connectionString);
connection.SqlClientAppId = SqlClientApp.EntityFramework;
connection.Open();
</code>
</example>
<exception cref="T:System.ArgumentOutOfRangeException">
The value is outside the range 0 to 65535.
</exception>
<exception cref="T:System.InvalidOperationException">
The connection is opening or open. The identity is reported during login, so it must be set beforehand.
</exception>
<remarks>
<para>
This API is intended for registered applications that reserve an identifier in
<see cref="T:Microsoft.Data.SqlClient.SqlClientApp" />. An unregistered identifier may be reported by casting
a value to that type, provided it is within the 16-bit range the protocol allows.
</para>
<para>
This value is telemetry. It is supplied entirely by the client, which may report any identifier in range,
so it is not an authenticated identity and must not be used for authorization or any other security
decision.
</para>
<para>
The identity is sent once, during login, so it must be set before the connection is opened.
</para>
<para>
When pooling is enabled the value is reported only while establishing a new physical connection, and it is
not part of the pool key. A connection served from the pool therefore reports the identity of whichever
connection caused that physical connection to be created, and physical connections opened in the background
to satisfy <c>Min Pool Size</c> report
<see cref="F:Microsoft.Data.SqlClient.SqlClientApp.Unknown" />. Applications that mix identities over one
connection string should treat this telemetry as indicative rather than exact, or disable pooling where an
exact attribution is required.
</para>
</remarks>
</SqlClientAppId>
<WorkstationId>
<summary>
Gets a string that identifies the database client.
Expand Down
33 changes: 33 additions & 0 deletions src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,37 @@ public void LogError(string type, string method, string message) { }
public void LogInfo(string type, string method, string message) { }
}

/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlClientApp/*' />
public enum SqlClientApp
{
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/Unknown/*' />
Unknown = 0x0000,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFramework/*' />
EntityFramework = 0x0001,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SemanticKernel/*' />
SemanticKernel = 0x0002,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/ManagementStudio/*' />
ManagementStudio = 0x0003,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlManagementObjects/*' />
SqlManagementObjects = 0x0004,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DataTierApplicationFramework/*' />
DataTierApplicationFramework = 0x0005,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlToolsService/*' />
SqlToolsService = 0x0006,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AspNetCoreDistributedSqlServerCache/*' />
AspNetCoreDistributedSqlServerCache = 0x0007,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFramework6/*' />
EntityFramework6 = 0x0008,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AzureFunctionsSqlExtension/*' />
AzureFunctionsSqlExtension = 0x0009,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/OrleansAdoNet/*' />
OrleansAdoNet = 0x000A,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DurableTaskSqlServer/*' />
DurableTaskSqlServer = 0x000B,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlPackage/*' />
SqlPackage = 0x000C
}

/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml' path='docs/members[@name="SqlClientMetaDataCollectionNames"]/SqlClientMetaDataCollectionNames/*'/>
public static class SqlClientMetaDataCollectionNames
{
Expand Down Expand Up @@ -1010,6 +1041,8 @@ public SqlConnection() { }
public SqlConnection(string connectionString) { }
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/ctorConnectionStringCredential/*'/>
public SqlConnection(string connectionString, Microsoft.Data.SqlClient.SqlCredential credential) { }
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/SqlClientAppId/*' />
public Microsoft.Data.SqlClient.SqlClientApp SqlClientAppId { get { throw null; } set { } }

/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml' path='docs/members[@name="SqlConnection"]/AccessToken/*'/>
[System.ComponentModel.BrowsableAttribute(false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,12 @@ internal class SqlConnectionInternal : DbConnectionInternal, IDisposable

private bool _sessionRecoveryRequested;

/// <summary>
/// The middleware application identity of the <see cref="SqlConnection"/> that caused
/// this physical connection to be created. Reported once, at login.
/// </summary>
private readonly SqlClientApp _sqlClientAppId;

private int _threadIdOwningParserLock = -1;

// @TODO: Rename to indicate this has to do with routing
Expand Down Expand Up @@ -344,12 +350,14 @@ internal SqlConnectionInternal(
IDbConnectionPool pool = null,
Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticationToken>> accessTokenCallback = null,
SspiContextProvider sspiContextProvider = null,
ISqlClientMetrics metrics = null)
ISqlClientMetrics metrics = null,
SqlClientApp sqlClientAppId = SqlClientApp.Unknown)
: base(metrics)
{
Debug.Assert(connectionOptions is not null, "null connectionOptions");

ConnectionOptions = connectionOptions;
_sqlClientAppId = sqlClientAppId;

#if DEBUG
if (reconnectSessionData != null)
Expand Down Expand Up @@ -3063,6 +3071,7 @@ private void Login(
login.password = ConnectionOptions.Password;
login.applicationName = ConnectionOptions.ApplicationName;
login.language = _currentLanguage;
login.appId = _sqlClientAppId;

if (!login.userInstance)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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.

#nullable enable

namespace Microsoft.Data.SqlClient;

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlClientApp/*' />
public enum SqlClientApp
{
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/Unknown/*' />
Unknown = 0x0000,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFramework/*' />
EntityFramework = 0x0001,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SemanticKernel/*' />
SemanticKernel = 0x0002,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/ManagementStudio/*' />
ManagementStudio = 0x0003,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlManagementObjects/*' />
SqlManagementObjects = 0x0004,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DataTierApplicationFramework/*' />
DataTierApplicationFramework = 0x0005,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlToolsService/*' />
SqlToolsService = 0x0006,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AspNetCoreDistributedSqlServerCache/*' />
AspNetCoreDistributedSqlServerCache = 0x0007,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFramework6/*' />
EntityFramework6 = 0x0008,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AzureFunctionsSqlExtension/*' />
AzureFunctionsSqlExtension = 0x0009,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/OrleansAdoNet/*' />
OrleansAdoNet = 0x000A,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DurableTaskSqlServer/*' />
DurableTaskSqlServer = 0x000B,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlPackage/*' />
SqlPackage = 0x000C
Comment thread
cheenamalhotra marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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;

#nullable enable

namespace Microsoft.Data.SqlClient;

/// <summary>
/// Driver-owned feature flags reported in the Driver Properties part of the
/// USERAGENT login feature extension payload.
/// </summary>
/// <remarks>
/// This part is driver-owned, so its meaning is defined entirely by
/// Microsoft.Data.SqlClient and carries no cross-driver contract. Other
/// drivers use the same part for their own purposes.
/// </remarks>
[Flags]
internal enum SqlClientDriverProperties : ushort
{
/// <summary>No tracked features are enabled.</summary>
None = 0x0000,

/// <summary>
/// The connection pool V2 implementation
/// (<c>Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2</c>) is
/// enabled.
/// </summary>
ConnectionPoolV2 = 0x0001
Comment thread
cheenamalhotra marked this conversation as resolved.
}

/// <summary>
/// Resolves the <see cref="SqlClientDriverProperties"/> flags that describe
/// how this process is configured.
/// </summary>
internal static class SqlClientDriverPropertiesResolver
{
/// <summary>
/// The flags describing the current process.
/// </summary>
/// <remarks>
/// The flags are sourced from process-wide switches, so this is stable
/// for the life of the process.
/// </remarks>
internal static SqlClientDriverProperties Current =>
Resolve(LocalAppContextSwitches.UseConnectionPoolV2);

/// <summary>
/// Maps the process configuration to the flags that describe it.
/// </summary>
/// <param name="useConnectionPoolV2">
/// Whether the connection pool V2 implementation is enabled.
/// </param>
/// <returns>
/// The flags describing the supplied configuration.
/// </returns>
/// <remarks>
/// The mapping is kept separate from <see cref="Current"/> because the
/// switches it reads are cached for the life of the process, which makes
/// them impractical to vary in a test.
/// </remarks>
internal static SqlClientDriverProperties Resolve(bool useConnectionPoolV2)
{
SqlClientDriverProperties properties = SqlClientDriverProperties.None;

if (useConnectionPoolV2)
{
properties |= SqlClientDriverProperties.ConnectionPoolV2;
}

return properties;
}
}
Loading
Loading