如何查看实体框架生成的SQL ?

(在我的特殊情况下,我使用mysql提供商-如果它重要)


当前回答

我的答案是针对EF core的。我参考了这个github问题,以及配置DbContext的文档:

简单的

重写DbContext类(YourCustomDbContext)的onconfiguration方法,如下所示使用ConsoleLoggerProvider;你的查询应该记录到控制台:

public class YourCustomDbContext : DbContext
{
    #region DefineLoggerFactory
    public static readonly LoggerFactory MyLoggerFactory
        = new LoggerFactory(new[] {new ConsoleLoggerProvider((_, __) => true, true)});
    #endregion


    #region RegisterLoggerFactory
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseLoggerFactory(MyLoggerFactory); // Warning: Do not create a new ILoggerFactory instance each time                
    #endregion
}

复杂的

这个Complex case避免覆盖DbContext onconfiguration方法。,这在文档中是不鼓励的:“这种方法不适合测试,除非测试的目标是整个数据库。”

这个复杂的案例使用:

启动类ConfigureServices方法中的IServiceCollection (而不是重写onconfiguration方法;好处是DbContext和你想要使用的ILoggerProvider之间的耦合更松散) ILoggerProvider的实现(而不是使用上面所示的ConsoleLoggerProvider实现;好处是我们的实现展示了我们如何将日志记录到文件(我没有看到一个文件日志记录提供程序与EF核心))

是这样的:

public class Startup

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        var lf = new LoggerFactory();
        lf.AddProvider(new MyLoggerProvider());

        services.AddDbContext<YOUR_DB_CONTEXT>(optionsBuilder => optionsBuilder
                .UseSqlServer(connection_string)
                //Using the LoggerFactory 
                .UseLoggerFactory(lf));
        ...
    }
}

这里是MyLoggerProvider的实现(以及它的MyLogger,它将其日志附加到您可以配置的文件中;您的EF Core查询将出现在文件中。)

public class MyLoggerProvider : ILoggerProvider
{
    public ILogger CreateLogger(string categoryName)
    {
        return new MyLogger();
    }

    public void Dispose()
    { }

    private class MyLogger : ILogger
    {
        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            File.AppendAllText(@"C:\temp\log.txt", formatter(state, exception));
            Console.WriteLine(formatter(state, exception));
        }

        public IDisposable BeginScope<TState>(TState state)
        {
            return null;
        }
    } 
}

其他回答

从EF6.1开始,您可以使用拦截器来注册数据库记录器。 请参见“拦截器”和“记录数据库操作”章节

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

我刚刚做了这个:

IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);

结果显示在Output:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Code] AS [Code], 
    [Extent1].[Name] AS [Name], 
    [Extent2].[Id] AS [Id1], 
    [Extent2].[FileName] AS [FileName], 
    FROM  [dbo].[Products] AS [Extent1]
    INNER JOIN [dbo].[PersistedFiles] AS [Extent2] ON [Extent1].[PersistedFileId] = [Extent2].[Id]
    WHERE [Extent1].[Id] = @p__linq__0

我正在做集成测试,需要在实体框架核心2.1中调试生成的SQL语句,所以我使用DebugLoggerProvider或ConsoleLoggerProvider,如下所示:

[Fact]
public async Task MyAwesomeTest
    {
        //setup log to debug sql queries
        var loggerFactory = new LoggerFactory();
        loggerFactory.AddProvider(new DebugLoggerProvider());
        loggerFactory.AddProvider(new ConsoleLoggerProvider(new ConsoleLoggerSettings()));

        var builder = new DbContextOptionsBuilder<DbContext>();
        builder
            .UseSqlServer("my connection string") //"Server=.;Initial Catalog=TestDb;Integrated Security=True"
            .UseLoggerFactory(loggerFactory);

        var dbContext = new DbContext(builder.Options);

        ........

下面是Visual Studio控制台的输出示例:

实体框架4解决方案

这里的大多数答案都是ef6特异性的。这里有一个给那些还在使用EF4的人。

这个方法替换了@p__linq__0/etc。参数的实际值,所以您可以复制并粘贴输出到SSMS并运行它或调试它。

    /// <summary>
    /// Temporary debug function that spits out the actual SQL query LINQ is generating (with parameters)
    /// </summary>
    /// <param name="q">IQueryable object</param>
    private string Debug_GetSQLFromIQueryable<T>(IQueryable<T> q)
    {
        System.Data.Objects.ObjectQuery oq = (System.Data.Objects.ObjectQuery)q;
        var result = oq.ToTraceString();
        List<string> paramNames = new List<string>();
        List<string> paramVals = new List<string>();
        foreach (var parameter in oq.Parameters)
        {
            paramNames.Add(parameter.Name);
            paramVals.Add(parameter.Value == null ? "NULL" : ("'" + parameter.Value.ToString() + "'"));
        }
        //replace params in reverse order, otherwise @p__linq__1 incorrectly replaces @p__linq__10 for instance
        for (var i = paramNames.Count - 1; i >= 0; i--)
        {
            result = result.Replace("@" + paramNames[i], paramVals[i]);
        }
        return result;
    }

对我来说,使用EF6和Visual Studio 2015,我在即时窗口中输入查询,它给了我生成的SQL语句