J'utilise Entity Framework Core et j'ai besoin de voir quel code SQL est généré. Dans les versions précédentes d'Entity Framework, je pouvais utiliser ce qui suit:
string sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
Où la requête est un objet IQueryable ... Mais ToTraceString n'est pas disponible dans EF Core.
Comment puis-je faire quelque chose de similaire dans EF Core?
entity-framework-core
Miguel Moura
la source
la source
Réponses:
EF core 5 / Net 5
query.ToQueryString()
Voir les nouveautés d'EF Core 5.0var query = _context.Widgets.Where(w => w.IsReal && w.Id == 42); var sql = query.ToQueryString();
Pour les frameworks Net Core plus anciens, une extension peut être utilisée.
Noyau 2.1.2
using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Query.Expressions; using Microsoft.EntityFrameworkCore.Query.Sql; using static Microsoft.EntityFrameworkCore.DbLoggerCategory; public static class QueryableExtensions { 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 FieldInfo QueryModelGeneratorField = typeof(QueryCompiler).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryModelGenerator"); 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) { var queryCompiler = (QueryCompiler) QueryCompilerField.GetValue(query.Provider); var queryModelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler); var queryModel = queryModelGenerator.ParseQuery(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; } }
EF Core 3.0
public static string ToSql<TEntity>(this IQueryable<TEntity> query) { var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator(); var enumeratorType = enumerator.GetType(); var selectFieldInfo = enumeratorType.GetField("_selectExpression", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"cannot find field _selectExpression on type {enumeratorType.Name}"); var sqlGeneratorFieldInfo = enumeratorType.GetField("_querySqlGeneratorFactory", BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new InvalidOperationException($"cannot find field _querySqlGeneratorFactory on type {enumeratorType.Name}"); var selectExpression = selectFieldInfo.GetValue(enumerator) as SelectExpression ?? throw new InvalidOperationException($"could not get SelectExpression"); var factory = sqlGeneratorFieldInfo.GetValue(enumerator) as IQuerySqlGeneratorFactory ?? throw new InvalidOperationException($"could not get IQuerySqlGeneratorFactory"); var sqlGenerator = factory.Create(); var command = sqlGenerator.GetCommand(selectExpression); var sql = command.CommandText; return sql; }
voir Gist de RosiOli
EF Core 3.1
using System.Linq; using System.Reflection; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Query; public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class { var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator(); var relationalCommandCache = enumerator.Private("_relationalCommandCache"); var selectExpression = relationalCommandCache.Private<SelectExpression>("_selectExpression"); var factory = relationalCommandCache.Private<IQuerySqlGeneratorFactory>("_querySqlGeneratorFactory"); var sqlGenerator = factory.Create(); var command = sqlGenerator.GetCommand(selectExpression); string sql = command.CommandText; return sql; } private static object Private(this object obj, string privateField) => obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj); private static T Private<T>(this object obj, string privateField) => (T)obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);
Le problème est également suivi par l' équipe principale d'EF net et est prévu pour la prochaine version.
la source
IQueryable
et pas unIQueryable<T>
?IQueryable<T>
. Voir l'widget
exemple ci-dessus. Avez-vous un exemple qui n'a qu'un IQueryable.IQueryable
justeDepuis EF 7 est renommé Entity Framework Core, je vais vous résumer les options pour EF Core.
Il existe 3 approches pour la journalisation des instructions SQL à partir de
IQueryable<>
:Voici le code de réflexion fou (méthode d'extension):
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 FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator"); 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 { var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider); var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler); var queryModel = modelGenerator.ParseQuery(query.Expression); var database = (IDatabase)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; } }
Après avoir ajouté cette méthode d'extension à votre code, vous pouvez utiliser la méthode comme suit:
// Build a query using Entity Framework var query = _context.Widgets.Where(w => w.IsReal && w.Id == 42); // Get the generated SQL var sql = query.ToSql();
Parrainage: http://rion.io/2016/10/19/accessing-entity-framework-core-queries-behind-the-scenes-in-asp-net-core/ et https://gist.github.com / rionmonster / 2c59f449e67edf8cd6164e9fe66c545a
la source
optionsBuilder.UseLoggerFactory(LoggerFactory);
public static readonly LoggerFactory LoggerFactory = new LoggerFactory(new[] { new ConsoleLoggerProvider((_, __) => true, true) });
car il génère des sql encore plus beaux, mais malheureusement aussi beaucoup de spam.Pour tous ceux qui essaient simplement de diagnostiquer une requête EF Core ayant un raté unique ou autre et ne souhaitant pas modifier leur code, il existe plusieurs options:
Utiliser le profileur SQL SQL Server Management Studio (SSMS)
Si vous avez installé SQL Server Management Studio (SSMS), vous pouvez simplement lancer le SQL Profiler à partir du menu Outils de SSMS:
Et puis démarrez une nouvelle trace exécutée dans SQL Profiler une fois qu'elle s'ouvre.
Vous pourrez alors voir la requête SQL entrante d'EF, elles sont généralement assez bien formées et faciles à lire.
Vérifiez la fenêtre de sortie dans Visual Studio
Dans ma copie de VS2019, en utilisant EF2.2, je peux modifier la fenêtre de sortie pour afficher la sortie du serveur Web (sélectionnez le nom de votre application et de votre serveur Web dans la liste déroulante "Afficher la sortie de" en haut du volet Sortie) et le SQL sortant y est également montré. J'ai vérifié mon code et pour autant que je sache, je n'ai rien fait pour l'activer, donc je pense qu'il doit le faire par défaut:
Si vous souhaitez voir les paramètres envoyés au serveur SQL dans les requêtes, vous pouvez l'activer lors de la configuration du DBContext avec la
EnableSensitiveDataLogging
méthode, par exempleservices.AddDbContext<FusionContext>(options => options .UseSqlServer(connectionString)) //.EnableDetailedErrors() .EnableSensitiveDataLogging()
@Tich - Lil3p mentionne dans les commentaires qu'ils avaient également besoin d'utiliser un commutateur pour activer le débogage SQL dans l'onglet Débogage de la page Propriétés du projet (qui définit
"sqlDebugging": true
dans LaunchSettings.json). J'ai vérifié et je ne l'ai activé pour aucun de mes projets, mais cela vaut peut-être la peine d'être expérimenté si ce qui précède ne fonctionne pas pour vous.la source
app.UseDeveloperExceptionPage()
dans Startup.Configure etservices.AddServerSideBlazor() .AddCircuitOptions(options => { options.DetailedErrors = true; });
Startup.ConfigureServices. L'un de ceux-ci pourrait afficher les paramètres.Mon point de vue basé sur la réponse @ nikolay-kostov.
La différence est que j'obtiens la commande SQL avec des paramètres extraits au lieu de codés en dur, ce qui correspond davantage à la façon dont EF Core envoie des commandes à la base de données. De plus, si vous souhaitez modifier et envoyer la commande à la base de données, il est préférable d'utiliser des paramètres.
private static class IQueryableUtils { 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 FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator"); private static readonly FieldInfo queryContextFactoryField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryContextFactory"); private static readonly FieldInfo loggerField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_logger"); 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 sql, IReadOnlyDictionary<string, object> parameters) ToSql<TEntity>(IQueryable<TEntity> query) where TEntity : class { var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider); var queryContextFactory = (IQueryContextFactory)queryContextFactoryField.GetValue(queryCompiler); var logger = (Microsoft.EntityFrameworkCore.Diagnostics.IDiagnosticsLogger<DbLoggerCategory.Query>)loggerField.GetValue(queryCompiler); var queryContext = queryContextFactory.Create(); var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler); var newQueryExpression = modelGenerator.ExtractParameters(logger, query.Expression, queryContext); var queryModel = modelGenerator.ParseQuery(newQueryExpression); var database = (IDatabase)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 command = modelVisitor.Queries.First().CreateDefaultQuerySqlGenerator() .GenerateSql(queryContext.ParameterValues); return (command.CommandText, queryContext.ParameterValues); } }
la source
Ajout de cette réponse car toutes les suggestions ici ont rompu avec les nouvelles versions d'EF Core (c'est-à-dire que toutes les réponses ici sont cassées sur EF Core 2.2). Voici le code qui a fonctionné pour moi du premier coup et qui semble être indépendant de la version .NET Core (jusqu'à présent): https://blogs.msdn.microsoft.com/dbrowne/2017/09/22/simple-logging-for -ef-core /
la source
Entity Framework Core 3.x
Vous pouvez l'obtenir via la journalisation.
Créez l'usine:
Dites à l'
DbContext
usine à utiliser:De ce post
Vous pouvez obtenir plus d'informations si vous souhaitez implémenter ILogger:
la source
Pour EF Core 3.1 avec des variables, j'ai ce qui suit (basé sur certains commentaires GitHub de halllo ) qui a été lié ci-dessus dans le commentaire de @ Thom Kiesewetter et al.
Cela ne remplace peut-être pas tous les types, mais la plupart sont couverts. N'hésitez pas à prolonger.
la source
En tant que service public:
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; } } }
la source
Pour EF Core 3 et supérieur, EFCore.BulkExtensions a une méthode ToParametrizedSql. Mon seul reproche est qu'il renvoie les paramètres en tant que Microsoft.Data.SqlClient, donc parfois je dois les convertir en System.Data.SqlClient si c'est mon type de connexion.
https://github.com/borisdj/EFCore.BulkExtensions
la source