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

时间:2009-09-11 19:33:00

标签: entity-framework ado.net

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

(在我的特殊情况下,我正在使用mysql提供程序 - 如果重要的话)

22 个答案:

答案 0 :(得分:859)

对于使用Entity Framework 6及更高版本的用户,如果要在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模式运行项目。

答案 1 :(得分:435)

您可以执行以下操作:

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();

这将为您提供生成的SQL。

答案 2 :(得分:74)

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

var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();

答案 3 :(得分:74)

从EF6.1开始,您可以使用Interceptor注册数据库记录器。 请参阅文章here

中的“拦截器”和“记录数据库操作”章节
<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>

答案 4 :(得分:17)

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

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

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

这将为您提供生成的SQL。

答案 5 :(得分:16)

有两种方法:

  1. 要查看将生成的SQL,只需调用ToTraceString()即可。您可以将它添加到监视窗口并设置断点,以查看任何给定点的查询对于任何LINQ查询。
  2. 您可以将跟踪器附加到您选择的SQL服务器上,该服务器将显示其所有血腥细节的最终查询。对于MySQL,跟踪查询的最简单方法就是使用tail -f来查询查询日志。您可以在the official documentation中了解有关MySQL日志记录功能的更多信息。对于SQL Server,最简单的方法是使用附带的SQL Server探查器。

答案 6 :(得分:11)

我的回答是EF 核心。我引用了这个github issueconfiguring DbContext上的文档:

<强>简单

覆盖OnConfiguring类(DbContextas shown hereYourCustomDbContext方法以使用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
}

<强>复合

这个复杂的案例避免了覆盖the DbContext OnConfiguring method.,这在文档中是不允许的:&#34;这种方法不适合测试,除非测试针对整个数据库。&#34;

此复杂案例使用:

  • IServiceCollectionStartup方法中的ConfigureServices (而不是覆盖OnConfiguring方法;好处是DbContext和您要使用的ILoggerProvider之间的松散耦合。
  • ILoggerProvider的实现(而不是使用上面显示的ConsoleLoggerProvider实现;好处是我们的实现显示了我们如何登录文件(我没有看到File Logging Provider shipped with 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;
        }
    } 
}

答案 7 :(得分:6)

使查询始终方便,无需更改代码 将此添加到您的DbContext并在visual studio的输出窗口中进行检查。

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

与@Matt Nibecker的答案类似,但是每次您需要查询时,都不必在当前代码中添加它。

答案 8 :(得分:6)

EF Core 5.0

此持久功能已在EF Core 5.0中提供!这来自weekly status updates

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或类似工具,以便查询可以 执行以进行调试/分析。

woohoo !!!

答案 9 :(得分:4)

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

将返回sql查询。使用EntityFramework 6的datacontext工作

答案 10 :(得分:4)

我正在进行集成测试,并且需要它来调试Entity Framework Core 2.1中生成的SQL语句,因此我像这样使用DebugLoggerProviderConsoleLoggerProvider

[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

答案 11 :(得分:4)

好吧,我目前正在使用Express Profiler,但缺点是它只适用于MS SQL Server。您可以在此处找到此工具:https://expressprofiler.codeplex.com/

答案 12 :(得分:2)

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

答案 13 :(得分:2)

我刚刚这样做了:

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

结果显示在输出

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

答案 14 :(得分:2)

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

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

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

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

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

HTH。

答案 15 :(得分:2)

SQL Management Studio =>工具=> SQL Server分析器

File => New Trace ...

使用模板=>空白

事件选择=> T-SQL

左侧检查:SP.StmtComplete

列过滤器可用于选择特定的ApplicationName或DatabaseName

开始运行该配置文件,然后触发查询。

点击此处查看Source information

答案 16 :(得分:2)

在Entity Framework Core 3.x中使用日志记录

Entity Framework Core通过日志记录系统发出SQL。只有几个小技巧。您必须指定ILoggerFactory,并且必须指定过滤器。这是this article

中的示例

创建工厂:

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
    .AddConsole((options) => { })
    .AddFilter((category, level) =>
        category == DbLoggerCategory.Database.Command.Name
        && level == LogLevel.Information);
});

通过DbContext方法告诉OnConfiguring使用工厂:

optionsBuilder.UseLoggerFactory(_loggerFactory);

