Como visualizo o SQL gerado pelo Entity Framework?

624

Como visualizo o SQL gerado pela estrutura da entidade?

(No meu caso em particular, estou usando o provedor mysql - se isso importa)

n
fonte
1
Este artigo da MSDN Magazine descreve algumas opções de criação de perfil para o Entity Framework 4
Arve
2
A questão "duplicada" vinculada é para LINQ to SQL, portanto, na verdade, não é uma duplicata.
11119 jjrmell
2
Ao executar no depurador, o IntelliTrace mostra as consultas SQL feitas, embora sem seus resultados.
precisa saber é o seguinte
Se você estiver interessado em ver o SQL apenas durante o desenvolvimento, poderá usar o LINQPad . Quando você executa uma consulta LINQ nos resultados, haverá uma guia SQL que mostra a instrução SQL executada. Para o mySQL, você precisará instalar um driver. Não tenho um banco de dados mySQL disponível, mas deve funcionar.
gligoran

Respostas:

472

Você pode fazer o seguinte:

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

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

ou no EF6:

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

Isso fornecerá o SQL que foi gerado.

Nick Berardi
fonte
20
Você não receberá SQL para consultas que terminem com .Single (), .Count (), .Any () etc. dessa maneira.
springy76
24
Isso porque depois de executar o .Single()seu objeto não existe mais, IQueryableeu acho.
Suhas 19/06/2013
11
com o EF6, eu conseguia entender apenas com reflexão. Mas, primeiro, eu tinha que converter resultpara System.Data.Entity.Infrastructure.DbQuery<T>, em seguida, obter propriedade interna InternalQuerycomo (System.Data.Entity.Internal.Linq.InternalQuery<T>), e só então, o usoToTraceString()
itsho
9
adicione referência a System.Data.Entity, System.Data.Objects.ObjectQuery existente na dll acima
Mahesh
54
No EF6 você pode simplesmente fazerresult.ToString()
Scott Chamberlain
956

Para aqueles que usam o Entity Framework 6 e superior, se quiser exibir o SQL de saída no Visual Studio (como eu fiz), use a nova funcionalidade de log / interceptação.

A adição da seguinte linha cuspirá o SQL gerado (junto com detalhes adicionais relacionados à execução) no painel de saída do Visual Studio:

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

Mais informações sobre o logon no EF6 nesta série de blogs bacana: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/

Nota: Verifique se você está executando seu projeto no modo DEBUG.

Matt Nibecker
fonte
107
Esta resposta merece mais amor (se você estiver usando EF6 +) - ótima adição de depuração, basta adicioná-lo no construtor DBContext (this.Database.Log = ...) #
keithl8041
21
Verifique se você está executando seu projeto no modo DEBUG, verifique se o item "Debug" foi selecionado na caixa de combinação do painel Saída e verifique se a sua depuração não está redirecionando para Imediato (Ferramentas> Opções> Depuração> Redirecionar todo o texto da Janela de Saída para Imediato Janela)
rkawano 21/04
5
existe uma maneira de fazer isso incluir os valores da variável diretamente no sql gerado? Um pouco de dor com os maiores.
28414 Chris
22
@Matt Nibecker Isso não funciona no EF Core. Qual é a alternativa para o EF Core?
nam
9
AVISO: Eu implementei isso com a intenção de rodar apenas em desenvolvimento. Quando implantamos em nosso ambiente de teste, começamos a ver abruptamente vazamentos de memória no processo de trabalho do IIS. Após o perfil da memória, percebemos que mesmo o GC explícito não estava mais coletando os objetos de contexto da entidade (sim, eles estavam usando instruções). A remoção desta linha retornou tudo ao normal. Portanto, embora essa seja uma ótima ferramenta, certifique-se de incorporá-la apenas no seu aplicativo para desenvolvimento.
Brandon Barkley
82

A partir do EF6.1, você pode usar o Interceptors para registrar um criador de logs de banco de dados. Consulte os capítulos "Interceptores" e "Registrando operações do banco de dados" em um arquivo aqui

<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
fonte
1
Publicação do
Tim Abell
12
Precisão, está em: <configuração> <estrutura da entidade> <interceptores> ... </interceptors> </ estrutura da estrutura> </ configuração>
Christophe P:
80

Se você estiver usando um DbContext, poderá fazer o seguinte para obter o SQL:

var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();
Doug Clutter
fonte
12
ToString()vai dar-lhe a consulta com variáveis nele, como p__linq__0, em vez dos valores finais (por exemplo: 34563 em vez de p__linq__0)
esportes
24

Aplicável ao EF 6.0 e superior: para aqueles que desejam saber mais sobre a funcionalidade de log e adicionar algumas das respostas já fornecidas.

