Comment afficher le SQL généré par le framework d'entité?
(Dans mon cas particulier, j'utilise le fournisseur mysql - si cela est important)
Comment afficher le SQL généré par le framework d'entité?
(Dans mon cas particulier, j'utilise le fournisseur mysql - si cela est important)
Réponses:
Vous pouvez effectuer les opérations suivantes:
IQueryable query = from x in appEntities
where x.id == 32
select x;
var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
ou dans EF6:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
.ToTraceString();
Cela vous donnera le SQL qui a été généré.
.Single()
votre objet n'est plus, IQueryable
je suppose.
result
à System.Data.Entity.Infrastructure.DbQuery<T>
, puis obtenir la propriété interne InternalQuery
comme (System.Data.Entity.Internal.Linq.InternalQuery<T>)
, et alors seulement, l' utilisationToTraceString()
result.ToString()
Pour ceux qui utilisent Entity Framework 6 et plus, si vous souhaitez afficher le SQL de sortie dans Visual Studio (comme je l'ai fait), vous devez utiliser la nouvelle fonctionnalité de journalisation / interception.
L'ajout de la ligne suivante crachera le SQL généré (ainsi que des détails supplémentaires liés à l'exécution) dans le panneau de sortie de Visual Studio:
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
// query the database using EF here.
}
Plus d'informations sur la connexion dans EF6 dans cette série de blogs astucieux: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/
Remarque: assurez-vous que vous exécutez votre projet en mode DEBUG.
À partir d'EF6.1, vous pouvez utiliser des intercepteurs pour enregistrer un enregistreur de base de données. Voir les chapitres "Intercepteurs" et "Journalisation des opérations de base de données" dans un fichier ici
<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 vous utilisez un DbContext, vous pouvez effectuer les opérations suivantes pour obtenir le SQL:
var result = from i in myContext.appEntities
select new Model
{
field = i.stuff,
};
var sql = result.ToString();
ToString()
vous donnera la requête avec des variables, comme p__linq__0
, au lieu des valeurs finales (par exemple: 34563 au lieu de p__linq__0
)
Applicable pour EF 6.0 et supérieur: pour ceux d'entre vous qui souhaitent en savoir plus sur la fonctionnalité de journalisation et ajouter à certaines des réponses déjà données.
Toute commande envoyée par l'EF à la base de données peut maintenant être enregistrée. Pour afficher les requêtes générées à partir d'EF 6.x, utilisez leDBContext.Database.Log property
Ce qui est enregistré
- 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.
Exemple:
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();
}
Production:
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
Pour vous connecter à un fichier externe:
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();
}
}
Plus d'informations ici: Journalisation et interception des opérations de base de données
Vous pouvez effectuer les opérations suivantes dans EF 4.1:
var result = from x in appEntities
where x.id = 32
select x;
System.Diagnostics.Trace.WriteLine(result .ToString());
Cela vous donnera le SQL qui a été généré.
ToString()
sortie est l'espace de noms de ce type personnalisé. Par exemple, si le code ci-dessus était select new CustomType { x = x.Name }
, la valeur retournée serait quelque chose comme Company.Models.CustomType
au lieu du SQL généré.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
pour moi.
Ma réponse concerne le noyau EF . Je fais référence à ce problème de github et aux documents sur la configurationDbContext
:
Facile
Remplacez la OnConfiguring
méthode de votre DbContext
classe ( YourCustomDbContext
) comme indiqué ici pour utiliser un ConsoleLoggerProvider; vos requêtes doivent se connecter à la 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
}
Complexe
Ce cas complexe évite de surcharger la DbContext
OnConfiguring
méthode. , ce qui est déconseillé dans la documentation: "Cette approche ne se prête pas aux tests, à moins que les tests ne ciblent la base de données complète."
Ce cas complexe utilise:
IServiceCollection
in Startup
class ConfigureServices
(au lieu de remplacer la OnConfiguring
méthode; l'avantage est un couplage plus lâche entre le DbContext
et le que ILoggerProvider
vous souhaitez utiliser)ILoggerProvider
(au lieu d'utiliser l' ConsoleLoggerProvider
implémentation indiquée ci-dessus; l'avantage est que notre implémentation montre comment nous nous connecterions à File (je ne vois pas de fournisseur d' enregistrement de fichiers livré avec EF Core ))Comme ça:
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));
...
}
}
Voici la mise en œuvre d'un MyLoggerProvider
(et son MyLogger
qui ajoute ses journaux à un fichier que vous pouvez configurer; vos requêtes EF Core apparaîtront dans le fichier.)
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;
}
}
}
Il y a deux façons:
ToTraceString()
. Vous pouvez l'ajouter dans votre fenêtre de surveillance et définir un point d'arrêt pour voir quelle serait la requête à tout moment donné pour n'importe quelle requête LINQ.tail -f
. Vous pouvez en savoir plus sur les fonctionnalités de journalisation de MySQL dans la documentation officielle . Pour SQL Server, le moyen le plus simple consiste à utiliser le profileur SQL Server inclus.Pour que la requête soit toujours à portée de main, sans modifier le code, ajoutez-la à votre DbContext et vérifiez-la dans la fenêtre de sortie de Visual Studio.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
Semblable à la réponse de @Matt Nibecker, mais avec cela, vous n'avez pas à l'ajouter dans votre code actuel, chaque fois que vous avez besoin de la requête.
SQL Management Studio => Outils => Profileur SQL Server
Fichier => Nouvelle trace ...
Utilisez le modèle => Blank
Sélection d'événement => T-SQL
Contrôle à gauche pour: SP.StmtComplete
Les filtres de colonne peuvent être utilisés pour sélectionner un ApplicationName ou DatabaseName spécifique
Démarrez ce profil en cours d'exécution, puis lancez la requête.
Cliquez ici pour les informations sur la source
Eh bien, j'utilise Express profiler à cet effet pour le moment, l'inconvénient est qu'il ne fonctionne que pour MS SQL Server. Vous pouvez trouver cet outil ici: https://expressprofiler.codeplex.com/
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
Renvoie la requête SQL. Utilisation du contexte de données d'EntityFramework 6
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1 [System.Linq.IGrouping 2[System.Int32,String]]
au lieu de la requête réelle. Suis-je en train de manquer quelque chose ou avez-vous oublié de mentionner quelque chose?
Je fais un test d'intégration, et j'en avais besoin pour déboguer l'instruction SQL générée dans Entity Framework Core 2.1, donc j'utilise DebugLoggerProvider
ou ConsoleLoggerProvider
j'aime:
[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);
........
Voici un exemple de sortie de la console Visual Studio:
Nécromancement.
Cette page est le premier résultat de recherche lorsque vous recherchez une solution pour n'importe quel .NET Framework, alors voici en tant que service public, comment cela se fait dans EntityFramework Core (pour .NET Core 1 et 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);
Et puis ces méthodes d'extension (IQueryableExtensions1 pour .NET Core 1.0, IQueryableExtensions pour .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();
Dans mon cas pour EF 6+, au lieu d'utiliser ceci dans la fenêtre immédiate pour trouver la chaîne de requête:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();
J'ai fini par devoir l'utiliser pour obtenir la commande SQL générée:
var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();
Bien sûr, votre signature de type anonyme peut être différente.
HTH.
Je viens de faire ça:
IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);
Et le résultat affiché dans la sortie :
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
Pour moi, en utilisant EF6 et Visual Studio 2015, je suis entré query
dans la fenêtre immédiate et cela m'a donné l'instruction SQL générée
Si vous souhaitez également avoir des valeurs de paramètre (non seulement, @p_linq_0
mais aussi leurs valeurs), vous pouvez utiliser IDbCommandInterceptor
et ajouter une journalisation à la ReaderExecuted
méthode.
Bien qu'il existe de bonnes réponses ici, aucune n'a résolu mon problème complètement (je souhaitais obtenir l'intégralité de l'instruction SQL, y compris les paramètres , à partir du DbContext à partir de n'importe quel IQueryable. Le code suivant fait exactement cela. Il s'agit d'une combinaison d'extraits de code de Google. I l'ont testé qu'avec EF6 + .
Soit dit en passant, cette tâche m'a pris beaucoup plus de temps que je ne le pensais. L'abstraction dans Entity Framework est un peu trop, à mon humble avis.
D'abord l'utilisation. Vous aurez besoin d'une référence explicite à «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 classe suivante convertit un IQueryable en un DataTable. Modifiez selon vos besoins:
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;
}
}
Pour l'utiliser, il suffit de l'appeler comme ci-dessous:
var context = new MyContext();
var data = ....//Query, return type can be anonymous
.AsQueryable();
var dt = new EntityFrameworkCommand()
.Initialize(context, data)
.GetData();
La plupart des réponses ici étaient spécifiques à EF6. En voici une pour ceux d'entre vous qui utilisent encore EF4.
Cette méthode remplace le @p__linq__0
/ etc. paramètres avec leurs valeurs réelles, vous pouvez donc simplement copier et coller la sortie dans SSMS et l'exécuter ou la déboguer.
/// <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;
}