Jak wyświetlić SQL wygenerowany przez Entity Framework?

624

Jak wyświetlić kod SQL wygenerowany przez strukturę encji?

(W moim szczególnym przypadku korzystam z dostawcy mysql - jeśli to ma znaczenie)

nos
źródło
1
Ten artice z MSDN Magazine opisuje niektóre opcje profilowania dla Entity Framework 4
Arve
2
Połączone pytanie „duplikatu” dotyczy LINQ-SQL, więc tak naprawdę nie jest duplikatem.
jrummell
2
Podczas pracy w trybie debugowania IntelliTrace pokazuje wykonane zapytania SQL, choć bez ich wyników.
ivan_pozdeev
Jeśli chcesz zobaczyć SQL podczas programowania, możesz użyć LINQPad . Po uruchomieniu zapytania LINQ w wynikach pojawi się karta SQL, która pokazuje wykonaną instrukcję SQL. W przypadku mySQL musisz zainstalować sterownik. Nie mam dostępnej bazy danych mySQL, ale powinna działać.
gligoran

Odpowiedzi:

472

Możesz wykonać następujące czynności:

IQueryable query = from x in appEntities
             where x.id == 32
             select x;

var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();

lub w EF6:

var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
            .ToTraceString();

To da ci wygenerowany SQL.

Nick Berardi
źródło
20
Nie otrzymasz SQL dla zapytań kończących się na .Single (), .Count (), .Any () itp. W ten sposób.
springy76
24
To dlatego, że po uruchomieniu .Single()twojego obiektu już nie ma IQueryable.
Suhas,
11
z EF6 mogłem uzyskać tylko z refleksją. ale najpierw musiałem konwertować resultdo System.Data.Entity.Infrastructure.DbQuery<T>, a następnie uzyskać własność wewnętrzną InternalQueryjak (System.Data.Entity.Internal.Linq.InternalQuery<T>)i tylko wtedy, zastosowanieToTraceString()
itsho
9
dodaj odniesienie do System.Data.Entity, System.Data.Objects.ObjectQuery istnieje w powyższej bibliotece DLL
Mahesh
54
W EF6 możesz to zrobićresult.ToString()
Scott Chamberlain
955

Dla tych, którzy używają Entity Framework 6 i nowszych, jeśli chcesz wyświetlić wyjściowy SQL w Visual Studio (tak jak ja), musisz użyć nowej funkcji rejestrowania / przechwytywania.

Dodanie następującego wiersza wypluje wygenerowany kod SQL (wraz z dodatkowymi szczegółami związanymi z wykonaniem) w panelu wyjściowym programu Visual Studio:

using (MyDatabaseEntities context = new MyDatabaseEntities())
{
    context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
    // query the database using EF here.
}

Więcej informacji o logowaniu EF6 w tej fajnej serii blogów: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/

Uwaga: Upewnij się, że projekt jest uruchomiony w trybie DEBUG.

Matt Nibecker
źródło
107
Ta odpowiedź zasługuje na więcej miłości (jeśli używasz EF6 +) - świetne dodanie do debugowania, po prostu dodaj ją do konstruktora DBContext (this.Database.Log = ...)
keithl8041
21
Upewnij się, że prowadzisz projekt w TRYBIE DEBUGOWANIA, sprawdź, czy element „Debuguj” został wybrany w polu kombi okienka Wyjście, a także sprawdź, czy debugowanie nie przekierowuje do opcji Natychmiastowe (Narzędzia> Opcje> Debugowanie> Przekieruj cały tekst okna wyników do Natychmiastowej Window)
rkawano
5
czy istnieje sposób, aby włączyć to do uwzględnienia wartości zmiennych bezpośrednio w wygenerowanym sql? Trochę bólu z większymi.
Chris
22
@Matt Nibecker To nie działa w EF Core. Jaka jest alternatywa dla EF Core?
nam
9
OSTRZEŻENIE: wdrożyłem to z zamiarem, aby działało tylko w fazie rozwoju. Po wdrożeniu w naszym środowisku testowym zaczęliśmy nagle zauważać wycieki pamięci w procesie roboczym IIS. Po profilowaniu pamięci zdaliśmy sobie sprawę, że nawet jawna GC nie gromadziła już obiektów kontekstu encji (tak, używali instrukcji). Usunięcie tej linii przywróciło wszystko do normy. Chociaż jest to świetne narzędzie, pamiętaj, aby wbudować je tylko w aplikację na potrzeby programowania.
Brandon Barkley,
82

