如何查看实体框架生成的SQL?
(在我的特殊情况下,我正在使用mysql提供程序 - 如果重要的话)
答案 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)
有两种方法:
ToTraceString()
即可。您可以将它添加到监视窗口并设置断点,以查看任何给定点的查询对于任何LINQ查询。tail -f
来查询查询日志。您可以在the official documentation中了解有关MySQL日志记录功能的更多信息。对于SQL Server,最简单的方法是使用附带的SQL Server探查器。答案 6 :(得分:11)
我的回答是EF 核心。我引用了这个github issue和configuring DbContext
上的文档:
<强>简单强>
覆盖OnConfiguring
类(DbContext
)as shown here的YourCustomDbContext
方法以使用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;
此复杂案例使用:
IServiceCollection
类Startup
方法中的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中提供!这来自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语句,因此我像这样使用DebugLoggerProvider
或ConsoleLoggerProvider
:
[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控制台的示例输出:
答案 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通过日志记录系统发出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)
这里的大多数答案都是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)