Come posso visualizzare l'SQL generato dal framework di entità?
(Nel mio caso particolare sto usando il provider mysql - se è importante)
Come posso visualizzare l'SQL generato dal framework di entità?
(Nel mio caso particolare sto usando il provider mysql - se è importante)
Risposte:
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.
.Single()
l'oggetto non è più IQueryable
immagino.
result
a System.Data.Entity.Infrastructure.DbQuery<T>
, quindi ottenere proprietà interna InternalQuery
come (System.Data.Entity.Internal.Linq.InternalQuery<T>)
, e solo allora, l'usoToTraceString()
result.ToString()
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.
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>
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();
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
)
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
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.
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.CustomType
quello dell'SQL generato.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
per me.
La mia risposta si rivolge a EF core . Mi riferisco a questo problema di github e ai documenti sulla configurazioneDbContext
:
Semplice
Sostituisci il OnConfiguring
metodo della tua DbContext
classe ( 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
OnConfiguring
metodo. , 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:
IServiceCollection
in Startup
class ConfigureServices
(invece di sovrascrivere il OnConfiguring
metodo; il vantaggio è un accoppiamento più flessibile tra il DbContext
e il che ILoggerProvider
si desidera utilizzare)ILoggerProvider
(anziché utilizzare l' ConsoleLoggerProvider
implementazione 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 MyLogger
che 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;
}
}
}
Ci sono due modi:
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.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.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.
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
Bene, sto usando Express profiler per quello scopo al momento, lo svantaggio è che funziona solo per MS SQL Server. Puoi trovare questo strumento qui: https://expressprofiler.codeplex.com/
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
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1 [System.Linq.IGrouping 2[System.Int32,String]]
invece della query effettiva. Mi sto perdendo qualcosa o hai dimenticato di menzionare qualcosa?
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 DebugLoggerProvider
o ConsoleLoggerProvider
piace 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:
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;
}
}
}
var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
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.
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
Per me, usando EF6 e Visual Studio 2015 sono entrato query
nella finestra immediata e mi ha dato l'istruzione SQL generata
Se si desidera avere valori di parametro (non solo @p_linq_0
ma anche i loro valori), è possibile utilizzare IDbCommandInterceptor
e aggiungere un po 'di registrazione al ReaderExecuted
metodo.
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();
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;
}