Comment afficher le SQL généré par Entity Framework?


625

Comment afficher le SQL généré par le framework d'entité?

(Dans mon cas particulier, j'utilise le fournisseur mysql - si cela est important)


1
Cet article de MSDN Magazine décrit certaines options de profilage pour Entity Framework 4
Arve

2
La question "en double" liée est pour LINQ to SQL, donc ce n'est pas vraiment un doublon.
jrummell

2
Lors de l'exécution sous le débogueur, IntelliTrace affiche les requêtes SQL effectuées, mais sans leurs résultats.
ivan_pozdeev

Si vous souhaitez voir le SQL pendant le développement, vous pouvez utiliser LINQPad . Lorsque vous exécutez une requête LINQ dans les résultats, un onglet SQL affiche l'instruction SQL exécutée. Pour mySQL, vous devrez installer un pilote. Je n'ai pas de base de données mySQL disponible, mais cela devrait fonctionner.
gligoran

Réponses:


473

Vous pouvez effectuer les opérations suivantes:

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

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

ou dans EF6:

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

Cela vous donnera le SQL qui a été généré.


20
Vous n'obtiendrez pas SQL pour les requêtes se terminant par .Single (), .Count (), .Any (), etc. de cette façon.
springy76

24
C'est parce qu'après avoir exécuté .Single()votre objet n'est plus, IQueryableje suppose.
Suhas

11
avec EF6, je ne pouvais l'obtenir qu'avec réflexion. mais d' abord, je devais convertir resultà System.Data.Entity.Infrastructure.DbQuery<T>, puis obtenir la propriété interne InternalQuerycomme (System.Data.Entity.Internal.Linq.InternalQuery<T>), et alors seulement, l' utilisationToTraceString()
itsho

9
ajouter une référence à System.Data.Entity, System.Data.Objects.ObjectQuery existe dans la DLL ci-dessus
Mahesh

54
Dans EF6, vous pouvez simplement le faireresult.ToString()
Scott Chamberlain

957

Pour ceux qui utilisent Entity Framework 6 et plus, si vous souhaitez afficher le SQL de sortie dans Visual Studio (comme je l'ai fait), vous devez utiliser la nouvelle fonctionnalité de journalisation / interception.

L'ajout de la ligne suivante crachera le SQL généré (ainsi que des détails supplémentaires liés à l'exécution) dans le panneau de sortie de Visual Studio:

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

Plus d'informations sur la connexion dans EF6 dans cette série de blogs astucieux: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/

Remarque: assurez-vous que vous exécutez votre projet en mode DEBUG.


107
Cette réponse mérite plus d'amour (si vous utilisez EF6 +) - un excellent ajout au débogage, ajoutez-le simplement sur le constructeur DBContext (this.Database.Log = ...)
keithl8041

21
Assurez-vous que vous exécutez votre projet en MODE DÉBOGAGE, vérifiez si l'élément "Débogage" a sélectionné dans la zone de liste déroulante du volet Sortie et vérifiez également si votre débogage ne redirige pas vers Immédiat (Outils> Options> Débogage> Rediriger tout le texte de la fenêtre de sortie vers Immédiat Fenêtre)
rkawano

5
existe-t-il un moyen d'obtenir ceci pour inclure les valeurs des variables directement dans le sql généré? Un peu pénible avec les plus gros.
Chris

22
@Matt Nibecker Cela ne fonctionne pas dans EF Core. Quelle est l'alternative pour EF Core?
nam

9
AVERTISSEMENT: j'ai implémenté cela avec l'intention de ne l'exécuter qu'en cours de développement. Lorsque nous avons déployé notre environnement de test, nous avons commencé à voir brusquement des fuites de mémoire dans le processus de travail IIS. Après le profilage de la mémoire, nous avons réalisé que même GC explicite ne collectait plus les objets de contexte d'entité (oui, ils utilisaient des instructions). La suppression de cette ligne est revenue à la normale. Donc, même s'il s'agit d'un excellent outil, assurez-vous de l'intégrer uniquement dans votre application pour le développement.
Brandon Barkley

82

À partir d'EF6.1, vous pouvez utiliser des intercepteurs pour enregistrer un enregistreur de base de données. Voir les chapitres "Intercepteurs" et "Journalisation des opérations de base de données" dans un fichier ici

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


12
La précision, c'est sous: <configuration> <entityFramework> <interceptors> ... </interceptors> </entityFramework> </configuration>
Christophe P

80

Si vous utilisez un DbContext, vous pouvez effectuer les opérations suivantes pour obtenir le SQL:

var result = from i in myContext.appEntities
             select new Model
             {
                 field = i.stuff,
             };
var sql = result.ToString();

12
ToString()vous donnera la requête avec des variables, comme p__linq__0, au lieu des valeurs finales (par exemple: 34563 au lieu de p__linq__0)
sports

25

Applicable pour EF 6.0 et supérieur: pour ceux d'entre vous qui souhaitent en savoir plus sur la fonctionnalité de journalisation et ajouter à certaines des réponses déjà données.

Toute commande envoyée par l'EF à la base de données peut maintenant être enregistrée. Pour afficher les requêtes générées à partir d'EF 6.x, utilisez leDBContext.Database.Log property

Ce qui est enregistré

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

Exemple:

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

Production:

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

Pour vous connecter à un fichier externe:

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

Plus d'informations ici: Journalisation et interception des opérations de base de données


21

Vous pouvez effectuer les opérations suivantes dans EF 4.1:

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

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

Cela vous donnera le SQL qui a été généré.


1
En fait, je pense que cela ne fonctionne que lorsque la requête renvoie un type anonyme. S'il renvoie un type personnalisé, la ToString()sortie est l'espace de noms de ce type personnalisé. Par exemple, si le code ci-dessus était select new CustomType { x = x.Name }, la valeur retournée serait quelque chose comme Company.Models.CustomTypeau lieu du SQL généré.
Chad Levy,

8
Cette technique produit System.Data.Objects.ObjectQuery``1[MyProject.Models.Product]pour moi.
Carl G

1
@CarlG System.Data.Objects.ObjectQuery n'est pas EF 4.1 (DbContext). En utilisant DbContext, ce serait System.Data.Entity.Infrastructure.DbQuery`1 [MyProject.Models.Product] qui afficherait en effet son SQL lors d'un appel à "ToString ()"
springy76

Cela vous donnera le SQL qui a été généré, où, dans la fenêtre de sortie? quelle option dans la liste déroulante?
JsonStatham

17

Ma réponse concerne le noyau EF . Je fais référence à ce problème de github et aux documents sur la configurationDbContext :

Facile

Remplacez la OnConfiguringméthode de votre DbContextclasse ( YourCustomDbContext) comme indiqué ici pour utiliser un ConsoleLoggerProvider; vos requêtes doivent se connecter à la 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
}

