Bagikan melalui


Menggunakan Listener Diagnostik di EF Core

Tip

Anda dapat mengunduh sampel artikel ini dari GitHub.

Pendengar diagnostik memungkinkan mendengarkan peristiwa EF Core apa pun yang terjadi di dalam proses .NET saat ini. Kelas DiagnosticListener adalah bagian dari mekanisme umum di seluruh .NET untuk mendapatkan informasi diagnostik dari menjalankan aplikasi.

Pendengar diagnostik tidak sesuai untuk mendapatkan peristiwa hanya dari satu instans DbContext. Pencegat EF Core menyediakan akses ke peristiwa yang sama dengan pendaftaran per konteks.

Pendengar diagnostik tidak dirancang untuk melakukan pengelogan. Pertimbangkan untuk menggunakan pengelogan sederhana atau Microsoft.Extensions.Logging untuk pengelogan.

Contoh: Mengamati peristiwa diagnostik

Menyelesaikan peristiwa EF Core adalah proses dua langkah. Pertama, pengamat untuk DiagnosticListener dirinya sendiri harus dibuat:

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());
        }
    }
}

Metode OnNext ini mencari DiagnosticListener yang berasal dari EF Core. Pendengar ini memiliki nama "Microsoft.EntityFrameworkCore", yang dapat diperoleh dari kelas seperti yang ditunjukkan DbLoggerCategory .

Pengamat ini kemudian harus terdaftar secara global, misalnya dalam metode aplikasi Main :

DiagnosticListener.AllListeners.Subscribe(new DiagnosticObserver());

Kedua, setelah EF Core DiagnosticListener ditemukan, pengamat nilai kunci baru dibuat untuk berlangganan peristiwa EF Core yang sebenarnya. Contohnya:

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

Metode OnNext ini kali ini dipanggil dengan pasangan kunci/nilai untuk setiap peristiwa EF Core. Kuncinya adalah nama peristiwa, yang dapat diperoleh dari salah satu dari:

  • CoreEventId untuk peristiwa yang umum untuk semua penyedia database EF Core
  • RelationalEventId untuk peristiwa yang umum untuk semua penyedia database relasional
  • Kelas serupa untuk peristiwa khusus untuk penyedia database saat ini. Misalnya, SqlServerEventId untuk penyedia SQL Server.

Nilai pasangan kunci/nilai adalah jenis payload khusus untuk peristiwa tersebut. Jenis payload yang diharapkan didokumentasikan pada setiap peristiwa yang ditentukan dalam kelas peristiwa ini.

Misalnya, kode di atas menangani ContextInitialized dan ConnectionOpening peristiwa. Untuk yang pertama, payload adalah ContextInitializedEventData. Untuk yang kedua, itu adalah ConnectionEventData.

Tip

ToString ditimpa di setiap kelas data peristiwa EF Core untuk menghasilkan pesan log yang setara untuk peristiwa tersebut. Misalnya, panggilan ContextInitializedEventData.ToString menghasilkan "Entity Framework Core 5.0.0 inisialisasi 'BlogsContext' menggunakan penyedia 'Microsoft.EntityFrameworkCore.Sqlite' dengan opsi: None".

Sampel berisi aplikasi konsol sederhana yang membuat perubahan pada database blogging dan mencetak peristiwa diagnostik yang ditemui.

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();
    }

Output dari kode ini menunjukkan peristiwa yang terdeteksi:

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