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

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

(En mi caso particular, estoy usando el proveedor de mysql, si importa)

preguntado el 11 de septiembre de 09 a las 16:09

Este planteamiento de « artice de MSDN Magazine describe algunas opciones de creación de perfiles para Entity Framework 4:

La pregunta "duplicada" vinculada es para LINQ to SQL, por lo que en realidad no es un duplicado. -

Cuando se ejecuta en el depurador, IntelliTrace muestra las consultas SQL realizadas, aunque sin sus resultados. -

Si está interesado en ver SQL durante el desarrollo, puede usar LINQPad. Cuando ejecute 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. -

22 Respuestas

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

Respondido el 30 de enero de 20 a las 10:01

No obtendrá SQL para consultas que terminen en .Single (), .Count (), .Any (), etc. de esa manera. - elástico76

Eso es porque despues de correr .Single() tu objeto ya no es IQueryable Supongo. - Suhas

con EF6, podría obtenerlo solo con reflexión. pero primero tuve que convertir result a System.Data.Entity.Infrastructure.DbQuery<T>, luego obtén la propiedad interna InternalQuery as (System.Data.Entity.Internal.Linq.InternalQuery<T>), y solo entonces, usa ToTraceString() - essho

agregar referencia a System.Data.Entity, System.Data.Objects.ObjectQuery existe en el dll anterior - Mahesh

En EF6 puedes simplemente hacer result.ToString() - scott chambelán

Para aquellos que usan Entity Framework 6 en adelante, si desea ver el SQL de salida en Visual Studio (como lo hice yo), 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 que está ejecutando su proyecto en modo DEBUG.

Respondido 03 Abr '16, 17:04

Esta respuesta merece más amor (si está usando EF6 +) - gran adición de depuración, simplemente agréguela en el constructor DBContext (this.Database.Log = ...) - keithl8041

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 se redirige a Inmediato (Herramientas> Opciones> Depuración> Redirigir todo el texto de la ventana de salida a Inmediato Ventana) - rkawano

¿Hay alguna manera de hacer que esto incluya los valores de las variables directamente dentro del sql generado? Un poco de dolor con los más grandes. - chris owens

@Matt Nibecker Esto no funciona en EF Core. ¿Cuál es la alternativa para EF Core? - nam

ADVERTENCIA: implementé esto con la intención de que solo se ejecute en desarrollo. Cuando lo implementamos en nuestro entorno de prueba, comenzamos a ver repentinamente pérdidas de memoria en el proceso de trabajo de IIS. Después de la generación de perfiles de memoria, nos dimos cuenta de que incluso GC explícito ya no recopilaba los objetos de contexto de la entidad (sí, estaban usando declaraciones). Al eliminar esta línea, todo volvió a la normalidad. Entonces, si bien esta es una gran herramienta, asegúrese de compilarla solo en su aplicación para el desarrollo. - brandon barkley

A partir de EF6.1, puede usar Interceptores para registrar un registrador de base de datos. Consulte los capítulos "Interceptores" y "Registro de operaciones de bases de datos" en 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>

Respondido 07 Jul 14, 08:07

Publicación de blog sobre el tema blog.oneunicorn.com/2014/02/09/… - tim abel

Precisión, está por debajo de: ... - Cristóbal P.

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

Respondido 02 Oct 13, 23:10

ToString() le dará la consulta con variables en ella, como p__linq__0, en lugar de los valores finales (por ejemplo: 34563 en lugar de p__linq__0) - deportes

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

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

Qué 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

Respondido 22 ago 16, 06:08

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

Respondido el 01 de Septiembre de 11 a las 09:09

De hecho, creo que esto solo funciona cuando la consulta devuelve un tipo anónimo. Si devuelve un tipo personalizado, el ToString() la salida es el espacio de nombres de ese tipo personalizado. Por ejemplo, si el código anterior fue select new CustomType { x = x.Name }, el valor devuelto sería algo como Company.Models.CustomType en lugar del SQL generado. - Chad Levy

Esta técnica produce System.Data.Objects.ObjectQuery``1[MyProject.Models.Product] para mi. - Carlos G.

@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 ()" - elástico76

Esto le dará el SQL que se generó, ¿dónde, en la ventana de salida? ¿Qué opción del menú desplegable? - JsonStatham

Mi respuesta se dirige a EF núcleo. Hago referencia a esto problema de githuby los documentos en Configurando DbContext:

Sencillo

