-
Notifications
You must be signed in to change notification settings - Fork 335
Add application identity to USERAGENT payload (V2) #4632
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
60037bc
c9f0cec
171d890
1d296c9
0c2bfcc
a1ae554
9c7f4b0
2f82e5e
1510d6f
d849cf4
095d84e
e8a5509
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2294,6 +2294,49 @@ The following sample tries to open a connection to an invalid database to simula | |
| </para> | ||
| </remarks> | ||
| </System.ICloneable.Clone> | ||
| <SqlClientAppId> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need the |
||
| <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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should our example use modern property initialization syntax? |
||
| connection.Open(); | ||
| </code> | ||
| </example> | ||
| <exception cref="T:System.ArgumentOutOfRangeException"> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This property won't throw out-of-range. |
||
| The value is outside the range 0 to 65535. | ||
| </exception> | ||
|
cheenamalhotra marked this conversation as resolved.
|
||
| <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> | ||
| 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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Set the underlying type to Applies here and in src/ |
||
| { | ||
| /// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/Unknown/*' /> | ||
| Unknown = 0x0000, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think using decimal integers would be more readable and greppable. Applies here and in src/ |
||
| /// <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 | ||
| { | ||
|
|
@@ -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)] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need the |
||
|
|
||
| private int _threadIdOwningParserLock = -1; | ||
|
|
||
| // @TODO: Rename to indicate this has to do with routing | ||
|
|
@@ -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) | ||
|
|
@@ -3063,6 +3071,7 @@ private void Login( | |
| login.password = ConnectionOptions.Password; | ||
| login.applicationName = ConnectionOptions.ApplicationName; | ||
| login.language = _currentLanguage; | ||
| login.appId = _sqlClientAppId; | ||
|
|
||
| if (!login.userInstance) | ||
| { | ||
|
|
||
| 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 | ||
|
cheenamalhotra marked this conversation as resolved.
|
||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing tests. |
| 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest |
||
| { | ||
| /// <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 | ||
|
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; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only need the first sentence here. The compiler won't allow values larger than
ushortonce we set that as the enum's underlying type. It won't be possible to specify larger values.