You are currently viewing .NET EF Core Interceptors to modify database commands
.NET - EF Core Interceptors

.NET EF Core Interceptors to modify database commands

Entity Framework core exposes some events via DbContext and we have seen how those events can be used. In this article, we are going to have a look at another concept – Interceptors.

What is an EF Core Interceptor ?

As the name suggests, an interceptor can be used to intercept the EF core operations. This feature can be useful for enabling logging and auditing the operations performed by EF core. Interestingly, the interceptors can also be used to modify the operations or EF core operations can also be suppressed altogether via an interceptor.

Note that the interceptors themselves do not log anything. It is just a mechanism to customize the EF core operations. The custom code can either log the data to some store or it can just enhance the queries, without logging anything.

If you wish to enable logging, then  Simple logging or Microsoft.Extensions.Logging can be better choices instead of adding a new interceptor.

How do they work ?

IInterceptor is the god interface of all interceptors. This interface has been used as base interface for other interceptor interfaces. Below are some of the interceptor interfaces and abstract classes which ultimately derive from IInterceptor:

The interfaces (and corresponding abstract too) provide both sync and async versions of the “hook-points”. If you wish to use interface, you will have to implement all the methods brought by the interface. Probably, it may be better to use abstract class if you wish to use only one of the hook-point.

All of these abstract classes have many virtual methods. For every operation that EF core performs, the abstract classes provide a pre and a post method for interception. Although the class is marked as abstract, it does not have any abstract method. All the methods in those classes are virtual. So the derived class can choose which method to override.

How to attach an Interceptor to DbContext?

Once a custom interceptor is created, it must be attached with a DbContext. It can be done by calling AddInterceptors from OnConfiguring method of DbContext.

Demo

For the demo, let’s create a simple DbCommandInterceptor. This custom interceptor would add the order by clause to the query. Note that order by clause can be specified direclty in the LINQ query. The example is just to demonstrate how intercept can be used.

EF Core Class Library

Let’s create a simple .NET core class library – EFCoreInterceptorsDemo.Data.EF. Add references to two NuGet packages in that library:

Then add a simple EF Core model – Student – for holding very basic information related to university students.

Custom Interceptor

Now, order by clause makes sense only if the query is a SELECT query and it returns more than one records. That means in custom interceptor, we will have to identify if order by clause is required or not.

One way is to check the DbCommand.CommandText to see if query is a select query. But this approach may have its own problems and may fail for many cases. Another approach is to use query tagging. A LINQ query can be tagged to let interceptor know if order by clause is required or not. And if the tag is found, then the interceptor will add the clause, otherwise interceptor will skip the query.

For the purpose of this demo, we are going to add some records to the database. and immediately we are going to retrieve them. So, the records will always be fetched in the order in which they were inserted. Hence, in order to see the difference in the resultset returned, let’s add order by descending clause via interceptor.

Below is the demo code for interceptor. Add class this to EFCoreInterceptorsDemo.Data.EF class library.

Demo DbContext

Next, add a new class a UniversityContext class as shown below. The class overrides OnConfiguring method and attaches custom interceptor to the context.

Console App

Next, let’s create a console app (EFCoreInterceptorsDemo) and add reference to EFCoreInterceptorsDemo.Data.EF class library.

Below is how the console app looks like. It creates two instances of context. First instance is used just to add two new entities to database. Second instance is used to perform edit and delete operation and both operations are saved together.

Run and Verify

Now, let’s run the console app to check if we get to see the expected result. The console app should show the output as shown in below snapshot.

.NET Console App – For demonstrating concept of Interceptors

Design Considerations and Limitations

Like .NET Events, the interceptors are also attached to a single instance of DbContext. So if you have multiple DbContexts in the application, you may want to explore other options like Diagnostic Listener.

Although SaveChangesInterceptor provides a way to intercept save changes operation, many times it may be easier to override the SaveChanges or SaveChangesAsync method in the DbContext.

I hope you find this helpful. Let me know your thoughts.

Leave a Reply