如何查看Entity Framework生成的SQL语句?

801

如何查看由Entity Framework生成的SQL语句?

(在我的特定情况下,我正在使用MySQL提供程序 - 如果有影响)


2
这篇来自 MSDN Magazine 的文章介绍了 Entity Framework 4 的一些分析选项。 - Arve
3
这个链接的“重复”问题是关于LINQ to SQL的,所以它实际上不是重复问题。 - jrummell
3
在调试器下运行时,IntelliTrace会显示已执行的SQL查询语句,但不会显示它们的结果。 - ivan_pozdeev
2
如果您在开发过程中想要查看 SQL 语句,可以使用 LINQPad。当您运行 LINQ 查询时,在结果中会有一个 SQL 选项卡,显示执行的 SQL 语句。对于 mySQL,您需要安装驱动程序。我没有可用的 mySQL 数据库,但它应该可以工作。 - gligoran
1
这个问题的复杂答案让我重新考虑EF。在ORM中,这似乎是一个非常基本的功能。 - Kellen Stuart
我看到的大多数方法似乎对我不起作用,但是我最终使用EF 6.4得到了类似于此的东西 var sql = ((dynamic)result).ToString(); - Jazzeroki
24个回答

1129
对于那些使用Entity Framework 6及以上版本(不适用于EF CORE - 请参见下面的注释)的人来说,如果你想在Visual Studio中查看生成的SQL输出(就像我一样),你需要使用新的日志记录/拦截功能。 添加以下代码行将在Visual Studio的输出面板中显示生成的SQL(以及其他与执行相关的详细信息):
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
    context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
    // query the database using EF here.
}

关于在EF6中登录的更多信息,请参阅这个很棒的博客系列:http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/

注意:确保您的项目在DEBUG模式下运行。


115
这个答案值得更多的关注(如果你在使用EF6+)——非常棒的调试补充功能,只需在DBContext构造函数中添加它(this.Database.Log = ...)。 - keithl8041
23
请确保您的项目处于DEBUG MODE下,检查输出窗格组合框中是否选择了“Debug”选项,并确保您的调试未重定向到Immediate(工具>选项>调试>将所有输出窗口文本重定向到即时窗口)。 - rkawano
6
有没有一种方法可以直接将变量值包含在生成的 SQL 中?对于大一些的变量来说有点麻烦。 - Chris Owens
40
这在 EF Core 中无法使用。EF Core 有什么替代方案? - nam
21
警告:我实现这个程序只是为了在开发阶段使用。当我们部署到测试环境后,我们突然开始看到 IIS 工作进程中的内存泄漏。经过内存分析,我们发现即使明确调用 GC,实体上下文对象也无法被回收(是的,它们在 using 语句块中)。移除这行代码会将一切恢复正常。因此,虽然这是一个很好的工具,但请确保仅将其集成到您的应用程序中以进行开发。 - Brandon Barkley
显示剩余14条评论

549

你可以按照以下方式操作:

IQueryable query = from x in appEntities
             where x.id == 32
             select x;

var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();

或者在EF6中:

var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
            .ToTraceString();

或者在EF6.3+中:

var sql = ((dynamic)flooringStoresProducts).Sql;

那将为您提供生成的SQL语句。


22
以这种方式,您将无法获取以.Single()、.Count()、.Any()等结尾的查询的SQL。 - springy76
29
这是因为运行.Single()后,您的对象不再是IQueryable,我想。 - Suhas
14
使用EF6,我只能通过反射获得它。但首先,我必须将“result”转换为“System.Data.Entity.Infrastructure.DbQuery<T>”,然后获取内部属性“InternalQuery”作为“(System.Data.Entity.Internal.Linq.InternalQuery<T>)”,然后才能使用“ToTraceString()”。 - itsho
9
请添加对System.Data.Entity的引用,System.Data.Objects.ObjectQuery存在于上述dll中。 - Mahesh
69
在EF6中,您可以直接使用result.ToString() - Scott Chamberlain
显示剩余14条评论

99

从 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>

