Uso de agentes de escucha de diagnóstico en EF Core
Sugerencia
Puede descargar el ejemplo de este artículo desde GitHub.
Las escuchas de diagnóstico permiten escuchar cualquier evento de EF Core que se produzca en el proceso de .NET actual. La DiagnosticListener clase forma parte de un mecanismo común en .NET para obtener información de diagnóstico de las aplicaciones en ejecución.
Las escuchas de diagnóstico no son adecuadas para obtener eventos de una sola instancia de DbContext. Los interceptores de EF Core proporcionan acceso a los mismos eventos con el registro por contexto.
Las escuchas de diagnóstico no están diseñadas para el registro. Considere la posibilidad de usar el registro simple o Microsoft.Extensions.Logging para el registro.
Ejemplo: observación de eventos de diagnóstico
La resolución de eventos de EF Core es un proceso de dos pasos. En primer lugar, se debe crear un observador para DiagnosticListener
sí mismo:
public class DiagnosticObserver : IObserver<DiagnosticListener>
{
public void OnCompleted()
=> throw new NotImplementedException();
public void OnError(Exception error)
=> throw new NotImplementedException();
public void OnNext(DiagnosticListener value)
{
if (value.Name == DbLoggerCategory.Name) // "Microsoft.EntityFrameworkCore"
{
value.Subscribe(new KeyValueObserver());
}
}
}
El OnNext
método busca diagnosticListener que procede de EF Core. Este agente de escucha tiene el nombre "Microsoft.EntityFrameworkCore", que se puede obtener de la DbLoggerCategory clase tal como se muestra.
A continuación, este observador debe registrarse globalmente, por ejemplo, en el método Main
de la aplicación:
DiagnosticListener.AllListeners.Subscribe(new DiagnosticObserver());
En segundo lugar, una vez que se encuentra diagnosticListener de EF Core, se crea un nuevo observador de clave-valor para suscribirse a los eventos reales de EF Core. Por ejemplo:
public class KeyValueObserver : IObserver<KeyValuePair<string, object>>
{
public void OnCompleted()
=> throw new NotImplementedException();
public void OnError(Exception error)
=> throw new NotImplementedException();
public void OnNext(KeyValuePair<string, object> value)
{
if (value.Key == CoreEventId.ContextInitialized.Name)
{
var payload = (ContextInitializedEventData)value.Value;
Console.WriteLine($"EF is initializing {payload.Context.GetType().Name} ");
}
if (value.Key == RelationalEventId.ConnectionOpening.Name)
{
var payload = (ConnectionEventData)value.Value;
Console.WriteLine($"EF is opening a connection to {payload.Connection.ConnectionString} ");
}
}
}
Este método OnNext
se llama a este tiempo con un par clave-valor para cada evento de EF Core. La clave es el nombre del evento, que se puede obtener de uno de los siguientes elementos:
- CoreEventId para eventos comunes a todos los proveedores de bases de datos de EF Core
- RelationalEventId para eventos comunes a todos los proveedores de bases de datos relacionales
- Clase similar para eventos específicos del proveedor de base de datos actual. Por ejemplo, SqlServerEventId para el proveedor de SQL Server.
El valor del par clave-valor es un tipo de carga específico del evento. El tipo de carga que se espera se documenta en cada evento definido en estas clases de eventos.
Por ejemplo, el código anterior manipula el ContextInitialized y los eventos ConnectionOpening. Para el primero de estos, la carga es ContextInitializedEventData. Para el segundo, es ConnectionEventData.
Sugerencia
ToString se invalida en cada clase de datos de eventos de EF Core para generar el mensaje de registro equivalente para el evento. Por ejemplo, la llamada ContextInitializedEventData.ToString
genera "Entity Framework Core 5.0.0 inicializado "BlogsContext" mediante el proveedor "Microsoft.EntityFrameworkCore.Sqlite" con opciones: None".
La muestra contiene una aplicación de consola sencilla que realiza cambios en la base de datos de registro e imprime los eventos de diagnóstico encontrados.
public static void Main()
{
DiagnosticListener.AllListeners.Subscribe(new DiagnosticObserver());
using (var context = new BlogsContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.Add(
new Blog { Name = "EF Blog", Posts = { new Post { Title = "EF Core 3.1!" }, new Post { Title = "EF Core 5.0!" } } });
context.SaveChanges();
}
using (var context = new BlogsContext())
{
var blog = context.Blogs.Include(e => e.Posts).Single();
blog.Name = "EF Core Blog";
context.Remove(blog.Posts.First());
blog.Posts.Add(new Post { Title = "EF Core 6.0!" });
context.SaveChanges();
}
La salida de este código muestra los eventos detectados:
EF is initializing BlogsContext
EF is opening a connection to Data Source=blogs.db;Mode=ReadOnly
EF is opening a connection to DataSource=blogs.db
EF is opening a connection to Data Source=blogs.db;Mode=ReadOnly
EF is opening a connection to DataSource=blogs.db
EF is opening a connection to DataSource=blogs.db
EF is opening a connection to DataSource=blogs.db
EF is initializing BlogsContext
EF is opening a connection to DataSource=blogs.db
EF is opening a connection to DataSource=blogs.db