由实体框架 DbContext 执行的日志查询

问题描述 投票:0回答:8

我在 MVC 5 项目中使用 EF 6.0 和 LINQ。我想记录实体框架 DbContext 执行的所有 SQL 查询,以用于调试/性能测量目的。

在 Java/Hibernate 中,可以通过设置属性

hibernate.show_sql=true
来实现等效行为。实体框架中是否可能有类似的行为?

c# linq entity-framework asp.net-mvc-5 entity-framework-6
8个回答
95
投票

记录和拦截数据库操作 MSDN 上的文章就是您正在寻找的内容。

DbContext.Database.Log
属性可以设置为任何采用字符串的方法的委托。最常见的是,它通过将其设置为该 TextWriter 的“Write”方法来与任何
TextWriter
一起使用。当前上下文生成的所有 SQL 都将记录到该编写器。例如,以下代码会将 SQL 记录到控制台:

using (var context = new BlogContext())
{
    context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);

    // Your code here...
}

更新 2022:现在在 ef core 的开发中默认启用日志记录。可以在数据库上下文的构建器中配置该行为(启用敏感数据日志记录以记录查询参数值或指定要记录的事件):

      services.AddDbContext<IDbContext, ApplicationDbContext>(
        options => options.UseSqlServer(dbConnectionString)
                          .LogTo(s => System.Diagnostics.Debug.WriteLine(s))
                          .EnableDetailedErrors(isDebugMode)
                          .EnableSensitiveDataLogging(isDebugMode));

或者您可以使用 app.settings 文件来定义您感兴趣的事件的日志记录配置


43
投票
您可以使用此行仅将 SQL 查询记录到 Visual Studio“输出”窗口,而不是控制台窗口,同样仅在调试模式下。

public class YourContext : DbContext { public YourContext() { Database.Log = sql => Debug.Write(sql); } }
    

9
投票
如果您已经设置了带有记录器的 .NET Core,那么 EF 会将其查询记录到您想要的任何输出:调试输出窗口、控制台、文件等。

您只需在应用程序设置中配置“信息”日志级别即可。例如,这将 EF 日志记录到调试输出窗口:

"Logging": { "PathFormat": "Logs/log-{Date}.txt", "IncludeScopes": false, "Debug": { "LogLevel": { "Default": "Information", "System": "Information", "Microsoft": "Information" } }, "Console": { "LogLevel": { "Default": "Information", "System": "Warning", "Microsoft": "Warning" } }, "File": { "LogLevel": { "Default": "Information", "System": "Warning", "Microsoft": "Warning" } }, "LogLevel": { "Default": "Information", "System": "Warning", "Microsoft": "Warning" } }
    

5
投票
EF Core 日志记录自动与 .NET Core 的日志记录机制集成。如何使用它登录控制台的示例:

public class SchoolContext : DbContext { //static LoggerFactory object public static readonly ILoggerFactory loggerFactory = new LoggerFactory(new[] { new ConsoleLoggerProvider((_, __) => true, true) }); //or // public static readonly ILoggerFactory loggerFactory = new LoggerFactory().AddConsole((_,___) => true); public SchoolContext():base() { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseLoggerFactory(loggerFactory) //tie-up DbContext with LoggerFactory object .EnableSensitiveDataLogging() .UseSqlServer(@"Server=.\SQLEXPRESS;Database=SchoolDB;Trusted_Connection=True;"); } public DbSet<Student> Students { get; set; } }

如果您想登录到输出窗口,请使用此选项:

public static readonly ILoggerFactory loggerFactory = new LoggerFactory(new[] { new DebugLoggerProvider() });

https://www.entityframeworktutorial.net/efcore/logging-in-entityframework-core.aspx


4
投票
Entity Framework Core 5 及更高版本

使用附加行覆盖

OnConfiguring

 中的 
YourDbContext
 方法:

public class YourContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.LogTo(Console.WriteLine); } }
    

3
投票
如果有人使用 EF6.1+,有一个简单的方法。检查以下链接了解更多详情。

https://learn.microsoft.com/en-us/ef/ef6/fundamentals/configuring/config-file#interceptors-ef61-onwards

示例代码

<interceptors> <interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework"> <parameters> <parameter value="C:\Stuff\LogOutput.txt"/> <parameter value="true" type="System.Boolean"/> </parameters> </interceptor> </interceptors>
    

