Saya menggunakan Entity Framework Core dan saya perlu melihat kode SQL mana yang sedang dibuat. Di versi sebelumnya dari Entity Framework saya bisa menggunakan yang berikut ini:
string sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
Jika kueri merupakan objek IQuerable ... Tapi ToTraceString tidak tersedia di EF Core.
Bagaimana saya bisa melakukan hal serupa di EF Core?
entity-framework-core
Miguel Moura
sumber
sumber
Jawaban:
EF inti 5 / Net 5
query.ToQueryString()
Lihat Yang Baru di EF Core 5.0var query = _context.Widgets.Where(w => w.IsReal && w.Id == 42); var sql = query.ToQueryString();
Untuk kerangka inti bersih yang lebih lama, Ekstensi dapat digunakan.
Inti 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; }
lihat Inti dari 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);
Masalah ini juga dilacak oleh tim inti bersih EF dan dijadwalkan untuk rilis berikutnya.
sumber
IQueryable
dan bukanIQueryable<T>
?IQueryable<T>
. Lihatwidget
contoh di atas. Apakah Anda memiliki contoh yang hanya memiliki IQuerizable.IQueryable
hanyaKarena EF 7 diubah namanya menjadi Entity Framework Core, saya akan meringkas opsi untuk EF Core.
Ada 3 pendekatan untuk mencatat pernyataan SQL dari
IQueryable<>
:Berikut adalah kode refleksi gila (metode ekstensi):
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; } }
Setelah menambahkan metode ekstensi ini ke kode Anda, Anda dapat menggunakan metode sebagai berikut:
// 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();
Referensi: http://rion.io/2016/10/19/accessing-entity-framework-core-queries-behind-the-scenes-in-asp-net-core/ dan https://gist.github.com / rionmonster / 2c59f449e67edf8cd6164e9fe66c545a
sumber
optionsBuilder.UseLoggerFactory(LoggerFactory);
public static readonly LoggerFactory LoggerFactory = new LoggerFactory(new[] { new ConsoleLoggerProvider((_, __) => true, true) });
karena menghasilkan sql yang lebih indah, tetapi sayangnya juga banyak spam.Untuk siapa pun yang hanya mencoba mendiagnosis kueri EF Core yang salah sasaran atau sejenisnya dan tidak ingin mengubah kodenya, ada beberapa opsi:
Gunakan SQL Server Management Studio (SSMS) SQL Profiler
Jika Anda telah menginstal SQL Server Management Studio (SSMS), Anda dapat menjalankan SQL Profiler dari menu Tools di SSMS:
Dan kemudian mulai jejak baru yang berjalan di SQL Profiler setelah terbuka.
Anda kemudian akan dapat melihat permintaan SQL yang masuk dari EF, biasanya format tersebut cukup baik dan mudah dibaca.
Periksa Output Window di Visual Studio
Dalam salinan VS2019 saya, menggunakan EF2.2 saya dapat mengubah jendela keluaran untuk menampilkan keluaran dari Server Web (pilih nama aplikasi dan server web Anda di kombo "Tampilkan keluaran dari" di bagian atas panel Keluaran) dan SQL keluar juga ditampilkan di sana. Saya telah memeriksa kode saya dan sejauh yang saya lihat saya belum melakukan apa pun untuk mengaktifkannya, jadi saya pikir itu harus melakukan ini secara default:
Jika Anda ingin melihat parameter yang dikirim ke server SQL dalam kueri, Anda dapat mengaktifkannya saat menyiapkan DBContext dengan
EnableSensitiveDataLogging
metode, mis.services.AddDbContext<FusionContext>(options => options .UseSqlServer(connectionString)) //.EnableDetailedErrors() .EnableSensitiveDataLogging()
@Tich - Lil3p menyebutkan di komentar bahwa mereka juga perlu menggunakan sakelar untuk mengaktifkan SQL Debugging di tab Debug pada halaman Properti proyek (yang
"sqlDebugging": true
disetel di LaunchSettings.json). Saya telah memeriksa dan saya belum mengaktifkannya untuk salah satu proyek saya, tetapi itu mungkin layak untuk dicoba juga jika hal di atas tidak berhasil untuk Anda.sumber
app.UseDeveloperExceptionPage()
di Startup.Configure danservices.AddServerSideBlazor() .AddCircuitOptions(options => { options.DetailedErrors = true; });
di Startup.ConfigureServices. Salah satunya mungkin menampilkan parameter.Saya mengambil berdasarkan jawaban @ nikolay-kostov.
Perbedaannya adalah saya mendapatkan perintah SQL dengan parameter yang diekstrak daripada hard code yang lebih sejalan dengan cara EF Core mengirim perintah ke database. Selain itu, jika Anda ingin mengedit dan mengirim perintah ke database, sebaiknya gunakan parameter.
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); } }
sumber
Menambahkan jawaban ini karena semua saran di sini telah rusak dengan rilis EF Core baru (yaitu, semua jawaban di sini rusak pada EF Core 2.2). Berikut kode yang berfungsi untuk saya pada percobaan pertama, dan tampaknya versi .NET Core agnostik (sejauh ini): https://blogs.msdn.microsoft.com/dbrowne/2017/09/22/simple-logging-for -ef-core /
sumber
Entity Framework Core 3.x
Anda bisa mendapatkannya melalui logging.
Buat pabrik:
Beri tahu
DbContext
pabrik mana yang akan digunakan:Dari postingan ini
Anda bisa mendapatkan lebih banyak informasi jika Anda ingin menerapkan ILogger:
sumber
Untuk EF Core 3.1 dengan variabel, saya memiliki berikut ini (berdasarkan beberapa komentar GitHub dari halllo ) yang ditautkan di atas dalam komentar dari @ Thom Kiesewetter et al.
Ini mungkin tidak menggantikan semua jenis, tetapi sebagian besar sudah tercakup. Jangan ragu untuk memperpanjang.
sumber
Sebagai layanan publik:
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);
Dan kemudian metode ekstensi ini (IQueryableExtensions1 untuk .NET Core 1.0, IQueryableExtensions untuk .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; } } }
sumber
Untuk EF Core 3 dan yang lebih baru, EFCore.BulkExtensions memiliki metode ToParametrizedSql. Satu-satunya keluhan saya adalah ia mengembalikan parameter sebagai Microsoft.Data.SqlClient, jadi terkadang saya harus mengubahnya menjadi System.Data.SqlClient jika itu adalah jenis koneksi saya.
https://github.com/borisdj/EFCore.BulkExtensions
sumber