diff --git a/src/EFCore.ClickHouse/EFCore.ClickHouse.csproj b/src/EFCore.ClickHouse/EFCore.ClickHouse.csproj
index 2e616f1..9d36a85 100644
--- a/src/EFCore.ClickHouse/EFCore.ClickHouse.csproj
+++ b/src/EFCore.ClickHouse/EFCore.ClickHouse.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/src/EFCore.ClickHouse/Storage/Internal/ClickHouseClientIdentity.cs b/src/EFCore.ClickHouse/Storage/Internal/ClickHouseClientIdentity.cs
new file mode 100644
index 0000000..b101cb0
--- /dev/null
+++ b/src/EFCore.ClickHouse/Storage/Internal/ClickHouseClientIdentity.cs
@@ -0,0 +1,26 @@
+using ClickHouse.Driver.ADO;
+
+namespace ClickHouse.EntityFrameworkCore.Storage.Internal;
+
+///
+/// Tags the ClickHouse client's User-Agent with this provider's identity so that
+/// queries it issues are attributable server-side via
+/// (ClickHouse.Driver 1.3.0+).
+///
+internal static class ClickHouseClientIdentity
+{
+ ///
+ /// Value of the lib User-Agent tag identifying this library.
+ ///
+ internal const string LibraryName = "ClickHouse.EntityFrameworkCore";
+
+ ///
+ /// Builds from a connection string with the
+ /// lib User-Agent tag set to .
+ ///
+ internal static ClickHouseClientSettings CreateSettings(string connectionString)
+ => new(connectionString)
+ {
+ ApplicationInfo = new Dictionary { ["lib"] = LibraryName },
+ };
+}
diff --git a/src/EFCore.ClickHouse/Storage/Internal/ClickHouseDataSourceManager.cs b/src/EFCore.ClickHouse/Storage/Internal/ClickHouseDataSourceManager.cs
index 47993e3..6d68c90 100644
--- a/src/EFCore.ClickHouse/Storage/Internal/ClickHouseDataSourceManager.cs
+++ b/src/EFCore.ClickHouse/Storage/Internal/ClickHouseDataSourceManager.cs
@@ -29,7 +29,8 @@ private ClickHouseDataSource GetOrCreateDataSource(string connectionString, bool
if (_dataSources.TryGetValue(effectiveConnectionString, out var existing))
return existing;
- var newDataSource = new ClickHouseDataSource(effectiveConnectionString);
+ var newDataSource = new ClickHouseDataSource(
+ ClickHouseClientIdentity.CreateSettings(effectiveConnectionString));
var added = _dataSources.GetOrAdd(effectiveConnectionString, newDataSource);
if (!ReferenceEquals(added, newDataSource))
diff --git a/src/EFCore.ClickHouse/Storage/Internal/ClickHouseRelationalConnection.cs b/src/EFCore.ClickHouse/Storage/Internal/ClickHouseRelationalConnection.cs
index 680d691..33f7084 100644
--- a/src/EFCore.ClickHouse/Storage/Internal/ClickHouseRelationalConnection.cs
+++ b/src/EFCore.ClickHouse/Storage/Internal/ClickHouseRelationalConnection.cs
@@ -37,9 +37,10 @@ private ClickHouseRelationalConnection(
protected override DbConnection CreateDbConnection()
=> _dataSource?.CreateConnection()
?? new ClickHouseConnection(
- _joinNullSemanticsDisabled
- ? ConnectionString!
- : ClickHouseDataSourceManager.EnsureDefaultSettings(ConnectionString!));
+ ClickHouseClientIdentity.CreateSettings(
+ _joinNullSemanticsDisabled
+ ? ConnectionString!
+ : ClickHouseDataSourceManager.EnsureDefaultSettings(ConnectionString!)));
protected override bool SupportsAmbientTransactions => false;
diff --git a/test/EFCore.ClickHouse.FunctionalTests/EFCore.ClickHouse.FunctionalTests.csproj b/test/EFCore.ClickHouse.FunctionalTests/EFCore.ClickHouse.FunctionalTests.csproj
index 7f51093..6b2c019 100644
--- a/test/EFCore.ClickHouse.FunctionalTests/EFCore.ClickHouse.FunctionalTests.csproj
+++ b/test/EFCore.ClickHouse.FunctionalTests/EFCore.ClickHouse.FunctionalTests.csproj
@@ -9,7 +9,7 @@
-
+
all
diff --git a/test/EFCore.ClickHouse.FunctionalTests/UserAgentClickHouseTest.cs b/test/EFCore.ClickHouse.FunctionalTests/UserAgentClickHouseTest.cs
new file mode 100644
index 0000000..d0a3ba4
--- /dev/null
+++ b/test/EFCore.ClickHouse.FunctionalTests/UserAgentClickHouseTest.cs
@@ -0,0 +1,57 @@
+using ClickHouse.Driver.ADO;
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using Xunit;
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Verifies end-to-end that the provider tags its ClickHouse HTTP requests with the
+/// lib User-Agent token, by reading it back from system.query_log.
+///
+public class UserAgentClickHouseTest
+{
+ private sealed class ProbeContext(string connectionString) : DbContext
+ {
+ private readonly string _connectionString = connectionString;
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder.UseClickHouse(_connectionString);
+ }
+
+ [Fact]
+ public async Task Queries_are_tagged_with_lib_in_user_agent()
+ {
+ var connectionString = TestEnvironment.DefaultConnection;
+ var marker = $"ua_probe_{Guid.NewGuid():N}";
+
+ // Issue a query through EF Core's (tagged) connection. The marker (a GUID) is
+ // concatenated rather than interpolated to keep clear of EF1002.
+ var probeSql = "SELECT 1 /* " + marker + " */";
+ await using (var context = new ProbeContext(connectionString))
+ {
+ await context.Database.ExecuteSqlRawAsync(probeSql);
+ }
+
+ // Use a separate, untagged connection for verification so the lib tag we assert
+ // on can only have come from the provider's own connection.
+ await using var connection = new ClickHouseConnection(connectionString);
+ await connection.OpenAsync();
+
+ using (var flush = connection.CreateCommand())
+ {
+ flush.CommandText = "SYSTEM FLUSH LOGS";
+ await flush.ExecuteNonQueryAsync();
+ }
+
+ using var query = connection.CreateCommand();
+ query.CommandText =
+ "SELECT count() FROM system.query_log " +
+ $"WHERE query LIKE '%{marker}%' " +
+ "AND http_user_agent LIKE '%lib:ClickHouse.EntityFrameworkCore%' " +
+ "AND event_time > now() - INTERVAL 5 MINUTE";
+
+ var count = Convert.ToInt64(await query.ExecuteScalarAsync());
+ Assert.True(count > 0,
+ "Expected EF Core's query to carry lib:ClickHouse.EntityFrameworkCore in the HTTP User-Agent.");
+ }
+}
diff --git a/test/EFCore.ClickHouse.Tests/EFCore.ClickHouse.Tests.csproj b/test/EFCore.ClickHouse.Tests/EFCore.ClickHouse.Tests.csproj
index e570710..9f28695 100644
--- a/test/EFCore.ClickHouse.Tests/EFCore.ClickHouse.Tests.csproj
+++ b/test/EFCore.ClickHouse.Tests/EFCore.ClickHouse.Tests.csproj
@@ -9,7 +9,7 @@
-
+