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

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

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

467312 次浏览

有两种方法:

  1. 要查看将要生成的SQL,只需调用ToTraceString()。您可以将其添加到监视窗口中并设置断点以查看任何LINQ查询的任何给定点的查询。
  2. 您可以将一个跟踪程序附加到您选择的SQL服务器,它将向您显示最终查询的所有血腥细节。对于MySQL,跟踪查询的最简单方法就是使用tail -f跟踪查询日志。您可以在官方留档中了解有关MySQL日志记录工具的更多信息。对于SQLServer,最简单的方法是使用随附的SQLServer分析器。

您可以执行以下操作:

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。

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

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


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

这将为您提供生成的SQL。

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

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

对于那些使用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模式下运行项目。

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

嗯,我目前正在为此目的使用Express分析器,缺点是它仅适用于MSSQLServer。您可以在此处找到此工具:https://expressprofiler.codeplex.com/

在我的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();

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

嗯。

我刚刚做了这个:

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

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

适用于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.

示例:

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

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

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

将返回sql查询。使用EntityFramework 6的数据文本工作

让查询始终方便,无需更改代码 将其添加到您的DbContext并在Visual Studio的输出窗口中检查它。

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

类似于@Matt Nibecker的回答,但有了这个,你不必每次需要查询时都将其添加到当前代码中。

如果你也想有参数值(不仅是@p_linq_0,还有它们的值),你可以使用IDbCommandInterceptor并在ReaderExecuted方法中添加一些日志记录。

我的答案是EF核心。我参考了这个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
}

复杂

这个Complex case避免了覆盖#EYZ0OnConfiguring方法。,这在文档中是不鼓励的:“这种方法不适合测试,除非测试针对完整的数据库。”

这个复杂的案例使用:

  • StartupConfigureServices方法中的IServiceCollection (而不是重写OnConfiguring方法;好处是DbContext和您要使用的ILoggerProvider之间的耦合更松)
  • 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));
...
}
}

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

巫术。
此页面是搜索任何. NET Framework解决方案时的第一个搜索结果,因此这里作为公共服务,它是如何在EntityFramework核心(用于. NET核心 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);

然后是这些扩展方法(IQueryableExtensions1 for. NET Core 1.0,IQueryableExtensions1 for. NET Core 2.0):

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




}




}

我正在做联调,需要这个来调试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控制台的示例输出:

示例SQL语句输出

SQLManagement Studio=>Tools=>SQL服务器分析器

File=>新跟踪…

使用模板=>空白

事件选择=>T-SQL

左侧检查:SP. Stmt完整

列筛选器可用于选择特定的Application ationName或Database aseName

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

点击这里查看来源信息

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

顺便说一句,这项任务花了我比我想象的要长的时间。实体框架中的抽象有点多,IMHO。

首先是使用。您需要对“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;
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;
}
}

要使用它,只需如下所示:

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

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

EF Core 5.0+

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

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

使用SQLServer数据库提供程序时会导致以下输出:

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

呜呜呜!!!

(注:您需要using Microsoft.EntityFrameworkCore;

将日志记录与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);
});

告诉DbContextOnConfiguring方法中使用工厂:

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
}

实体框架核心5.0+开始,可以简单地覆盖DbContext中的OnConfigure方法一次以进行日志记录。这也适用于单()或任何()查询。

对于记录到调试窗口:

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

对于记录到控制台:

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

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