엔터티 프레임 워크에서 생성 된 SQL을 어떻게 봅니까?
(필자의 경우 mysql 공급자를 사용하고 있습니다-중요하다면)
엔터티 프레임 워크에서 생성 된 SQL을 어떻게 봅니까?
(필자의 경우 mysql 공급자를 사용하고 있습니다-중요하다면)
답변:
다음을 수행 할 수 있습니다.
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이 제공됩니다.
.Single()
객체를 실행 한 후에 더 이상 IQueryable
추측 하지 않기 때문 입니다.
result
위해 System.Data.Entity.Infrastructure.DbQuery<T>
, 다음 내부 속성 수 InternalQuery
등을 (System.Data.Entity.Internal.Linq.InternalQuery<T>)
, 오직 다음, 사용ToTraceString()
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/
참고 : 프로젝트를 디버그 모드로 실행 중인지 확인하십시오.
EF6.1부터 인터셉터를 사용하여 데이터베이스 로거를 등록 할 수 있습니다. 파일에 대한 "인터셉터"및 "데이터베이스 조작 로그"장을 참조 하십시오.
<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>
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();
}
}
자세한 정보는 여기 : 데이터베이스 작업 로깅 및 인터셉트
EF 4.1에서 다음을 수행 할 수 있습니다.
var result = from x in appEntities
where x.id = 32
select x;
System.Diagnostics.Trace.WriteLine(result .ToString());
그러면 생성 된 SQL이 제공됩니다.
ToString()
출력은 해당 사용자 정의 유형의 네임 스페이스입니다. 예를 들어, 위 코드가 인 경우 생성 된 SQL 대신 select new CustomType { x = x.Name }
반환 된 값이 Company.Models.CustomType
됩니다.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
나를 위해 생산 합니다.
내 대답은 EF 핵심을 해결합니다 . 이 github 문제 와 구성에DbContext
대한 문서를 참조하십시오 .
단순한
ConsoleLoggerProvider를 사용하려면 여기 에 표시된대로 클래스 ( ) 의 OnConfiguring
메소드를 대체하십시오 . 쿼리는 콘솔에 기록해야합니다.DbContext
YourCustomDbContext
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
}
복잡한
이 복잡한 경우는 오버라이드 (override) 방지 방법.DbContext
OnConfiguring
문서에서 권장하지 않습니다. "이 방법은 테스트가 전체 데이터베이스를 대상으로하지 않는 한 테스트 자체에는 적합하지 않습니다."
이 복잡한 경우는 다음을 사용합니다.
IServiceCollection
에서 Startup
클래스 ConfigureServices
방법 (대신 재정의 OnConfiguring
방법을 상기 이득 간의 느슨한 커플 링이다 DbContext
하고는ILoggerProvider
당신이 사용하고자하는)ILoggerProvider
( ConsoleLoggerProvider
위에 표시된 구현 을 사용하는 대신 이점은 구현에 파일에 로그하는 방법을 보여줍니다 ( 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;
}
}
}
두 가지 방법이 있습니다.
ToTraceString()
됩니다. 감시 창에 추가하고 중단 점을 설정하여 LINQ 쿼리에 대해 특정 시점에 쿼리가 무엇인지 확인할 수 있습니다.tail -f
. 공식 문서 에서 MySQL의 로깅 기능에 대해 자세히 알아볼 수 있습니다 . SQL Server의 경우 가장 쉬운 방법은 포함 된 SQL Server 프로파일 러를 사용하는 것입니다.쿼리를 항상 편리하게하려면 코드를 변경하지 않고이를 DbContext에 추가하고 Visual Studio의 출력 창에서 확인하십시오.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
@Matt Nibecker 답변과 비슷하지만 쿼리가 필요할 때마다 현재 코드에 추가 할 필요가 없습니다.
SQL Management Studio => 도구 => SQL Server 프로파일 러
파일 => 새 추적 ...
템플릿 사용 => 비우기
이벤트 선택 => T-SQL
왼쪽 확인 : SP.StmtComplete
열 필터를 사용하여 특정 ApplicationName 또는 DatabaseName을 선택할 수 있습니다.
해당 프로필을 실행 한 다음 쿼리를 트리거하십시오.
소스 정보를 보려면 여기를 클릭하십시오
글쎄, 나는 현재 그 목적으로 Express profiler를 사용하고 있는데 단점은 MS SQL Server에서만 작동한다는 것입니다. 이 도구는 여기에서 찾을 수 있습니다 : https://expressprofiler.codeplex.com/
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
SQL 쿼리를 반환합니다. EntityFramework 6의 데이터 컨텍스트를 사용하여 작업
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1 [System.Linq.IGrouping 개체를 추적 2[System.Int32,String]]
합니다. 내가 뭔가를 놓치고 있거나 언급하는 것을 잊었습니까?
나는 통합 테스트를하고,이 필요 내가 사용하므로, 엔티티 프레임 워크 코어 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 콘솔의 샘플 출력입니다.
괴롭힘.
이 페이지는 모든 .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);
그런 다음 이러한 확장 방법 (.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
{
// /programming/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;
}
}
}
var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
필자의 경우 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.
방금이 작업을 수행했습니다.
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
매개 변수 값 (값 @p_linq_0
뿐만 아니라 값)도 갖고 싶다면 IDbCommandInterceptor
로깅을 사용 하고 ReaderExecuted
메소드에 추가 할 수 있습니다 .
좋은 해답이 여기가 있지만, 아무도 완전히 내 문제는 (I 전체 SQL 문을 얻을 싶다고 해결되지 매개 변수를 포함하여 어떤 된 IQueryable에서 DbContext에서. 다음 코드는 구글에서 코드 조각의 조합이 단지. 않습니다. I를 EF6 +에서만 테스트했습니다 .
제쳐두고,이 작업은 생각보다 오래 걸렸습니다. Entity Framework의 추상화는 약간 중요합니다. 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;
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();
여기에있는 대부분의 답변은 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;
}