Interception in Entity Framework Core
Right now, Entity Framework Core still does not have all of the features provided by pre-Core versions, I even wrote a post about this. One of this features is the ability to intercept queries, something that was provided by the IDbCommandInterceptor, IDbCommandTreeInterceptor interfaces.
What we have in EF Core 2.0 is interception at the SQL level, and we can only get to this in a rather convoluted way, through the DiagnosticSource mechanism (yes, I do intend to write about it!). So, let’s create a class with three methods, like this:
public class CommandListener
{
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting")]
public void OnCommandExecuting(DbCommand command, DbCommandMethod executeMethod, Guid commandId, Guid connectionId, bool async, DateTimeOffset startTime)
{
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted")]
public void OnCommandExecuted(object result, bool async)
{
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandError")] public void OnCommandError(Exception exception, bool async) { }
}
You will need the Microsoft.Extensions.DiagnosticAdapter NuGet package to compile this.
We hook these methods to EF Core like this:
var listener = ctx.GetService<DiagnosticSource>();
(listener as DiagnosticListener).SubscribeWithAdapter(new CommandListener());
Here, ctx is a DbContext, and we are accessing it’s registered DiagnosticSource service, which is actually a DiagnosticListener, just not registered as such. We just need to add a subscriber, and that’s all!
From the OnCommandExecuted method – by the way, you can call these methods whatever you like, as long as you keep the [DiagnosticName] attributes –, the result parameter will contain either a scalar (in the case of a ExecuteScalar/ExecuteScalarAsync call), a number (for ExecuteNonQuery/ExecuteNonQueryAsync) or a RelationalDataReader object (in the case of a ExecuteReader/ExecuteReaderAsync call). async, of course, will tell you exactly what version of the method was executed.
OnCommandExecuting is more interesting, because here we actually get access to the DbCommand that is about to execute the SQL command, and we can therefore modify its SQL or parameters. Finally, in OnCommandError you can get any exception that is caught during SQL execution.
There are other interception mechanisms, such as IQuerySqlGenerator and IQueryCompiler, and I will probably cover them in a future post. For now, hope this helps!