共用方式為


在 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} ");
        }
    }
}

這個 OnNext 方法這次會針對每個 EF Core 事件使用索引鍵/值組來呼叫。 索引鍵是事件的名稱,可從下列其中一個取得:

  • CoreEventId 適用于所有 EF Core 資料庫提供者通用的事件
  • RelationalEventId 適用于所有關系資料庫提供者通用的事件
  • 與目前資料庫提供者特定的事件類似的類別。 例如, SqlServerEventId 針對 SQL Server 提供者。

索引鍵/值組的值是事件特有的承載類型。 預期承載的類型記載于這些事件類別中定義的每個事件上。

例如,上述程式碼會處理 ContextInitializedConnectionOpening 事件。 對於其中第一個,承載為 ContextInitializedEventData 。 第二個是 ConnectionEventData

提示

ToString 會在每個 EF Core 事件資料類別中覆寫,以產生事件的對等記錄訊息。 例如,呼叫 ContextInitializedEventData.ToString 會產生 「Entity Framework Core 5.0.0 使用提供者 'Microsoft.EntityFrameworkCore.Sqlite' 來初始化 'BlogsCoNtext',且選項為:None」。

範例 包含簡單的主控台應用程式,對部落格資料庫進行變更,並列印出所遇到的診斷事件。

public static async Task Main()
{
    DiagnosticListener.AllListeners.Subscribe(new DiagnosticObserver());

    using (var context = new BlogsContext())
    {
        await context.Database.EnsureDeletedAsync();
        await context.Database.EnsureCreatedAsync();

        context.Add(
            new Blog { Name = "EF Blog", Posts = { new Post { Title = "EF Core 3.1!" }, new Post { Title = "EF Core 5.0!" } } });

        await context.SaveChangesAsync();
    }

    using (var context = new BlogsContext())
    {
        var blog = await context.Blogs.Include(e => e.Posts).SingleAsync();

        blog.Name = "EF Core Blog";
        context.Remove(blog.Posts.First());
        blog.Posts.Add(new Post { Title = "EF Core 6.0!" });

        await context.SaveChangesAsync();
    }

此程式碼的輸出會顯示偵測到的事件:

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