Description
File a bug
It was suggested on an issue that a new issue be created for the issue I am experiencing. Here is the behavior.
I have attempted to build both an IDbConnectionInterceptor or IDbCommandInterceptor implementation that allows me to attach to the underlying SqlConnection's InfoMessage event in order to retrieve the messages created by running the command:
SET STATISTICS IO ON;
just prior to individual EF Core SQL command.
Initially, I attempted to do this within an IDbCommandInterceptor since I must modify the command text to include the prefixed statement. Although the IDbCommandInterceptor fired, no such event could be picked up, despite attaching to the event.
At @roji 's suggestion, I attempted to create an implementation of IDbConnectionInterceptor, but none of the events on the basic implementation are being fired.
Include your code
IDbConnectionInterceptor:
public class BareDbConnectionInterceptor : DbConnectionInterceptor, IDbConnectionInterceptor
{
bool attached = false;
public BareDbConnectionInterceptor()
{
}
void InfoMessageHandler(object sender, SqlInfoMessageEventArgs args)
{
Debug.WriteLine(args.Message);
}
void attachStatisticsTracking(SqlConnection sqlConnection)
{
if (attached)
{
sqlConnection.InfoMessage += InfoMessageHandler;
attached = true;
}
}
public override InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
{
attachStatisticsTracking(connection as SqlConnection);
return result;
}
public override Task<InterceptionResult> ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)
{
attachStatisticsTracking(connection as SqlConnection);
return Task.FromResult(result);
}
public override void ConnectionOpened(DbConnection connection, ConnectionEndEventData eventData)
{
attachStatisticsTracking(connection as SqlConnection);
}
public override Task ConnectionOpenedAsync(DbConnection connection, ConnectionEndEventData eventData, CancellationToken cancellationToken = default)
{
attachStatisticsTracking(connection as SqlConnection);
return Task.CompletedTask;
}
public override void ConnectionFailed(DbConnection connection, ConnectionErrorEventData eventData)
{
base.ConnectionFailed(connection, eventData);
}
public override Task ConnectionFailedAsync(DbConnection connection, ConnectionErrorEventData eventData, CancellationToken cancellationToken = default)
{
return base.ConnectionFailedAsync(connection, eventData, cancellationToken);
}
public override InterceptionResult ConnectionClosing(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
{
return base.ConnectionClosing(connection, eventData, result);
}
public override Task<InterceptionResult> ConnectionClosingAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
{
return base.ConnectionClosingAsync(connection, eventData, result);
}
public override void ConnectionClosed(DbConnection connection, ConnectionEndEventData eventData)
{
base.ConnectionClosed(connection, eventData);
}
public override Task ConnectionClosedAsync(DbConnection connection, ConnectionEndEventData eventData)
{
return base.ConnectionClosedAsync(connection, eventData);
}
}
Adding Interceptors to the DbContext:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!(_sharedConnectionFactory?.IsNull).GetValueOrDefault(true))
{
// Shared connections/transactions enable us to handle the entire test run under a single transaction within the database.
optionsBuilder
.UseSqlServer(_sharedConnectionFactory.Connection)
.AddInterceptors(_interceptors);
}
base.OnConfiguring(optionsBuilder);
}
NOTE: A SharedConnectionFactory is being utilized in order to facilitate handling transactions between context instances as part of the same ASP.NET request.
I can confirm the following with breakpoints and stepping over the respective lines of code:
1.) The constructor of the BareDbConnectionInterceptor is being executed.
2.) The IEnumerable (_interceptors, above) contains the BareDbConnectionInterceptor.
3.) That the .AddInterceptors method is being called with the IEnumerable.
However, none of the following methods of the BareDbConnectionInterceptor are being hit by breakpoint and breakpoints are attached correctly:
- ConnectionOpening
- ConnectionOpeningAsync
- ConnectionOpened
- ConnectionOpenedAsync
- ConnectionClosing
- ConnectionClosingAsync
- ConnectionClosed
- ConnectionClosedAsync
- ConnectionFailed
- ConnectionFailedAsync
Include stack traces
No exceptions, including any first-chance exceptions, are being thrown.
Include verbose output
I included what I believed to be the pertinent section, but removed customer-code-specific information
Using assembly '[Project].API'.
Using startup assembly '[Project].API'.
Using application base '[path]\[Project].API\bin\Debug\netcoreapp3.1'.
Using working directory '[path]\[Project].API'.
Using root namespace '[Project].API'.
Using project directory '[path]\[Project].API\'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider...
Finding Microsoft.Extensions.Hosting service provider...
Using environment 'Development'.
Using application service provider from Microsoft.Extensions.Hosting.
Found DbContext '[Project]Context'.
Finding DbContext classes in the project...
Include provider and version information
EF Core version:
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: 3.1.402 [C:\Program Files\dotnet\sdk]
Operating system: Windows 10 V1909
IDE: VS 2019 Version 16.7.3
Thanks for your assistance with this two-part issue.