Як я бачу SQL, що генерується за допомогою сутності?
(У моєму конкретному випадку я використовую постачальник mysql - якщо це має значення)
Як я бачу SQL, що генерується за допомогою сутності?
(У моєму конкретному випадку я використовую постачальник mysql - якщо це має значення)
Відповіді:
Ви можете зробити наступне:
IQueryable query = from x in appEntities
where x.id == 32
select x;
var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
або в EF6:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
.ToTraceString();
Це дасть вам сформований SQL.
.Single()
вашого об'єкта IQueryable
я не думаю більше .
result
в System.Data.Entity.Infrastructure.DbQuery<T>
, а потім отримати внутрішнє властивість , InternalQuery
як (System.Data.Entity.Internal.Linq.InternalQuery<T>)
, і тільки тоді, використанняToTraceString()
result.ToString()
Для тих, хто використовує Entity Framework 6 і новіших версій, якщо ви хочете переглянути вихідний SQL у Visual Studio (як я це робив), вам потрібно використовувати новий функціонал реєстрації / перехоплення.
Додаючи наступний рядок, виплюнете згенерований SQL (разом із додатковими деталями, що стосуються виконання) на вихідній панелі Visual Studio:
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
// query the database using EF here.
}
Більше інформації про вхід у EF6 у цій чудовій серії блогу: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/
Примітка. Переконайтеся, що ви виконуєте проект у режимі DEBUG.
Починаючи з EF6.1, ви можете використовувати перехоплювачі для реєстрації реєстратора баз даних. Дивіться розділи «Перехоплювачі» і «Logging операцій з базою даних» в файл тут
<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>
Якщо ви використовуєте DbContext, ви можете зробити наступне, щоб отримати SQL:
var result = from i in myContext.appEntities
select new Model
{
field = i.stuff,
};
var sql = result.ToString();
ToString()
дасть вам запит із змінними в ньому, наприклад p__linq__0
, замість кінцевих значень (наприклад: 34563 замість p__linq__0
)
Застосовується для EF 6.0 і вище: для тих, хто хоче дізнатися більше про функцію ведення журналу та додати до деяких відповідей.
Будь-яка команда, надіслана з EF до бази даних, тепер може реєструватися. Щоб переглянути згенеровані запити з EF 6.x, використовуйтеDBContext.Database.Log property
Що отримує журнал
- 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.
Приклад:
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();
}
Вихід:
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
Для входу у зовнішній файл:
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();
}
}
Більше інформації тут: Реєстрація та перехоплення операцій з базою даних
У EF 4.1 можна зробити наступне:
var result = from x in appEntities
where x.id = 32
select x;
System.Diagnostics.Trace.WriteLine(result .ToString());
Це дасть вам сформований SQL.
ToString()
вихід - це область імен цього користувацького типу. Наприклад, якби вищевказаний код був select new CustomType { x = x.Name }
, повернене значення було б чимось на зразок Company.Models.CustomType
замість згенерованого SQL.
System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]
для мене.
Моя відповідь стосується ядра EF . Я посилаюся на цю проблему github та документи на налаштуванняDbContext
:
Простий
Замініть OnConfiguring
метод вашого DbContext
класу ( YourCustomDbContext
), як показано тут, щоб використовувати ConsoleLoggerProvider; ваші запити повинні увійти до консолі:
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
}
Складні
Цей складний випадок дозволяє уникнути перевищення значенняDbContext
OnConfiguring
методі. , що не рекомендується в документах: "Цей підхід не піддається тестуванню, якщо тільки тести не спрямовані на повну базу даних."
Цей складний випадок використовує:
IServiceCollection
in Startup
class ConfigureServices
(замість того, щоб переосмислити OnConfiguring
метод; перевагою є більш слабке з'єднання міжDbContext
та, яке ILoggerProvider
ви хочете використовувати)ILoggerProvider
(замість використання ConsoleLoggerProvider
показаної вище реалізації; користь полягає в тому, що наша реалізація показує, як ми входитимемо у файл (я не бачу постачальника журналів файлів, що постачається разом із EF Core) ))Подобається це:
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));
...
}
}
Ось реалізація a MyLoggerProvider
(і його MyLogger
додає свої журнали до файлу, який ви можете налаштувати; ваші запити EF Core з'являться у файлі.)
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;
}
}
}
Є два способи:
ToTraceString()
. Ви можете додати його у вікно перегляду та встановити точку перерви, щоб побачити, яким буде запит у будь-який момент для будь-якого запиту LINQ.tail -f
. Ви можете дізнатися більше про засоби реєстрації даних MySQL в офіційній документації . Для SQL Server найпростішим способом є використання включеного SQL Server-профілера.Щоб запит був завжди під рукою, не змінюючи код, додайте це у свій DbContext і перевірте його у вікні виводу у візуальній студії.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
Подібно до @Matt Nibecker відповіді, але при цьому вам не потрібно додавати її у свій поточний код, кожного разу, коли вам потрібен запит.
SQL Management Studio => Інструменти => Профілер SQL Server
Файл => Нова трасування ...
Використовуйте шаблон => Пустий
Вибір події => T-SQL
Перевірка на лівій стороні для: SP.StmtComplete
Фільтри стовпців можна використовувати для вибору конкретного ApplicationName або DatabaseName
Запустіть цей профіль, а потім запустіть запит.
Натисніть тут, щоб отримати інформацію про джерела
На даний момент я для цього використовую експрес-профілер, недолік - це те, що він працює лише для MS SQL Server. Ви можете знайти цей інструмент тут: https://expressprofiler.codeplex.com/
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
Поверне запит sql. Робота з використанням даних контексту EntityFramework 6
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
виявляє об'єкт: 1 [System.Linq.IGгрупування 2[System.Int32,String]]
замість фактичного запиту. Я щось пропускаю чи ти забув щось згадати?
Я роблю тест на інтеграцію, і мені знадобилося це для налагодження згенерованого оператора SQL в Entity Framework Core 2.1, тому я використовую DebugLoggerProvider
або ConsoleLoggerProvider
так:
[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);
........
Ось зразок виводу з консолі Visual Studio:
Некромантування.
Ця сторінка є першим результатом пошуку під час пошуку рішення для будь-якої .NET Framework, тому тут, як публічна служба, як це робиться в EntityFramework Core (для .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);
А потім ці методи розширення (IQueryableExtensions1 для .NET Core 1.0, IQueryableExtensions для .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();
У моєму випадку для EF 6+, замість того, щоб використовувати це у вікні негайного пошуку, щоб знайти рядок запиту:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();
Мені в кінцевому підсумку довелося скористатися цим для отримання згенерованої команди SQL:
var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();
Звичайно, ваш анонімний підпис типу може бути різним.
HTH.
Я щойно зробив це:
IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);
І результат, показаний у висновку :
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
Для мене за допомогою EF6 та Visual Studio 2015 я ввійшов query
у найближче вікно, і це дало мені згенеровану заяву SQL
Якщо ви також хочете мати значення параметрів (не тільки, @p_linq_0
але й їхніх значень), ви можете використовувати IDbCommandInterceptor
та додати ReaderExecuted
метод реєстрації до журналу .
Хоча тут є хороші відповіді, жодна проблема повністю не вирішила мою проблему (я хотів отримати весь оператор SQL, включаючи параметри , з DbContext від будь-якого IQueryable. Наступний код робить саме це. Це комбінація фрагментів коду від Google. I протестували лише за допомогою EF6 + .
Тільки вбік, це завдання зайняло мені шлях довше, ніж я думав, що буде. Абстракція в Entity Framework трохи більше, ІМХО.
Спочатку використання. Вам знадобиться явна посилання на "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;
Наступний клас перетворює IQueryable в DataTable. Модифікуйте залежно від вашої потреби:
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;
}
}
Для використання просто зателефонуйте, як зазначено нижче:
var context = new MyContext();
var data = ....//Query, return type can be anonymous
.AsQueryable();
var dt = new EntityFrameworkCommand()
.Initialize(context, data)
.GetData();
Більшість відповідей тут були специфічними для EF6. Ось для тих, хто ще використовує EF4.
Цей метод замінює @p__linq__0
/ і т.д. параметрів з їх фактичними значеннями, тому ви можете просто скопіювати та вставити вихід у SSMS та запустити його чи налагодити його.
/// <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;
}