다음을 통해 공유


EF Core에서 진단 수신기 사용

GitHub에서 이 문서의 샘플을 다운로드할 수 있습니다.

진단 수신기를 사용하면 현재 .NET 프로세스에서 발생하는 모든 EF Core 이벤트를 수신할 수 있습니다. DiagnosticListener 클래스는 실행 중인 애플리케이션에서 진단 정보를 얻기 위한 .NET의 일반적인 메커니즘의 일부입니다.

진단 수신기는 단일 DbContext 인스턴스에서 이벤트를 가져오는 데 적합하지 않습니다. EF Core 인터셉터는 컨텍스트별 등록을 통해 동일한 이벤트에 대한 액세스를 제공합니다.

진단 수신기는 로깅용으로 설계되지 않았습니다. 로깅에 간단한 로깅 또는 Microsoft.Extensions.Logging을 사용하는 것이 좋습니다.

예: 진단 이벤트 관찰

EF Core 이벤트 해결은 2단계 프로세스입니다. 먼저 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 이벤트에 대한 키/값 쌍을 사용하여 호출됩니다. 키는 다음 중 하나에서 가져올 수 있는 이벤트의 이름입니다.

  • 모든 EF Core 데이터베이스 공급자에 공통적인 이벤트의 경우 CoreEventId
  • 모든 관계형 데이터베이스 공급자에 공통적인 이벤트의 경우 RelationalEventId
  • 현재 데이터베이스 공급자와 관련된 이벤트에 대한 유사한 클래스입니다. 예를 들어 SQL Server 공급자의 경우 SqlServerEventId입니다.

키/값 쌍의 값은 이벤트와 관련된 페이로드 형식입니다. 예상되는 페이로드 유형은 이러한 이벤트 클래스에 정의된 각 이벤트에 대해 설명합니다.

예를 들어 위의 코드는 ContextInitializedConnectionOpening 이벤트를 처리합니다. 이 중 첫 번째 페이로드는 ContextInitializedEventData입니다. 두 번째의 경우 ConnectionEventData입니다.

ToString은 모든 EF Core 이벤트 데이터 클래스에서 재정의되어 이벤트에 해당하는 로그 메시지를 생성합니다. 예를 들어 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