Complexe

Ce cas complexe évite de surcharger la DbContext OnConfiguringméthode. , ce qui est déconseillé dans la documentation: "Cette approche ne se prête pas aux tests, à moins que les tests ne ciblent la base de données complète."

Ce cas complexe utilise:

  • La méthode IServiceCollectionin Startupclass ConfigureServices(au lieu de remplacer la OnConfiguringméthode; l'avantage est un couplage plus lâche entre le DbContextet le que ILoggerProvidervous souhaitez utiliser)
  • Une implémentation de ILoggerProvider(au lieu d'utiliser l' ConsoleLoggerProviderimplémentation indiquée ci-dessus; l'avantage est que notre implémentation montre comment nous nous connecterions à File (je ne vois pas de fournisseur d' enregistrement de fichiers livré avec EF Core ))

Comme ça:

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

Voici la mise en œuvre d'un MyLoggerProvider (et son MyLoggerqui ajoute ses journaux à un fichier que vous pouvez configurer; vos requêtes EF Core apparaîtront dans le fichier.)

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

Alors ... il n'y a pas de moyen pour le faire?
Juan De la Cruz

1
@JuanDelaCruz J'ai simplifié ma réponse; essayez l'alternative simple
The Red Pea

16

Il y a deux façons:

  1. Pour afficher le SQL qui sera généré, appelez simplement ToTraceString(). Vous pouvez l'ajouter dans votre fenêtre de surveillance et définir un point d'arrêt pour voir quelle serait la requête à tout moment donné pour n'importe quelle requête LINQ.
  2. Vous pouvez attacher un traceur à votre serveur SQL de choix, qui vous montrera la requête finale dans tous ses détails sanglants. Dans le cas de MySQL, le moyen le plus simple de suivre les requêtes est simplement de suivre le journal des requêtes avec tail -f. Vous pouvez en savoir plus sur les fonctionnalités de journalisation de MySQL dans la documentation officielle . Pour SQL Server, le moyen le plus simple consiste à utiliser le profileur SQL Server inclus.

27
Le ToTraceString de quoi?
nos

L'ObjectQuery, comme Nick l'a noté juste après avoir posté ma réponse.
Benjamin Pollack

2
SQL Server Profiler capture les 4000 premiers caractères, mais les requêtes EF peuvent être beaucoup plus longues que cela.

8

Pour que la requête soit toujours à portée de main, sans modifier le code, ajoutez-la à votre DbContext et vérifiez-la dans la fenêtre de sortie de Visual Studio.

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

Semblable à la réponse de @Matt Nibecker, mais avec cela, vous n'avez pas à l'ajouter dans votre code actuel, chaque fois que vous avez besoin de la requête.


La meilleure réponse!
AlexSC

7