1
有关主题的博客文章http://blog.oneunicorn.com/2014/02/09/ef-6-1-turning-on-logging-without-recompiling/ - Tim Abell
17
精确性,它在以下配置中: <configuration> <entityFramework> <interceptors> ... </interceptors> </entityFramework> </configuration> - Christophe P

90
如果您正在使用 DbContext,您可以使用 .ToString() 来获取 SQL 语句。
var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();

17
ToString() 方法会将查询语句中的变量显示为 p__linq__0 等形式,而不是其最终的值(例如:显示 p__linq__0 而非 34563)。 - sports

53

EF Core 5.0+

这个期待已久的功能在 EF Core 5.0 中可用!这是来自每周状态更新的消息:

var query = context.Set<Customer>().Where(c => c.City == city);
Console.WriteLine(query.ToQueryString())

使用SQL Server数据库提供程序时,此输出结果如下:

DECLARE p0 nvarchar(4000) = N'London';

SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName],
[c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone],
[c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[City] = @__city_0

注意到正确类型的参数声明也包含在输出中。这使得可以将其复制/粘贴到SQL Server Management Studio或类似工具中,以便进行调试/分析。

哇呼!!!

(注意:您需要using Microsoft.EntityFrameworkCore;)


3
@ChristianFindlay 这里是接口,包含方法:https://github.com/dotnet/efcore/blob/master/src/EFCore/Query/IQueryingEnumerable.cs。这里是它被包含在EntityFrameworkQueryableExtensions中的位置:https://github.com/dotnet/efcore/blob/master/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs。您正在使用EF Core 5.0吗? - Josh Withee
1
如果您正在觀察或檢查窗口中檢查查詢,而且您沒有將 using Microsoft.EntityFrameworkCore; 添加到您的類中,則可以使用以下方法直接調用擴展方法: Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToQueryString(query); - Heitor Marcos

31

适用于EF 6.0及以上版本: 对于那些想要了解更多关于日志功能和添加到已有答案中的人。

从EF发送到数据库的任何命令都可以被记录。 要查看来自EF 6.x生成的查询,请使用DBContext.Database.Log属性。

被记录的内容

 - SQL for all different kinds of commands. For example:
    - Queries, including normal LINQ queries, eSQL queries, and raw queries from methods such as SqlQuery.
    - Inserts, updates, and deletes generated as part of SaveChanges
    - Relationship loading queries such as those generated by lazy loading
 - Parameters
 - Whether or not the command is being executed asynchronously
 - A timestamp indicating when the command started executing
 - Whether or not the command completed successfully, failed by throwing an exception, or, for async, was canceled
 - Some indication of the result value
 - The approximate amount of time it took to execute the command. Note that this is the time from sending the command to getting the result object back. It does not include time to read the results.
需要翻译的内容未提供,请提供需要翻译的内容。
using (var context = new BlogContext()) 
{ 
    context.Database.Log = Console.Write; 

    var blog = context.Blogs.First(b => b.Title == "One Unicorn"); 

    blog.Posts.First().Title = "Green Eggs and Ham"; 

    blog.Posts.Add(new Post { Title = "I do not like them!" }); 

    context.SaveChangesAsync().Wait(); 
}

输出:

SELECT TOP (1)
    [Extent1].[Id] AS [Id],
    [Extent1].[Title] AS [Title]
    FROM [dbo].[Blogs] AS [Extent1]
    WHERE (N'One Unicorn' = [Extent1].[Title]) AND ([Extent1].[Title] IS NOT NULL)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 4 ms with result: SqlDataReader

SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Title] AS [Title],
    [Extent1].[BlogId] AS [BlogId]
    FROM [dbo].[Posts] AS [Extent1]
    WHERE [Extent1].[BlogId] = @EntityKeyValue1
-- EntityKeyValue1: '1' (Type = Int32)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader

UPDATE [dbo].[Posts]
SET [Title] = @0
WHERE ([Id] = @1)
-- @0: 'Green Eggs and Ham' (Type = String, Size = -1)
-- @1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 12 ms with result: 1

INSERT [dbo].[Posts]([Title], [BlogId])
VALUES (@0, @1)
SELECT [Id]
FROM [dbo].[Posts]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'I do not like them!' (Type = String, Size = -1)
-- @1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader

记录到外部文件:

