在 EF Core 中使用诊断侦听器
提示
可以从 GitHub 中下载本文的示例。
诊断侦听器允许侦听当前 .NET 进程中发生的任何 EF Core 事件。 DiagnosticListener 类是跨 .NET 的通用机制的一部分,用于从正在运行的应用程序中获取诊断信息。
诊断侦听器不适合从单个 DbContext 实例获取事件。 EF Core 侦听器使用按个上下文注册提供对相同事件的访问。
诊断侦听器不能用于日志记录。 考虑使用简单日志记录或 Microsoft.Extensions.Logging 进行日志记录。
示例:观察诊断事件
解决 EF Core 事件的过程分为两个步骤。 首先,必须为 DiagnosticListener
本身创建一个观察程序:
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());
}
}
}
OnNext
方法查找来自 EF Core 的 DiagnosticListener。 此侦听器的名称为“Microsoft.EntityFrameworkCore”,可从 DbLoggerCategory 类中获取,如下所示。
然后,必须全局注册此观察程序,例如在应用程序的 Main
方法中:
DiagnosticListener.AllListeners.Subscribe(new DiagnosticObserver());
其次,找到 EF Core DiagnosticListener 后,便会创建一个新的键值观察程序来订阅实际的 EF Core 事件。 例如:
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} ");
}
}
}
这次使用每个 EF Core 事件的键/值对调用 OnNext
方法。 键是事件的名称,可以从以下类之一获取:
- CoreEventId,适用于所有 EF Core 数据库提供程序的通用事件
- RelationalEventId,适用于所有关系数据库提供程序的通用事件
- 一个类似类,适用于当前数据库提供程序特定的事件。 例如,SQL Server 提供程序的 SqlServerEventId。
键/值对的值是特定于事件的有效负载类型。 预期的有效负载类型记录在这些事件类中定义的每个事件中。
例如,上述代码处理 ContextInitialized 和 ConnectionOpening 事件。 对于其中第一个,有效负载为 ContextInitializedEventData。 对于第二个,为 ConnectionEventData。
提示
在每个 EF Core 事件数据类中替代 ToString,以生成事件的等效日志消息。 例如,调用 ContextInitializedEventData.ToString
会生成“Entity Framework Core 5.0.0 使用提供程序“Microsoft.EntityFrameworkCore.Sqlite”初始化的“BlogsContext”,其选项为“无””。
示例包含一个简单的控制台应用程序,该应用程序对博客数据库进行更改并打印出遇到的诊断事件。
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();
}
此代码的输出显示检测到的事件:
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