Як я бачу SQL, сформований Entity Framework?


624

Як я бачу SQL, що генерується за допомогою сутності?

(У моєму конкретному випадку я використовую постачальник mysql - якщо це має значення)


1
Ця стаття з журналу MSDN описує деякі варіанти профілювання Entity Framework 4
Arve

2
Зв'язане "дублікат" питання для LINQ до SQL, тому його насправді не є дублікатом.
jrummell

2
Під час роботи під налагоджувачем IntelliTrace показує SQL запити, зроблені, хоча і без їх результатів.
ivan_pozdeev

Якщо вам цікаво бачити SQL під час розробки, ви можете використовувати LINQPad . При запуску запиту LINQ в результатах з'явиться вкладка SQL, яка показує виконаний оператор SQL. Для mySQL вам доведеться встановити драйвер. У мене немає бази даних mySQL, але вона повинна працювати.
gligoran

Відповіді:


472

Ви можете зробити наступне:

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я не думаю більше .
Сухас

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()
Скотт Чемберлен

955

Для тих, хто використовує 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.


107
Ця відповідь заслуговує більшої любові (якщо ви використовуєте EF6 +) - чудове доповнення налагодження, просто додайте її до конструктора DBContext (this.Database.Log = ...)
keithl8041

21
Переконайтеся, що ви запускаєте проект у режимі DEBUG MODE, перевірте, чи вибрано пункт "Налагодження" на комбобоксі панелі вихідних даних, а також перевірте, чи ваша налагодження не перенаправляється на негайне (Інструменти> Параметри> Налагодження> Перенаправити весь текст вікна виводу на негайне Вікно)
rkawano

5
Чи є спосіб отримати це, щоб включити значення змінних безпосередньо в створений sql? Трохи біль з більшими.
Кріс

22
@Matt Nibecker Це не працює в EF Core. Яка альтернатива EF Core?
наз

9
ПОПЕРЕДЖЕННЯ. Я реалізував це з наміром, щоб він працював лише у розробці. Коли ми розгорнулися в нашому тестовому середовищі, ми почали різко бачити витоки пам'яті в IIS Worker Process. Після профілювання пам'яті ми зрозуміли, що навіть явний GC вже не збирав об'єкти контексту сутності (так, вони використовували оператори). Видалення цього рядка повернуло нормальний стан. Тож, хоча це чудовий інструмент, переконайтеся, що ви лише вбудовуєте його у додаток для розробки.
Брендон Барклі

82

Починаючи з 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>

1
Повідомлення в блозі на тему blog.oneunicorn.com/2014/02/09/…
Тім Абел

12
Точність, це під: <configuration> <entityFramework> <interceptors> ... </interceptors> </entityFramework> </configuration>
Christophe P

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)
спорт

24

Застосовується для 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();
   }
}

Більше інформації тут: Реєстрація та перехоплення операцій з базою даних


21

У EF 4.1 можна зробити наступне:

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

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

Це дасть вам сформований SQL.


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показаної вище реалізації; користь полягає в тому, що наша реалізація показує, як ми входитимемо у файл (я не бачу постачальника журналів файлів, що постачається разом із 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;
        }
    } 
}

Отже ... немає жодного способу зробити це?
Хуан Де ла Крус

1
@JuanDelaCruz я спростив свою відповідь; спробуйте просту альтернативу
The Red Pea

16

Є два способи:

  1. Щоб переглянути SQL, який буде створений, просто зателефонуйте ToTraceString(). Ви можете додати його у вікно перегляду та встановити точку перерви, щоб побачити, яким буде запит у будь-який момент для будь-якого запиту LINQ.
  2. Ви можете приєднати трассіровку до свого вибору SQL-сервера, який покаже вам остаточний запит у всіх його деталях. У випадку з MySQL, найпростіший спосіб відстежити запити - це просто вилучити журнал запитів tail -f. Ви можете дізнатися більше про засоби реєстрації даних MySQL в офіційній документації . Для SQL Server найпростішим способом є використання включеного SQL Server-профілера.

27
ToTraceString чого?
ніс

ObjectQuery, як відмітив Нік відразу після того, як я опублікував свою відповідь.
Бенджамін Поллак

2
Профілер SQL Server фіксує перші 4000 символів, але запити EF можуть бути набагато довшими.

7

Щоб запит був завжди під рукою, не змінюючи код, додайте це у свій DbContext і перевірте його у вікні виводу у візуальній студії.

    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
andrew pate

5

На даний момент я для цього використовую експрес-профілер, недолік - це те, що він працює лише для MS SQL Server. Ви можете знайти цей інструмент тут: https://expressprofiler.codeplex.com/


5
IQueryable query = from x in appEntities
                   where x.id = 32
                   select x;
var queryString = query.ToString();

Поверне запит sql. Робота з використанням даних контексту EntityFramework 6


4
Я просто спробував це, і він Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryableвиявляє об'єкт: 1 [System.Linq.IGгрупування 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

Некромантування.
Ця сторінка є першим результатом пошуку під час пошуку рішення для будь-якої .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.RelationalVuitor'uery'oder'oder'uery'oder'oder'uery'oder'oder'uery'odery' рядок: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Кріс Вольф

2
@ChrisWolf, якщо ви дотримуєтесь оригінальної суті автора, ви можете знайти когось, хто надав оновлену версію цього методу розширення . Працювали для мене.
B12Toaster

2

У моєму випадку для 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.


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, а справжні значення
Том Стікель

Цей спосіб все ще працює в EF 6, і він буде корисним, якщо ви дбаєте лише про те, як виглядає структура запитів. У моєму випадку проект, який я створюю, об'єкт IQueryable <T> не має посилання на System.Data.Entity, ні я не хочу його додавати лише для налагодження. Тож цей метод спрацював просто чудово.
wctiger

2

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


1

Якщо ви також хочете мати значення параметрів (не тільки, @p_linq_0але й їхніх значень), ви можете використовувати IDbCommandInterceptorта додати ReaderExecutedметод реєстрації до журналу .


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

Рішення сутності 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;
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.