Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
60037bc
Add SqlClient agent identifier to USERAGENT payload
cheenamalhotra Sep 2, 2026
c9f0cec
Address PR review feedback
cheenamalhotra Sep 2, 2026
171d890
Make SqlClientAgent CLS-compliant and cover config failures
cheenamalhotra Sep 2, 2026
1d296c9
Lock SqlClientAgent underlying type and drop stale attribute
cheenamalhotra Sep 2, 2026
0c2bfcc
Inline the agent registration sample
cheenamalhotra Sep 2, 2026
a1ae554
Tests | Stop CancelAndDisposePreparedCommand deadlocking on the catalog
cheenamalhotra Sep 2, 2026
9c7f4b0
Report agent registration outcome and stop using the catalog in cance…
cheenamalhotra Sep 3, 2026
2f82e5e
Report application identity in USERAGENT payload V2
cheenamalhotra Sep 3, 2026
1510d6f
Preserve application identity across clones, add sqlpackage
cheenamalhotra Sep 3, 2026
d849cf4
Reject identity changes after login, propagate to discovery login
cheenamalhotra Sep 3, 2026
095d84e
Document identity exception and trust boundary
cheenamalhotra Sep 3, 2026
e8a5509
Leave localized resx files to the OneLocBuild pipeline
cheenamalhotra Sep 4, 2026
4c29388
Refine USERAGENT application API
cheenamalhotra Sep 9, 2026
9bfbc07
Remove unused using directive for System.Diagnostics
cheenamalhotra Sep 9, 2026
f087c29
Rename application property and complete review coverage
cheenamalhotra Sep 10, 2026
597e75a
Rename SqlClientApp to SqlClientApplication
cheenamalhotra Sep 10, 2026
a301c0b
Rename application enum to RegisteredApplication
cheenamalhotra Sep 10, 2026
05516e0
Add registered application state and pooling tests
cheenamalhotra Sep 10, 2026
474fdc4
Make registered application assignment atomic
cheenamalhotra Sep 10, 2026
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
147 changes: 147 additions & 0 deletions doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<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.
</para>
</remarks>
</SqlClientApp>
<Unknown>
<summary>
No application identity is reported. This is the default.
</summary>
<value>
0
</value>
</Unknown>
<EntityFrameworkCore>
<summary>
The Microsoft Entity Framework Core SQL Server provider.
</summary>
<value>
1
</value>
</EntityFrameworkCore>
<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>
<EntityFramework>
<summary>
Microsoft Entity Framework 6 SQL Server provider.
</summary>
<value>
8
</value>
</EntityFramework>
<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>
<DataApiBuilder>
<summary>
Microsoft Data API builder.
</summary>
<value>
13
</value>
</DataApiBuilder>
</members>
</docs>
50 changes: 50 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,56 @@ The following sample tries to open a connection to an invalid database to simula
</para>
</remarks>
</System.ICloneable.Clone>
<SqlClientApp>
<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 SqlConnection connection = new(connectionString)
{
SqlClientApp = SqlClientApp.EntityFrameworkCore
};
connection.Open();
</code>
</example>
<exception cref="T:System.InvalidOperationException">
The connection is opening or open. The identity is reported during login, so it must be set beforehand.
Comment thread
paulmedynski marked this conversation as resolved.
</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.
</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>
</SqlClientApp>
<WorkstationId>
<summary>
Gets a string that identifies the database client.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@
<NoWarn>$(NoWarn);CS0618</NoWarn>
</PropertyGroup>

<!-- CLS Compliance ================================================== -->
<ItemGroup>
<AssemblyAttribute Include="System.CLSCompliantAttribute">
<_Parameter1>true</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<!-- Versioning ====================================================== -->
<!-- Import our Versions.props unless it was already imported (e.g. by Directory.Packages.props
in Package mode). The guard property inside Versions.props makes this a no-op on the
Expand Down
37 changes: 37 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,40 @@ 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/*' />
[System.CLSCompliantAttribute(false)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since this type is opt-in telemetry for existing apps, I don't think the lack of CLS compliance is a problem. Any existing apps relying on CLS compliance will need to take explicit steps to use this enum and the new SqlConnection.SqlClientApp property - I'm fine with that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed. The enum and property remain explicitly non-CLS-compliant.

public enum SqlClientApp : ushort
{
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/Unknown/*' />
Unknown = 0,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFrameworkCore/*' />
EntityFrameworkCore = 1,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SemanticKernel/*' />
SemanticKernel = 2,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/ManagementStudio/*' />
ManagementStudio = 3,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlManagementObjects/*' />
SqlManagementObjects = 4,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DataTierApplicationFramework/*' />
DataTierApplicationFramework = 5,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlToolsService/*' />
SqlToolsService = 6,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AspNetCoreDistributedSqlServerCache/*' />
AspNetCoreDistributedSqlServerCache = 7,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFramework/*' />
EntityFramework = 8,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AzureFunctionsSqlExtension/*' />
AzureFunctionsSqlExtension = 9,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/OrleansAdoNet/*' />
OrleansAdoNet = 10,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DurableTaskSqlServer/*' />
DurableTaskSqlServer = 11,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlPackage/*' />
SqlPackage = 12,
/// <include file='../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DataApiBuilder/*' />
DataApiBuilder = 13
}

/// <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 +1044,9 @@ 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"]/SqlClientApp/*' />
[System.CLSCompliantAttribute(false)]
public Microsoft.Data.SqlClient.SqlClientApp SqlClientApp { 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 _sqlClientApp;

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 sqlClientApp = SqlClientApp.Unknown)
: base(metrics)
{
Debug.Assert(connectionOptions is not null, "null connectionOptions");

ConnectionOptions = connectionOptions;
_sqlClientApp = sqlClientApp;

#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 = _sqlClientApp;

if (!login.userInstance)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

namespace Microsoft.Data.SqlClient;

/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlClientApp/*' />
[System.CLSCompliant(false)]
public enum SqlClientApp : ushort
{
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/Unknown/*' />
Unknown = 0,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFrameworkCore/*' />
EntityFrameworkCore = 1,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SemanticKernel/*' />
SemanticKernel = 2,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/ManagementStudio/*' />
ManagementStudio = 3,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlManagementObjects/*' />
SqlManagementObjects = 4,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DataTierApplicationFramework/*' />
DataTierApplicationFramework = 5,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlToolsService/*' />
SqlToolsService = 6,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AspNetCoreDistributedSqlServerCache/*' />
AspNetCoreDistributedSqlServerCache = 7,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/EntityFramework/*' />
EntityFramework = 8,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/AzureFunctionsSqlExtension/*' />
AzureFunctionsSqlExtension = 9,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/OrleansAdoNet/*' />
OrleansAdoNet = 10,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DurableTaskSqlServer/*' />
DurableTaskSqlServer = 11,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/SqlPackage/*' />
SqlPackage = 12,
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlClientApp.xml' path='docs/members[@name="SqlClientApp"]/DataApiBuilder/*' />
DataApiBuilder = 13
}
Loading
Loading