Począwszy od wersji EF6.1, można użyć przechwytywaczy do zarejestrowania rejestratora bazy danych. Zobacz rozdziały „Przechwytywacze” i „Rejestrowanie operacji na bazie danych” w pliku tutaj

<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
źródło
1
Wpis
Tim Abell
12
Precyzja, jest poniżej: <configuration> <entityFramework> <interceptors> ... </interceptors> </entityFramework> </configuration>
Christophe P
80

Jeśli używasz DbContext, możesz wykonać następujące czynności, aby uzyskać SQL:

var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();
Doug Clutter
źródło
12
ToString()poda zapytanie ze zmiennymi, np. p__linq__0zamiast wartości końcowych (np .: 34563 zamiast p__linq__0)
sport
24

Dotyczy EF 6.0 i nowszych: dla tych, którzy chcą dowiedzieć się więcej o funkcji rejestrowania i dodać do niektórych już udzielonych odpowiedzi.

Każde polecenie wysłane z EF do bazy danych może być teraz zarejestrowane. Aby wyświetlić wygenerowane zapytania z EF 6.x, użyjDBContext.Database.Log property

Co zostanie zarejestrowane?

 - 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.

Przykład:

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(); 
}

Wynik:

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

Aby zalogować się do pliku zewnętrznego:

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();
   }
}

Więcej informacji tutaj: Rejestrowanie i przechwytywanie operacji na bazie danych

NullReference
źródło
21

W EF 4.1 możesz wykonać następujące czynności:

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

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

To da ci wygenerowany SQL.

