Estou usando o Entity Framework Core e preciso ver qual código SQL está sendo gerado. Nas versões anteriores do Entity Framework, eu poderia usar o seguinte:
string sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
Onde query é um objeto IQueryable ... Mas ToTraceString não está disponível no EF Core.
Como posso fazer algo semelhante no EF Core?
entity-framework-core
Miguel Moura
fonte
fonte
Respostas:
EF core 5 / Net 5
query.ToQueryString()
Veja a documentação ToQueryString () e o que há de novo no EF Core 5.0
var query = _context.Widgets.Where(w => w.IsReal && w.Id == 42); var sql = query.ToQueryString();
Para estruturas de núcleo de rede mais antigas, uma extensão pode ser usada.
Core 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) { using 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; }
veja a essência do 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 { using 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);
O problema também é rastreado pela equipe central da EF net e está agendado para o próximo lançamento.
fonte
IQueryable
e não com umIQueryable<T>
?IQueryable<T>
. Veja owidget
exemplo acima. Você tem um exemplo que tem apenas um IQueryable.IQueryable
justoEF Core 2.1
, esses são os únicos queusing
eu precisava (para evitar o problema de referências ambíguas).using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Storage;
Uma vez que EF 7 foi renomeado para Entity Framework Core, irei resumir as opções para EF Core.
Existem 3 abordagens para registrar instruções SQL de
IQueryable<>
:Aqui está o código de reflexão louco (método de extensão):
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; } }
Depois de adicionar este método de extensão ao seu código, você pode usar o método da seguinte maneira:
// 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();
Referência: http://rion.io/2016/10/19/accessing-entity-framework-core-queries-behind-the-scenes-in-asp-net-core/ e https://gist.github.com / rionmonster / 2c59f449e67edf8cd6164e9fe66c545a
fonte
optionsBuilder.UseLoggerFactory(LoggerFactory);
public static readonly LoggerFactory LoggerFactory = new LoggerFactory(new[] { new ConsoleLoggerProvider((_, __) => true, true) });
porque gera sql ainda mais bonito, mas infelizmente também muito spam.Para qualquer um que esteja apenas tentando diagnosticar uma consulta única com falha no EF Core ou semelhante e não querendo alterar seu código, existem algumas opções:
Use o SQL Server Management Studio (SSMS) SQL Profiler
Se você tiver o SQL Server Management Studio (SSMS) instalado, basta iniciar o SQL Profiler no menu Ferramentas no SSMS:
Em seguida, inicie um novo rastreamento em execução no SQL Profiler assim que ele for aberto.
Em seguida, você poderá ver a solicitação de entrada de SQL do EF, eles geralmente são bem formados e fáceis de ler.
Verifique a janela de saída no Visual Studio
Na minha cópia do VS2019, usando EF2.2, posso alterar a janela de saída para mostrar a saída do servidor web (selecione o nome do seu aplicativo e servidor web no combo "Mostrar saída de" no topo do painel Saída) e o SQL de saída também é mostrado lá. Eu verifiquei meu código e, pelo que posso ver, não fiz nada para habilitar isso, então acho que deve fazer isso por padrão:
Se você deseja ver os parâmetros enviados para o servidor SQL nas consultas, pode ativá-los ao configurar o DBContext com o
EnableSensitiveDataLogging
método, por exemploservices.AddDbContext<FusionContext>(options => options .UseSqlServer(connectionString)) //.EnableDetailedErrors() .EnableSensitiveDataLogging()
@Tich - Lil3p menciona nos comentários que eles também precisavam usar um switch para ativar o SQL Debugging na guia Debug da página Propriedades do projeto (que é
"sqlDebugging": true
definida em LaunchSettings.json). Eu verifiquei e não tenho ativado para nenhum dos meus projetos, mas pode valer a pena experimentar também se o acima não estiver funcionando para você.fonte
app.UseDeveloperExceptionPage()
em Startup.Configure eservices.AddServerSideBlazor() .AddCircuitOptions(options => { options.DetailedErrors = true; });
em Startup.ConfigureServices. Um deles pode mostrar os parâmetros.Minha opinião com base na resposta de @ nikolay-kostov.
A diferença é que recebo o comando SQL com parâmetros extraídos em vez de codificados, o que está mais de acordo com a forma como o EF Core envia comandos para o banco de dados. Além disso, se você deseja editar e enviar o comando para o banco de dados, é uma prática melhor usar parâmetros.
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); } }
fonte
Adicionando esta resposta porque todas as sugestões aqui quebraram com os novos lançamentos do EF Core (ou seja, todas as respostas aqui estão quebradas no EF Core 2.2). Este é o código que funcionou para mim na primeira tentativa e parece não ter versão do .NET Core (até agora): https://blogs.msdn.microsoft.com/dbrowne/2017/09/22/simple-logging-for -ef-core /
fonte
Entity Framework Core 3.x
Você pode obtê-lo por meio do registro.
Crie a fábrica:
Informe
DbContext
qual fábrica usar:A partir deste post
Você pode obter mais informações se quiser implementar ILogger:
fonte
Para EF Core 3.1 com variáveis, tenho o seguinte (com base em alguns comentários do GitHub de halllo ) que foi vinculado acima no comentário de @Thom Kiesewetter et al.
Talvez isso não substitua todos os tipos, mas a maioria é coberta. Sinta-se à vontade para estender.
fonte
Como serviço público:
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 então estes métodos de extensão (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; } } }
fonte
Para EF Core 3 e superior, EFCore.BulkExtensions tem um método ToParametrizedSql. Minha única reclamação é que ele retorna os parâmetros como Microsoft.Data.SqlClient, então às vezes tenho que convertê-los em System.Data.SqlClient se esse for meu tipo de conexão.
https://github.com/borisdj/EFCore.BulkExtensions
fonte