Como registrar consultas SQL no EF Core

Resumo : neste tutorial, você aprenderá como registrar consultas SQL no EF Core para fins aprimorados de depuração e solução de problemas.

Configurando o log do EF Core

Para registrar informações geradas pelo EF Core em um destino como o Console, um arquivo ou um banco de dados, você pode configurá-lo no OnConfiguring()método da DbContextclasse assim:

protected override void OnConfiguring(
        DbContextOptionsBuilder 
        optionsBuilder
    )
    {
        optionsBuilder
            .UseSqlServer(connectionString)
            .LogTo(target)
    }Linguagem de código:  C#  ( cs )

Por exemplo, para registrar informações geradas pelo EF Core no Console, você pode passar the Console.WriteLinepara o OnConfiguringmétodo da seguinte maneira:

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace HR;

public class HRContext : DbContext
{
    public DbSet<Employee> Employees { get; set;}
    public DbSet<Department> Departments  {  get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {

        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

        var connectionString = configuration.GetConnectionString("Local");

        optionsBuilder.UseSqlServer(connectionString)
                      .LogTo(Console.WriteLine)
                      .EnableSensitiveDataLogging();
    }
}Linguagem de código:  C#  ( cs )

Se você executar o projeto, verá muitas informações exibidas no Console.

Filtrando os registros

O EF Core atribui a cada mensagem de log uma categoria e um nível de log. Para filtrar as mensagens, você pode especificar a categoria da mensagem e o nível de log usando o LogTo()método.

Categoria de mensagem

O EF Core atribui uma categoria de mensagem a cada mensagem de log. Ao filtrar as mensagens por categorias, você pode especificar quais mensagens o EF Core deve incluir nos logs. Para fazer isso, você passa um array de categorias de mensagens para o LogTo()método.

Por exemplo, o seguinte instrui o EF Core a incluir apenas comandos de banco de dados nos logs e gravá-los no Console:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{

    var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .Build();

    var connectionString = configuration.GetConnectionString("Local");


    optionsBuilder.UseSqlServer(connectionString)
                  .LogTo(Console.WriteLine,
                         new[] { DbLoggerCategory.Database.Command.Name });
}Linguagem de código:  C#  ( cs )

Se você executar o projeto, verá que o Console exibe apenas comandos de banco de dados.

Aqui está a versão extraída dos logs que mostra a consulta SQL que o EF Core enviou ao banco de dados para selecionar todas as linhas da Departmentstabela:

...
info: 6/8/2023 18:52:06.635 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (23ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [d].[Id], [d].[Name]
...Linguagem de código:  C#  ( cs )

Nível de registro

Além da categoria de mensagem, o EF Core atribui um nível de log a cada mensagem de log. O nível de log é definido pelo LogLevelenum com os seguintes membros:

  • Depurar (padrão)
  • Erro
  • Crítico
  • Informação
  • Vestígio
  • Aviso
  • Nenhum

Por padrão, o EF Core inclui mensagens de log no nível de depuração e superior. Para filtrar mensagens de log, você pode passar um nível de log mais alto para o LogTo()método.

Por exemplo, o seguinte grava apenas as mensagens de log de informações na Console:

optionsBuilder.UseSqlServer(connectionString)
               .LogTo(Console.WriteLine,
                      new[] { DbLoggerCategory.Database.Command.Name },
                      LogLevel.Information);Linguagem de código:  C#  ( cs )

Se você executar o programa, verá apenas as mensagens de registro de informações no console, como segue:

info: 6/8/2023 18:54:10.967 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (15ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [d].[Id], [d].[Name]
      FROM [Departments] AS [d]Linguagem de código:  C#  ( cs )

Habilitando dados confidenciais

Modifique no Program.csprojeto do aplicativo HR Console para o seguinte:

using HR;
using static System.Console;


using var context = new HRContext();

var name = "Sales";
var departments = context.Departments
                        .Where(d => d.Name == name)
                        .ToList();

foreach (var department in departments)
{
    WriteLine($"{department.Id} {department.Name}");
}Linguagem de código:  C#  ( cs )

Neste programa encontramos os departamentos por nome e exibimos os resultados no console. Se você executar o programa, verá as seguintes informações de log:

info: 6/8/2023 18:55:01.286 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (36ms) [Parameters=[@__name_0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
      SELECT [d].[Id], [d].[Name]
      FROM [Departments] AS [d]
      WHERE [d].[Name] = @__name_0
Linguagem de código:  C#  ( cs )

Como você pode ver no log, ele não mostra o valor real do nameparâmetro. Em vez disso, mostra um ponto de interrogação ( ?).

A razão é que os parâmetros de comando podem conter informações confidenciais, como senhas e números de segurança social. Se você armazenar informações confidenciais nos arquivos de log, elas se tornarão vulneráveis ​​a várias formas de ataques, como hackers, violações de dados e roubo de identidade. Portanto, por padrão, o EF Core não inclui dados do aplicativo no log.

Se você trabalha no ambiente de desenvolvimento ou teste, talvez queira incluir os dados confidenciais no log para fins de depuração e teste.

Para fazer isso você pode chamar o EnableSensitiveDataLogging()método do OptionBuilderobjeto assim:

optionsBuilder.UseSqlServer(connectionString)
              .LogTo(Console.WriteLine,
                    new[] { DbLoggerCategory.Database.Command.Name },
                    LogLevel.Information)
              .EnableSensitiveDataLogging();Linguagem de código:  C#  ( cs )

E se você executar o aplicativo, verá o valor do nome ( 'Sales') em vez de um ponto de interrogação ( ?) no log:

info: 6/8/2023 11:56:24.939 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (28ms) [Parameters=[@__name_0='Sales' (Size = 4000)], CommandType='Text', CommandTimeout='30']
      SELECT [d].[Id], [d].[Name]
      FROM [Departments] AS [d]
      WHERE [d].[Name] = @__name_0Linguagem de código:  C#  ( cs )

Configurando destinos de criação de log

O EF Core dá suporte ao log nos seguintes destinos:

  • Console
  • Arquivo viaStreamWriter
  • Janela de depuração
  • Ou API de registrador externo

Por exemplo, você pode definir o destino de registro para um logs.txtarquivo usando o StreamWriterseguinte:

using HR.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace HR.Data;

public class HRContext : DbContext
{
    // Write logs to logs.txt
    StreamWriter _writer = new("logs.txt", true);

    public DbSet<Employee> Employees { get; set;}

    public DbSet<Department> Departments {  get; set; }

    protected override void OnConfiguring(
        DbContextOptionsBuilder
        optionsBuilder
    )
    {

        // Pass the writer.WriterLine to 
        optionsBuilder
           .UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=HR")
           .LogTo(_writer.WriteLine);
    }

    public override void Dispose()
    {
        // dispose the StreamWriter to save the log file
        _writer.Dispose();
        base.Dispose();
    }
}Linguagem de código:  C#  ( cs )

Como funciona.

Primeiro, crie um novo StreamWriterobjeto que anexe mensagens de log ao logs.txtarquivo:

StreamWriter _writer = new("logs.txt", true);Linguagem de código:  C#  ( cs )

Observe que para anexar mensagens de log ao logs.txtarquivo “”, você define o segundo argumento de como StreamWriterverdadeiro. Caso contrário, substituirá os logs existentes.

Segundo, passe the _writer.WriteLinecomo alvo para o LogTo()método do OptionBuilderobjeto:

optionsBuilder.UseSqlServer(connectionString)
              .LogTo(_writer.WriteLine,
                    new[] { DbLoggerCategory.Database.Command.Name },
                    LogLevel.Information)
                .EnableSensitiveDataLogging();Linguagem de código:  C#  ( cs )

Terceiro, substitua o Dispose()método da HRContextclasse para descartar o StreamWriterobjeto e garantir que o arquivo de log seja salvo corretamente:

public override void Dispose()
{
    // dispose the StreamWriter to save the log file
    _writer.Dispose();
    base.Dispose();
}Linguagem de código:  C#  ( cs )

Se você executar o programa, verá as mensagens de log no logs.txtarquivo.

Observe que se você vir um arquivo logs.txt vazio, é provável que o objeto HRContext não esteja descartado corretamente. Nesse caso, você pode corrigir isso adicionando a instrução using nas declarações HRContext:

using HR;
using static System.Console;


using var context = new HRContext();

var name = "Sales";
var departments = context.Departments
                        .Where(d => d.Name == name)
                        .ToList();

foreach (var department in departments)
{
    WriteLine($"{department.Id} {department.Name}");
}Linguagem de código:  JavaScript  ( javascript )

Resumo

  • Os logs são úteis para depuração e solução de problemas.
  • Os logs do EF Core fornecem muitas informações, incluindo consultas SQL.
  • Use a API de registro simples ( LogTo()método) para configurar o registro em log do EF Core.
  • Use nível e categoria de log para filtrar as mensagens de log.
  • Configure explicitamente dados confidenciais em parâmetros para incluir no log chamando o EnableSensitiveDataLogging()método do OptionBuilderobject.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *