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

nos*_*nos 587 ado.net entity-framework

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

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

小智 913

对于使用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.
}
Run Code Online (Sandbox Code Playgroud)

有关在这个精彩的博客系列中登录EF6的更多信息:http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/

注意:确保以DEBUG模式运行项目.

  • 这个答案值得更多的爱(如果你正在使用EF6 +) - 很好的调试添加,只需将它添加到DBContext构造函数(this.Database.Log = ...) (102认同)
  • 确保在DEBUG MODE中运行项目,检查输出窗格的组合框中是否选择了"Debug"项,并检查调试是否未重定向到Immediate(工具>选项>调试>将所有输出窗口文本重定向到立即窗口) (21认同)
  • @Matt Nibecker这在EF Core中不起作用.EF Core的替代品是什么? (18认同)
  • 警告:我实现这个目的只是为了它只在开发中运行.当我们部署到我们的测试环境时,我们开始突然看到IIS工作进程中的内存泄漏.在内存分析之后,我们意识到即使显式GC也不再收集实体上下文对象(是的,它们在使用语句中).删除此行恢复正常.因此,虽然这是一个很棒的工具,但请确保只将其构建到您的应用程序中以进行开发. (8认同)
  • 有没有办法让这个变量值直接包含在生成的sql中?有点大的痛苦. (5认同)
  • 每个人都在寻找 ef core 的解决方案。请参阅 Rosdi Kasim 的答案:/sf/answers/3688076241/ (4认同)
  • 如何使用 ASP.NET Core 进行配置?DbContext 是自动注入的,我们不控制它的创建。 (2认同)
  • 你如何为核心做到这一点? (2认同)
  • .NET 6 > 错误 CS1656:无法分配给“日志”,因为它是“方法组” (2认同)

Nic*_*rdi 454

您可以执行以下操作:

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

var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
Run Code Online (Sandbox Code Playgroud)

或在EF6中:

var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
            .ToTraceString();
Run Code Online (Sandbox Code Playgroud)

这将为您提供生成的SQL.

  • 在EF6中,你可以做`result.ToString()` (46认同)
  • 那是因为在运行`.Single()之后,你的对象不再是`IQueryable`我猜. (22认同)
  • 对于以.Single(),. Count(.),. Any()等结尾的查询,您将不会获得SQL. (20认同)
  • 使用EF6,我只能通过反射得到它.但首先,我必须将`result`转换为`System.Data.Entity.Infrastructure.DbQuery <T>`,然后将内部属性`InternalQuery`作为`(System.Data.Entity.Internal.Linq.InternalQuery <T> )``,然后才使用`ToTraceString()` (11认同)
  • 添加对System.Data.Entity的引用,System.Data.Objects.ObjectQuery存在于上面的dll中 (8认同)
  • @sports - re:EF 6,System.Data.Objects没有删除..它实际上在这里:System.Data.Entity.Core.Objects.ObjectQuery (5认同)
  • @NickBerardi这在EF Core中不起作用.EF Core的替代品是什么? (3认同)
  • 当查询是DTO时似乎不起作用,例如IQueryable <MyDto>异常消息="无法转换类型为'System.Data.Entity.Infrastructure.DbQuery`1 [My.Controllers.MyDto]'的对象以'键入' System.Data.Entity.Core.Objects.ObjectQuery"." (3认同)
  • @ScottChamberlain它返回查询,但没有绑定值。 (2认同)
  • 在使用 `((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString()` 的 EF6 中,我“抛出了类型为 'System.InvalidCastException' 的异常”消息。为什么?使用 'query.ToString()' 我可以看到所有的树,但不能看到 SQL。 (2认同)
  • 这在 EF6 中不起作用;我必须将转换更改为 DbQuery&lt;T&gt;,例如:`var sql = ((System.Data.Entity.Infrastruct.DbQuery&lt;MyEntity&gt;)query).Sql;` (2认同)

Dou*_*ter 78

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

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

  • `ToString()`会给你带有变量的查询,比如`p__linq__0`,而不是最终值(例如:34563而不是`p__linq__0`) (12认同)

小智 78

从EF6.1开始,您可以使用Interceptor来注册数据库记录器.请在此处查看"拦截器"和"记录数据库操作"章节

<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>
Run Code Online (Sandbox Code Playgroud)

  • 精确,它位于:<configuration> <entityFramework> <interceptors> ... </ interceptors> </ entityFramework> </ configuration> (11认同)

Nul*_*nce 22

适用于EF 6.0及以上版本:对于那些想要了解日志记录功能并添加已经给出的一些答案的人.

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

记录的内容

 - 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.
Run Code Online (Sandbox Code Playgroud)

例:

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(); 
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

要登录外部文件:

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();
   }
}
Run Code Online (Sandbox Code Playgroud)

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


Jos*_*hee 21

英孚核心 5.0

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

var query = context.Set<Customer>().Where(c => c.City == city);
Console.WriteLine(query.ToQueryString())
Run Code Online (Sandbox Code Playgroud)

使用 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
Run Code Online (Sandbox Code Playgroud)

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

