¿Cómo veo el SQL generado por el marco de la entidad?
(En mi caso particular, estoy usando el proveedor mysql, si es importante)
¿Cómo veo el SQL generado por el marco de la entidad?
(En mi caso particular, estoy usando el proveedor mysql, si es importante)
Respuestas:
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ó.
.Single()
su objeto ya no está IQueryable
, supongo.
result
a System.Data.Entity.Infrastructure.DbQuery<T>
, luego obtener propiedad interna InternalQuery
como (System.Data.Entity.Internal.Linq.InternalQuery<T>)
, y solo entonces, usarToTraceString()
result.ToString()
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.
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>
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();
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
)
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
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ó.
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.CustomType
el SQL generado.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
para mí.
Mi respuesta aborda EF core . Me refiero a este problema de github y los documentos sobre la configuraciónDbContext
:
Simple
Anule el OnConfiguring
método de su DbContext
clase ( 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
OnConfiguring
mé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:
IServiceCollection
en Startup
clase ConfigureServices
(en lugar de anular el OnConfiguring
método; el beneficio es un acoplamiento más flexible entre el DbContext
y el ILoggerProvider
que desea usar)ILoggerProvider
(en lugar de utilizar la ConsoleLoggerProvider
implementació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 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;
}
}
}
Hay dos maneras:
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.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.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.
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
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/
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
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1 [System.Linq.IGrouping en 2[System.Int32,String]]
lugar de la consulta real. ¿Me estoy perdiendo algo o te olvidaste de mencionar algo?
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
o me ConsoleLoggerProvider
gusta 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:
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;
}
}
}
var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
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
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
Para mí, usando EF6 y Visual Studio 2015, ingresé query
en la ventana inmediata y me dio la instrucción SQL generada
Si desea tener valores de parámetros (no solo @p_linq_0
sino también sus valores), puede usar IDbCommandInterceptor
y agregar algunos registros al ReaderExecuted
método.
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();
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;
}