2
投票
实体框架核心3

来自

这篇文章

创建工厂并设置过滤器。

var loggerFactory = LoggerFactory.Create(builder => { builder .AddConsole((options) => { }) .AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information); });
在OnConfiguring方法中告诉DbContext使用工厂:

optionsBuilder.UseLoggerFactory(_loggerFactory);
    

0
投票
您也可以使用

IDbCommandInterceptor

,这是一个完整的示例:

// Created By [email protected] at 2024-11-16 14:17:52+0800 using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Common; using System.Text.RegularExpressions; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Newtonsoft.Json; using NLog; using NLog.Config; using NLog.Targets; const string layout = "[${date}] [${level:uppercase=true:padding=5}] [${threadid:padding=2}]" + " [${logger}:${callsite-linenumber}] ${message:withexception=true}"; LogManager.Configuration = new LoggingConfiguration(); LogManager.Configuration.AddRuleForAllLevels(new ConsoleTarget { Layout = layout }); LogManager.Configuration = LogManager.Configuration; var logger = LogManager.GetLogger(nameof(Program)); var db = new MyDbContext(); await db.Database.EnsureCreatedAsync(); await db.Persons.AddAsync(new Person { Name = "Alpha" }); await db.Persons.AddAsync(new Person { Name = "Beta" }); await db.SaveChangesAsync(); var persons = await db.Persons.Where(x => x.Id > 0).ToListAsync(); logger.Info("Persons: {0}", JsonConvert.SerializeObject(persons)); internal class MyDbContext : DbContext { public DbSet<Person> Persons { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { var conn = new SqliteConnection("DataSource=:memory:"); conn.Open(); base.OnConfiguring(optionsBuilder.UseSqlite(conn).AddInterceptors(new SqlInterceptor())); } } [Table("person")] [Index(nameof(Name), IsUnique = true)] internal class Person { [Column("id")] [Key] public int Id { get; set; } [Column("name")] public string Name { get; set; } } internal static partial class MyObjectUtils { public static string PrettyPrint(this DbCommand @this) { var parameters = string.Join(", ", @this.Parameters.OfType<DbParameter>().Select(x => $"{x.ParameterName}={x.Value}")); var command = PrettySpaceRegex().Replace(@this.CommandText, " "); return @this.Parameters.Count == 0 ? command : $"{command} [{parameters}]"; } [GeneratedRegex(@"\s+")] private static partial Regex PrettySpaceRegex(); } internal class SqlInterceptor : IDbCommandInterceptor { private readonly Logger _logger = LogManager.GetLogger(nameof(SqlInterceptor)); public ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = new()) { _logger.Info("[R] {0}", command.PrettyPrint()); return new ValueTask<InterceptionResult<DbDataReader>>(result); } public ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = new()) { _logger.Info("[S] {0}", command.PrettyPrint()); return new ValueTask<InterceptionResult<object>>(result); } public ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = new()) { _logger.Info("[N] {0}", command.PrettyPrint()); return new ValueTask<InterceptionResult<int>>(result); } }
输出示例:

[2024/11/16 14:17:12.478] [ INFO] [ 1] [SqlInterceptor:86] [N] CREATE TABLE "person" ( "id" INTEGER NOT NULL CONSTRAINT "PK_person" PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL ); [2024/11/16 14:17:12.504] [ INFO] [ 1] [SqlInterceptor:86] [N] CREATE UNIQUE INDEX "IX_person_name" ON "person" ("name"); [2024/11/16 14:17:12.644] [ INFO] [ 1] [SqlInterceptor:72] [R] INSERT INTO "person" ("name") VALUES (@p0) RETURNING "id"; [@p0=Alpha] [2024/11/16 14:17:12.660] [ INFO] [ 1] [SqlInterceptor:72] [R] INSERT INTO "person" ("name") VALUES (@p0) RETURNING "id"; [@p0=Beta] [2024/11/16 14:17:12.952] [ INFO] [ 1] [SqlInterceptor:72] [R] SELECT "p"."id", "p"."name" FROM "person" AS "p" WHERE "p"."id" > 0 [2024/11/16 14:17:13.005] [ INFO] [ 1] [Program:28] Persons: [{"Id":1,"Name":"Alpha"},{"Id":2,"Name":"Beta"}]
    
© www.soinside.com 2019 - 2024. All rights reserved.