Qualquer comando enviado do EF ao banco de dados agora pode ser registrado. Para visualizar as consultas geradas do EF 6.x, use oDBContext.Database.Log property

O que é registrado

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

Exemplo:

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

Resultado:

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

Para fazer logon em um arquivo externo:

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

Mais informações aqui: Registrando e interceptando operações do banco de dados

NullReference
fonte
21

Você pode fazer o seguinte no EF 4.1:

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

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

Isso fornecerá o SQL que foi gerado.

Capriols
fonte
1
De fato, acredito que isso só funciona quando a consulta retorna um tipo anônimo. Se ele retornar um tipo personalizado, a ToString()saída será o espaço para nome desse tipo personalizado. Por exemplo, se o código acima fosse select new CustomType { x = x.Name }, o valor retornado seria algo como, em Company.Models.CustomTypevez do SQL gerado.
Chad Levy
8
Essa técnica produz System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]para mim.
Carl G
1
@CarlG System.Data.Objects.ObjectQuery não é EF 4.1 (DbContext). Usando DbContext seria System.Data.Entity.Infrastructure.DbQuery`1 [MyProject.Models.Product] que de fato gera-lo de SQL em uma chamada para "ToString ()"
springy76
Isso fornecerá o SQL que foi gerado, onde, na janela de saída? qual opção do menu suspenso?
precisa
17

Minha resposta aborda o núcleo da EF . Mencionei esse problema do github e os documentos sobre a configuraçãoDbContext :

Simples

Substitua o OnConfiguringmétodo da sua DbContextclasse ( YourCustomDbContext) como mostrado aqui para usar um ConsoleLoggerProvider; suas consultas devem se registrar no console:

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
}

Complexo

Este caso Complex evita a substituição do DbContext OnConfiguringmétodo. , o que é desencorajado nos documentos: "Essa abordagem não se presta a testes, a menos que os testes tenham como alvo o banco de dados completo".

Este caso Complex usa:

  • O método IServiceCollectionin Startupclass ConfigureServices(em vez de substituir o OnConfiguringmétodo; o benefício é um acoplamento mais flexível entre o DbContexte o que ILoggerProvidervocê deseja usar)
  • Uma implementação de ILoggerProvider(em vez de usar a ConsoleLoggerProviderimplementação mostrada acima; o benefício é nossa implementação mostra como faríamos logon no arquivo (não vejo um provedor de log de arquivo enviado com o EF Core ))

Como isso:

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

Aqui está a implementação de um MyLoggerProvider(e seu MyLoggerque anexa seus logs a um arquivo que você pode configurar; suas consultas do EF Core serão exibidas no arquivo).

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;
        }
    } 
}
A ervilha vermelha
fonte
Então ... não há maneira de fazê-lo?
Juan De la Cruz
1
@JuanDelaCruz Eu simplifiquei minha resposta; tente a alternativa simples
The Red Pea
16

Existem duas maneiras:

  1. Para visualizar o SQL que será gerado, basta chamar ToTraceString(). Você pode adicioná-lo à sua janela de inspeção e definir um ponto de interrupção para ver qual seria a consulta em um determinado momento para qualquer consulta LINQ.
  2. Você pode anexar um rastreador ao servidor SQL de sua escolha, que mostrará a consulta final com todos os seus detalhes sangrentos. No caso do MySQL, a maneira mais fácil de rastrear as consultas é simplesmente ajustar o log de consultas tail -f. Você pode aprender mais sobre os recursos de registro do MySQL na documentação oficial . Para o SQL Server, a maneira mais fácil é usar o criador de perfil do SQL Server incluído.
Benjamin Pollack
fonte
27
O ToTraceString de quê?
Nos
O ObjectQuery, como Nick observou logo após eu postar minha resposta.
Benjamin Pollack
2
O SQL Server Profiler captura os primeiros 4.000 caracteres, mas as consultas EF podem ser muito mais longas que isso.
7

Para ter a consulta sempre à mão, sem alterar o código, adicione-o ao seu DbContext e verifique-o na janela de saída no visual studio.

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

Semelhante à resposta do @Matt Nibecker, mas com isso você não precisa adicioná-lo ao seu código atual, toda vez que precisar da consulta.

Gerrie Pretorius
fonte
A melhor resposta!
AlexSC 19/02/19
7

SQL Management Studio => Ferramentas => Profiler do SQL Server

Arquivo => Novo rastreamento ...

Use o Template => Blank

Seleção de evento => T-SQL

Verificação do lado esquerdo para: SP.StmtComplete

Os filtros de coluna podem ser usados ​​para selecionar um ApplicationName ou DatabaseName específico

Inicie esse perfil em execução e inicie a consulta.

Clique aqui para obter informações sobre a fonte

andrew pate
fonte
1
isso é muito Apenas para servidor SQL, não MySQL
andrew pate
5

Bem, no momento estou usando o Express Profiler para esse fim, a desvantagem é que ele só funciona para o MS SQL Server. Você pode encontrar essa ferramenta aqui: https://expressprofiler.codeplex.com/

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

Retornará a consulta sql. Trabalhando com o datacontext do EntityFramework 6

Gianluca Conte
fonte
4
Eu apenas tentei isso e ele rastreia o objeto: Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1 [System.Linq.IGrouping em 2[System.Int32,String]]vez da consulta real. Estou faltando alguma coisa ou você esqueceu de mencionar alguma coisa?
loganjones16
5

Eu estou fazendo teste de integração, e precisava disso para depurar a instrução SQL gerado no Entity Framework Core 2.1, então eu uso DebugLoggerProviderou ConsoleLoggerProviderassim:

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

        ........

Aqui está um exemplo de saída do console do Visual Studio:

Saída de Instrução SQL de Amostra

Rosdi Kasim
fonte
1
DebugLoggerPrivider e ConsoleLoggerProvider parecem existir apenas em .NET Núcleo: docs.microsoft.com/en-us/dotnet/api/...
Gabriel Magana
4

Necromante.
Esta página é o primeiro resultado da pesquisa ao procurar uma solução para qualquer .NET Framework, portanto, aqui como serviço público, como é feito no EntityFramework Core (para .NET Core 1 e 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);

E, em seguida, esses métodos de extensão (IQueryableExtensions1 para .NET Core 1.0, IQueryableExtensions para .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
fonte
Estou usando o EF Core 2.0.1 e a sugestão acima resulta em: System.InvalidCastException: 'Não foi possível converter o objeto do tipo Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor' para digitar '' Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor '' para a linha: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Chris Wolf
2
@ ChrisWolf, se você seguir a essência do autor original, poderá encontrar alguém que forneceu uma versão atualizada desse método de extensão . Trabalhou para mim.
B12Toaster 5/10
2

No meu caso para EF 6+, em vez de usá-lo na janela Immediate para encontrar a string de consulta:

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

Acabei tendo que usar isso para obter o comando SQL gerado:

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

É claro que sua assinatura de tipo anônimo pode ser diferente.

HTH.

user8128167
fonte
2

Acabei de fazer isso:

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

E o resultado mostrado na saída :

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
fonte
Sim, mas eu acredito que ninguém quer ver o p__linq__i, mas os valores reais
Tom Stickel
Dessa forma, ainda funciona no EF 6 e será útil se você se importar apenas com a aparência da estrutura da consulta. No meu caso, o projeto que eu crio o objeto IQueryable <T> não tem referência a System.Data.Entity nem quero adicioná-lo apenas para fins de depuração. Portanto, esse método funcionou bem.
Wctiger #
2

Para mim, usando o EF6 e o ​​Visual Studio 2015, entrei queryna janela imediata e me deu a instrução SQL gerada

Jonas Stawski
fonte
1

Se você quiser ter valores de parâmetros (não apenas @p_linq_0mas também os valores deles), poderá usar IDbCommandInterceptore adicionar algum log ao ReaderExecutedmétodo

michal.jakubeczy
fonte
1

Embora existam boas respostas aqui, nenhum resolveu o meu problema completamente (eu desejava para obter toda a instrução SQL, incluindo parâmetros , a partir do DbContext de qualquer IQueryable. O código a seguir faz exatamente isso. É uma combinação de trechos de código do Google. I só o testaram com EF6 + .

Apenas um aparte, essa tarefa me levou muito mais tempo do que eu pensava. Abstração no Entity Framework é um pouco demais, IMHO.

Primeiro o uso. Você precisará de uma referência explícita ao '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;

A classe a seguir converte um IQueryable em um DataTable. Modifique conforme sua necessidade:

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

Para usar, basta chamá-lo como abaixo:

var context = new MyContext();
var data = ....//Query, return type can be anonymous
    .AsQueryable();
var dt = new EntityFrameworkCommand()
    .Initialize(context, data)
    .GetData();
Jeremy Morren
fonte
0

Solução do Entity Framework 4

A maioria das respostas aqui foram específicas do EF6. Aqui está um para aqueles que ainda usam o EF4.

Este método substitui o @p__linq__0/ etc. parâmetros com seus valores reais, para que você possa copiar e colar a saída no SSMS e executá-la ou depurá-la.

    /// <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
fonte