Skip to content

Inject Application Name to SQL Server connection string when not set#36548

Merged
roji merged 1 commit intodotnet:mainfrom
roji:SqlClientConnectionString
Aug 14, 2025
Merged

Inject Application Name to SQL Server connection string when not set#36548
roji merged 1 commit intodotnet:mainfrom
roji:SqlClientConnectionString

Conversation

@roji
Copy link
Member

@roji roji commented Aug 11, 2025

Note: this support via the connection string is temporary and is likely to go away in EF 11 once SqlClient introduce a more first-class user-agent-like mechanism (see #35730 (comment)).

Closes #35730

@roji roji requested review from a team and Copilot August 11, 2025 07:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements automatic injection of application name information into SQL Server connection strings when not explicitly set by the user. The application name includes EF Core version, OS details, framework information, and SqlClient version to aid in monitoring and diagnostics.

  • Adds automatic Application Name injection to SQL Server connections containing EF Core and environment details
  • Modifies connection string handling to preserve user-defined Application Names while adding EF defaults when missing
  • Includes comprehensive test coverage for both connection string and DbConnection scenarios

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/EFCore.SqlServer/Storage/Internal/SqlServerConnection.cs Core implementation adding connection string modification logic and Application Name injection
test/EFCore.SqlServer.Tests/Storage/Internal/SqlServerConnectionTest.cs Test coverage for Application Name injection scenarios and helper method for existing tests

@roji
Copy link
Member Author

roji commented Aug 11, 2025

Hmm, test failures indicate that unfortunate there's a 128-char limit on Application Name (The value's length for key 'Application Name' exceeds its limit of '128').

So instead of a nice long EFCore/10.0.0-dev (macOS 15.6.0 Arm64) .NET 10.0.0-preview.6.25358.103 SqlClient/6.1.0+1af7327454f9cafbf3aaaa8b3d615489aad480ab, we'll just do EFCore/10.0.0-dev (macOS 15.6.0 Arm64) for now.

@ErikEJ
Copy link
Contributor

ErikEJ commented Aug 11, 2025

@roji Could you note use a shorther representation of the SqlClient version?

@roji roji force-pushed the SqlClientConnectionString branch 2 times, most recently from a36ce56 to daf752b Compare August 13, 2025 09:05
@roji
Copy link
Member Author

roji commented Aug 13, 2025

@AndriySvyryd @cincuranet @ErikEJ pushed changes to only rewrite when the user configures EF with a connection string, as discussed, should be ready for review.

// Application Name with EF and versioning info.
// Note that we don't do anything if EF was configured with a SqlConnection, as that could reset the
// password because of Persist Security Info=true
var relationalOptions = RelationalOptionsExtension.Extract(dependencies.ContextOptions);
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't love that we need to extract the options here again (or the smelly assignment from ConnectionString to ConnectionString), but RelationalConnection works hard to prevent implementations from ever knowing if it was configured with a connection string or connection and generally to implement this kind of feature.

I'd propose some refactorings here, but this is temporary throwaway code, since SqlClient will be including a first-class user-agent-like feature anyway - so as a stop-gap this seems ok...

@ErikEJ
Copy link
Contributor

ErikEJ commented Aug 13, 2025

LOTM

@roji roji force-pushed the SqlClientConnectionString branch from daf752b to f41e45e Compare August 13, 2025 09:56
@roji roji force-pushed the SqlClientConnectionString branch from f41e45e to 38632ed Compare August 13, 2025 13:54
@roji
Copy link
Member Author

roji commented Aug 13, 2025

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@roji roji enabled auto-merge (squash) August 13, 2025 19:22
Copy link
Member

@AndriySvyryd AndriySvyryd left a comment

Choose a reason for hiding this comment

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

Good enough for now

// detect that and overwrite.
if (connectionStringBuilder.ApplicationName is "Core Microsoft SqlClient Data Provider" or "" or null)
{
var efVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be replaced with this in order to be single-file deployment compliant:

var efVersion = typeof(SqlServerConnection).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;

I had already reported a similar issue in Microsoft.PowerPlatform.Dataverse.Client a few years ago: microsoft/PowerPlatform-DataverseServiceClient#257 (comment) (except there wasn't a try/catch) 😉

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for flagging this @0xced, opened #37115.

Copy link
Member Author

Choose a reason for hiding this comment

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

@0xced I'd like to fix this, but can you provide a bit more context on what the exact problem is? Is FileVersionInfo somehow incompatible with single-file deployments, is ProductVersion somehow less reliable than AssemblyInformationalVersionAttribute? Any pointers to resources on this would be appreciated.

Copy link
Contributor

Choose a reason for hiding this comment

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

With single-file deployment, the EF Core assembly is bundled into the app host executable, so Assembly.GetExecutingAssembly().Location returns an empty string. This is documented in the Assembly.Location property (remarks):

In .NET 5 and later versions, for bundled assemblies, the value returned is an empty string.

Then, calling FileVersionInfo.GetVersionInfo("") throws System.ArgumentException: The path is empty. (Parameter 'path')

Using AssemblyInformationalVersionAttribute works for both standard and single-file deployments.

Copy link
Contributor

Choose a reason for hiding this comment

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

But are those not completely different data structures??

Copy link
Contributor

Choose a reason for hiding this comment

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

But are those not completely different data structures??

Copy link
Contributor

Choose a reason for hiding this comment

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

From what I understand by reading the source code of the FileVersionInfo class, FileVersionInfo.ProductVersion contains exactly the same value as the AssemblyInformationalVersion attribute when this attribute exists.

https://github.com/dotnet/runtime/blob/v10.0.1/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Unix.cs#L174-L175

[...] unless there is an AssemblyInformationalVersionAttribute, in which case it always uses that for the product version [...]

Reading the whole LoadManagedAssemblyMetadata method seems to confirm this too.

It seems pretty safe to me to use the AssemblyInformationalVersionAttribute to retrieve the EF Core version.

Also, typeof(SqlServerDbContextOptionsExtensions).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion returns 10.0.1 which is exactly what we expect.

Copy link
Member Author

@roji roji Dec 30, 2025

Choose a reason for hiding this comment

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

Thanks @0xced!

Verified via test ApplicationName_is_injected_when_not_defined_with_connection_string that the injected Application Name is the same before and after this change. Submitted #37428 to implement the fix.

@ErikEJ let me know if you see an issue here.

Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM, thanks for the detailed explanation @0xced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQL Server: Introduce EF and its version into the connection string's Application Name

5 participants