Как посмотреть SQL, сгенерированный Entity Framework?

625

Как посмотреть SQL, сгенерированный структурой сущностей?

(В моем конкретном случае я использую провайдер MySQL - если это имеет значение)

н.у.к.
источник
1
В этой статье из журнала MSDN описаны некоторые параметры профилирования для Entity Framework 4
Arve
2
Связанный вопрос «дубликат» относится к LINQ to SQL, поэтому на самом деле он не является дубликатом.
jrummell
2
При работе в отладчике IntelliTrace отображает выполненные SQL-запросы, хотя и без их результатов.
ivan_pozdeev
Если вам интересно видеть SQL прямо во время разработки, вы можете использовать LINQPad . Когда вы запустите запрос LINQ в результатах, появится вкладка SQL, которая показывает выполненный оператор SQL. Для mySQL вам нужно установить драйвер. У меня нет базы данных MySQL, но она должна работать.
gligoran

Ответы:

473

Вы можете сделать следующее:

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.

Ник Берарди
источник
20
Вы не получите SQL для запросов, заканчивающихся на .Single (), .Count (), .Any () и т. Д. Таким образом.
springy76
24
Это потому, что после запуска .Single()вашего объекта больше нет, IQueryableя думаю.
Suhas
11
с EF6 я мог получить это только с отражением. но сначала я должен был преобразовать resultв System.Data.Entity.Infrastructure.DbQuery<T>, затем получить внутреннее свойство InternalQueryкак (System.Data.Entity.Internal.Linq.InternalQuery<T>), и только потом использоватьToTraceString()
itsho
9
добавить ссылку на System.Data.Entity, System.Data.Objects.ObjectQuery существуют в вышеуказанной DLL
Mahesh
54
В EF6 вы можете просто сделатьresult.ToString()
Скотт Чемберлен
957

Для тех, кто использует 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/

Примечание. Убедитесь, что ваш проект запущен в режиме отладки.

Мэтт Нибекер
источник
107
Этот ответ заслуживает большей любви (если вы используете EF6 +) - отличное дополнение для отладки, просто добавьте его в конструктор DBContext (this.Database.Log = ...)
keithl8041
21
Убедитесь, что ваш проект выполняется в режиме отладки, проверьте, выбран ли элемент «Отладка» в выпадающем списке на панели «Вывод», а также проверьте, не перенаправляет ли ваша отладка на «Немедленное» («Инструменты»> «Параметры»> «Отладка»> «Перенаправить весь текст окна вывода на немедленное»). Окно)
rkawano
5
Есть ли способ получить это, чтобы включить значения переменных непосредственно в сгенерированном SQL? Немного боли с большими.
Крис
22
@Matt Nibecker Это не работает в EF Core. Какая альтернатива для EF Core?
Nam
9
ВНИМАНИЕ: Я реализовал это с намерением, чтобы он работал только в разработке. Когда мы развернули в нашей среде тестирования, мы начали внезапно видеть утечки памяти в рабочем процессе IIS. После профилирования памяти мы поняли, что даже явный GC больше не собирает объекты контекста сущности (да, они используют операторы). Удаление этой строки вернуло все в норму. Так что, хотя это отличный инструмент, убедитесь, что вы только встраиваете его в свое приложение для разработки.
Брэндон Баркли
82

Начиная с 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>
isepise
источник
1
Сообщение в блоге на эту тему blog.oneunicorn.com/2014/02/09/…
Тим Абелл
12
Точность, она находится под: <конфигурация> <entityFramework> <interceptors> ... </ interceptors> </ entityFramework> </ configuration>
Кристоф П
80

Если вы используете DbContext, вы можете сделать следующее, чтобы получить SQL:

var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();
Даг Клаттер
источник
12
ToString()даст вам запрос с переменными в нем, например p__linq__0, вместо конечных значений (например, вместо 34563 p__linq__0)
спортивные состязания
25

Применимо для 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();
   }
}

Более подробная информация здесь: ведение журнала и перехват операций с базой данных

NullReference
источник
21

В EF 4.1 вы можете сделать следующее:

var result = from x in appEntities
             where x.id = 32
             select x;

System.Diagnostics.Trace.WriteLine(result .ToString());

Это даст вам сгенерированный SQL.

