Come posso visualizzare l'SQL generato da Entity Framework?


625

Come posso visualizzare l'SQL generato dal framework di entità?

(Nel mio caso particolare sto usando il provider mysql - se è importante)


1
Questo articolo della rivista MSDN descrive alcune opzioni di profilazione per Entity Framework 4
Arve

2
La domanda "duplicata" collegata è relativa a LINQ to SQL, quindi non è in realtà un duplicato.
jrummell,

2
Quando viene eseguito con il debugger, IntelliTrace mostra le query SQL fatte, anche se senza i loro risultati.
ivan_pozdeev

Se sei interessato a vedere l'SQL solo durante lo sviluppo, puoi utilizzare LINQPad . Quando si esegue una query LINQ nei risultati, verrà visualizzata una scheda SQL che mostra l'istruzione SQL eseguita. Per mySQL dovrai installare un driver. Non ho un database mySQL disponibile, ma dovrebbe funzionare.
Gligoran,

Risposte:


473

Puoi fare quanto segue:

IQueryable query = from x in appEntities
             where x.id == 32
             select x;

var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();

o in EF6:

var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
            .ToTraceString();

Questo ti darà l'SQL che è stato generato.


20
Non otterrai SQL per le query che terminano con .Single (), .Count (), .Any (), ecc. In quel modo.
Springy76,

24
Questo perché dopo aver eseguito .Single()l'oggetto non è più IQueryableimmagino.
Suhas,

11
con EF6, ho potuto ottenerlo solo con la riflessione. ma in primo luogo, ho dovuto convertire resulta System.Data.Entity.Infrastructure.DbQuery<T>, quindi ottenere proprietà interna InternalQuerycome (System.Data.Entity.Internal.Linq.InternalQuery<T>), e solo allora, l'usoToTraceString()
itsho

9
aggiungi riferimento a System.Data.Entity, System.Data.Objects.ObjectQuery esiste nella dll sopra
Mahesh,

54
In EF6 puoi semplicemente fareresult.ToString()
Scott Chamberlain il

957

Per coloro che utilizzano Entity Framework 6 e versioni successive, se si desidera visualizzare l'output SQL in Visual Studio (come ho fatto io) è necessario utilizzare la nuova funzionalità di registrazione / intercettazione.

L'aggiunta della riga seguente sputerà l'SQL generato (insieme a ulteriori dettagli relativi all'esecuzione) nel pannello di output di Visual Studio:

using (MyDatabaseEntities context = new MyDatabaseEntities())
{
    context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
    // query the database using EF here.
}

Maggiori informazioni sulla registrazione in EF6 in questa nifty serie di blog: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/

Nota: assicurarsi di eseguire il progetto in modalità DEBUG.


107
Questa risposta merita più amore (se stai usando EF6 +) - grande aggiunta di debug, basta aggiungerla nel costruttore DBContext (this.Database.Log = ...)
keithl8041

21
Assicurati di eseguire il tuo progetto in MODALITÀ DEBUG, controlla se la voce "Debug" è stata selezionata nella casella combinata del riquadro Output e controlla anche se il tuo debug non sta reindirizzando su Immediato (Strumenti> Opzioni> Debug> Reindirizza tutto il testo della finestra di output su Immediato Window)
rkawano,

5
c'è un modo per ottenere questo per includere i valori delle variabili direttamente all'interno del sql generato? Un po 'di dolore con quelli più grandi.
Chris,

22
@Matt Nibecker Questo non funziona in EF Core. Qual è l'alternativa a EF Core?
nam

9
ATTENZIONE: l'ho implementato con l'intenzione che funzionasse solo in sviluppo. Quando siamo stati distribuiti nel nostro ambiente di test, abbiamo iniziato a vedere improvvisamente perdite di memoria nel processo di lavoro IIS. Dopo la profilazione della memoria, ci siamo resi conto che anche GC esplicito non stava più raccogliendo gli oggetti di contesto dell'entità (sì, stavano usando istruzioni). La rimozione di questa riga è tornata alla normalità. Quindi, sebbene sia un ottimo strumento, assicurati di inserirlo nella tua app solo per lo sviluppo.
Brandon Barkley,

82

A partire da EF6.1 è possibile utilizzare gli intercettori per registrare un registratore di database. Vedere i capitoli "Intercettori" e "Registrazione delle operazioni del database" in un file qui

<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
Post di blog sull'argomento blog.oneunicorn.com/2014/02/09/…
Tim Abell,

12
Precisione, è sotto: <configurazione> <entityFramework> <interceptors> ... </interceptors> </entityFramework> </configuration>
Christophe P

80

Se si utilizza un DbContext, è possibile effettuare le seguenti operazioni per ottenere l'SQL:

var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();

12
ToString()ti darà la query con le variabili al suo interno, come p__linq__0, invece dei valori finali (ad esempio: 34563 anziché p__linq__0)
sport

25

Applicabile per EF 6.0 e versioni successive: per quelli di voi che desiderano saperne di più sulla funzionalità di registrazione e aggiungere ad alcune delle risposte già fornite.

Ora è possibile registrare qualsiasi comando inviato dall'EF al database. Per visualizzare le query generate da EF 6.x, utilizzare ilDBContext.Database.Log property

Cosa viene registrato

 - 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.

Esempio:

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

Produzione:

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

Per accedere a un file esterno:

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

Maggiori informazioni qui: Registrazione e intercettazione delle operazioni del database


21

Puoi fare quanto segue in EF 4.1:

var result = from x in appEntities
             where x.id = 32
             select x;

System.Diagnostics.Trace.WriteLine(result .ToString());

Questo ti darà l'SQL che è stato generato.


1
Punto di fatto, credo che questo funzioni solo quando la query restituisce un tipo anonimo. Se restituisce un tipo personalizzato, l' ToString()output è lo spazio dei nomi di quel tipo personalizzato. Ad esempio, se il codice sopra riportato fosse select new CustomType { x = x.Name }, il valore restituito sarebbe simile a Company.Models.CustomTypequello dell'SQL generato.
Chad Levy,

8
Questa tecnica produce System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]per me.
Carl G,

