Hooking IDbInterceptor to EntityFramework DbContext only once
c#, entity-framework, entity-framework-6, interceptor, logging
Solution
I figured out that my `DbContext` class just needed to have the `DbConfigurationType` attribute, to attach a configuration at runtime:
[DbConfigurationType(typeof(MyDBConfiguration))]
public partial class MyDbContext // : DbContext
{
public MyDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
{ }
}
public class MyDBConfiguration : DbConfiguration {
public MyDBConfiguration() {
this.AddInterceptor(new MyCommandInterceptor());
}
}
Problem
The `IDbCommandInterceptor` interface is not very well documented. And I've only found a few scarce tutorials on it: - http://www.entityframeworktutorial.net/entityframework6/database-command-interception.aspx - https://msdn.microsoft.com/en-us/data/jj556606%28v=vs.113%29.aspx - https://entityframework.codeplex.com/wikipage?title=Interception - https://www.tutorialspoint.com/entity_framework/entity_framework_command_interception.htm - https://msdn.microsoft.com/en-us/data/dn469464%28v=vs.113%29.aspx And a few SO questions: - Entity Framework 6 - Timing queries - Getting DbContext from implementation of IDbCommandInterceptor These are the suggestions on hooking I've found: 1 - The static `DbInterception` class: ``` DbInterception.Add(new MyCommandInterceptor()); ``` 2 - Doing the above suggestion in a `DbConfiguration` class ``` public class MyDBConfiguration : DbConfiguration { public MyDBConfiguration() { DbInterception.Add(new MyCommandInterceptor()); } } ``` 3 - Using the config file: ``` <entityFramework> <interceptors> <interceptor type="EFInterceptDemo.MyCommandInterceptor, EFInterceptDemo"/> </interceptors> </entityFramework> ``` Although I couldn't figure out how to hook the `DbConfiguration` class to the DbContext, and neither what to put in the `type` part of the config method. Another example I found seemed to suggest that you write the namespace of a logger: ``` type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework" ``` I noted that `DataBaseLogger` implements `IDisposable`, `IDbConfigurationInterceptor` and `IDbInterceptor`. `IDbCommandInterceptor` also implements `IDbInterceptor`, so I tried (without success) to format it like this: ``` type="DataLayer.Logging.MyCommandInterceptor, DataLayer" ``` And when I called the static `DbInterception` class directly, it added another interceptor every call. So my quick and dirty solution was to utilize static constructors: ``` //This partial class is a seperate file from the Entity Framework auto-generated class, //to allow dynamic connection strings public partial class MyDbContext // : DbContext { public Guid RequestGUID { get; private set; } public MyDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { DbContextListeningInitializer.EnsureListenersAdded(); RequestGUID = Guid.NewGuid(); //Database.Log = m => System.Diagnostics.Debug.Write(m); } private static class DbContextListeningInitializer { static DbContextListeningInitializer() //Threadsafe { DbInterception.Add(new MyCommandInterceptor()); } //When this method is called, the static ctor is called the first time only internal static void EnsureListenersAdded() { } } } ``` But what are the proper/intended ways to do it?