ฉันจะดู SQL ที่สร้างโดย Entity Framework ได้อย่างไร


624

ฉันจะดู SQL ที่สร้างโดยกรอบงานเอนทิตีได้อย่างไร

(ในกรณีของฉันโดยเฉพาะฉันใช้ผู้ให้บริการ mysql - ถ้ามันเป็นเรื่องสำคัญ)


1
บทความนี้จาก MSDN Magazine อธิบายถึงตัวเลือกการทำโปรไฟล์สำหรับ 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แล้วฉันก็เดาไม่ได้อีกแล้ว
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()
Scott Chamberlain

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 ตรวจสอบว่ารายการ "Debug" ได้เลือกไว้ที่ combobox ของบานหน้าต่างเอาท์พุทและตรวจสอบว่า debug ของคุณไม่เปลี่ยนเส้นทางไปยังทันที (เครื่องมือ> ตัวเลือก> แก้ไขข้อบกพร่อง> เปลี่ยนเส้นทาง หน้าต่าง)
rkawano

5
มีวิธีที่จะได้รับสิ่งนี้เพื่อรวมค่าตัวแปรโดยตรงภายใน sql สร้าง? ความเจ็บปวดเล็กน้อยกับสิ่งที่ใหญ่กว่า
Chris

22
@Matt Nibecker สิ่งนี้ไม่ทำงานใน EF Core อะไรคือทางเลือกของ EF Core
nam

9
คำเตือน: ฉันใช้สิ่งนี้ด้วยความตั้งใจในการพัฒนาเท่านั้น เมื่อเราปรับใช้กับสภาพแวดล้อมการทดสอบของเราเราเริ่มเห็นการรั่วไหลของหน่วยความจำทันทีในกระบวนการ IIS Worker หลังจากการทำโปรไฟล์หน่วยความจำเรารู้ว่า GC อย่างชัดเจนไม่ได้รวบรวมวัตถุบริบทเอนทิตีอีกต่อไป (ใช่พวกเขากำลังใช้คำสั่ง) การลบบรรทัดนี้กลับเป็นปกติทั้งหมด ดังนั้นในขณะที่นี่เป็นเครื่องมือที่ยอดเยี่ยมตรวจสอบให้แน่ใจว่าคุณสร้างมันลงในแอปของคุณเพื่อการพัฒนาเท่านั้น
Brandon Barkley

82

เริ่มต้นด้วย EF6.1 คุณสามารถใช้ Interceptors เพื่อลงทะเบียนตัวบันทึกฐานข้อมูล ดูบท "Interceptors" และ "Logging Database Operations" ไปยังไฟล์ที่นี่

<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/…
Tim Abell

12
ความแม่นยำอยู่ภายใต้: <configuration> <entityFramework> <interceptors> ... </interceptors> </entityFramework> </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)
sports

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()เอาท์พุทเป็น namespace ของประเภทที่กำหนดเองนั้น ตัวอย่างเช่นถ้าโค้ดด้านบนเป็นselect new CustomType { x = x.Name }ค่าที่ส่งคืนจะเป็นสิ่งที่ต้องการCompany.Models.CustomTypeแทน SQL ที่สร้างขึ้น
Chad Levy

8
เทคนิคนี้ผลิตSystem.Data.Objects.ObjectQuery``1[MyProject.Models.Product]สำหรับฉัน
Carl G

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แทนที่ ซึ่งไม่แนะนำให้ใช้ในเอกสาร: "วิธีการนี้ไม่ได้ให้ยืมกับการทดสอบเว้นแต่ว่าการทดสอบจะกำหนดเป้าหมายไปยังฐานข้อมูลแบบเต็ม"

กรณีที่ซับซ้อนนี้ใช้:

  • 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));
        ...
    }
}

ต่อไปนี้คือการนำไปใช้งานของ 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;
        }
    } 
}

ดังนั้น ... ไม่มีทางที่ขอทาน
Juan De la Cruz

1
@JuanDelaCruz ฉันทำให้คำตอบของฉันง่ายขึ้น; ลองใช้ทางเลือกง่าย ๆ
The Red Pea

16

มีสองวิธี:

  1. เพื่อดู SQL ToTraceString()ที่จะถูกสร้างขึ้นเพียงโทร คุณสามารถเพิ่มลงในหน้าต่างการเฝ้าดูของคุณและตั้งค่าเบรกพอยต์เพื่อดูว่าแบบสอบถามจะอยู่ที่จุดใดที่กำหนดสำหรับแบบสอบถาม LINQ ใด ๆ
  2. คุณสามารถแนบตัวติดตามไปยังเซิร์ฟเวอร์ SQL ที่คุณเลือกซึ่งจะแสดงคิวรีสุดท้ายในรายละเอียดที่เต็มไปด้วยเลือดทั้งหมด ในกรณีของ MySQL tail -fวิธีที่ง่ายที่สุดในการติดตามคำสั่งเป็นเพียงหางบันทึกแบบสอบถามที่มี คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับสิ่งอำนวยความสะดวกการเข้าสู่ระบบของ MySQL ในเอกสารอย่างเป็นทางการ สำหรับ SQL Server วิธีที่ง่ายที่สุดคือการใช้ Profiler ของ SQL Server ที่รวมอยู่

27
ToTraceString ของอะไร
nos

ObjectQuery ดังที่ Nick บันทึกไว้หลังจากที่ฉันโพสต์คำตอบ
Benjamin Pollack

2
ตัวสร้างโปรไฟล์ของ SQL Server รวบรวมอักขระ 4000 ตัวแรก แต่แบบสอบถามของ EF อาจยาวกว่านั้นมาก

7

เพื่อให้การสืบค้นมีประโยชน์อยู่เสมอโดยไม่ต้องเปลี่ยนรหัสให้เพิ่มสิ่งนี้ลงใน DbContext ของคุณและตรวจสอบในหน้าต่างเอาต์พุตใน visual studio

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

คล้ายกับ @Matt Nibecker คำตอบ แต่ด้วยวิธีนี้คุณไม่จำเป็นต้องเพิ่มในรหัสปัจจุบันของคุณทุกครั้งที่คุณต้องการสืบค้น


คำตอบที่ดีที่สุด!
AlexSC

7

SQL Management Studio => เครื่องมือ => ตัวสร้างโปรไฟล์ของเซิร์ฟเวอร์ SQL

ไฟล์ => ติดตามใหม่ ...

ใช้เทมเพลต => ว่าง

การเลือกเหตุการณ์ => T-SQL

ตรวจสอบ Lefthandside: 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 การทำงานโดยใช้ 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/…เท่านั้น
Gabriel Magana

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.RelationalQuery บรรทัด: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Chris Wolf

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 แต่คุณค่าที่แท้จริง
Tom Stickel

วิธีนี้ยังคงใช้งานได้ใน 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. ฉัน ได้ทดสอบกับ EF6 +เท่านั้น

นอกเหนือจากนี้ภารกิจนี้ใช้เวลานานกว่าที่ฉันคิดไว้ Abstraction ใน Entity Framework นั้นค่อนข้างมาก IMHO

ก่อนอื่นให้ใช้ คุณจะต้องมีการอ้างอิงที่ชัดเจนถึง '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 Solution

คำตอบส่วนใหญ่ที่นี่เป็นเฉพาะของ 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;
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.