当然不要忘记,官网才是最好的老师:docs.microsoft.com/zh-cn/dotnet/core/
沙盒学习指南: 免费环境docs.microsoft.com/zh-cn/learn/browse
posted @ 2020 初久的私房菜 推荐出品

EF日志记录和诊断

 

正文

Entity Framework 中的日志记录与诊断

在当今的软件开发中,日志记录是每个应用程序不可或缺的一部分。它对于调试、故障排除、性能优化、监控、告警以及安全审计等方面都至关重要。Entity Framework 提供了多种机制来生成日志和获取诊断信息。接下来,我们将深入探讨如何使用这些机制。

ToQueryString:快速查看生成的查询

查看 Entity Framework 生成的查询的最简单方法是使用 ToQueryString 方法。这个方法可以返回查询的字符串表示形式,非常适合快速调试。

    .Where(p => p.Id == 1)
    .Select(p => new { p.FirstName, p.LastName })
    .ToQueryString();

Console.WriteLine(query);

// DECLARE @__p_0 bigint = CAST(1 AS bigint);

// SELECT[p].[FirstName], [p].[LastName]
// FROM[People] AS[p]
// WHERE[p].[Id] = @__p_0

这种方法简单易用,但功能有限。它仅适用于查询操作,而不适用于创建、修改或删除操作。因此,建议仅在调试时使用。

简单日志记录:记录所有 EF 操作

如果你希望记录所有 EF 操作的日志,包括事务管理和迁移,可以在配置 DbContext 时启用简单日志记录。

        DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .LogTo(Console.WriteLine)
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();

LogTo 方法接受一个字符串委托,EF 会为每个生成的日志消息调用此委托。通常,我们会使用 Console.WriteLine 将日志输出到控制台,但你也可以使用 Debug.WriteLine 将日志输出到调试窗口,或者使用 StreamWriter 将日志写入文件。

默认情况下,EF 不会在异常消息中包含数据值。如果你希望启用这一功能,可以使用 EnableSensitiveDataLogging 方法。此外,EnableDetailedErrors 方法会为每个数据库调用添加 try-catch 块,这对于诊断某些难以排查的异常非常有帮助。

需要注意的是,简单日志记录仅适用于开发阶段的调试。你不希望在生产环境中暴露敏感数据,同时 try-catch 块也可能会降低性能。

你还可以通过过滤日志来查找特定问题。

        DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .LogTo(Console.WriteLine, LogLevel.Warning)
                .LogTo(Console.WriteLine, new[] { CoreEventId.SaveChangesCompleted });

高级日志记录:使用 Microsoft.Extensions.Logging

对于高级日志记录,你可以使用 UseLoggerFactory 方法结合 Microsoft.Extensions.Logging 来实现。你可以使用 Microsoft 自带的日志记录器,也可以使用其他支持的日志记录器,例如 Serilog

    .WriteTo.Console()
    .CreateLogger();

builder.Logging.ClearProviders();
builder.Logging.AddProvider(
    new SerilogLoggerProvider(logger));
{
    public DbSet<Person> People { get; set; }

    private readonly ILoggerFactory _loggerFactory;

    public MyContext(DbContextOptions<MyContext> options,
        ILoggerFactory loggerFactory) : base(options)
    {
        _loggerFactory = loggerFactory;
    }

    protected override void OnConfiguring(
        DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .UseLoggerFactory(_loggerFactory);
}

这种方法适用于所有环境,包括生产环境。不过,一定要注意不要记录敏感数据,或者配置日志记录器以避免暴露敏感数据。

拦截器:拦截数据库操作

拦截器允许你在数据库操作发送到数据库之前或之后进行拦截,并且可以对其进行修改。拦截器也可以用于日志记录和诊断。

Entity Framework 提供了多种拦截器接口,例如:

  • IDbCommandInterceptor:拦截命令的创建和执行、命令失败以及命令的 DbDataReader 的释放。
  • IDbConnectionInterceptor:拦截连接的打开和关闭以及连接失败。
  • IDbTransactionInterceptor:拦截与事务和保存点相关的所有操作。

要注册拦截器,你需要在配置 DbContext 时使用 AddInterceptors 方法。

{
    public override InterceptionResult<DbDataReader> ReaderExecuting(
               DbCommand command,
                      CommandEventData eventData,
                             InterceptionResult<DbDataReader> result)
    {
        Console.WriteLine(command.CommandText);

        return base.ReaderExecuting(command, eventData, result);
    }
}
        DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .AddInterceptors(new CommandQueryInterceptor());

你可以注册多个拦截器,但需要注意的是,拦截器是按 DbContext 注册的。

诊断监听器:跨多个 DbContext 实例的诊断

诊断监听器可以提供与拦截器相同的信息,但它可以跨当前 .NET 进程中的多个 DbContext 实例工作。

诊断监听器是 .NET 中的标准机制,但它并不是为日志记录而设计的。它更适合用于诊断。要使用诊断监听器,你需要创建一个观察者并将其注册为全局对象。

{
    public void OnCompleted()
        => throw new NotImplementedException();

    public void OnError(Exception error)
        => throw new NotImplementedException();

    public void OnNext(DiagnosticListener value)
    {
        // "Microsoft.EntityFrameworkCore"
        if (value.Name == DbLoggerCategory.Name)
        {
            value.Subscribe(new KeyValueObserver());
        }
    }
}

OnNext 方法中,我们查找 EF 的 DiagnosticListener

接下来,我们需要为特定的 EF 事件创建一个键值观察者。

{
    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.ContextIntialized.Name)
        {
            var payload = (ContextIntializedEventData)value.Value;
            Console.WriteLine($"EF is initializing {payload.Context.GetType().Name} ");
        }
    }
}

OnNext 方法中,我们查找负责上下文初始化的 EF 事件。

总结

日志记录和诊断在 Entity Framework 中至关重要,它们有助于调试、优化性能、确保安全合规、实时监控以及教育开发人员。Entity Framework 提供了多种诊断和日志记录的方法,每种方法都有其优缺点。了解并学习如何使用这些方法来满足你的需求是非常重要的。

posted @   初久的私房菜  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
历史上的今天:
2023-02-21 .Net6 + GraphQL + MongoDb拦截器
2022-02-21 关于Code Review
2022-02-21 使用 dotnet watch 时包含 CSHTML 文件
2022-02-21 记一次公司项目缓存击穿
2022-02-21 C# 9 特性二
2022-02-21 C# 9 特性三
作者:初久的私房菜
好好学习,天天向上
返回顶部小火箭
好友榜:
如果愿意,把你的博客地址放这里
张弛:https://blog.zhangchi.fun/
点击右上角即可分享
微信分享提示