Použití diagnostických naslouchacích procesů v EF Core

Tip

Ukázku tohoto článku si můžete stáhnout z GitHubu.

Diagnostické naslouchací procesy umožňují naslouchat všem událostem EF Core, ke kterým dochází v aktuálním procesu .NET. Třída DiagnosticListener je součástí běžného mechanismu v rozhraní .NET pro získání diagnostických informací ze spuštěných aplikací.

Diagnostické naslouchací procesy nejsou vhodné pro získávání událostí z jedné instance DbContextu. Průsečíky EF Core poskytují přístup ke stejným událostem s registrací podle kontextu.

Diagnostické naslouchací procesy nejsou navržené pro protokolování. Zvažte použití jednoduchého protokolování nebo Microsoft.Extensions.Logging pro protokolování.

Příklad: Sledování diagnostických událostí

Řešení událostí EF Core je dvoustupňový proces. Nejprve musí být vytvořen pozorovatel pro DiagnosticListener sebe:

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

Metoda OnNext hledá diagnostickýlistener, který pochází z EF Core. Tento naslouchací proces má název Microsoft.EntityFrameworkCore, který lze získat z DbLoggerCategory třídy, jak je znázorněno.

Tento pozorovatel se pak musí zaregistrovat globálně, například v metodě aplikace Main :

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

Za druhé, jakmile se najde diagnostickýlistener EF Core, vytvoří se nový pozorovatel klíč-hodnota pro přihlášení k odběru skutečných událostí EF Core. Příklad:

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

Metoda OnNext je tentokrát volána s párem klíč/hodnota pro každou událost EF Core. Klíč je název události, kterou lze získat z jednoho z těchto:

  • CoreEventId pro události společné pro všechny poskytovatele databáze EF Core
  • RelationalEventId pro události společné pro všechny zprostředkovatele relačních databází
  • Podobná třída pro události specifické pro aktuálního poskytovatele databáze. Například SqlServerEventId pro poskytovatele SQL Serveru.

Hodnota páru klíč/hodnota je typ datové části specifický pro událost. Typ datové části, kterou očekáváte, je zdokumentovaný pro každou událost definovanou v těchto třídách událostí.

Výše uvedený kód například zpracovává ContextInitialized události a ConnectionOpening události. Pro první z nich je ContextInitializedEventDatadatová část . Za druhé, je ConnectionEventDatato .

Tip

ToString je přepsán v každé datové třídě událostí EF Core, aby se vygenerovala ekvivalentní zpráva protokolu pro událost. Volání například ContextInitializedEventData.ToString vygeneruje "Entity Framework Core 5.0.0 inicializováno 'BlogsContext' pomocí zprostředkovatele Microsoft.EntityFrameworkCore.Sqlite s možnostmi: None".

Ukázka obsahuje jednoduchou konzolovou aplikaci, která provádí změny v databázi blogování a vypíše zjištěné diagnostické události.

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

Výstup z tohoto kódu ukazuje zjištěné události:

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