Bienvenidos a todos a un nuevo post dentro de Entity Framework en el que hoy vamos a ver un tema algo más avanzado en Entity framework. Los interceptores.
1 - ¿Qué son los interceptores?
Antes de pasar al código es muy importante entender que son los interceptores (interceptors). Para entenderlo de forma fácil, son clases que pueden manipular las consultas o los comandos a una base de datos antes o después de que se ejecuten.
Los interceptores utilizan una técnica de programación que la podemos definir como orientada a aspectos (AOP) es una técnica que nos permite interceptar puntos específicos de ejecución en nuestras aplicaciones, permitiéndonos introducir funcionalidad adicional sin tener que modificar el código existente.
En Entity Framework Core permiten la intercepción de las operaciones de la base de datos a bajo nivel, permitiéndonos extender y personalizar el comportamiento de la base de datos.
En este post veremos un ejemplo sencillo, pero en el siguiente, veremos la caché de segundo nivel, la cual se implementa con el uso de interceptores.
2 - ¿Son Los interceptores triggers?
A simple vista, puedes pensar que un interceptor es como un trigger de la propia base de datos, y bueno, la verdad es que no estás muy lejos. Y aunque son similares ya que ambos pueden realizar acciones antes o después de ciertos eventos, no son iguales. Pero como punto comparativo para entender el funcionamiento, nos sirve.
Por entrar un poco más en detalle, los interceptores son mucho más completos, me explico, un trigger se va a ejecutar comúnmente cuando tenemos operaciones CRUD en la propia base de datos. Mientras que un interceptor tiene la posibilidad, además, de actuar, por ejemplo cuando se abre o cierra la conexión, transacciones, etc.
Obviamente el código en los interceptores está escrito en C# y por lo tanto la lógica está en la propia aplicación, esto implica que además de realizar las propias acciones en la base de datos, puedes inyectar servicios o realizar cualquier ejecución de código desde el interceptor.
3 - Tipos de interceptores en Entity Framework Core
Dentro de Entity framework Core disponemos de una serie de interceptores por defecto los cuales van a actuar en sobre diferentes acciones realiadas:
-
DbCommandInterceptor
: Permite interceptar las operaciones de ejecución de comandos SQL, donde incluimos tanto los selects de lectura, como las operaciones de escritura o modificación (insert, update, delete); Es con diferencia el interceptor mas usado, tanto en la empresa, si utilizais alguno, como en librerías populares de GitHub. -
DbconnectionInterceptor
: Permite interceptar cuando una conexión a la base de datos se abre o se cierra. Un ejemplo de su uso, es además de un log típico, sería cambiar la cadena de conexión, directamente en tiempo de ejecución (aunque hay otras formas de hacer esto mejor). -
DbTransactionInterceptor
: Permite interceptar las operaciones de transacciones, útil para saber cuando se inicia, se confirma o se hace rollback de una transacción. -
DbParameterInterceptor
: Permite interceptar las operaciones relacionadas con los parámetros de los comandos SQL. Por ejemplo podemos cambiar el valor de los parámetros. -
SaveChangesInterceptor
: Permite interceptar las operaciones de guardar cambios en la base de datos. Útil si queremos realizar acciones adicionales justo antes o después de dichos cambios. -
DbContextInterceptor
: Permite interceptar eventos relacionados con el ciclo de vida del DbContext de Entity framework.
Además de estos hay mas, por ejemplo el lazy loading que vimos hace un par de posts se hace a través de un interceptor.
Cada uno de estos interceptores nos da una serie de métodos que podemos sobreescribir para personalizar su comportamiento.
4 - Creación de un interceptor para Entity Framework Core
Para entender la implementación lo que vamos a hacer es simplemente ver un ejemplo de un interceptor, en este caso uno sencillito, que no haga mucho, simplemente cuando leemos de la base de datos nos muestre por pantalla lo que está haciendo.
Para ello, creamos una clase que hereda de DbCommandInterceptor
aquí podemos sobreescribir múltiples métodos, lo utilizados para leer o para los commands. En este caso, lo que tenemos que hacer es sobreescribir el método ReaderExecuting
, que es el de leer.
public class ReadExampleInterceptor : DbCommandInterceptor
{
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
}
}
Y simplemente para ver que funciona le incluimos salida por pantalla, en este caso vamos a incluir un mensaje antes de que se ejecute, y otro después:
public class ReadExampleInterceptor : DbCommandInterceptor
{
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
Console.WriteLine("======== HERE start Executing ==========");
return base.ReaderExecuting(command, eventData, result);
}
public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
{
Console.WriteLine("======== HERE executed ==========");
return base.ReaderExecuted(command, eventData, result);
}
}
Que no se nos olvide llamar al método base para que así todo el código siga funcionando como se espera.
Antes de probar el código, debemos registrar dicho interceptor, y para ello, lo hacemos en el DbContextOptionsBuilder
del DbContext
, ya bien sea en el método OnConfiguring
o cuando lo añadimos al contenedor de dependencias:
public static void AddMySql(this IServiceCollection services)
{
services.AddDbContext<CursoEfContext>(options=>
options
.UseLazyLoadingProxies()
.AddInterceptors(new ReadExampleInterceptor())
.UseMySQL("server=127.0.0.1;port=4306;database=cursoEF;user=root;password=cursoEFpass"));
}
Ahora si ejecutamos el código, podemos ver que funciona cuando hacemos consultas con Entity Framework a la base de datos: