Как посмотреть SQL, сгенерированный Entity Framework?
Как посмотреть SQL, сгенерированный структурой сущностей?
(В моем конкретном случае я использую провайдер MySQL - если это имеет значение)
25 ответов
Вы можете сделать следующее:
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.
Для тех, кто использует 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/
Примечание. Убедитесь, что ваш проект запущен в режиме отладки.
Начиная с EF6.1 вы можете использовать Interceptors для регистрации регистратора базы данных. Смотрите главы "Перехватчики" и "Ведение журнала операций базы данных" в файл здесь
<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();
EF Core 5.0
Эта долгожданная функция доступна в EF Core 5.0! Это из еженедельных обновлений статуса:
var query = context.Set<Customer>().Where(c => c.City == city); Console.WriteLine(query.ToQueryString())
приводит к этому выводу при использовании поставщика базы данных SQL Server:
DECLARE p0 nvarchar(4000) = N'London'; SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] WHERE [c].[City] = @__city_0
Обратите внимание, что объявления для параметров правильного типа также включаются в вывод. Это позволяет копировать / вставлять в SQL Server Management Studio или аналогичные инструменты, так что запрос может быть выполнен для отладки / анализа.
уууу!!!
Применимо для 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.
Мой ответ адресован ядру 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
вStartup
учебный класс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));
...
}
}
Вот реализация 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;
}
}
}
Есть два способа:
- Чтобы просмотреть SQL, который будет сгенерирован, просто позвоните
ToTraceString()
, Вы можете добавить его в окно просмотра и установить точку останова, чтобы увидеть, каким будет запрос в любой заданной точке для любого запроса LINQ. - Вы можете прикрепить трассировщик к вашему выбранному SQL-серверу, который покажет вам окончательный запрос во всех его подробностях. В случае с MySQL, самый простой способ отследить запросы - просто привязать журнал запросов к
tail -f
, Вы можете узнать больше о средствах ведения журнала MySQL в официальной документации. Для SQL Server самый простой способ - использовать включенный профилировщик SQL Server.
Чтобы запрос всегда был под рукой, без изменения кода добавьте его в свой DbContext и проверьте его в окне вывода в Visual Studio.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
Аналогично ответу @Matt Nibecker, но при этом вам не нужно добавлять его в ваш текущий код каждый раз, когда вам нужен запрос.
Я делаю интеграционный тест, и мне это нужно для отладки сгенерированного оператора 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:
SQL Management Studio => Инструменты => Профилировщик SQL Server
Файл => Новая трассировка...
Используйте Шаблон => Пусто
Выбор события => T-SQL
Проверка левой стороны для: SP.StmtComplete
Фильтры столбцов могут использоваться для выбора определенного ApplicationName или DatabaseName
Запустите этот профиль и запустите запрос.
Нажмите здесь для получения информации об источнике
Хотя здесь есть хорошие ответы, ни один из них не решил мою проблему полностью (я хотел получить весь оператор 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();
Использование ведения журнала с Entity Framework Core 3.x
Entity Framework Core генерирует SQL через систему ведения журнала. Есть только пара маленьких хитростей. Вы должны указатьILoggerFactory
и вы должны указать фильтр. Вот пример из этой статьи
Создайте фабрику:
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole((options) => { })
.AddFilter((category, level) =>
category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Information);
});
Скажите DbContext
использовать завод в OnConfiguring
метод:
optionsBuilder.UseLoggerFactory(_loggerFactory);
Отсюда вы можете значительно усложнить задачу и подключиться к методу Log для извлечения подробностей о выполненном SQL. См. Статью для полного обсуждения.
public class EntityFrameworkSqlLogger : ILogger
{
#region Fields
Action<EntityFrameworkSqlLogMessage> _logMessage;
#endregion
#region Constructor
public EntityFrameworkSqlLogger(Action<EntityFrameworkSqlLogMessage> logMessage)
{
_logMessage = logMessage;
}
#endregion
#region Implementation
public IDisposable BeginScope<TState>(TState state)
{
return default;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (eventId.Id != 20101)
{
//Filter messages that aren't relevant.
//There may be other types of messages that are relevant for other database platforms...
return;
}
if (state is IReadOnlyList<KeyValuePair<string, object>> keyValuePairList)
{
var entityFrameworkSqlLogMessage = new EntityFrameworkSqlLogMessage
(
eventId,
(string)keyValuePairList.FirstOrDefault(k => k.Key == "commandText").Value,
(string)keyValuePairList.FirstOrDefault(k => k.Key == "parameters").Value,
(CommandType)keyValuePairList.FirstOrDefault(k => k.Key == "commandType").Value,
(int)keyValuePairList.FirstOrDefault(k => k.Key == "commandTimeout").Value,
(string)keyValuePairList.FirstOrDefault(k => k.Key == "elapsed").Value
);
_logMessage(entityFrameworkSqlLogMessage);
}
}
#endregion
}
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
Вернет SQL-запрос. Работа с использованием datacontext EntityFramework 6
Necromancing.
Эта страница является первым результатом поиска при поиске решения для любой.NET Framework, так что здесь, в качестве общедоступной службы, как это делается в EntityFrameworkCore (для.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
{
// https://stackru.com/questions/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;
}
}
}
Начиная с Entity Framework Core 5.0+, можно просто один раз переопределить метод OnConfiguring в DbContext для ведения журнала. Это также работает для запросов Single() или Any().
Для входа в окно отладки:
public class ExampleDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// using System.Diagnostics;
optionsBuilder.LogTo(message => Debug.WriteLine(message));
}
}
Для входа в консоль:
public class ExampleDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.LogTo(Console.WriteLine);
}
}
Дополнительные сведения, включая уровни журналов и фильтрацию, см. здесь: https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/simple-logging .
Ну, на данный момент я использую Express Profiler для этой цели, недостатком является то, что он работает только для MS SQL Server. Вы можете найти этот инструмент здесь: https://expressprofiler.codeplex.com/
Для меня, используя EF6 и Visual Studio 2015, я вошел query
в ближайшем окне, и это дало мне сгенерированный оператор SQL
Решение Entity Framework 4
Большинство ответов здесь относились к 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;
}
Для меня
result.ToQueryString()
не работал, потому что я использовал .SingleAsync() или .ToListAsync()
Поэтому, когда я удалил .SingleAsync() и разделил его на два оператора, это сработало.
Пример ранее
var result = await Context.Work.AsNoTracking().Where(x => x.WorkId == workId).SingleAsync();
Пример после
var query = Context.Work.AsNoTracking().Where(x => x.WorkId == workId);
Debug.WriteLine(query.ToQueryString()); // Separated into two pieces so I can see what the query is
var result = await query.SingleAsync();
Я только что сделал это:
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
В моем случае для EF 6+ вместо использования в Immediate Window, чтобы найти строку запроса:
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();
Конечно, ваша подпись анонимного типа может отличаться.
НТН.
Если вы хотите иметь значения параметров (не только @p_linq_0
но и их значения) тоже можно использовать IDbCommandInterceptor
и добавить некоторые записи в ReaderExecuted
метод.