SQL Management Studio => Outils => Profileur SQL Server

Fichier => Nouvelle trace ...

Utilisez le modèle => Blank

Sélection d'événement => T-SQL

Contrôle à gauche pour: SP.StmtComplete

Les filtres de colonne peuvent être utilisés pour sélectionner un ApplicationName ou DatabaseName spécifique

Démarrez ce profil en cours d'exécution, puis lancez la requête.

Cliquez ici pour les informations sur la source


1
désolé c'est juste pour le serveur SQL, pas pour MySQL
andrew pate


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

Renvoie la requête SQL. Utilisation du contexte de données d'EntityFramework 6


4
Je viens d'essayer cela et il trace l'objet: Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1 [System.Linq.IGrouping 2[System.Int32,String]]au lieu de la requête réelle. Suis-je en train de manquer quelque chose ou avez-vous oublié de mentionner quelque chose?
loganjones16

5

Je fais un test d'intégration, et j'en avais besoin pour déboguer l'instruction SQL générée dans Entity Framework Core 2.1, donc j'utilise DebugLoggerProviderou ConsoleLoggerProviderj'aime:

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

        ........

Voici un exemple de sortie de la console Visual Studio:

Exemple de sortie d'instruction SQL


1
DebugLoggerPrivider et ConsoleLoggerProvider semblent exister uniquement dans .NET Core: docs.microsoft.com/en-us/dotnet/api/…
Gabriel Magana

4

Nécromancement.
Cette page est le premier résultat de recherche lorsque vous recherchez une solution pour n'importe quel .NET Framework, alors voici en tant que service public, comment cela se fait dans EntityFramework Core (pour .NET Core 1 et 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);

Et puis ces méthodes d'extension (IQueryableExtensions1 pour .NET Core 1.0, IQueryableExtensions pour .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;
        }


    }


}

J'utilise EF Core 2.0.1 et la suggestion ci-dessus entraîne: System.InvalidCastException: 'Impossible de caster un objet de type Microsoft.EntityFrameworkCore.Query.Internal.InMemoryQueryModelVisitor' pour taper '' Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor'` pour la ligne: var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
Chris Wolf

2
@ChrisWolf si vous suivez l'essentiel de l'auteur original, vous pouvez trouver quelqu'un qui a fourni une version mise à jour de cette méthode d'extension . A travaillé pour moi.
B12Toaster

2

Dans mon cas pour EF 6+, au lieu d'utiliser ceci dans la fenêtre immédiate pour trouver la chaîne de requête:

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

J'ai fini par devoir l'utiliser pour obtenir la commande SQL générée:

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

Bien sûr, votre signature de type anonyme peut être différente.

HTH.


2

Je viens de faire ça:

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

Et le résultat affiché dans la sortie :

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

Oui, mais je crois que personne ne veut voir le p__linq__i, mais les vraies valeurs
Tom Stickel

Cette méthode fonctionne toujours dans EF 6 et elle sera utile si vous ne vous souciez que de la structure de la requête. Dans mon cas, le projet que je crée l'objet IQueryable <T> n'a pas de référence à System.Data.Entity et je ne veux pas l'ajouter uniquement à des fins de débogage. Cette méthode a donc très bien fonctionné.
wctiger

2

Pour moi, en utilisant EF6 et Visual Studio 2015, je suis entré querydans la fenêtre immédiate et cela m'a donné l'instruction SQL générée


1

Si vous souhaitez également avoir des valeurs de paramètre (non seulement, @p_linq_0mais aussi leurs valeurs), vous pouvez utiliser IDbCommandInterceptoret ajouter une journalisation à la ReaderExecutedméthode.


1

Bien qu'il existe de bonnes réponses ici, aucune n'a résolu mon problème complètement (je souhaitais obtenir l'intégralité de l'instruction SQL, y compris les paramètres , à partir du DbContext à partir de n'importe quel IQueryable. Le code suivant fait exactement cela. Il s'agit d'une combinaison d'extraits de code de Google. I l'ont testé qu'avec EF6 + .

Soit dit en passant, cette tâche m'a pris beaucoup plus de temps que je ne le pensais. L'abstraction dans Entity Framework est un peu trop, à mon humble avis.

D'abord l'utilisation. Vous aurez besoin d'une référence explicite à «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;

La classe suivante convertit un IQueryable en un DataTable. Modifiez selon vos besoins:

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

Pour l'utiliser, il suffit de l'appeler comme ci-dessous:

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

0

Solution Entity Framework 4

La plupart des réponses ici étaient spécifiques à EF6. En voici une pour ceux d'entre vous qui utilisent encore EF4.

Cette méthode remplace le @p__linq__0/ etc. paramètres avec leurs valeurs réelles, vous pouvez donc simplement copier et coller la sortie dans SSMS et l'exécuter ou la déboguer.

    /// <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;
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.