Jak wyświetlić kod SQL wygenerowany przez strukturę encji?
(W moim szczególnym przypadku korzystam z dostawcy mysql - jeśli to ma znaczenie)
Jak wyświetlić kod SQL wygenerowany przez strukturę encji?
(W moim szczególnym przypadku korzystam z dostawcy mysql - jeśli to ma znaczenie)
Odpowiedzi:
Możesz wykonać następujące czynności:
IQueryable query = from x in appEntities
where x.id == 32
select x;
var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
lub w EF6:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
.ToTraceString();
To da ci wygenerowany SQL.
.Single()
twojego obiektu już nie ma IQueryable
.
result
do System.Data.Entity.Infrastructure.DbQuery<T>
, a następnie uzyskać własność wewnętrzną InternalQuery
jak (System.Data.Entity.Internal.Linq.InternalQuery<T>)
i tylko wtedy, zastosowanieToTraceString()
result.ToString()
Dla tych, którzy używają Entity Framework 6 i nowszych, jeśli chcesz wyświetlić wyjściowy SQL w Visual Studio (tak jak ja), musisz użyć nowej funkcji rejestrowania / przechwytywania.
Dodanie następującego wiersza wypluje wygenerowany kod SQL (wraz z dodatkowymi szczegółami związanymi z wykonaniem) w panelu wyjściowym programu Visual Studio:
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
// query the database using EF here.
}
Więcej informacji o logowaniu EF6 w tej fajnej serii blogów: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/
Uwaga: Upewnij się, że projekt jest uruchomiony w trybie DEBUG.
Począwszy od wersji EF6.1, można użyć przechwytywaczy do zarejestrowania rejestratora bazy danych. Zobacz rozdziały „Przechwytywacze” i „Rejestrowanie operacji na bazie danych” w pliku tutaj
<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>
Jeśli używasz DbContext, możesz wykonać następujące czynności, aby uzyskać SQL:
var result = from i in myContext.appEntities
select new Model
{
field = i.stuff,
};
var sql = result.ToString();
ToString()
poda zapytanie ze zmiennymi, np. p__linq__0
zamiast wartości końcowych (np .: 34563 zamiast p__linq__0
)
Dotyczy EF 6.0 i nowszych: dla tych, którzy chcą dowiedzieć się więcej o funkcji rejestrowania i dodać do niektórych już udzielonych odpowiedzi.
Każde polecenie wysłane z EF do bazy danych może być teraz zarejestrowane. Aby wyświetlić wygenerowane zapytania z EF 6.x, użyjDBContext.Database.Log property
Co zostanie zarejestrowane?
- 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.
Przykład:
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();
}
Wynik:
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
Aby zalogować się do pliku zewnętrznego:
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();
}
}
Więcej informacji tutaj: Rejestrowanie i przechwytywanie operacji na bazie danych
W EF 4.1 możesz wykonać następujące czynności:
var result = from x in appEntities
where x.id = 32
select x;
System.Diagnostics.Trace.WriteLine(result .ToString());
To da ci wygenerowany SQL.
ToString()
wynikiem jest przestrzeń nazw tego typu niestandardowego. Na przykład, jeśli powyższy kod select new CustomType { x = x.Name }
byłby, zwrócona wartość byłaby podobna Company.Models.CustomType
do wygenerowanego kodu SQL.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
dla mnie.
Moja odpowiedź dotyczy rdzenia EF . Odnoszę się do tego problemu z github i dokumentów dotyczących konfiguracjiDbContext
:
Prosty
Przesłoń OnConfiguring
metodę swojej DbContext
klasy ( YourCustomDbContext
) jak pokazano tutaj, aby użyć ConsoleLoggerProvider; twoje zapytania powinny zalogować się do konsoli:
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
}
Złożony
Kompleks ten przypadek unika przesłanianie się DbContext
OnConfiguring
metodę., co jest odradzane w dokumentach: „Takie podejście nie nadaje się do testowania, chyba że testy są ukierunkowane na pełną bazę danych”.
W tym złożonym przypadku zastosowano:
IServiceCollection
w Startup
klasie ConfigureServices
(zamiast przesłonięcia OnConfiguring
metody; zaletą jest luźniejsze połączenie między DbContext
iILoggerProvider
którego chcesz użyć)ILoggerProvider
(zamiast korzystania z ConsoleLoggerProvider
implementacji pokazanej powyżej; korzyścią jest to, że nasza implementacja pokazuje, w jaki sposób logujemy się do pliku (nie widzę dostawcy rejestrowania plików dostarczanego z EF Core ))Lubię to:
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));
...
}
}
Oto implementacja a MyLoggerProvider
( MyLogger
która dołącza swoje dzienniki do pliku, który możesz skonfigurować; twoje zapytania EF Core pojawią się w pliku).
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;
}
}
}
Istnieją dwa sposoby:
ToTraceString()
. Możesz dodać go do okna zegarka i ustawić punkt przerwania, aby zobaczyć, jakie zapytanie będzie w danym momencie dla dowolnego zapytania LINQ.tail -f
. Możesz dowiedzieć się więcej o funkcjach logowania MySQL w oficjalnej dokumentacji . W przypadku programu SQL Server najprostszym sposobem jest użycie dołączonego narzędzia do profilowania programu SQL Server.Aby zapytanie było zawsze pod ręką, bez zmiany kodu, dodaj to do DbContext i sprawdź w oknie wyników w Visual Studio.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
Podobnie do odpowiedzi @Matt Nibecker, ale dzięki temu nie musisz dodawać jej do bieżącego kodu za każdym razem, gdy potrzebujesz zapytania.
SQL Management Studio => Narzędzia => SQL Server profiler
Plik => Nowy ślad ...
Użyj szablonu => puste
Wybór zdarzenia => T-SQL
Sprawdź po lewej stronie: SP.StmtComplete
Filtrów kolumnowych można użyć do wybrania konkretnej nazwy aplikacji lub bazy danych
Uruchom ten profil, a następnie uruchom zapytanie.
Kliknij tutaj, aby uzyskać informacje o źródle
W tej chwili używam do tego celu Express profilera, wadą jest to, że działa tylko dla MS SQL Server. Możesz znaleźć to narzędzie tutaj: https://expressprofiler.codeplex.com/
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
Zwróci zapytanie SQL. Praca z wykorzystaniem tekstu danych EntityFramework 6
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
wykrywa obiekt: 1 [System.Linq.IGrouping 2[System.Int32,String]]
zamiast rzeczywistego zapytania. Czy coś przeoczyłem, czy zapomniałeś o czymś wspomnieć?
Robię test integracji i potrzebowałem tego do debugowania wygenerowanej instrukcji SQL w Entity Framework Core 2.1, więc używam DebugLoggerProvider
lub ConsoleLoggerProvider
lubię tak:
[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);
........
Oto przykładowe dane wyjściowe z konsoli Visual Studio:
Nekromancja.
Ta strona jest pierwszym wynikiem wyszukiwania podczas wyszukiwania rozwiązania dla dowolnej platformy .NET Framework, więc tutaj jako usługa publiczna, jak to się robi w EntityFramework Core (dla .NET Core 1 i 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);
A następnie te metody rozszerzeń (IQueryableExtensions1 dla .NET Core 1.0, IQueryableExtensions dla .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();
W moim przypadku dla EF 6+, zamiast używać tego w Natychmiastowym oknie, aby znaleźć ciąg zapytania:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();
Skończyło się na tym, aby uzyskać wygenerowane polecenie SQL:
var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();
Oczywiście twój anonimowy podpis może się różnić.
HTH.
Właśnie to zrobiłem:
IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);
Wynik pokazany w danych wyjściowych :
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
Dla mnie, używając EF6 i Visual Studio 2015, wszedłem query
do bezpośredniego okna i dało mi to wygenerowaną instrukcję SQL
Jeśli chcesz także mieć wartości parametrów (nie tylko, @p_linq_0
ale także ich wartości), możesz użyć IDbCommandInterceptor
i dodać trochę logowania do ReaderExecuted
metody.
Chociaż są tutaj dobre odpowiedzi, żadne nie rozwiązało mojego problemu całkowicie (chciałem uzyskać całą instrukcję SQL, w tym parametry , z DbContext z dowolnego IQueryable. Poniższy kod właśnie to robi. Jest to kombinacja fragmentów kodu od Google. I przetestowałem to tylko z EF6 + .
Na marginesie, to zadanie zajęło mi znacznie dłużej, niż się spodziewałem. Abstrakcja w Entity Framework to trochę dużo, IMHO.
Najpierw za pomocą. Będziesz potrzebował wyraźnego odwołania do „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;
Następująca klasa przekształca IQueryable w DataTable. Zmodyfikuj w zależności od potrzeb:
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;
}
}
Aby użyć, po prostu nazwij to jak poniżej:
var context = new MyContext();
var data = ....//Query, return type can be anonymous
.AsQueryable();
var dt = new EntityFrameworkCommand()
.Initialize(context, data)
.GetData();
Większość odpowiedzi tutaj dotyczyła EF6. Oto jeden z tych, którzy nadal używają EF4.
Ta metoda zastępuje @p__linq__0
/ etc. parametry z ich rzeczywistymi wartościami, więc możesz po prostu skopiować i wkleić dane wyjściowe do SSMS i uruchomić je lub debugować.
/// <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;
}