Wie kann ich das vom Entity Framework generierte SQL anzeigen?


624

Wie kann ich das vom Entity Framework generierte SQL anzeigen?

(In meinem speziellen Fall verwende ich den MySQL-Anbieter - wenn es darauf ankommt)


1
Dieser Artikel aus dem MSDN Magazine beschreibt einige Profilierungsoptionen für Entity Framework 4
Arve

2
Die verknüpfte "Duplikat" -Frage bezieht sich auf LINQ to SQL, ist also eigentlich kein Duplikat.
Jrummell

2
Wenn IntelliTrace unter dem Debugger ausgeführt wird, werden SQL-Abfragen angezeigt, allerdings ohne deren Ergebnisse.
ivan_pozdeev

Wenn Sie SQL nur während der Entwicklung sehen möchten , können Sie LINQPad verwenden . Wenn Sie eine LINQ-Abfrage in den Ergebnissen ausführen, wird eine SQL-Registerkarte angezeigt, auf der die ausgeführte SQL-Anweisung angezeigt wird. Für mySQL müssen Sie einen Treiber installieren. Ich habe keine mySQL-Datenbank zur Verfügung, aber es sollte funktionieren.
Gligoran

Antworten:


472

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.


20
Auf diese Weise erhalten Sie kein SQL für Abfragen, die mit .Single (), .Count (), .Any () usw. enden.
springy76

24
Das liegt daran, dass .Single()Ihr Objekt nach dem Ausführen wohl nicht mehr vorhanden ist IQueryable.
Suhas

11
mit EF6 konnte ich es nur mit Reflexion bekommen. aber zuerst musste ich konvertieren resultzu System.Data.Entity.Infrastructure.DbQuery<T>, dann interne Eigenschaft zu erhalten , InternalQuerywie (System.Data.Entity.Internal.Linq.InternalQuery<T>), und nur dann, EinsatzToTraceString()
itsho

9
Verweis auf System.Data.Entity hinzufügen, System.Data.Objects.ObjectQuery existiert in der obigen DLL
Mahesh

54
In EF6 können Sie einfach tunresult.ToString()
Scott Chamberlain

956

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.


107
Diese Antwort verdient mehr Liebe (wenn Sie EF6 + verwenden) - eine großartige Debug-Ergänzung, fügen Sie sie einfach im DBContext-Konstruktor hinzu (this.Database.Log = ...)
keithl8041

21
Stellen Sie sicher, dass Sie Ihr Projekt im DEBUG-MODUS ausführen, überprüfen Sie, ob das Element "Debug" in der Combobox des Ausgabebereichs ausgewählt wurde, und prüfen Sie, ob Ihr Debug nicht auf Sofort umleitet (Extras> Optionen> Debugging> Text des gesamten Ausgabefensters auf Sofort umleiten Fenster)
rkawano

5
Gibt es eine Möglichkeit, die Variablenwerte direkt in die generierte SQL aufzunehmen? Ein bisschen Schmerz mit den größeren.
Chris

22
@ Matt Nibecker Dies funktioniert nicht in EF Core. Was ist die Alternative für EF Core?
Nam

9
WARNUNG: Ich habe dies mit der Absicht implementiert, dass es nur in der Entwicklung ausgeführt wird. Bei der Bereitstellung in unserer Testumgebung stellten wir plötzlich Speicherverluste im IIS-Worker-Prozess fest. Nach der Speicherprofilerstellung stellten wir fest, dass selbst explizite GC die Entitätskontextobjekte nicht mehr sammelten (ja, sie verwendeten Anweisungen). Durch Entfernen dieser Zeile wurde alles wieder normal. Obwohl dies ein großartiges Tool ist, sollten Sie es nur für die Entwicklung in Ihre App integrieren.
Brandon Barkley

82

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>

1

12
Präzision, es ist unter: <Konfiguration> <entityFramework> <Interceptors> ... </ Interceptors> </ EntityFramework> </ Konfiguration>
Christophe P

80

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

12
ToString()gibt Ihnen die Abfrage mit Variablen darin, wie p__linq__0anstelle der Endwerte (zB: 34563 statt p__linq__0)
Sport

24

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


21

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.


1
Tatsächlich glaube ich, dass dies nur funktioniert, wenn die Abfrage einen anonymen Typ zurückgibt. Wenn ein benutzerdefinierter Typ zurückgegeben wird, ist die 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.CustomTypeanstelle des generierten SQL.
Chad Levy

8
Diese Technik produziert System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]für mich.
Carl G