呜呜!!!

  • 这个方法不存在。 (2认同)
  • @ChristianFindlay [此处](https://github.com/dotnet/efcore/blob/master/src/EFCore/Query/IQueryingEnumerable.cs) 是该方法的接口,[此处](https://github.com /dotnet/efcore/blob/master/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs) 是它包含在 EntityFrameworkQueryableExtensions 中的位置。您使用的是 EF Core 5.0 吗? (2认同)

小智 19

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

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

System.Diagnostics.Trace.WriteLine(result .ToString());
Run Code Online (Sandbox Code Playgroud)

这将为您提供生成的SQL.

  • 这个技术为我生成`System.Data.Objects.ObjectQuery``1 [MyProject.Models.Product]`. (8认同)

Ben*_*ack 16

有两种方法:

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

  • ToTraceString是什么? (26认同)
  • SQL Server Profiler捕获前4000个字符,但EF查询可能比这长得多. (2认同)

The*_*Pea 14

我的回答是针对EF 核心的.我引用了这个github问题,以及有关配置DbContext的文档:

简单

覆盖class()的OnConfiguring方法,如此处所示,以使用ConsoleLoggerProvider; 您的查询应该登录到控制台:DbContextYourCustomDbContext

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
}
Run Code Online (Sandbox Code Playgroud)

复杂

此复杂案例避免了覆盖DbContext OnConfiguring方法.在文档中不鼓励:"这种方法不适合测试,除非测试针对整个数据库."

此复杂案例使用:

  • IServiceCollectionStartupConfigureServices方法(而不是重写OnConfiguring方法的好处是之间的松耦合DbContextILoggerProvider要使用)
  • 实现ILoggerProvider(而不是使用ConsoleLoggerProvider上面显示的实现;好处是我们的实现显示我们将如何登录到File(我没有看到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));
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

这是a的实现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;
        }
    } 
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您发布此内容。令我大吃一惊的是,在 .NET Core 中已经没有办法在立即窗口中执行此操作了。我不想写代码,我只是想调试它。SQL 分析器显然是一个选项,但仍然比以前更复杂 (3认同)

and*_*ate 8

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

文件 => 新跟踪...

使用模板 => 空白

事件选择 => T-SQL

左侧检查:SP.StmtComplete

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

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

单击此处获取源信息

  • 抱歉,这仅适用于 SQL 服务器,而不适用于 MySQL (2认同)

Ger*_*ius 7

要使查询始终方便,不更改代码,请将其添加到DbContext并在visual studio的输出窗口中进行检查.

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.Log = (query)=> Debug.Write(query);
    }
Run Code Online (Sandbox Code Playgroud)

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

  • 遗憾的是 EF Core / 5|6 甚至不再有这个 `.Log` 方法/属性。:( (4认同)

Jer*_*ren 7

虽然这里有很好的答案,但没有一个能完全解决我的问题(我希望从任何 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;
Run Code Online (Sandbox Code Playgroud)

以下类将 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;
            foreach (var p in Parameters)
            {
                var param = cmd.CreateParameter();
                param.Name = "@" + p.Name;
                param.Value = p.Value;
                cmd.Parameters.Add(param);
            }
            using (var da = DbProviderFactories.GetFactory(connection).CreateDataAdapter())
            {
                da.SelectCommand = cmd;
                da.Fill(dt);
            }
        }
        if (!(state == ConnectionState.Open))
            connection.Close();
        return dt;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用时,只需按如下方式调用即可:

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


Mel*_*per 7

将日志记录与 Entity Framework Core 3.x 结合使用

Entity Framework Core 通过日志系统发出 SQL。只有几个小技巧。您必须指定一个ILoggerFactory并且必须指定一个过滤器。这是本文中的一个示例

创建工厂:

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

告诉DbContext方法中使用工厂OnConfiguring

optionsBuilder.UseLoggerFactory(_loggerFactory);
Run Code Online (Sandbox Code Playgroud)

从这里,您可以变得更加复杂,并挂钩 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
}
Run Code Online (Sandbox Code Playgroud)


Bac*_*ata 6

Entity Framework Core 5.0+开始,只需重写 DbContext 中的 OnConfiguring 方法一次即可进行日志记录。这也适用于 Single() 或 Any() 查询。

用于记录到调试窗口:

public class ExampleDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // using System.Diagnostics;
        optionsBuilder.LogTo(message => Debug.WriteLine(message));
    }
}
Run Code Online (Sandbox Code Playgroud)

用于记录到控制台:

public class ExampleDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.LogTo(Console.WriteLine);
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅此处了解更多详细信息,包括日志级别和过滤:https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/simple-logging


Vin*_*ANG 5

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


小智 5

IQueryable query = from x in appEntities
                   where x.id = 32
                   select x;
var queryString = query.ToString();
Run Code Online (Sandbox Code Playgroud)

将返回sql查询。使用EntityFramework 6的数据上下文进行工作

  • 我刚刚尝试了这个,它跟踪出对象:`Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[System.Linq.IGrouping`2[System.Int32,String]]`而不是实际查询。我是不是遗漏了什么,还是你忘了提什么? (6认同)

Ste*_*ger 5

死灵法术。
此页面是搜索任何 .NET Framework 解决方案时的第一个搜索结果,因此这里作为一项公共服务,它是如何在 EntityFramework Core 中完成的(对于 .NET Core 1 和 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);
Run Code Online (Sandbox Code Playgroud)

然后是这些扩展方法(.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
{

    // /sf/ask/98900441/
    // 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;
        }


    }


}
Run Code Online (Sandbox Code Playgroud)

  • @ChrisWolf 如果您遵循原作者的要点,您可以找到 [提供该扩展方法的更新版本] 的人(https://gist.github.com/rionmonster/2c59f449e67edf8cd6164e9fe66c545a#gistcomment-2372507)。为我工作。 (2认同)

Ros*_*sim 5

我正在进行集成测试,并且需要它来调试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);

        ........
Run Code Online (Sandbox Code Playgroud)

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

示例SQL语句输出

  • `new ConsoleLoggerSettings()` 中有什么? (2认同)