エンティティフレームワークによって生成された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()
出力はそのカスタムタイプの名前空間です。たとえば、上記のコードがのselect new CustomType { x = x.Name }
場合、戻り値はCompany.Models.CustomType
生成されたSQLではなく、次のようになります。
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
私に役立ちます。
私の答えはEF コアに対処します。私はこのgithubの問題と設定DbContext
に関するドキュメントを参照しています:
シンプルな
ここに示すようOnConfiguring
に、DbContext
クラスのメソッド(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
}
繁雑
このComplexケースは、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コア・クエリは、ファイルに表示されます。)
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;
}
}
}
2つの方法があります。
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の回答に似ていますが、これにより、クエリが必要になるたびに現在のコードに追加する必要がなくなります。
ええと、現時点ではその目的でExpressプロファイラを使用していますが、欠点は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のdatacontextを使用した作業
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1 [System.Linq.IGrouping 2[System.Int32,String]]
。私は何かが足りないのですか、それとも何かを言及するのを忘れましたか?
私は統合テストをやって、私の使用して、Entity Frameworkのコア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
私にとって、EF6とVisual Studio 2015を使用しquery
て、イミディエイトウィンドウに入力すると、生成されたSQLステートメントが表示されました
パラメータ値(@p_linq_0
それらの値だけでなく)も必要な場合はIDbCommandInterceptor
、ReaderExecuted
メソッドにロギングを追加して使用できます。
良い答えがここにありますが、どれも完全に私の問題を解決していない(私はSQL文全体、取得することを望んだのパラメータを含む DbContextからの任意のIQueryableからは、。これは、Googleからのコードスニペットの組み合わせでないことだけ。次のコード。 I EF6 +でのみテスト済みです。
余談ですが、この作業は思ったよりもずっと長くかかりました。Enhoity Frameworkでの抽象化は、少なからず、私見です。
まず使用。'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;
}