Bagaimana cara melihat SQL yang dihasilkan oleh kerangka kerja entitas?
(Dalam kasus khusus saya, saya menggunakan penyedia mysql - jika itu penting)
Bagaimana cara melihat SQL yang dihasilkan oleh kerangka kerja entitas?
(Dalam kasus khusus saya, saya menggunakan penyedia mysql - jika itu penting)
Jawaban:
Anda dapat melakukan hal berikut:
IQueryable query = from x in appEntities
where x.id == 32
select x;
var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
atau di EF6:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
.ToTraceString();
Itu akan memberi Anda SQL yang dihasilkan.
.Single()
objek Anda tidak lagi IQueryable
saya kira.
result
ke System.Data.Entity.Infrastructure.DbQuery<T>
, kemudian mendapatkan properti internal InternalQuery
sebagai (System.Data.Entity.Internal.Linq.InternalQuery<T>)
, dan hanya pada saat itu, gunakanToTraceString()
result.ToString()
Bagi mereka yang menggunakan Entity Framework 6 dan lebih tinggi, jika Anda ingin melihat output SQL dalam Visual Studio (seperti yang saya lakukan), Anda harus menggunakan fungsi logging / intersepsi yang baru.
Menambahkan baris berikut akan memuntahkan SQL yang dihasilkan (bersama dengan detail terkait eksekusi tambahan) di panel output Visual Studio:
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
// query the database using EF here.
}
Informasi lebih lanjut tentang masuk dalam EF6 dalam seri blog yang bagus ini: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/
Catatan: Pastikan Anda menjalankan proyek Anda dalam mode DEBUG.
Dimulai dengan EF6.1 Anda dapat menggunakan Interceptors untuk mendaftarkan logger basis data. Lihat bab "Pencegat" dan "Operasi Database Pencatatan" ke File di sini
<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>
Jika Anda menggunakan DbContext, Anda dapat melakukan hal berikut untuk mendapatkan SQL:
var result = from i in myContext.appEntities
select new Model
{
field = i.stuff,
};
var sql = result.ToString();
ToString()
akan memberi Anda kueri dengan variabel di dalamnya, seperti p__linq__0
, alih-alih nilai akhir (misal: 34563 alih-alih p__linq__0
)
Berlaku untuk EF 6.0 ke atas: Bagi Anda yang ingin tahu lebih banyak tentang fungsionalitas pencatatan dan menambahkan ke beberapa jawaban yang sudah diberikan.
Perintah apa pun yang dikirim dari EF ke database sekarang dapat dicatat. Untuk melihat pertanyaan yang dihasilkan dari EF 6.x, gunakanDBContext.Database.Log property
Apa yang Mendapat Log
- 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.
Contoh:
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();
}
Keluaran:
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
Untuk masuk ke file eksternal:
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();
}
}
Info lebih lanjut di sini: Logging dan Mencegah Operasi Database
Anda dapat melakukan hal berikut di EF 4.1:
var result = from x in appEntities
where x.id = 32
select x;
System.Diagnostics.Trace.WriteLine(result .ToString());
Itu akan memberi Anda SQL yang dihasilkan.
ToString()
output adalah namespace dari tipe kustom itu. Sebagai contoh, jika kode di atas adalah select new CustomType { x = x.Name }
, nilai yang dikembalikan akan menjadi seperti Company.Models.CustomType
bukannya SQL yang dihasilkan.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
bagi saya.
Jawaban saya membahas inti EF . Saya merujuk masalah github ini , dan dokumen tentang konfigurasiDbContext
:
Sederhana
Ganti OnConfiguring
metode DbContext
kelas Anda ( YourCustomDbContext
) seperti yang ditunjukkan di sini untuk menggunakan ConsoleLoggerProvider; pertanyaan Anda harus masuk ke konsol:
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
}
Kompleks
Kasus Kompleks ini menghindari override yang DbContext
OnConfiguring
metode. , yang tidak disarankan dalam dokumen: "Pendekatan ini tidak cocok untuk pengujian, kecuali jika tes menargetkan basis data lengkap."
Kasing Kompleks ini menggunakan:
IServiceCollection
in Startup
class ConfigureServices
(alih-alih mengganti OnConfiguring
metode; manfaatnya adalah kopling yang lebih longgar antara DbContext
dan yang ILoggerProvider
ingin Anda gunakan)ILoggerProvider
(alih-alih menggunakan ConsoleLoggerProvider
implementasi yang ditunjukkan di atas; manfaat adalah implementasi kami yang menunjukkan bagaimana kami akan masuk ke File (Saya tidak melihat Penyedia Logging File dikirimkan bersama EF Core ))Seperti ini:
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));
...
}
}
Inilah implementasi dari a MyLoggerProvider
(dan MyLogger
yang menambahkan log-nya ke File yang dapat Anda konfigurasikan; kueri EF Core Anda akan muncul dalam 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;
}
}
}
Ada dua cara:
ToTraceString()
. Anda bisa menambahkannya ke jendela arloji Anda dan mengatur titik istirahat untuk melihat seperti apa kueri pada titik tertentu untuk setiap kueri LINQ.tail -f
. Anda dapat mempelajari lebih lanjut tentang fasilitas logging MySQL di dokumentasi resmi . Untuk SQL Server, cara termudah adalah dengan menggunakan profiler SQL Server yang disertakan.Agar kueri selalu praktis, tanpa mengubah kode, tambahkan ini ke DbContext Anda dan periksa di jendela output di visual studio.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
Mirip dengan @Matt Nibecker, tetapi dengan ini Anda tidak perlu menambahkannya dalam kode Anda saat ini, setiap kali Anda membutuhkan kueri.
SQL Management Studio => Tools => SQL Server profiler
File => Jejak Baru ...
Gunakan Template => Kosong
Pilihan acara => T-SQL
Lefthandside memeriksa: SP.StmtComplete
Filter kolom dapat digunakan untuk memilih ApplicationName atau DatabaseName tertentu
Mulai menjalankan profil itu kemudian memicu permintaan.
Klik di sini untuk informasi Sumber
Yah, saya menggunakan profiler Express untuk tujuan itu saat ini, kekurangannya adalah itu hanya bekerja untuk MS SQL Server. Anda dapat menemukan alat ini di sini: https://expressprofiler.codeplex.com/
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
Akan mengembalikan kueri sql. Bekerja menggunakan datacontext dari EntityFramework 6
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1 [System.Linq.IGrouping 2[System.Int32,String]]
bukan permintaan yang sebenarnya. Apakah saya kehilangan sesuatu atau Anda lupa menyebutkan sesuatu?
Saya sedang melakukan tes integrasi, dan perlu ini untuk men-debug pernyataan SQL yang dihasilkan di Entity Framework Core 2.1, jadi saya menggunakan DebugLoggerProvider
atau ConsoleLoggerProvider
seperti itu:
[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);
........
Berikut ini contoh keluaran dari konsol Visual Studio:
Necromancing.
Halaman ini adalah hasil pencarian pertama ketika mencari solusi untuk .NET Framework, jadi di sini sebagai layanan publik, bagaimana hal itu dilakukan dalam EntityFramework Core (untuk .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);
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;
}
}
}
var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Dalam kasus saya untuk EF 6+, alih-alih menggunakan ini di Jendela Segera untuk menemukan string kueri:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();
Saya akhirnya harus menggunakan ini untuk mendapatkan perintah SQL yang dihasilkan:
var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();
Tentu saja tanda tangan jenis anonim Anda mungkin berbeda.
HTH.
Saya baru saja melakukan ini:
IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);
Dan hasilnya ditunjukkan pada 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
Bagi saya, menggunakan EF6 dan Visual Studio 2015 saya masuk query
di jendela langsung dan itu memberi saya Pernyataan SQL yang dihasilkan
Jika Anda ingin memiliki nilai parameter (tidak hanya @p_linq_0
tetapi juga nilainya), Anda dapat menggunakan IDbCommandInterceptor
dan menambahkan beberapa logging ke ReaderExecuted
metode.
Walaupun ada jawaban yang baik di sini, tidak ada yang menyelesaikan masalah saya sepenuhnya (saya ingin mendapatkan seluruh pernyataan SQL, termasuk Parameter , dari DbContext dari IQueryable. Kode berikut tidak hanya itu. Ini adalah kombinasi dari potongan kode dari Google. Saya hanya mengujinya dengan EF6 + .
Di samping itu, tugas ini membawa saya jauh lebih lama daripada yang saya kira. Abstraksi dalam Entity Framework sedikit banyak, IMHO.
Pertama menggunakan. Anda akan memerlukan referensi eksplisit ke '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;
Kelas berikut mengubah IQueryable menjadi DataTable. Ubah sesuai kebutuhan Anda mungkin:
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;
}
}
Untuk menggunakannya, cukup sebut sebagai berikut:
var context = new MyContext();
var data = ....//Query, return type can be anonymous
.AsQueryable();
var dt = new EntityFrameworkCommand()
.Initialize(context, data)
.GetData();
Sebagian besar jawaban di sini adalah khusus EF6. Ini satu untuk Anda yang masih menggunakan EF4.
Metode ini menggantikan @p__linq__0
/ etc. parameter dengan nilai aktualnya, jadi Anda bisa menyalin dan menempelkan output ke SSMS dan menjalankannya atau men-debug-nya.
/// <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;
}