Wie kann ich das vom Entity Framework generierte SQL anzeigen?
(In meinem speziellen Fall verwende ich den MySQL-Anbieter - wenn es darauf ankommt)
Wie kann ich das vom Entity Framework generierte SQL anzeigen?
(In meinem speziellen Fall verwende ich den MySQL-Anbieter - wenn es darauf ankommt)
Antworten:
Sie können Folgendes tun:
IQueryable query = from x in appEntities
where x.id == 32
select x;
var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
oder in EF6:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
.ToTraceString();
Dadurch erhalten Sie die generierte SQL.
.Single()
Ihr Objekt nach dem Ausführen wohl nicht mehr vorhanden ist IQueryable
.
result
zu System.Data.Entity.Infrastructure.DbQuery<T>
, dann interne Eigenschaft zu erhalten , InternalQuery
wie (System.Data.Entity.Internal.Linq.InternalQuery<T>)
, und nur dann, EinsatzToTraceString()
result.ToString()
Wenn Sie Entity Framework 6 und höher verwenden und die Ausgabe-SQL in Visual Studio anzeigen möchten (wie ich), müssen Sie die neue Protokollierungs- / Abfangfunktion verwenden.
Durch Hinzufügen der folgenden Zeile wird das generierte SQL (zusammen mit zusätzlichen ausführungsbezogenen Details) im Visual Studio-Ausgabefenster ausgegeben:
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
// query the database using EF here.
}
Weitere Informationen zum Anmelden bei EF6 finden Sie in dieser raffinierten Blogserie: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/
Hinweis: Stellen Sie sicher, dass Sie Ihr Projekt im DEBUG-Modus ausführen.
Ab EF6.1 können Sie mit Interceptors einen Datenbanklogger registrieren. Siehe Kapitel "Interceptors" und "Protokollieren von Datenbankoperationen" in einer Datei hier
<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>
Wenn Sie einen DbContext verwenden, können Sie Folgendes tun, um SQL abzurufen:
var result = from i in myContext.appEntities
select new Model
{
field = i.stuff,
};
var sql = result.ToString();
ToString()
gibt Ihnen die Abfrage mit Variablen darin, wie p__linq__0
anstelle der Endwerte (zB: 34563 statt p__linq__0
)
Anwendbar für EF 6.0 und höher: Für diejenigen unter Ihnen, die mehr über die Protokollierungsfunktionalität erfahren und einige der bereits gegebenen Antworten ergänzen möchten.
Jeder von der EF an die Datenbank gesendete Befehl kann jetzt protokolliert werden. Verwenden Sie die, um die von EF 6.x generierten Abfragen anzuzeigenDBContext.Database.Log property
Was wird protokolliert?
- 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.
Beispiel:
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();
}
Ausgabe:
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
So melden Sie sich bei einer externen Datei an:
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();
}
}
Weitere Informationen hier: Protokollieren und Abfangen von Datenbankoperationen
In EF 4.1 können Sie Folgendes tun:
var result = from x in appEntities
where x.id = 32
select x;
System.Diagnostics.Trace.WriteLine(result .ToString());
Dadurch erhalten Sie die generierte SQL.
ToString()
Ausgabe der Namespace dieses benutzerdefinierten Typs. Wenn beispielsweise der obige Code select new CustomType { x = x.Name }
wäre, wäre der zurückgegebene Wert so etwas wie Company.Models.CustomType
anstelle des generierten SQL.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
für mich.
Meine Antwort richtet sich an EF Core . Ich verweise auf dieses Github-Problem und die Dokumente zur KonfigurationDbContext
:
Einfach
Überschreiben Sie die hier gezeigteOnConfiguring
Methode Ihrer DbContext
Klasse ( YourCustomDbContext
) , um einen ConsoleLoggerProvider zu verwenden. Ihre Abfragen sollten an der Konsole protokolliert werden:
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
}
Komplex
Dieser komplexe Fall vermeidet das Überschreiben der DbContext
OnConfiguring
Methode. , von dem in den Dokumenten abgeraten wird: "Dieser Ansatz eignet sich nicht zum Testen, es sei denn, die Tests zielen auf die vollständige Datenbank ab."
Dieser komplexe Fall verwendet:
IServiceCollection
In- Startup
Class- ConfigureServices
Methode (anstatt die OnConfiguring
Methode zu überschreiben ; der Vorteil ist eine lockerere Kopplung zwischen der DbContext
und der, die ILoggerProvider
Sie verwenden möchten).ILoggerProvider
(anstelle der ConsoleLoggerProvider
oben gezeigten Implementierung; der Vorteil ist, dass unsere Implementierung zeigt, wie wir uns bei File anmelden würden (ich sehe keinen mit EF Core gelieferten File Logging Provider ))So was:
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));
...
}
}
Hier ist die Implementierung von a MyLoggerProvider
(und dessen MyLogger
Protokolle an eine Datei angehängt werden, die Sie konfigurieren können; Ihre EF Core-Abfragen werden in der Datei angezeigt.)
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;
}
}
}
Es gibt zwei Möglichkeiten:
ToTraceString()
. Sie können es in Ihr Überwachungsfenster einfügen und einen Haltepunkt festlegen, um zu sehen, wie die Abfrage an einem bestimmten Punkt für eine LINQ-Abfrage aussehen würde.tail -f
. Weitere Informationen zu den Protokollierungsfunktionen von MySQL finden Sie in der offiziellen Dokumentation . Für SQL Server ist es am einfachsten, den enthaltenen SQL Server-Profiler zu verwenden.Damit die Abfrage immer griffbereit ist, ohne den Code zu ändern, fügen Sie dies Ihrem DbContext hinzu und überprüfen Sie es im Ausgabefenster in Visual Studio.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
Ähnlich wie bei @Matt Nibecker, aber damit müssen Sie es nicht jedes Mal in Ihren aktuellen Code einfügen, wenn Sie die Abfrage benötigen.
SQL Management Studio => Tools => SQL Server-Profiler
Datei => Neue Spur ...
Verwenden Sie die Vorlage => Leer
Ereignisauswahl => T-SQL
Überprüfen Sie die linke Seite auf: SP.StmtComplete
Spaltenfilter können verwendet werden, um einen bestimmten Anwendungsnamen oder Datenbanknamen auszuwählen
Starten Sie die Ausführung dieses Profils und lösen Sie die Abfrage aus.
Klicken Sie hier für Quellinformationen
Nun, ich verwende derzeit Express Profiler für diesen Zweck. Der Nachteil ist, dass es nur für MS SQL Server funktioniert. Sie finden dieses Tool hier: https://expressprofiler.codeplex.com/
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
Gibt die SQL-Abfrage zurück. Arbeiten mit dem Datenkontext von EntityFramework 6
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1 [System.Linq.IGrouping 2[System.Int32,String]]
anstelle der eigentlichen Abfrage. Vermisse ich etwas oder hast du vergessen etwas zu erwähnen?
Ich mache einen Integrationstest und benötige diesen, um die generierte SQL-Anweisung in Entity Framework Core 2.1 zu debuggen. Ich verwende DebugLoggerProvider
oder ConsoleLoggerProvider
mag Folgendes:
[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);
........
Hier ist eine Beispielausgabe der Visual Studio-Konsole:
Nekromantie.
Diese Seite ist das erste Suchergebnis bei der Suche nach einer Lösung für ein .NET Framework. Hier also als öffentlicher Dienst, wie es in EntityFramework Core (für .NET Core 1 und 2) gemacht wird:
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);
Und dann diese Erweiterungsmethoden (IQueryableExtensions1 für .NET Core 1.0, IQueryableExtensions für .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
{
// /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();
In meinem Fall für EF 6+, anstatt dies im Direktfenster zu verwenden, um die Abfragezeichenfolge zu finden:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();
Ich musste dies letztendlich verwenden, um den generierten SQL-Befehl abzurufen:
var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();
Natürlich kann Ihre anonyme Typensignatur anders sein.
HTH.
Ich habe gerade Folgendes getan:
IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);
Und das in der Ausgabe gezeigte Ergebnis :
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
Mit EF6 und Visual Studio 2015 habe ich query
in das unmittelbare Fenster eingegeben und mir die generierte SQL-Anweisung gegeben
Wenn Sie Parameterwerte (nicht nur, @p_linq_0
sondern auch deren Werte) haben möchten , können Sie die Methode verwenden IDbCommandInterceptor
und der Protokollierung etwas hinzufügen ReaderExecuted
.
Obwohl es hier gute Antworten gibt, hat keine mein Problem vollständig gelöst (ich wollte die gesamte SQL-Anweisung, einschließlich der Parameter , aus dem DbContext von jedem IQueryable abrufen. Der folgende Code macht genau das. Es ist eine Kombination von Codefragmenten von Google. I. habe es nur mit EF6 + getestet .
Abgesehen davon hat diese Aufgabe viel länger gedauert, als ich gedacht hatte. Abstraktion im Entity Framework ist meiner Meinung nach ein bisschen viel.
Zuerst die Verwendung. Sie benötigen einen expliziten Verweis auf '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;
Die folgende Klasse konvertiert eine IQueryable in eine DataTable. Ändern Sie sie nach Bedarf:
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;
}
}
Nennen Sie es einfach wie folgt:
var context = new MyContext();
var data = ....//Query, return type can be anonymous
.AsQueryable();
var dt = new EntityFrameworkCommand()
.Initialize(context, data)
.GetData();
Die meisten Antworten hier waren EF6-spezifisch. Hier ist eine für diejenigen unter Ihnen, die noch EF4 verwenden.
Diese Methode ersetzt die @p__linq__0
/ etc. Parameter mit ihren tatsächlichen Werten, sodass Sie die Ausgabe einfach kopieren und in SSMS einfügen und ausführen oder debuggen können.
/// <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;
}