How do I view the SQL generated by the Entity Framework?

后端 未结 22 2813
别那么骄傲
别那么骄傲 2020-11-21 05:35

How do I view the SQL generated by entity framework ?

(In my particular case I\'m using the mysql provider - if it matters)

相关标签:
22条回答
  • 2020-11-21 06:13
    IQueryable query = from x in appEntities
                       where x.id = 32
                       select x;
    var queryString = query.ToString();
    

    Will return the sql query. Working using datacontext of EntityFramework 6

    0 讨论(0)
  • 2020-11-21 06:13

    Use Logging with Entity Framework Core 3.x

    Entity Framework Core emits SQL via the logging system. There are only a couple of small tricks. You must specify an ILoggerFactory and you must specify a filter. Here is an example from this article

    Create the factory:

    var loggerFactory = LoggerFactory.Create(builder =>
    {
        builder
        .AddConsole((options) => { })
        .AddFilter((category, level) =>
            category == DbLoggerCategory.Database.Command.Name
            && level == LogLevel.Information);
    });
    

    Tell the DbContext to use the factory in the OnConfiguring method:

    optionsBuilder.UseLoggerFactory(_loggerFactory);
    

    From here, you can get a lot more sophisticated and hook into the Log method to extract details about the executed SQL. See the article for a full discussion.

    public class EntityFrameworkSqlLogger : ILogger
    {
        #region Fields
        Action<EntityFrameworkSqlLogMessage> _logMessage;
        #endregion
        #region Constructor
        public EntityFrameworkSqlLogger(Action<EntityFrameworkSqlLogMessage> logMessage)
        {
            _logMessage = logMessage;
        }
        #endregion
        #region Implementation
        public IDisposable BeginScope<TState>(TState state)
        {
            return default;
        }
        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (eventId.Id != 20101)
            {
                //Filter messages that aren't relevant.
                //There may be other types of messages that are relevant for other database platforms...
                return;
            }
            if (state is IReadOnlyList<KeyValuePair<string, object>> keyValuePairList)
            {
                var entityFrameworkSqlLogMessage = new EntityFrameworkSqlLogMessage
                (
                    eventId,
                    (string)keyValuePairList.FirstOrDefault(k => k.Key == "commandText").Value,
                    (string)keyValuePairList.FirstOrDefault(k => k.Key == "parameters").Value,
                    (CommandType)keyValuePairList.FirstOrDefault(k => k.Key == "commandType").Value,
                    (int)keyValuePairList.FirstOrDefault(k => k.Key == "commandTimeout").Value,
                    (string)keyValuePairList.FirstOrDefault(k => k.Key == "elapsed").Value
                );
                _logMessage(entityFrameworkSqlLogMessage);
            }
        }
        #endregion
    }
    
    0 讨论(0)
  • 2020-11-21 06:14

    Entity Framework 4 Solution

    Most of the answers here were EF6-specific. Here's one for those of you still using EF4.

    This method replaces the @p__linq__0/etc. parameters with their actual values, so you can just copy and paste the output into SSMS and run it or debug it.

        /// <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;
        }
    
    0 讨论(0)
  • 2020-11-21 06:15

    Starting with EF6.1 you can use Interceptors to register a database logger. See chapters "Interceptors" and "Logging Database Operations" to a File here

    <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>
    
    0 讨论(0)
  • 2020-11-21 06:15

    If you want to have parameter values (not only @p_linq_0 but also their values) too, you can use IDbCommandInterceptor and add some logging to ReaderExecuted method.

    0 讨论(0)
  • 2020-11-21 06:15

    While there are good answers here, none solved my problem completely (I wished to get the entire SQL statement, including Parameters, from the DbContext from any IQueryable. The following code does just that. It is a combination of code snippets from Google. I have only tested it with EF6+.

    Just an aside, this task took me way longer than I thought it would. Abstraction in Entity Framework is a bit much, IMHO.

    First the using. You will need an explicit reference to '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;
    

    The following class converts an IQueryable into a DataTable. Modify as your need may be:

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

    To use, simply call it as below:

    var context = new MyContext();
    var data = ....//Query, return type can be anonymous
        .AsQueryable();
    var dt = new EntityFrameworkCommand()
        .Initialize(context, data)
        .GetData();
    
    0 讨论(0)
提交回复
热议问题