Capriols
źródło
1
W rzeczywistości uważam, że działa to tylko wtedy, gdy zapytanie zwraca anonimowy typ. Jeśli zwraca typ niestandardowy, ToString()wynikiem jest przestrzeń nazw tego typu niestandardowego. Na przykład, jeśli powyższy kod select new CustomType { x = x.Name }byłby, zwrócona wartość byłaby podobna Company.Models.CustomTypedo wygenerowanego kodu SQL.
Chad Levy,
8
Ta technika produkuje System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]dla mnie.
Carl G
1
@CarlG System.Data.Objects.ObjectQuery nie jest EF 4.1 (DbContext). Przy użyciu DbContext byłby to System.Data.Entity.Infrastructure.DbQuery`1 [MyProject.Models.Product], który rzeczywiście wypisuje swój SQL podczas wywołania „ToString ()”
springy76
To da ci wygenerowany SQL, gdzie w oknie wyjściowym? która opcja z menu rozwijanego?
JsonStatham,
17

Moja odpowiedź dotyczy rdzenia EF . Odnoszę się do tego problemu z github i dokumentów dotyczących konfiguracjiDbContext :

Prosty

Przesłoń OnConfiguringmetodę swojej DbContextklasy ( YourCustomDbContext) jak pokazano tutaj, aby użyć ConsoleLoggerProvider; twoje zapytania powinny zalogować się do konsoli:

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
}

Złożony

Kompleks ten przypadek unika przesłanianie się DbContext OnConfiguringmetodę., co jest odradzane w dokumentach: „Takie podejście nie nadaje się do testowania, chyba że testy są ukierunkowane na pełną bazę danych”.

W tym złożonym przypadku zastosowano:

  • Metoda IServiceCollectionw Startupklasie ConfigureServices(zamiast przesłonięcia OnConfiguringmetody; zaletą jest luźniejsze połączenie między DbContextiILoggerProvider którego chcesz użyć)
  • Implementacja ILoggerProvider(zamiast korzystania z ConsoleLoggerProviderimplementacji pokazanej powyżej; korzyścią jest to, że nasza implementacja pokazuje, w jaki sposób logujemy się do pliku (nie widzę dostawcy rejestrowania plików dostarczanego z EF Core ))

Lubię to:

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

Oto implementacja a MyLoggerProvider( MyLoggerktóra dołącza swoje dzienniki do pliku, który możesz skonfigurować; twoje zapytania EF Core pojawią się w pliku).

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;
        }
    } 
}
Czerwony groszek
źródło
Więc ... nie ma na to sposobu dla początkujących?
Juan De la Cruz
1
@JuanDelaCruz Upraszczam moją odpowiedź; wypróbuj prostą alternatywę
The Red Pea
16

Istnieją dwa sposoby:

  1. Aby wyświetlić kod SQL, który zostanie wygenerowany, wystarczy wywołać ToTraceString() . Możesz dodać go do okna zegarka i ustawić punkt przerwania, aby zobaczyć, jakie zapytanie będzie w danym momencie dla dowolnego zapytania LINQ.
  2. Możesz dołączyć znacznik do wybranego serwera SQL, który wyświetli ostatnie zapytanie ze wszystkimi szczegółami. W przypadku MySQL najłatwiejszym sposobem śledzenia zapytań jest po prostu dopasowanie dziennika zapytań tail -f. Możesz dowiedzieć się więcej o funkcjach logowania MySQL w oficjalnej dokumentacji . W przypadku programu SQL Server najprostszym sposobem jest użycie dołączonego narzędzia do profilowania programu SQL Server.
Benjamin Pollack
źródło
27
ToTraceString czego?
nos
ObjectQuery, jak zauważył Nick zaraz po opublikowaniu mojej odpowiedzi.
Benjamin Pollack,
2
SQL Server Profiler przechwytuje pierwsze 4000 znaków, ale zapytania EF mogą być znacznie dłuższe.
7

Aby zapytanie było zawsze pod ręką, bez zmiany kodu, dodaj to do DbContext i sprawdź w oknie wyników w Visual Studio.

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

Podobnie do odpowiedzi @Matt Nibecker, ale dzięki temu nie musisz dodawać jej do bieżącego kodu za każdym razem, gdy potrzebujesz zapytania.

Gerrie Pretorius
źródło
Najlepsza odpowiedź!
AlexSC
7

SQL Management Studio => Narzędzia => SQL Server profiler

Plik => Nowy ślad ...

Użyj szablonu => puste

Wybór zdarzenia => T-SQL

Sprawdź po lewej stronie: SP.StmtComplete

Filtrów kolumnowych można użyć do wybrania konkretnej nazwy aplikacji lub bazy danych

Uruchom ten profil, a następnie uruchom zapytanie.

Kliknij tutaj, aby uzyskać informacje o źródle

Andrzej Pasztet
źródło
1
przepraszam, to tylko dla serwera SQL, a nie MySQL
andrew pate
5
IQueryable query = from x in appEntities
                   where x.id = 32
                   select x;
var queryString = query.ToString();

Zwróci zapytanie SQL. Praca z wykorzystaniem tekstu danych EntityFramework 6

Gianluca Conte
źródło
4
Właśnie próbowałem tego i Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryablewykrywa obiekt: 1 [System.Linq.IGrouping 2[System.Int32,String]]zamiast rzeczywistego zapytania. Czy coś przeoczyłem, czy zapomniałeś o czymś wspomnieć?
loganjones16
5

Robię test integracji i potrzebowałem tego do debugowania wygenerowanej instrukcji SQL w Entity Framework Core 2.1, więc używam DebugLoggerProviderlub ConsoleLoggerProviderlubię tak:

[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);

        ........

Oto przykładowe dane wyjściowe z konsoli Visual Studio:

Przykładowe dane wyjściowe instrukcji SQL

Rosdi Kasim
źródło
1
DebugLoggerPrivider i ConsoleLoggerProvider wydają się istnieć tylko w .NET Rdzeń: docs.microsoft.com/en-us/dotnet/api/...
Gabriel Magana
4

Nekromancja.
Ta strona jest pierwszym wynikiem wyszukiwania podczas wyszukiwania rozwiązania dla dowolnej platformy .NET Framework, więc tutaj jako usługa publiczna, jak to się robi w EntityFramework Core (dla .NET Core 1 i 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);

A następnie te metody rozszerzeń (IQueryableExtensions1 dla .NET Core 1.0, IQueryableExtensions dla .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;
        }


    }


}
Stefan Steiger
źródło
Korzystam z EF Core 2.0.1 i powyższa sugestia powoduje: System.InvalidCastException: „Nie można rzutować obiektu typu Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor”, aby wpisać „” Microsoft.EntityFrameworkCore.Query.RelationalQuitor'uod linia: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Chris Wolf
2
@ChrisWolf, jeśli postępujesz zgodnie z założeniami autora, możesz znaleźć kogoś, kto dostarczył zaktualizowaną wersję tej metody rozszerzenia . Pracował dla mnie.
B12Toaster,
2

W moim przypadku dla EF 6+, zamiast używać tego w Natychmiastowym oknie, aby znaleźć ciąg zapytania:

var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();

Skończyło się na tym, aby uzyskać wygenerowane polecenie SQL:

var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();

Oczywiście twój anonimowy podpis może się różnić.

HTH.

użytkownik8128167
źródło
2

Właśnie to zrobiłem:

IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);

Wynik pokazany w danych wyjściowych :

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
Daniel Camargo
źródło
Tak, ale wierzę, że nikt nie chce zobaczyć p__linq__i, ale prawdziwe wartości
Tom Stickel
Ten sposób nadal działa w EF 6 i będzie przydatny, jeśli zależy ci tylko na tym, jak wygląda struktura zapytania. W moim przypadku projekt, który tworzę IQueryable <T> nie ma odniesienia do System.Data.Entity, ani nie chcę go dodawać wyłącznie w celu debugowania. Ta metoda działała dobrze.
wctiger
2

Dla mnie, używając EF6 i Visual Studio 2015, wszedłem querydo bezpośredniego okna i dało mi to wygenerowaną instrukcję SQL

Jonas Stawski
źródło
1

Jeśli chcesz także mieć wartości parametrów (nie tylko, @p_linq_0ale także ich wartości), możesz użyć IDbCommandInterceptori dodać trochę logowania do ReaderExecutedmetody.

michal.jakubeczy
źródło
1

Chociaż są tutaj dobre odpowiedzi, żadne nie rozwiązało mojego problemu całkowicie (chciałem uzyskać całą instrukcję SQL, w tym parametry , z DbContext z dowolnego IQueryable. Poniższy kod właśnie to robi. Jest to kombinacja fragmentów kodu od Google. I przetestowałem to tylko z EF6 + .

Na marginesie, to zadanie zajęło mi znacznie dłużej, niż się spodziewałem. Abstrakcja w Entity Framework to trochę dużo, IMHO.

Najpierw za pomocą. Będziesz potrzebował wyraźnego odwołania do „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;

Następująca klasa przekształca IQueryable w DataTable. Zmodyfikuj w zależności od potrzeb:

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;
    }
}

Aby użyć, po prostu nazwij to jak poniżej:

var context = new MyContext();
var data = ....//Query, return type can be anonymous
    .AsQueryable();
var dt = new EntityFrameworkCommand()
    .Initialize(context, data)
    .GetData();
Jeremy Morren
źródło
0

Rozwiązanie Entity Framework 4

Większość odpowiedzi tutaj dotyczyła EF6. Oto jeden z tych, którzy nadal używają EF4.

Ta metoda zastępuje @p__linq__0 / etc. parametry z ich rzeczywistymi wartościami, więc możesz po prostu skopiować i wkleić dane wyjściowe do SSMS i uruchomić je lub debugować.

    /// <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
źródło