关于ado.net:How如何查看实体框架生成的SQL?

How do I view the SQL generated by the Entity Framework?

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

(在我的特殊情况下,我使用的是MySQL提供程序-如果它很重要的话)


对于那些使用Entity Framework 6及更高版本的用户,如果希望在Visual Studio中查看输出SQL(如我所做的),则必须使用新的日志记录/拦截功能。

添加以下行将在Visual Studio输出面板中显示生成的SQL(以及其他与执行相关的详细信息):

1
2
3
4
5
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/

注意:确保在调试模式下运行项目。


您可以执行以下操作:

1
2
3
4
5
IQueryable query = from x in appEntities
             where x.id = 32
             select x;

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

或在EF6中:

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

这将提供生成的SQL。


如果使用的是dbContext,则可以执行以下操作来获取SQL:

1
2
3
4
5
6
var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();


从EF6.1开始,可以使用拦截器注册数据库记录器。请参阅"拦截器"和"将数据库操作记录到文件"章节。

1
2
3
4
5
6
7
8
<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>


适用于EF6.0及以上:对于那些想要了解更多关于日志功能和添加到已经给出的一些答案中的人。

现在可以记录从EF发送到数据库的任何命令。要查看从ef 6.x生成的查询,请使用DBContext.Database.Log property

记录的内容

1
2
3
4
5
6
7
8
9
10
 - 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.

例子:

1
2
3
4
5
6
7
8
9
10
11
12
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();
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

要登录到外部文件:

1
2
3
4
5
6
7
8
9
10
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();
   }
}

此处提供更多信息:日志记录和截取数据库操作


您可以在EF4.1中执行以下操作:

1
2
3
4
5
var result = from x in appEntities
             where x.id = 32
             select x;

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

这将提供生成的SQL。