Anular el OnConfiguring método de su DbContext clase (YourCustomDbContext) como se muestra aquí utilizar un ConsoleLoggerProvider; sus consultas deben registrarse 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 La DbContext OnConfiguring método. , 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:

  • La IServiceCollection in Startup clase ConfigureServices método (en lugar de anular el OnConfiguring método; el beneficio es un acoplamiento más flojo entre el DbContext y ILoggerProvider quieres usar)
  • Una implementación de ILoggerProvider (en lugar de usar el ConsoleLoggerProvider implementación mostrada arriba; El beneficio es que nuestra implementación muestra cómo registraríamos en el archivo (no veo un Proveedor de registro de archivos enviado con EF Core))

Me gusta:

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 un MyLoggerProvider (y es MyLogger que 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;
        }
    } 
}

Respondido el 12 de junio de 18 a las 18:06

Entonces ... ¿no hay forma de hacerlo para principiantes? - Juan de la cruz

@JuanDelaCruz Simplifiqué mi respuesta; prueba la alternativa simple - El guisante rojo

Gracias por publicar esto. Me sorprendió que ya no haya una forma de hacerlo en la ventana inmediata en .NET Core. No quiero escribir código, solo quiero depurarlo. El generador de perfiles SQL es obviamente una opción, pero aún es más complicado de lo que solía ser. EGP

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 rastreador al servidor SQL de su elección, que le mostrará la consulta final con todos sus detalles sangrientos. En el caso de MySQL, la forma más fácil de rastrear las consultas es simplemente seguir el registro de consultas con tail -f. Puede obtener más información sobre las funciones de registro de MySQL en la documentación oficial. Para SQL Server, la forma más sencilla es utilizar el generador de perfiles de SQL Server incluido.

Respondido el 11 de Septiembre de 09 a las 20:09

¿El ToTraceString de qué? - nuestros

ObjectQuery, como señaló Nick justo después de que publiqué mi respuesta. - Benjamín Pollack

SQL Server Profiler captura los primeros 4000 caracteres, pero las consultas EF pueden ser mucho más largas. - usuario334911

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 necesite la consulta.

Respondido el 21 de enero de 17 a las 18:01

¡La mejor respuesta! - AlexSC

EF Núcleo 5.0

¡Esta característica tan esperada está disponible en EF Core 5.0! Esto es del actualizaciones de estado semanales:

var query = context.Set<Customer>().Where(c => c.City == city);
Console.WriteLine(query.ToQueryString())

da como resultado esta salida cuando se utiliza el proveedor de base de datos de SQL Server:

DECLARE p0 nvarchar(4000) = N'London';

SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName],
[c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone],
[c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[City] = @__city_0

Tenga en cuenta que las declaraciones para los parámetros del tipo correcto también se incluyen en la salida. Esto permite copiar / pegar en SQL Server Management Studio, o herramientas similares, de modo que la consulta se pueda ejecutar para depurar / analizar.

woohoo !!!

Respondido el 24 de junio de 20 a las 19:06

Este método no existe. - Christian Findlay

@ChristianFindlay aquí es la interfaz con el método y aquí es donde se incluye en EntityFrameworkQueryableExtensions. ¿Está utilizando EF Core 5.0? - josh blanco

¡buen punto! No. No es de extrañar que no pudiera verlo. Supongo que esto será en una versión preliminar de NuGet. - Christian Findlay

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 DebugLoggerProvider or ConsoleLoggerProvider al igual que:

[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 una salida de muestra de la consola de Visual Studio:

Salida de declaración SQL de muestra

Respondido 13 Oct 18, 06:10

DebugLoggerPrivider y ConsoleLoggerProvider parecen existir solo en .NET Core: docs.microsoft.com/en-us/dotnet/api/… - Gabriel Magaña

SQL Management Studio => Herramientas => Generador de perfiles de SQL Server

Archivo => Nueva traza ...

Use 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 ApplicationName o DatabaseName específico

Inicie ese perfil en ejecución y luego active la consulta.

Haga clic aquí para Información de origen

respondido 06 mar '19, 17:03

lo siento, eso es solo para el servidor SQL, no MySQL - andrew pate

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

Devolverá la consulta SQL. Trabajando usando el contexto de datos de EntityFramework 6

Respondido 20 Oct 16, 08:10

Intenté esto y rastrea el objeto: Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1 [System.Linq.IGrouping2[System.Int32,String]] en lugar de la consulta real. ¿Me estoy perdiendo algo o te olvidaste de mencionar algo? - loganjones16

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

Respondido el 23 de diciembre de 14 a las 09:12

Nigromante.
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 EntityFrameworkCore (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
{

    // https://stackoverflow.com/questions/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;
        }


    }


}

Respondido 13 Feb 18, 18:02

Estoy usando EF Core 2.0.1 y la sugerencia anterior da como resultado: System.InvalidCastException: 'No se puede lanzar un objeto de tipo Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor' para escribir '' Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor'` para la línea: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor(); - Chris Wolf

@ChrisWolf si sigues la esencia del autor original, puedes encontrar a alguien que proporcionó una versión actualizada de ese método de extensión. Trabajó para mi. - B12 Tostadora

Utilice el registro con Entity Framework Core 3.x

Entity Framework Core emite SQL a través del sistema de registro. Solo hay un par de pequeños trucos. Debe especificar un ILoggerFactory y debe especificar un filtro. Aquí hay un ejemplo de este artículo

Crea la fábrica:

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
    .AddConsole((options) => { })
    .AddFilter((category, level) =>
        category == DbLoggerCategory.Database.Command.Name
        && level == LogLevel.Information);
});

