在 .NET Core中如何让EF Core在日志中记录由LINQ生成的SQL语句

在开发中,我们想在调试中查看EF Core执行的sql语句,可以使用SQL Studio Manager Tools工具,另一种方式是使用EF Core提供的日志。在ASP.NET Core使用Entity Framework Core的日志.

早在Entity Framework Core1.0 ,使用相关的ILoggerProvider ILogger 这些基础接口类.来实现过日志记录。.

在Entity Framework Core2.0  估计是为了配合ASP.NET Core的日志.所以对这些接口进行了更进一步的包装,也弃用了一些接口和类,如:IRelationalCommandBuilderFactory ,DbCommandLogData 但是Entity Framework Core2.0 在DbContextOptionsBuilder添加了新的扩展方法.UseLoggerFactory 看到LoggerFactory,研究过ASP.NET Core日志记录的,应该就很熟悉了..这是ASP.NET Core日志记录的工厂类. 也就是为什么我们如果在ASP.NET Core中注入自己的日志记录,也可以通过配置来记录相关的SQL操作的原因. 这里我们主要是直接只监控EF Core的日志.

using dotNET.Core;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace dotNET.EFCoreRepository
{
    /// <summary>
    /// ef 日志
    /// </summary>
    public class EFLoggerProvider : ILoggerProvider
    {
        public ILogger CreateLogger(string categoryName) => new EFLogger(categoryName);
        public void Dispose() { }
    }

    /// <summary>
    /// 
    /// </summary>
    public class EFLogger : ILogger
    {
        private readonly string categoryName;

        public EFLogger(string categoryName) => this.categoryName = categoryName;

        public bool IsEnabled(LogLevel logLevel) => true;

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            //ef core执行数据库查询时的categoryName为Microsoft.EntityFrameworkCore.Database.Command,日志级别为Information
            if (categoryName == "Microsoft.EntityFrameworkCore.Database.Command"
                    && logLevel == LogLevel.Information)
            {
                var logContent = formatter(state, exception);
                NLogger.Debug(logContent);
                //TraceMessage("Something happened.");
                //  NLogger.Info(GetCodeLineAndFileName());
                //TODO: 拿到日志内容想怎么玩就怎么玩吧
                Console.WriteLine();
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(logContent);
                Console.ResetColor();
            }
        }

        public IDisposable BeginScope<TState>(TState state) => null;


        public void TraceMessage(string message,
                                 [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
                                 [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
                                 [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0
)
        {
            NLogger.Debug("message: " + message);
            NLogger.Debug("member name: " + memberName);
            NLogger.Debug("source file path: " + sourceFilePath);
            NLogger.Debug("source line number: " + sourceLineNumber);
        }

        public string GetCodeLineAndFileName()
        {
            StackTrace insStackTrace = new StackTrace(true);
            var insStackFrames = insStackTrace.GetFrames();
            string str = "";
            foreach(var insStackFrame in insStackFrames)
            {
                str += String.Format("\nFile: {0}, Line: {1}\n", insStackFrame.GetFileName(), insStackFrame.GetFileLineNumber());
            }
            return str;
        }


    }
}

第一步:添加日志类

第二步:OnConfiguring  方法添加日志调用

  protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var loggerFactory = new LoggerFactory();
            loggerFactory.AddProvider(new EFLoggerProvider());
            optionsBuilder.UseLoggerFactory(loggerFactory);
            base.OnConfiguring(optionsBuilder);
        }