从这里,您可以变得更加复杂,并可以使用Log方法来提取有关已执行SQL的详细信息。请参阅本文以进行全面讨论。

public class EntityFrameworkSqlLogger : ILogger
{
    #region Fields
    Action<EntityFrameworkSqlLogMessage> _logMessage;
    #endregion
    #region Constructor
    public EntityFrameworkSqlLogger(Action<EntityFrameworkSqlLogMessage> logMessage)
    {
        _logMessage = logMessage;
    }
    #endregion
    #region Implementation
    public IDisposable BeginScope<TState>(TState state)
    {
        return default;
    }
    public bool IsEnabled(LogLevel logLevel)
    {
        return true;
    }
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if (eventId.Id != 20101)
        {
            //Filter messages that aren't relevant.
            //There may be other types of messages that are relevant for other database platforms...
            return;
        }
        if (state is IReadOnlyList<KeyValuePair<string, object>> keyValuePairList)
        {
            var entityFrameworkSqlLogMessage = new EntityFrameworkSqlLogMessage
            (
                eventId,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "commandText").Value,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "parameters").Value,
                (CommandType)keyValuePairList.FirstOrDefault(k => k.Key == "commandType").Value,
                (int)keyValuePairList.FirstOrDefault(k => k.Key == "commandTimeout").Value,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "elapsed").Value
            );
            _logMessage(entityFrameworkSqlLogMessage);
        }
    }
    #endregion
}

答案 17 :(得分:1)

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

答案 18 :(得分:1)

Necromancing。
此页面是搜索任何.NET Framework解决方案时的第一个搜索结果,因此这里作为公共服务,它是如何在EntityFramework 核心中完成的(对于.NET 核心 1&amp; 2):

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

然后是这些扩展方法(适用于.NET Core 1.0的IQueryableExtensions1,适用于.NET Core 2.0的IQueryableExtensions):

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


    }


}

答案 19 :(得分:1)

虽然这里有很好的答案,但没有一个能完全解决我的问题(我希望从任何IQueryable的DbContext中获取整个SQL语句,包括参数。下面的代码就是这样做的。它是Google的代码段的组合。我只在EF6 +上进行过测试

顺便说一句,这个任务花了我比我想象的更长的时间。恕我直言,实体框架中的抽象有点多。

首先使用。您将需要对“ System.Data.Entity.dll”的明确引用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Data.Common;
using System.Data.Entity.Core.Objects;
using System.Data.Entity;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Reflection;

以下类将IQueryable转换为DataTable。根据需要进行修改:

public class EntityFrameworkCommand
{
    DbContext Context;

    string SQL;

    ObjectParameter[] Parameters;

    public EntityFrameworkCommand Initialize<T>(DbContext context, IQueryable<T> query)
    {
        Context = context;
        var dbQuery = query as DbQuery<T>;
        // get the IInternalQuery internal variable from the DbQuery object
        var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
        var iq = iqProp.GetValue(dbQuery, null);
        // get the ObjectQuery internal variable from the IInternalQuery object
        var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
        var objectQuery = oqProp.GetValue(iq, null) as ObjectQuery<T>;
        SQL = objectQuery.ToTraceString();
        Parameters = objectQuery.Parameters.ToArray();
        return this;
    }

    public DataTable GetData()
    {
        DataTable dt = new DataTable();
        var connection = Context.Database.Connection;
        var state = connection.State;
        if (!(state == ConnectionState.Open))
            connection.Open();
        using (var cmd = connection.CreateCommand())
        {
            cmd.CommandText = SQL;
            cmd.Parameters.AddRange(Parameters.Select(p => new SqlParameter("@" + p.Name, p.Value)).ToArray());
            using (var da = DbProviderFactories.GetFactory(connection).CreateDataAdapter())
            {
                da.SelectCommand = cmd;
                da.Fill(dt);
            }
        }
        if (!(state == ConnectionState.Open))
            connection.Close();
        return dt;
    }
}

要使用,只需按以下方式调用它即可:

var context = new MyContext();
var data = ....//Query, return type can be anonymous
    .AsQueryable();
var dt = new EntityFrameworkCommand()
    .Initialize(context, data)
    .GetData();

答案 20 :(得分:0)

实体框架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;
    }

答案 21 :(得分:0)

在visual studio中更新数据库时只使用详细模式

update-database -v

enter image description here