1
@CarlG System.Data.Objects.ObjectQuery ist nicht EF 4.1 (DbContext). Mit DbContext wäre es System.Data.Entity.Infrastructure.DbQuery`1 [MyProject.Models.Product], das tatsächlich SQL bei einem Aufruf von "ToString ()"
ausgibt

Dadurch erhalten Sie die SQL, die wo im Ausgabefenster generiert wurde. Welche Option aus der Dropdown-Liste?
JsonStatham

17

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 DbContextKlasse ( 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 OnConfiguringMethode. , 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:

  • Die IServiceCollectionIn- StartupClass- ConfigureServicesMethode (anstatt die OnConfiguringMethode zu überschreiben ; der Vorteil ist eine lockerere Kopplung zwischen der DbContextund der, die ILoggerProviderSie verwenden möchten).
  • Eine Implementierung von ILoggerProvider(anstelle der ConsoleLoggerProvideroben 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 MyLoggerProtokolle 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;
        }
    } 
}

Also ... gibt es keine Möglichkeit für Anfänger, dies zu tun?
Juan De la Cruz

1
@JuanDelaCruz Ich habe meine Antwort vereinfacht; versuchen Sie die einfache Alternative
Die rote Erbse

16

Es gibt zwei Möglichkeiten:

  1. Rufen Sie einfach auf, um die zu generierende SQL anzuzeigen 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.
  2. Sie können einen Tracer an den SQL-Server Ihrer Wahl anhängen, der Ihnen die endgültige Abfrage in allen wichtigen Details anzeigt. Im Fall von MySQL besteht der einfachste Weg, die Abfragen zu verfolgen, darin, das Abfrageprotokoll mit zu verfolgen 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.

27
Der ToTraceString von was?
Nr.

Die ObjectQuery, wie Nick direkt nach dem Posten meiner Antwort feststellte.
Benjamin Pollack

2
SQL Server Profiler erfasst die ersten 4000 Zeichen, aber EF-Abfragen können viel länger sein.

7

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.


Die beste Antwort!
AlexSC

7

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


1
Entschuldigung, das ist nur für SQL Server, nicht für MySQL
Andrew Pate


5
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


4
Ich habe es gerade versucht und es verfolgt das Objekt: Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1 [System.Linq.IGrouping 2[System.Int32,String]]anstelle der eigentlichen Abfrage. Vermisse ich etwas oder hast du vergessen etwas zu erwähnen?
Loganjones16

5

Ich mache einen Integrationstest und benötige diesen, um die generierte SQL-Anweisung in Entity Framework Core 2.1 zu debuggen. Ich verwende DebugLoggerProvideroder ConsoleLoggerProvidermag 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:

Beispiel für eine SQL-Anweisungsausgabe


1
DebugLoggerPrivider und ConsoleLoggerProvider scheinen nur in .NET Core zu existieren: docs.microsoft.com/en-us/dotnet/api/…
Gabriel Magana

4

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


    }


}

Ich verwende EF Core 2.0.1 und der obige Vorschlag führt zu: System.InvalidCastException: 'Objekt vom Typ Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor kann nicht umgewandelt werden, um' 'Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVis' 'einzugeben die Linie: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Chris Wolf

2
@ChrisWolf Wenn Sie dem Kern des ursprünglichen Autors folgen, können Sie jemanden finden, der eine aktualisierte Version dieser Erweiterungsmethode bereitgestellt hat . Hat für mich gearbeitet.
B12Toaster

2

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.


2

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

Ja, aber ich glaube, dass niemand das p__linq__i sehen will, sondern die wirklichen Werte
Tom Stickel

Diese Methode funktioniert immer noch in EF 6 und ist hilfreich, wenn Sie sich nur darum kümmern, wie die Abfragestruktur aussieht. In meinem Fall enthält das Projekt, in dem ich das IQueryable <T> -Objekt erstelle, weder einen Verweis auf System.Data.Entity, noch möchte ich es nur zu Debugging-Zwecken hinzufügen. Diese Methode hat also gut funktioniert.
Wctiger

2

Mit EF6 und Visual Studio 2015 habe ich queryin das unmittelbare Fenster eingegeben und mir die generierte SQL-Anweisung gegeben


1

Wenn Sie Parameterwerte (nicht nur, @p_linq_0sondern auch deren Werte) haben möchten , können Sie die Methode verwenden IDbCommandInterceptorund der Protokollierung etwas hinzufügen ReaderExecuted.


1

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

0

Entity Framework 4-Lösung

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;
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.