Capriols
источник
1
На самом деле, я считаю, что это работает только тогда, когда запрос возвращает анонимный тип. Если он возвращает пользовательский тип, ToString()выводом является пространство имен этого пользовательского типа. Например, если приведенный выше код был select new CustomType { x = x.Name }, возвращаемое значение было бы что-то вроде Company.Models.CustomTypeвместо сгенерированного SQL.
Чад Леви
8
Эта техника производит System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]для меня.
Карл Дж.
1
@CarlG System.Data.Objects.ObjectQuery не является EF 4.1 (DbContext). Используя DbContext, это будет System.Data.Entity.Infrastructure.DbQuery`1 [MyProject.Models.Product], который действительно выводит свой SQL при вызове ToString ()
springy76
Это даст вам SQL, который был сгенерирован, где, в окне вывода? какой вариант из выпадающего?
JsonStatham
17

Мой ответ адресован ядру 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методе. , что не рекомендуется в документах: «Этот подход не подходит для тестирования, если тесты не нацелены на полную базу данных».

Этот сложный случай использует:

  • Метод IServiceCollectionin Startupclass ConfigureServices(вместо переопределения OnConfiguringметода; преимуществом является более слабая связь между DbContextи тем, что ILoggerProviderвы хотите использовать)
  • Реализация ILoggerProvider(вместо того, чтобы использовать ConsoleLoggerProviderреализацию, показанную выше; преимущество заключается в том, что наша реализация показывает, как мы должны регистрироваться в File (я не вижу провайдера File Logging, поставляемого с 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;
        }
    } 
}
Красный горох
источник
Так что ... у начинающих нет способа сделать это?
Хуан де ла Круз
1
@JuanDelaCruz Я упростил свой ответ; попробуйте простую альтернативу
Красный горох
16

Есть два способа:

  1. Чтобы просмотреть SQL, который будет сгенерирован, просто позвоните ToTraceString(). Вы можете добавить его в окно просмотра и установить точку останова, чтобы увидеть, каким будет запрос в любой заданной точке для любого запроса LINQ.
  2. Вы можете прикрепить трассировщик к вашему выбранному SQL-серверу, который покажет вам окончательный запрос во всех его подробностях. В случае с MySQL самый простой способ отследить запросы - просто привязать журнал запросов tail -f. Вы можете узнать больше о средствах ведения журнала MySQL в официальной документации . Для SQL Server самый простой способ - использовать включенный профилировщик SQL Server.
Бенджамин Поллак
источник
27
ToTraceString чего?
NOS
ObjectQuery, как заметил Ник сразу после того, как я опубликовал свой ответ.
Бенджамин Поллак
2
SQL Server Profiler захватывает первые 4000 символов, но запросы EF могут быть намного длиннее.
8

Чтобы запрос всегда был под рукой, без изменения кода добавьте его в свой DbContext и проверьте его в окне вывода в Visual Studio.

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.Log = (query)=> Debug.Write(query);
    }

Аналогично ответу @Matt Nibecker, но при этом вам не нужно добавлять его в ваш текущий код каждый раз, когда вам нужен запрос.

Джерри Преториус
источник
Лучший ответ!
AlexSC
7

SQL Management Studio => Инструменты => Профилировщик SQL Server

Файл => Новая трассировка ...

Используйте Шаблон => Пусто

Выбор события => T-SQL

Проверка левой стороны для: SP.StmtComplete

Фильтры столбцов могут использоваться для выбора определенного ApplicationName или DatabaseName

Запустите этот профиль и запустите запрос.

Нажмите здесь для получения информации об источнике

Эндрю Пэйт
источник
1
жаль вот только для сервера SQL, а не MySQL
андрей паштет
5

Ну, на данный момент я использую Express Profiler для этой цели, недостатком является то, что он работает только для MS SQL Server. Вы можете найти этот инструмент здесь: https://expressprofiler.codeplex.com/

VincentZHANG
источник
5
IQueryable query = from x in appEntities
                   where x.id = 32
                   select x;
var queryString = query.ToString();

Вернет SQL-запрос. Работа с использованием datacontext EntityFramework 6

Джанлука Конте
источник
4
Я только что попробовал это, и он отслеживает объект: Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1 [System.Linq.IGrouping 2[System.Int32,String]]вместо фактического запроса. Я что-то упустил или вы забыли что-то упомянуть?
loganjones16
5

Я делаю интеграционный тест, и мне это нужно для отладки сгенерированного оператора 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

Росди Касим
источник
1
Кажется, что DebugLoggerPrivider и ConsoleLoggerProvider существуют только в .NET Core: docs.microsoft.com/en-us/dotnet/api/…
Габриэль Магана,
4

Necromancing.
Эта страница является первым результатом поиска при поиске решения для любой платформы .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;
        }


    }


}
Стефан Штайгер
источник
Я использую EF Core 2.0.1, и приведенное выше предложение приводит к следующему: System.InvalidCastException: 'Невозможно привести объект типа Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor' для ввода '' Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVodel для линия: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Крис Вольф
2
@ChrisWolf, если вы следуете оригинальной идее автора, вы можете найти кого-то, кто предоставил обновленную версию этого метода расширения . Работал на меня.
B12Toaster
2

В моем случае для 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();

Конечно, ваша подпись анонимного типа может отличаться.

НТН.

user8128167
источник
2

Я только что сделал это:

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
Даниэль Камарго
источник
Да, но я считаю, что никто не хочет видеть p__linq__i, но настоящие ценности
Tom Stickel
Этот способ все еще работает в EF 6, и он будет полезен, если вам важно только то, как выглядит структура запроса. В моем случае проект, который я создаю, объект IQueryable <T> не имеет ссылки на System.Data.Entity, и я не хочу добавлять его только для целей отладки. Так что этот метод работал просто отлично.
wctiger
2

Для меня, используя EF6 и Visual Studio 2015, я вошел queryв ближайшее окно, и он дал мне сгенерированный оператор SQL

Йонас Ставски
источник
1

Если вы хотите иметь значения параметров (не только, @p_linq_0но и их значения), вы можете использовать IDbCommandInterceptorи добавить некоторые записи в ReaderExecutedметод.

michal.jakubeczy
источник
1

Хотя здесь есть хорошие ответы, ни один из них не решил полностью мою проблему (я хотел получить полный оператор 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();
Джереми Моррен
источник
0

Решение Entity Framework 4

Большинство ответов здесь были специфичными для EF6. Вот один из тех, кто все еще использует EF4.

Этот метод заменяет @p__linq__0/ etc. параметры с их фактическими значениями, так что вы можете просто скопировать и вставить вывод в 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;
    }
jramm
источник