using (var context = new BlogContext()) 
{  
    using (var sqlLogFile = new StreamWriter("C:\\temp\\LogFile.txt"))
    {          
         context.Database.Log = sqlLogFile.Write;     
         var blog = context.Blogs.First(b => b.Title == "One Unicorn"); 
         blog.Posts.First().Title = "Green Eggs and Ham"; 
         context.SaveChanges();
   }
}

更多信息请查看:记录和拦截数据库操作


如何在EFCore 7中实现此功能? - emrich

27

你可以在EF 4.1中执行以下操作:

var result = from x in appEntities
             where x.id = 32
             select x;

System.Diagnostics.Trace.WriteLine(result .ToString());

那将会给你生成的 SQL 语句。


1
事实上,我认为这仅在查询返回匿名类型时才有效。如果它返回自定义类型,则 ToString() 输出是该自定义类型的命名空间。例如,如果上面的代码是 select new CustomType { x = x.Name },则返回的值将类似于 Company.Models.CustomType 而不是生成的 SQL。 - Chad Levy
13
这项技术为我生成了一个 System.Data.Objects.ObjectQuery``1[MyProject.Models.Product] - Carl G
1
@CarlG System.Data.Objects.ObjectQuery不是EF 4.1 (DbContext)。使用DbContext,它将是System.Data.Entity.Infrastructure.DbQuery`1[MyProject.Models.Product],在调用“ToString()”时确实输出其SQL。 - springy76
这将为您提供生成的SQL语句,在哪里可以看到输出窗口?从下拉菜单中选择哪个选项? - JsonStatham

20

我的答案涉及EF core。我参考了这个Github问题和有关配置DbContext的文档:

简单方法

覆盖您的DbContext类(YourCustomDbContext)的OnConfiguring方法,如此示例所示,使用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
}

复杂

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

这个复杂情况使用:

  • Startup类的ConfigureServices方法中使用IServiceCollection (而不是覆盖OnConfiguring方法; 好处是DbContext和要使用的ILoggerProvider之间的耦合更松散)
  • ILoggerProvider的实现(而不是使用上面显示的ConsoleLoggerProvider实现; 好处是我们的实现显示了如何记录到文件(我没有看到EF Core带有的文件日志提供程序))

像这样:

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;
        }
    } 
}

那么...没有初学者的方法吗? - Juan De la Cruz
1
@JuanDelaCruz 我简化了我的答案;尝试一下简单的替代方案。 - Nate Anderson
5
感谢您发布这篇文章。我感到非常惊讶的是,在 .NET Core 中没有在即时窗口中执行此操作的方法。我不想编写代码,只想调试它。显然,SQL 分析器是一个选项,但仍比以前更复杂了些。 - EGP

17
为了使查询方便,且不改变代码, 将以下内容添加到您的DbContext中,在Visual Studio的输出窗口中检查它即可。

为了使查询方便,且不改变代码,将以下内容添加到您的DbContext中,在Visual Studio的输出窗口中检查它即可。

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.Log = (query)=> Debug.Write(query);
    }

与@Matt Nibecker的答案类似,但是使用这种方法,您无需每次需要查询时都将其添加到当前代码中。


最佳答案! - AlexSC
4
EF Core / 5|6 没有这个.Log方法/属性,真是遗憾。 :( - Scott Fraley

14
有两种方法:
  1. 要查看生成的 SQL,请调用 ToTraceString()。您可以将其添加到监视窗口并设置断点,以查看任何 LINQ 查询在任何给定时间点的查询情况。
  2. 您可以连接跟踪器到所选的 SQL 服务器,该跟踪器将显示所有详细信息的最终查询。对于 MySQL,追踪查询的最简单方法是使用 tail -f 尾部查询日志。您可以在 官方文档中了解有关 MySQL 日志记录工具的更多信息。对于 SQL Server 来说,最简单的方法是使用附带的 SQL Server 分析器。

38
什么的 ToTraceString? - nos
正如我在回复后所提到的,ObjectQuery。 - Benjamin Pollack
2
SQL Server Profiler 可以捕获前 4000 个字符,但 EF 查询可能比这更长。 - user334911

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,