¿Cómo veo el SQL generado por Entity Framework?

625

¿Cómo veo el SQL generado por el marco de la entidad?

(En mi caso particular, estoy usando el proveedor mysql, si es importante)

nos
fuente
1
Este artículo de MSDN Magazine describe algunas opciones de creación de perfiles para Entity Framework 4
Arve
2
La pregunta "duplicada" vinculada es para LINQ to SQL, por lo que en realidad no es un duplicado.
jrummell
2
Cuando se ejecuta bajo depurador, IntelliTrace muestra consultas SQL realizadas, aunque sin sus resultados.
ivan_pozdeev
Si está interesado en ver el SQL solo durante el desarrollo, puede usar LINQPad . Cuando ejecuta una consulta LINQ en los resultados, habrá una pestaña SQL que muestra la instrucción SQL ejecutada. Para mySQL tendrá que instalar un controlador. No tengo una base de datos mySQL disponible, pero debería funcionar.
gligoran

Respuestas:

473

Puedes hacer lo siguiente:

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

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

o en EF6:

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

Eso le dará el SQL que se generó.

Nick Berardi
fuente
20
No obtendrá SQL para consultas que terminen con .Single (), .Count (), .Any (), etc. de esa manera.
springy76
24
Eso es porque después de ejecutar .Single()su objeto ya no está IQueryable, supongo.
Suhas
11
con EF6, solo pude obtenerlo con reflexión. pero primero, tuve que convertir resulta System.Data.Entity.Infrastructure.DbQuery<T>, luego obtener propiedad interna InternalQuerycomo (System.Data.Entity.Internal.Linq.InternalQuery<T>), y solo entonces, usarToTraceString()
itsho
99
agregar referencia a System.Data.Entity, System.Data.Objects.ObjectQuery existe en el dll anterior
Mahesh
54
En EF6, puedes hacerloresult.ToString()
Scott Chamberlain, el
957

Para aquellos que usan Entity Framework 6 y versiones posteriores, si desea ver el SQL de salida en Visual Studio (como lo hice), debe usar la nueva funcionalidad de registro / intercepción.

Agregar la siguiente línea escupirá el SQL generado (junto con detalles adicionales relacionados con la ejecución) en el panel de salida de Visual Studio:

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

Más información sobre cómo iniciar sesión en EF6 en esta ingeniosa serie de blogs: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/

Nota: Asegúrese de ejecutar su proyecto en modo DEPURAR.

Matt Nibecker
fuente
107
Esta respuesta merece más amor (si está usando EF6 +): excelente adición de depuración, solo agréguela en el constructor DBContext (this.Database.Log = ...)
keithl8041
21
Asegúrese de que está ejecutando su proyecto en MODO DEPURACIÓN, verifique si el elemento "Depurar" se ha seleccionado en el cuadro combinado del panel Salida y también verifique si su depuración no está redirigiendo a Inmediato (Herramientas> Opciones> Depuración> Redirigir todo el texto de la Ventana de salida a Inmediato Window)
rkawano
55
¿hay alguna manera de hacer que esto incluya los valores de las variables directamente dentro del sql generado? Un poco doloroso con los más grandes.
Chris
22
@ Matt Nibecker Esto no funciona en EF Core. ¿Cuál es la alternativa para EF Core?
nam
99
ADVERTENCIA: implementé esto con la intención de que solo se ejecute en desarrollo. Cuando implementamos en nuestro entorno de prueba, comenzamos a ver abruptamente pérdidas de memoria en el proceso de trabajo de IIS. Después de la creación de perfiles de memoria, nos dimos cuenta de que incluso GC explícito ya no estaba recopilando los objetos de contexto de entidad (sí, estaban usando declaraciones). Al eliminar esta línea, todo volvió a la normalidad. Por lo tanto, si bien esta es una gran herramienta, asegúrese de incluirla en su aplicación para el desarrollo.
Brandon Barkley
82

Comenzando con EF6.1, puede usar Interceptores para registrar un registrador de base de datos. Vea los capítulos "Interceptores" y "Operaciones de base de datos de registro" a un archivo aquí

<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>
isepise
fuente
1
Publicación de
Tim Abell
12
Precisión, está bajo: <configuración> <entityFramework> <interceptors> ... </interceptors> </entityFramework> </configuration>
Christophe P
80

Si está utilizando un DbContext, puede hacer lo siguiente para obtener el SQL:

var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();
Doug Clutter
fuente
12
ToString()le dará la consulta con variables, como p__linq__0, en lugar de los valores finales (por ejemplo: 34563 en lugar de p__linq__0)
deportes
25

Aplicable para EF 6.0 y superior: para aquellos de ustedes que quieran saber más sobre la funcionalidad de registro y agregar algunas de las respuestas ya dadas.