1
@CarlG System.Data.Objects.ObjectQuery non è EF 4.1 (DbContext). Usando DbContext sarebbe System.Data.Entity.Infrastructure.DbQuery`1 [MyProject.Models.Product] che effettivamente emette il suo SQL su una chiamata a "ToString ()"
springy76

Questo ti darà l'SQL che è stato generato, dove, nella finestra di output? quale opzione dal menu a discesa?
JsonStatham,

17

La mia risposta si rivolge a EF core . Mi riferisco a questo problema di github e ai documenti sulla configurazioneDbContext :

Semplice

Sostituisci il OnConfiguringmetodo della tua DbContextclasse ( YourCustomDbContext) come mostrato qui per usare ConsoleLoggerProvider; le tue query dovrebbero accedere alla console:

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
}

Complesso

Questo caso complesso evita di ignorare il DbContext OnConfiguringmetodo. , che è sconsigliato nei documenti: "Questo approccio non si presta ai test, a meno che i test non siano indirizzati all'intero database".

Questo caso complesso utilizza:

  • Il metodo IServiceCollectionin Startupclass ConfigureServices(invece di sovrascrivere il OnConfiguringmetodo; il vantaggio è un accoppiamento più flessibile tra il DbContexte il che ILoggerProvidersi desidera utilizzare)
  • Un'implementazione di ILoggerProvider(anziché utilizzare l' ConsoleLoggerProviderimplementazione mostrata sopra; il vantaggio è che la nostra implementazione mostra come accedere a File (non vedo un provider di registrazione file fornito con EF Core ))

Come questo:

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

Ecco l'implementazione di un MyLoggerProvider(e relativo MyLoggerche accoda i suoi registri a un File che puoi configurare; le tue query EF Core appariranno nel file.)

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

Quindi ... non esiste un modo per iniziare?
Juan De la Cruz,

1
@JuanDelaCruz Ho semplificato la mia risposta; prova la semplice alternativa
The Red Pea,

16

Ci sono due modi:

  1. Per visualizzare l'SQL che verrà generato, è sufficiente chiamare ToTraceString(). È possibile aggiungerlo nella finestra di controllo e impostare un punto di interruzione per vedere quale sarebbe la query in un determinato punto per qualsiasi query LINQ.
  2. Puoi collegare un tracciante al tuo server SQL preferito, che ti mostrerà la query finale in tutti i suoi dettagli cruenti. Nel caso di MySQL, il modo più semplice per rintracciare le query è semplicemente quello di adattare il registro delle query tail -f. Puoi saperne di più sulle funzionalità di registrazione di MySQL nella documentazione ufficiale . Per SQL Server, il modo più semplice è utilizzare il profiler di SQL Server incluso.

27
ToTraceString di cosa?
nn.

L'ObjectQuery, come ha notato Nick subito dopo aver pubblicato la mia risposta.
Benjamin Pollack,

2
SQL Server Profiler acquisisce i primi 4000 caratteri, ma le query EF possono essere molto più lunghe di così.

8

Per avere la query sempre a portata di mano, senza modificare il codice aggiungilo a DbContext e controllalo nella finestra di output in Visual Studio.

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.Log = (query)=> Debug.Write(query);
    }

Simile alla risposta @Matt Nibecker, ma con questo non è necessario aggiungerlo nel codice corrente, ogni volta che è necessaria la query.


La migliore risposta!
AlexSC

7

SQL Management Studio => Strumenti => Profiler di SQL Server

File => Nuova traccia ...

Usa il modello => Vuoto

Selezione evento => T-SQL

Controllo a sinistra per: SP.StmtComplete

I filtri di colonna possono essere utilizzati per selezionare un ApplicationName o DatabaseName specifico

Avvia il profilo in esecuzione, quindi attiva la query.

Clicca qui per informazioni sulla fonte


1
scusate questo è solo per il server SQL, non per MySQL
andrew pate


5
IQueryable query = from x in appEntities
                   where x.id = 32
                   select x;
var queryString = query.ToString();

Restituirà la query sql. Lavorare con datacontext di EntityFramework 6


4
Ho appena provato questo e traccia l'oggetto: Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1 [System.Linq.IGrouping 2[System.Int32,String]]invece della query effettiva. Mi sto perdendo qualcosa o hai dimenticato di menzionare qualcosa?
loganjones16,

5

Sto eseguendo un test di integrazione e ne avevo bisogno per eseguire il debug dell'istruzione SQL generata in Entity Framework Core 2.1, quindi uso DebugLoggerProvidero ConsoleLoggerProviderpiace così:

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

        ........

Ecco un esempio di output dalla console di Visual Studio:

Esempio di output dell'istruzione SQL


1
DebugLoggerPrivider e ConsoleLoggerProvider sembrano esistere solo in .NET Core: docs.microsoft.com/en-us/dotnet/api/…
Gabriel Magana,

4

Necromancing.
Questa pagina è il primo risultato di ricerca quando si cerca una soluzione per qualsiasi .NET Framework, quindi qui come servizio pubblico, come è fatto in EntityFramework Core (per .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);

E poi questi metodi di estensione (IQueryableExtensions1 per .NET Core 1.0, IQueryableExtensions per .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;
        }


    }


}

Sto usando EF Core 2.0.1 e il suggerimento sopra riportato porta a: System.InvalidCastException: 'Impossibile eseguire il cast dell'oggetto di tipo Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor' per digitare '' Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor''` la linea: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Chris Wolf,