Di la DbContext para usar la fábrica en el OnConfiguring método:

optionsBuilder.UseLoggerFactory(_loggerFactory);

Desde aquí, puede volverse mucho más sofisticado y conectarse al método Log para extraer detalles sobre el SQL ejecutado. Vea el artículo para una discusión completa.

public class EntityFrameworkSqlLogger : ILogger
{
    #region Fields
    Action<EntityFrameworkSqlLogMessage> _logMessage;
    #endregion
    #region Constructor
    public EntityFrameworkSqlLogger(Action<EntityFrameworkSqlLogMessage> logMessage)
    {
        _logMessage = logMessage;
    }
    #endregion
    #region Implementation
    public IDisposable BeginScope<TState>(TState state)
    {
        return default;
    }
    public bool IsEnabled(LogLevel logLevel)
    {
        return true;
    }
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if (eventId.Id != 20101)
        {
            //Filter messages that aren't relevant.
            //There may be other types of messages that are relevant for other database platforms...
            return;
        }
        if (state is IReadOnlyList<KeyValuePair<string, object>> keyValuePairList)
        {
            var entityFrameworkSqlLogMessage = new EntityFrameworkSqlLogMessage
            (
                eventId,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "commandText").Value,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "parameters").Value,
                (CommandType)keyValuePairList.FirstOrDefault(k => k.Key == "commandType").Value,
                (int)keyValuePairList.FirstOrDefault(k => k.Key == "commandTimeout").Value,
                (string)keyValuePairList.FirstOrDefault(k => k.Key == "elapsed").Value
            );
            _logMessage(entityFrameworkSqlLogMessage);
        }
    }
    #endregion
}

Respondido 20 Jul 20, 05:07

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

Respondido 21 Jul 16, 16:07

No estoy seguro de por qué esto no se vota más a favor, esto es lo que funcionó para mí. - Codenova

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ónima puede ser diferente.

HTH.

Respondido el 02 de Septiembre de 15 a las 19:09

Acabo de hacer esto:

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

Y el resultado que se muestra en el 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

Respondido el 22 de junio de 16 a las 05:06

Sí, pero creo que nadie quiere ver el p__linq__i, pero los valores reales - tom stickel

De esta forma 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 creo el IQueryable El objeto no tiene referencia a System.Data.Entity ni quiero agregarlo solo con fines de depuración. Entonces este método funcionó bien. - wctiger

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

Solo un aparte, esta tarea me tomó mucho más tiempo de lo que pensé. La abstracción en Entity Framework es un poco excesiva, 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 usarlo, 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();

Respondido el 29 de enero de 20 a las 18:01

Gran respuesta En lugar de new SqlParameter, Deberías usar DbProviderFactories.GetFactory(connection).CreateParameter() para que sea compatible con otros proveedores de bases de datos. - FKDev

Si desea tener valores de parámetro (no solo @p_linq_0 sino también sus valores), puedes usar IDbCommandInterceptor y agregue algunos registros a ReaderExecuted método.

Respondido el 27 de enero de 17 a las 11:01

Solución Entity Framework 4

La mayoría de las respuestas aquí fueron específicas de EF6. Aquí hay uno para aquellos de ustedes 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 el resultado en SSMS y ejecutarlo o depurarlo.

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

contestado el 26 de mayo de 20 a las 05:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.