Cualquier comando enviado desde el EF a la base de datos ahora se puede registrar. Para ver las consultas generadas desde EF 6.x, use elDBContext.Database.Log property

Lo que se registra

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

Ejemplo:

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

Salida:

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

Para iniciar sesión en un archivo externo:

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

Más información aquí: Registro e interceptación de operaciones de bases de datos

Referencia nula
fuente
21

Puede hacer lo siguiente en EF 4.1:

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

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

Eso le dará el SQL que se generó.

Capriols
fuente
1
De hecho, creo que esto solo funciona cuando la consulta devuelve un tipo anónimo. Si devuelve un tipo personalizado, la ToString()salida es el espacio de nombres de ese tipo personalizado. Por ejemplo, si el código anterior fuera select new CustomType { x = x.Name }, el valor devuelto sería algo así como Company.Models.CustomTypeel SQL generado.
Chad Levy
8
Esta técnica produce System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]para mí.
Carl G
1
@CarlG System.Data.Objects.ObjectQuery no es EF 4.1 (DbContext). Usando DbContext sería System.Data.Entity.Infrastructure.DbQuery`1 [MyProject.Models.Product] que de hecho genera su SQL en una llamada a "ToString ()"
springy76
Esto le dará el SQL que se generó, ¿dónde, en la ventana de salida? ¿Qué opción del menú desplegable?
JsonStatham
17

Mi respuesta aborda EF core . Me refiero a este problema de github y los documentos sobre la configuraciónDbContext :

Simple

Anule el OnConfiguringmétodo de su DbContextclase ( YourCustomDbContext) como se muestra aquí para usar ConsoleLoggerProvider; sus consultas deben iniciar sesión en la consola:

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
}

Complejo

Este caso complejo evita anular el DbContext OnConfiguringmétodo. , lo que se desaconseja en los documentos: "Este enfoque no se presta a las pruebas, a menos que las pruebas se dirijan a la base de datos completa".

Este caso complejo utiliza:

  • El método IServiceCollectionen Startupclase ConfigureServices(en lugar de anular el OnConfiguringmétodo; el beneficio es un acoplamiento más flexible entre el DbContexty el ILoggerProviderque desea usar)
  • Una implementación de ILoggerProvider(en lugar de utilizar la ConsoleLoggerProviderimplementación que se muestra arriba; el beneficio es que nuestra implementación muestra cómo iniciaríamos sesión en el archivo (no veo un proveedor de registro de archivos enviado con EF Core ))

Me gusta esto:

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

Aquí está la implementación de a MyLoggerProvider(y su MyLoggerque agrega sus registros a un archivo que puede configurar; sus consultas de EF Core aparecerán en el archivo).

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;
        }
    } 
}
El guisante rojo
fuente
Entonces ... ¿no hay forma de principiantes de hacerlo?
Juan De la Cruz
1
@JuanDelaCruz simplifiqué mi respuesta; pruebe la alternativa simple
The Red Pea
16

Hay dos maneras:

  1. Para ver el SQL que se generará, simplemente llame ToTraceString(). Puede agregarlo a su ventana de observación y establecer un punto de interrupción para ver cuál sería la consulta en cualquier punto dado para cualquier consulta LINQ.
  2. Puede adjuntar un marcador a su servidor SQL de elección, que le mostrará la consulta final en todos sus detalles sangrientos. En el caso de MySQL, la forma más fácil de rastrear las consultas es simplemente ajustar el registro de consultas tail -f. Puede obtener más información sobre las instalaciones de registro de MySQL en la documentación oficial . Para SQL Server, la forma más fácil es utilizar el generador de perfiles de SQL Server incluido.
Benjamin Pollack
fuente
27
El ToTraceString de qué?
nos
ObjectQuery, como Nick notó justo después de que publiqué mi respuesta.
Benjamin Pollack
2
SQL Server Profiler captura los primeros 4000 caracteres, pero las consultas EF pueden ser mucho más largas que eso.
8

Para tener la consulta siempre a mano, sin cambiar el código, agregue esto a su DbContext y verifíquelo en la ventana de salida en Visual Studio.

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

Similar a la respuesta de @Matt Nibecker, pero con esto no tiene que agregarlo en su código actual, cada vez que necesita la consulta.

Gerrie Pretorius
fuente
La mejor respuesta!
AlexSC
7

SQL Management Studio => Herramientas => Analizador de SQL Server

Archivo => Nuevo seguimiento ...

Utilice la plantilla => en blanco

Selección de evento => T-SQL

Comprobación del lado izquierdo para: SP.StmtComplete

Los filtros de columna se pueden usar para seleccionar un nombre de aplicación o nombre de base de datos específico

Comience a ejecutar ese perfil y luego active la consulta.

Haga clic aquí para obtener información de la Fuente

Andrew Paté
fuente
1
lo siento, eso es solo para el servidor SQL, no MySQL
andrew pate
5

Bueno, en este momento estoy usando el generador de perfiles Express para ese propósito, el inconveniente es que solo funciona para MS SQL Server. Puede encontrar esta herramienta aquí: https://expressprofiler.codeplex.com/

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

Devolverá la consulta sql. Trabajar con el contexto de datos de EntityFramework 6

Gianluca Conte
fuente
44
Acabo de intentar esto y rastrea el objeto: Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1 [System.Linq.IGrouping en 2[System.Int32,String]]lugar de la consulta real. ¿Me estoy perdiendo algo o te olvidaste de mencionar algo?
loganjones16
5

Estoy haciendo una prueba de integración, y necesitaba esto para depurar la declaración SQL generada en Entity Framework Core 2.1, así que uso DebugLoggerProvidero me ConsoleLoggerProvidergusta así:

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

        ........

Aquí hay un ejemplo de salida de la consola de Visual Studio:

Ejemplo de salida de sentencia SQL

Rosdi Kasim
fuente
1
DebugLoggerPrivider y ConsoleLoggerProvider parecen existir solo en .NET Core: docs.microsoft.com/en-us/dotnet/api/…
Gabriel Magana
4

Nigromancia
Esta página es el primer resultado de búsqueda cuando se busca una solución para cualquier .NET Framework, así que aquí, como servicio público, cómo se hace en EntityFramework Core (para .NET Core 1 y 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);

Y luego estos métodos de extensión (IQueryableExtensions1 para .NET Core 1.0, IQueryableExtensions para .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;
        }


    }


}
Stefan Steiger
fuente
Estoy usando EF Core 2.0.1 y la sugerencia anterior da como resultado: System.InvalidCastException: 'No se puede emitir un objeto de tipo Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor' para escribir '' Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVoorVoitor ' la línea: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Chris Wolf
2
@ChrisWolf si sigues la esencia del autor original, puedes encontrar a alguien que proporcione una versión actualizada de ese método de extensión . Trabajó para mi.
B12Toaster
2

En mi caso para EF 6+, en lugar de usar esto en la ventana Inmediato para encontrar la cadena de consulta:

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

Terminé teniendo que usar esto para obtener el comando SQL generado:

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

Por supuesto, su firma de tipo anónimo puede ser diferente.

HTH

usuario8128167
fuente
2

Acabo de hacer esto:

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

Y el resultado que se muestra en la Salida :

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
Daniel Camargo
fuente
Sí, pero creo que nadie quiere ver el p__linq__i, sino los valores reales
Tom Stickel
De esta manera, todavía funciona en EF 6 y será útil si solo le importa cómo se ve la estructura de la consulta. En mi caso, el proyecto que creo el objeto IQueryable <T> no tiene referencia a System.Data.Entity ni quiero agregarlo solo para propósitos de depuración. Entonces este método funcionó bien.
wctiger
2

Para mí, usando EF6 y Visual Studio 2015, ingresé queryen la ventana inmediata y me dio la instrucción SQL generada

Jonas Stawski
fuente
1

Si desea tener valores de parámetros (no solo @p_linq_0sino también sus valores), puede usar IDbCommandInterceptory agregar algunos registros al ReaderExecutedmétodo.

michal.jakubeczy
fuente
1

Si bien hay buenas respuestas aquí, ninguna resolvió mi problema por completo (deseaba obtener la declaración SQL completa, incluidos los parámetros , del DbContext de cualquier IQueryable. El siguiente código hace exactamente eso. Es una combinación de fragmentos de código de Google. I solo lo he probado con EF6 + .

Solo a un lado, esta tarea me llevó mucho más tiempo de lo que pensaba. La abstracción en Entity Framework es un poco demasiado, en mi humilde opinión.

Primero el uso. Necesitará una referencia explícita 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 siguiente clase convierte un IQueryable en un DataTable. Modifique según su necesidad:

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

Para usar, simplemente llámelo de la siguiente manera:

var context = new MyContext();
var data = ....//Query, return type can be anonymous
    .AsQueryable();
var dt = new EntityFrameworkCommand()
    .Initialize(context, data)
    .GetData();
Jeremy Morren
fuente
0

Entity Framework 4 Solution

La mayoría de las respuestas aquí fueron específicas de EF6. Aquí hay uno para aquellos que todavía usan EF4.

Este método reemplaza el @p__linq__0/ etc. parámetros con sus valores reales, por lo que puede copiar y pegar la salida en SSMS y ejecutarla o depurarla.

    /// <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;
    }
jramm
fuente