2
@ChrisWolf se segui l'essenza dell'autore originale puoi trovare qualcuno che ha fornito una versione aggiornata di quel metodo di estensione . Ha funzionato per me.
B12 Toaster,

2

Nel mio caso per EF 6+, invece di usarlo nella finestra immediata per trovare la stringa di query:

var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();

Ho finito per usare questo per ottenere il comando SQL generato:

var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();

Naturalmente la firma del tuo tipo anonimo potrebbe essere diversa.

HTH.


2

Ho appena fatto questo:

IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);

E il risultato mostrato nell'output :

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

Sì, ma credo che nessuno voglia vedere il p__linq__i, ma i valori reali
Tom Stickel,

In questo modo funziona ancora in EF 6 e sarà utile se ti interessa solo l'aspetto della struttura della query. Nel mio caso il progetto che creo l'oggetto IQueryable <T> non ha riferimenti a System.Data.Entity né voglio aggiungerlo solo a scopo di debug. Quindi questo metodo ha funzionato bene.
wctiger,

2

Per me, usando EF6 e Visual Studio 2015 sono entrato querynella finestra immediata e mi ha dato l'istruzione SQL generata


1

Se si desidera avere valori di parametro (non solo @p_linq_0ma anche i loro valori), è possibile utilizzare IDbCommandInterceptore aggiungere un po 'di registrazione al ReaderExecutedmetodo.


1

Sebbene ci siano buone risposte qui, nessuna ha risolto completamente il mio problema (ho desiderato ottenere l'intera istruzione SQL, inclusi Parametri , da DbContext da qualsiasi IQueryable. Il seguente codice fa proprio questo. È una combinazione di frammenti di codice di Google. I l'ho testato solo con EF6 + .

A parte questo, questo compito mi ha portato molto più tempo di quanto pensassi. L'astrazione in Entity Framework è un po 'troppo, IMHO.

Innanzitutto l'utilizzo. Sarà necessario un riferimento esplicito a "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;

La seguente classe converte un IQueryable in un DataTable. Modifica in base alle tue necessità:

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

Per usare, basta chiamarlo come di seguito:

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 Solution

La maggior parte delle risposte qui erano specifiche per EF6. Eccone uno per quelli di voi che usano ancora EF4.

Questo metodo sostituisce @p__linq__0/ etc. parametri con i loro valori effettivi, quindi puoi semplicemente copiare e incollare l'output in SSMS ed eseguirlo o eseguirne il debug.

    /// <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;
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.