有两种方法:

  • 要查看将生成的SQL,只需调用ToTraceString()。您可以将它添加到监视窗口中,并设置一个断点,以查看对于任何LINQ查询,在任何给定点上查询都是什么。
  • 您可以将跟踪程序附加到您选择的SQL Server,它将在所有详细信息中向您显示最终查询。对于MySQL,跟踪查询的最简单方法就是用tail -f跟踪查询日志。您可以在官方文档中进一步了解MySQL的日志记录工具。对于SQL Server,最简单的方法是使用所包含的SQL Server事件探查器。

  • 我的答案是英孚核心。我参考了这个github问题,以及有关配置DbContext的文档:

    简单的

    重写DbContext类(YourCustomDbContext类)的OnConfiguring方法以使用consoleloggerprovider;您的查询应记录到控制台:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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
    }

    复合物

    这种复杂的情况避免了覆盖DbContextOnConfiguring方法。这在文档中是不鼓励的:"这种方法不适合于测试,除非测试以完整的数据库为目标。"

    这个复杂的案例使用:

    • StartupConfigureServices方法中的IServiceCollection。(而不是覆盖OnConfiguring方法;好处是您要使用的DbContextILoggerProvider之间的耦合更松)
    • ILoggerProvider的一个实现(而不是使用上面所示的ConsoleLoggerProvider实现;好处是我们的实现显示了我们如何登录到文件(我看不到带有ef core的文件日志记录提供程序)

    这样地:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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核心查询将出现在文件中。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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;
            }
        }
    }


    使查询始终方便,而不更改代码将其添加到dbContext中,并在Visual Studio的"输出"窗口中进行检查。

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

    类似于@matt nibecker answer,但使用它,您不必每次需要查询时都将其添加到当前代码中。


    我正在进行集成测试,需要这样做来调试实体框架核心2.1中生成的SQL语句,所以我使用DebugLoggerProviderConsoleLoggerProvider这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [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控制台的示例输出:

    Sample SQL statement output


    1
    2
    3
    4
    IQueryable query = from x in appEntities
                       where x.id = 32
                       select x;
    var queryString = query.ToString();

    将返回SQL查询。使用EntityFramework 6的DataContext工作


    嗯,目前我正使用ExpressProfiler来实现这个目的,缺点是它只适用于MS SQL Server。您可以在以下位置找到此工具:https://expressprofiler.codeplex.com/


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


    我刚刚做到了:

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

    结果显示在输出中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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


    在我的例子中,ef 6+,而不是在即时窗口中使用它来查找查询字符串:

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

    我最终不得不使用此命令来获取生成的SQL命令:

    1
    var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();

    当然,您的匿名类型签名可能不同。

    Hth.


    SQL Management Studio=>工具=>SQL Server事件探查器

    文件=>新建跟踪…

    使用模板=>空白

    事件选择=>T-SQL

    左侧手持检查:sp.stmtcomplete

    列筛选器可用于选择特定的applicationname或databasename

    启动运行的配置文件,然后触发查询。

    单击此处获取源信息


    如果您还想要参数值(不仅是@p_linq_0而且是它们的值),您可以使用IDbCommandInterceptor并向ReaderExecuted方法添加一些日志。


    亡灵术。< BR>此页是搜索任何.NET框架的解决方案时的第一个搜索结果,因此作为公共服务,这里介绍在EntityFrameworkCore(对于.NET Core 1&2)中如何执行此操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var someQuery = (
        from projects in _context.projects
        join issues in _context.issues on projects.Id equals issues.ProjectId into tmpMapp
        from issues in tmpMapp.DefaultIfEmpty()
        select issues
    ) //.ToList()
    ;

    // string sql = someQuery.ToString();
    // string sql = Microsoft.EntityFrameworkCore.IQueryableExtensions.ToSql(someQuery);
    // string sql = Microsoft.EntityFrameworkCore.IQueryableExtensions1.ToSql(someQuery);
    // using Microsoft.EntityFrameworkCore;
    string sql = someQuery.ToSql();
    System.Console.WriteLine(sql);

    然后是这些扩展方法(iqueryableextensions1 for.NET core 1.0,iqueryableextensions for.NET core 2.0):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    using System;
    using System.Linq;
    using System.Reflection;
    using Microsoft.EntityFrameworkCore.Internal;
    using Microsoft.EntityFrameworkCore.Query;
    using Microsoft.EntityFrameworkCore.Query.Internal;
    using Microsoft.EntityFrameworkCore.Storage;
    using Remotion.Linq.Parsing.Structure;


    namespace Microsoft.EntityFrameworkCore
    {

        // https://stackoverflow.com/questions/1412863/how-do-i-view-the-sql-generated-by-the-entity-framework
        // http://rion.io/2016/10/19/accessing-entity-framework-core-queries-behind-the-scenes-in-asp-net-core/

        public static class IQueryableExtensions
        {
            private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

            private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields
                .First(x => x.Name =="_queryCompiler");

            private static readonly PropertyInfo NodeTypeProviderField =
                QueryCompilerTypeInfo.DeclaredProperties.Single(x => x.Name =="NodeTypeProvider");

            private static readonly MethodInfo CreateQueryParserMethod =
                QueryCompilerTypeInfo.DeclaredMethods.First(x => x.Name =="CreateQueryParser");

            private static readonly FieldInfo DataBaseField =
                QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name =="_database");

            private static readonly PropertyInfo DatabaseDependenciesField =
                typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name =="Dependencies");

            public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
            {
                if (!(query is EntityQueryable<TEntity>) && !(query is InternalDbSet<TEntity>))
                {
                    throw new ArgumentException("Invalid query");
                }

                var queryCompiler = (QueryCompiler) QueryCompilerField.GetValue(query.Provider);
                var nodeTypeProvider = (INodeTypeProvider) NodeTypeProviderField.GetValue(queryCompiler);
                var parser = (IQueryParser) CreateQueryParserMethod.Invoke(queryCompiler, new object[] {nodeTypeProvider});
                var queryModel = parser.GetParsedQuery(query.Expression);
                var database = DataBaseField.GetValue(queryCompiler);
                var databaseDependencies = (DatabaseDependencies) DatabaseDependenciesField.GetValue(database);
                var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
                var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
                modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
                var sql = modelVisitor.Queries.First().ToString();

                return sql;
            }
        }



        public class IQueryableExtensions1
        {
            private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

            private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo()
                .DeclaredFields
                .First(x => x.Name =="_queryCompiler");

            private static readonly PropertyInfo NodeTypeProviderField =
                QueryCompilerTypeInfo.DeclaredProperties.Single(x => x.Name =="NodeTypeProvider");

            private static readonly MethodInfo CreateQueryParserMethod =
                QueryCompilerTypeInfo.DeclaredMethods.First(x => x.Name =="CreateQueryParser");

            private static readonly FieldInfo DataBaseField =
                QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name =="_database");

            private static readonly FieldInfo QueryCompilationContextFactoryField = typeof(Database).GetTypeInfo()
                .DeclaredFields.Single(x => x.Name =="_queryCompilationContextFactory");


            public static string ToSql<TEntity>(IQueryable<TEntity> query) where TEntity : class
            {
                if (!(query is EntityQueryable<TEntity>) && !(query is InternalDbSet<TEntity>))
                {
                    throw new ArgumentException("Invalid query");
                }

                var queryCompiler = (IQueryCompiler) QueryCompilerField.GetValue(query.Provider);

                var nodeTypeProvider = (INodeTypeProvider) NodeTypeProviderField.GetValue(queryCompiler);
                var parser =
                    (IQueryParser) CreateQueryParserMethod.Invoke(queryCompiler, new object[] {nodeTypeProvider});
                var queryModel = parser.GetParsedQuery(query.Expression);
                var database = DataBaseField.GetValue(queryCompiler);
                var queryCompilationContextFactory =
                    (IQueryCompilationContextFactory) QueryCompilationContextFactoryField.GetValue(database);
                var queryCompilationContext = queryCompilationContextFactory.Create(false);
                var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
                modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
                var sql = modelVisitor.Queries.First().ToString();

                return sql;
            }


        }


    }