【问题标题】:Entity Framework 6 - Timing queries实体框架 6 - 定时查询
【发布时间】:2015-01-27 14:27:02
【问题描述】:

我正在使用 Entity Framework 6,它是很棒的数据库拦截器功能,用于记录从应用程序发送到数据库的查询。但是,我正在努力为这些查询计时,我有一个长时间运行的查询,它返回数十万到数百万行,因此它需要大约 6 到 15 秒,具体取决于该查询将返回的数据量。实体框架返回一个 SqlDataReader,因此我无法获得获得结果所需的确切时间。我想知道从发送查询到读取最后一行的完整执行时间。有什么办法可以吗。

【问题讨论】:

  • 如果想查看SQL运行了多长时间,可以使用Profiler。
  • 添加到 SQL Server Profiler 工具建议中,报告的持续时间将反映 SQL Server 收到请求直到服务器填充最后一个 TDS 缓冲区的时间。 SQL Server 无法测量初始请求或接收最终响应的网络延迟,只能测量数据库引擎的时间。对于像您这样的大型结果集可能不是问题,但这种延迟可能是端到端响应时间的最大份额,这是一个返回单个结果缓冲区的小查询。
  • 感谢您的建议。但是,我想记录一些元数据以及查询,例如触发此查询的用户是谁,以及存在于 Web 服务器上的一堆其他元数据。甚至可以从 Web 服务器层执行此操作吗?
  • 将调用包装在跟踪记录器中。如果花费的时间超过一定时间,请记录传递的元数据和您想要的任何其他内容。
  • 包裹在跟踪记录器中是什么意思?

标签: c# sql-server entity-framework entity-framework-6


【解决方案1】:

这是我通常用于 EF 的记录器。

public class EFLoggerForTesting : IDbCommandInterceptor
{
    static readonly ConcurrentDictionary<DbCommand, DateTime> m_StartTime = new ConcurrentDictionary<DbCommand, DateTime>();

    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        Log(command, interceptionContext);
    }

    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        Log(command, interceptionContext);
    }

    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        Log(command, interceptionContext);
    }

    private static void Log<T>(DbCommand command, DbCommandInterceptionContext<T> interceptionContext)
    {
        DateTime startTime;
        TimeSpan duration;


        if (m_StartTime.TryRemove(command, out startTime))
        {
            duration = DateTime.Now - startTime;
        }
        else
            duration = TimeSpan.Zero;

        var requestId =-1;
        string message;

        var parameters = new StringBuilder();
        foreach (DbParameter param in command.Parameters)
        {
            parameters.AppendLine(param.ParameterName + " " + param.DbType + " = " + param.Value);
        }

        if (interceptionContext.Exception == null)
        {
            message = string.Format("Database call took {0} sec. RequestId {1} \r\nCommand:\r\n{2}", duration.TotalSeconds.ToString("N3"), requestId, parameters.ToString() + command.CommandText);
        }
        else
        {
            message = string.Format("EF Database call failed after {0} sec. RequestId {1} \r\nCommand:\r\n{2}\r\nError:{3} ", duration.TotalSeconds.ToString("N3"), requestId, parameters.ToString() + command.CommandText, interceptionContext.Exception);
        }

        Debug.WriteLine(message);
    }


    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        OnStart(command);
    }

    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        OnStart(command);
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        OnStart(command);
    }
    private static void OnStart(DbCommand command)
    {
        m_StartTime.TryAdd(command, DateTime.Now);
    }
}

不幸的是,该文档不存在,所以我不知道这是否适用于您的场景。

【讨论】:

  • 虽然这不是我正在寻找的答案,但这应该为其他人提供足够的信息来开始他们的工作。
  • @Jonathan Allen 你用 requestId 做什么?
  • 将网站调用与数据库调用相关联。假设有人点击了 //myapp.com//user/23 页面。我生成一个新的请求 ID,比如 107,并将其用于每个数据库调用。然后,如果我看到请求 107 对用户表进行了 15 次数据库调用,我知道有些事情搞砸了。 (是的,这是一个真实的例子。EF 让调用数据库变得非常容易,无需考虑它。)
  • P.S.我上面的示例缺少从 HttpContext.Current 读取以获取真实请求 ID 的代码。 -1 是与 Web 请求无关的数据库调用的占位符。
  • 我在别处问过:stackoverflow.com/q/40339358/1380710。奇怪的时间是因为DateTime 不能保证在系统计时器之上具有精度,Windows NT 是 10 毫秒,Windows 8+ 是 1 毫秒。我必须使用System.Diagnostics.Stopwatch 以获得更高的精度。
【解决方案2】:

如果您正在使用 Web 应用程序,可以尝试 Glimpse:http://getglimpse.com/。否则,试试 MiniProfiler:http://miniprofiler.com/

【讨论】:

  • 我不想跟踪整个周期。我只是想了解有关实体框架部分的信息。
  • 这里的想法是自己学习和做,而不是使用已经成熟的解决方案
【解决方案3】:

正如@Ricardo Peres 的回答所暗示的,Glimpse 对此有好处。它带有Glimpse.Ado 插件,可用于轻松分析任何DbConnection,这是此处扩展的主类。

Glimpse 中 ADO 的手动集成点是将 DbConnection 包装在 GlimpseDbConnection 中,如以下博客文章所示:http://getglimpse.com/Docs/Manual-ADO-Integration。 EF 等其他提供程序会自动与Glimpse.Ef 包集成。

如果您仍然决定要手动实现此目的,我建议您自己包装 DbConneciton 并使用它代替常规的 DbConnection。这样做可以得到简单的时间安排。

你可以在他们的 github 上看到 Glimpse 是如何做到的:https://github.com/Glimpse/Glimpse/tree/master/source/Glimpse.Ado/AlternateType

或者,始终可以选择在存储库级别或方法级别添加此类日志记录,具体取决于您需要多少日志记录

【讨论】:

    【解决方案4】:

    你可以试试the example explained here

    在本例中,我们在命令开始执行时启动Stopwatch,并在命令完成时停止Stopwatch,这样我们可以识别慢查询并记录它们。

    public class SqlMonitorInterceptor : IDbCommandInterceptor
    {
        private static readonly ILog logger = LogManager.GetCurrentClassLogger();
        private static readonly int sqlWarningThresholdMs = int.Parse(ConfigurationManager.AppSettings["sqlPerformance_warningThresholdMilliseconds"]);
        private readonly Stopwatch _stopwatch = new Stopwatch();
    
        public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            CommandExecuting();
        }
    
        public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            CommandExecuted(command, interceptionContext);
        }
    
        public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            CommandExecuting();
        }
    
        public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            CommandExecuted(command, interceptionContext);
        }
    
        public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            CommandExecuting();
        }
    
        public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            CommandExecuted(command, interceptionContext);
        }
    
        private void CommandExecuting() {
            _stopwatch.Restart();
        }
    
        private void CommandExecuted<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
        {
            _stopwatch.Stop();
            LogIfError(command, interceptionContext);
            LogIfTooSlow(command, _stopwatch.Elapsed);
        }
    
        private void LogIfError<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
        {
            if (interceptionContext.Exception != null)
            {
                logger.ErrorFormat("Command {0} failed with exception {1}",
                    command.CommandText, interceptionContext.Exception);
            }
        }
    
        private void LogIfTooSlow(DbCommand command, TimeSpan completionTime)
        {
            if (completionTime.TotalMilliseconds > sqlWarningThresholdMs)
            {
                logger.WarnFormat("Query time ({0}ms) exceeded the threshold of {1}ms. Command: \"{2}\"",
                    completionTime.TotalMilliseconds, sqlWarningThresholdMs, command.CommandText);
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      Johnathan 接受的答案的 EF Core 版本,供任何人搜索:

      using Microsoft.EntityFrameworkCore.Diagnostics;
      using System.Data.Common;
      
      public class EFLogger: DbCommandInterceptor
      {
          private static readonly ConcurrentDictionary<Guid, DateTime> _startTimes = new ConcurrentDictionary<Guid, DateTime>();
      
          public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader reader)
          {
              Log(command, eventData);
              return reader;
          }
      
          public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
          {
              Log(command, eventData);
              return result;
          }
      
          public override object ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object result)
          {
              Log(command, eventData);
              return result;
          }
      
          public override void CommandFailed(DbCommand command, CommandErrorEventData eventData)
          {
              Log(command, eventData);
          }
      
          private static void Log(DbCommand command, CommandEventData eventData)
          {
              TimeSpan? duration = null;
              if (_startTimes.TryRemove(eventData.CommandId, out DateTime startTime))
                  duration = DateTime.Now - startTime;
      
              var parameters = new StringBuilder();
              foreach (DbParameter param in command.Parameters)
              {
                  parameters.AppendLine(param.ParameterName + " " + param.DbType + " = " + param.Value);
              }
      
              string message = $"Database call {(eventData is CommandErrorEventData ? "FAILED" : "succeeded")} in {duration?.TotalMilliseconds ?? -1:N3} ms. CommandId {eventData.CommandId} \r\nCommand:\r\n{parameters}{command.CommandText}";
              Console.WriteLine(message);
          }
      
          public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
          {
              OnStart(eventData.CommandId);
              return result;
          }
          public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
          {
              OnStart(eventData.CommandId);
              return result;
          }
      
          public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
          {
              OnStart(eventData.CommandId);
              return result;
          }
      
          private void OnStart(Guid commandId)
          {
              _startTimes.TryAdd(commandId, DateTime.Now);
          }
      }
      

      【讨论】:

        【解决方案6】:

        下面是我对原始DatabaseLogFormatter 的简化版本。主要区别在于这个没有过滤,我没有记录实际的 SQL 查询或它的参数(因为我只对查询计时感兴趣)。它会记录打开连接、执行查询以及再次关闭连接的时间。正如@aske-b noticed above 使用DateTime (正如公认的答案那样)不是很准确。


        public class CustomDatabaseLogFormatter : IDbCommandInterceptor, IDbConnectionInterceptor
        {
            private readonly Action<string> _writeAction;
            private readonly Stopwatch _stopwatch = new Stopwatch();
        
            /// <summary>
            /// Creates a formatter that will log every command from any context and also commands that do not originate from a context.
            /// </summary>
            /// <remarks>
            /// This constructor is not used when a delegate is set on <see cref="Database.Log" />. Instead it can be
            /// used by setting the formatter directly using <see cref="DbInterception.Add" />.
            /// </remarks>
            /// <param name="writeAction">The delegate to which output will be sent.</param>
            public CustomDatabaseLogFormatter(Action<string> writeAction)
            {
                Check.NotNull(writeAction, "writeAction");
        
                _writeAction = writeAction;
            }
        
            internal Action<string> WriteAction
            {
                get { return _writeAction; }
            }
        
            /// <summary>
            /// Writes the given string to the underlying write delegate.
            /// </summary>
            /// <param name="output">The string to write.</param>
            protected virtual void Write(string output)
            {
                _writeAction(output);
            }
        
            /// <summary>
            /// The stopwatch used to time executions. This stopwatch is started at the end of
            /// <see cref="NonQueryExecuting" />, <see cref="ScalarExecuting" />, and <see cref="ReaderExecuting" />
            /// methods and is stopped at the beginning of the <see cref="NonQueryExecuted" />, <see cref="ScalarExecuted" />,
            /// and <see cref="ReaderExecuted" /> methods. If these methods are overridden and the stopwatch is being used
            /// then the overrides should either call the base method or start/stop the stopwatch themselves.
            /// </summary>
            /// <returns>The stopwatch.</returns>
            protected internal Stopwatch Stopwatch
            {
                get { return _stopwatch; }
            }
        
            private void RestartStopwatch()
            {
                Stopwatch.Restart();
            }
        
            private void StopStopwatch()
            {
                Stopwatch.Stop();
            }
        
            #region IDbCommandInterceptor
            /// <summary>
            /// This method is called before a call to <see cref="DbCommand.ExecuteNonQuery" /> or
            /// one of its async counterparts is made.
            /// Starts the stopwatch returned from <see cref="Stopwatch"/>.
            /// </summary>
            /// <param name="command">The command being executed.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public virtual void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                RestartStopwatch();
            }
        
            /// <summary>
            /// This method is called after a call to <see cref="DbCommand.ExecuteNonQuery" /> or
            /// one of its async counterparts is made.
            /// Stops the stopwatch returned from <see cref="Stopwatch"/> and calls <see cref="Executed" />.
            /// </summary>
            /// <param name="command">The command being executed.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public virtual void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                StopStopwatch();
                Executed(command, interceptionContext);
            }
        
            /// <summary>
            /// This method is called before a call to <see cref="DbCommand.ExecuteReader(CommandBehavior)" /> or
            /// one of its async counterparts is made.
            /// Starts the stopwatch returned from <see cref="Stopwatch"/>.
            /// </summary>
            /// <param name="command">The command being executed.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public virtual void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                RestartStopwatch();
            }
        
            /// <summary>
            /// This method is called after a call to <see cref="DbCommand.ExecuteReader(CommandBehavior)" /> or
            /// one of its async counterparts is made.
            /// Stops the stopwatch returned from <see cref="Stopwatch"/> and calls <see cref="Executed" />.
            /// </summary>
            /// <param name="command">The command being executed.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public virtual void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                StopStopwatch();
                Executed(command, interceptionContext);
            }
        
            /// <summary>
            /// This method is called before a call to <see cref="DbCommand.ExecuteScalar" />  or
            /// one of its async counterparts is made.
            /// Starts the stopwatch returned from <see cref="Stopwatch"/>.
            /// </summary>
            /// <param name="command">The command being executed.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public virtual void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                RestartStopwatch();
            }
        
            /// <summary>
            /// This method is called after a call to <see cref="DbCommand.ExecuteScalar" />  or
            /// one of its async counterparts is made.
            /// Stops the stopwatch returned from <see cref="Stopwatch"/> and calls
            /// <see cref="Executed" />.
            /// </summary>
            /// <param name="command">The command being executed.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public virtual void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                StopStopwatch();
                Executed(command, interceptionContext);
            }
        
            /// <summary>
            /// Called whenever a command has completed executing. Calls <see cref="LogResult" />.
            /// </summary>
            /// <typeparam name="TResult">The type of the operation's results.</typeparam>
            /// <param name="command">The command that was executed.</param>
            /// <param name="interceptionContext">Contextual information associated with the command.</param>
            public virtual void Executed<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                LogResult(command, interceptionContext);
            }
        
            /// <summary>
            /// Called to log the result of executing a command.
            /// </summary>
            /// <typeparam name="TResult">The type of the operation's results.</typeparam>
            /// <param name="command">The command being logged.</param>
            /// <param name="interceptionContext">Contextual information associated with the command.</param>
            public virtual void LogResult<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
            {
                Check.NotNull(command, "command");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                var stopwatch = Stopwatch;
        
                if (interceptionContext.Exception != null)
                {
                    Write(
                        String.Format(StringResources.CommandLogFailed, stopwatch.ElapsedMilliseconds, interceptionContext.Exception.Message)
                        );
                }
                else if (interceptionContext.TaskStatus.HasFlag(TaskStatus.Canceled))
                {
                    Write(String.Format(StringResources.CommandLogCanceled, stopwatch.ElapsedMilliseconds));
                }
                else
                {
                    var result = interceptionContext.Result;
                    var resultString = (object)result == null
                        ? "null"
                        : (result is DbDataReader)
                            ? result.GetType().Name
                            : result.ToString();
                    Write(String.Format(StringResources.CommandLogComplete, stopwatch.ElapsedMilliseconds, resultString));
                }
            }
            #endregion
        
            #region IDbConnectionInterceptor
            public void BeginningTransaction(DbConnection connection, BeginTransactionInterceptionContext interceptionContext)
            { }
        
            public void BeganTransaction(DbConnection connection, BeginTransactionInterceptionContext interceptionContext)
            { }
        
            public void Closing(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
            { }
        
            /// <summary>
            /// Called after <see cref="DbConnection.Close" /> is invoked.
            /// </summary>
            /// <param name="connection">The connection that was closed.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public void Closed(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
            {
                Check.NotNull(connection, "connection");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                if (interceptionContext.Exception != null)
                {
                    Write(String.Format(StringResources.ConnectionCloseErrorLog, DateTimeOffset.UtcNow, interceptionContext.Exception.Message));
                }
                else
                {
                    Write(String.Format(StringResources.ConnectionClosedLog, DateTimeOffset.UtcNow));
                }
            }
        
            public void ConnectionStringGetting(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void ConnectionStringGot(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void ConnectionStringSetting(DbConnection connection, DbConnectionPropertyInterceptionContext<string> interceptionContext)
            { }
        
            public void ConnectionStringSet(DbConnection connection, DbConnectionPropertyInterceptionContext<string> interceptionContext)
            { }
        
            public void ConnectionTimeoutGetting(DbConnection connection, DbConnectionInterceptionContext<int> interceptionContext)
            { }
        
            public void ConnectionTimeoutGot(DbConnection connection, DbConnectionInterceptionContext<int> interceptionContext)
            { }
        
            public void DatabaseGetting(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void DatabaseGot(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void DataSourceGetting(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void DataSourceGot(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void Disposing(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
            { }
        
            public void Disposed(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
            { }
        
            public void EnlistingTransaction(DbConnection connection, EnlistTransactionInterceptionContext interceptionContext)
            { }
        
            public void EnlistedTransaction(DbConnection connection, EnlistTransactionInterceptionContext interceptionContext)
            { }
        
            public void Opening(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
            { }
        
            /// <summary>
            /// Called after <see cref="DbConnection.Open" /> or its async counterpart is invoked.
            /// </summary>
            /// <param name="connection">The connection that was opened.</param>
            /// <param name="interceptionContext">Contextual information associated with the call.</param>
            public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext)
            {
                Check.NotNull(connection, "connection");
                Check.NotNull(interceptionContext, "interceptionContext");
        
                if (interceptionContext.Exception != null)
                {
                    Write(
                        interceptionContext.IsAsync
                            ? String.Format(StringResources.ConnectionOpenErrorLogAsync,
                                DateTimeOffset.UtcNow, interceptionContext.Exception.Message)
                            : String.Format(StringResources.ConnectionOpenErrorLog, DateTimeOffset.UtcNow, interceptionContext.Exception.Message));
                }
                else if (interceptionContext.TaskStatus.HasFlag(TaskStatus.Canceled))
                {
                    Write(String.Format(StringResources.ConnectionOpenCanceledLog, DateTimeOffset.UtcNow));
                }
                else
                {
                    Write(
                        interceptionContext.IsAsync
                            ? String.Format(StringResources.ConnectionOpenedLogAsync, DateTimeOffset.UtcNow)
                            : String.Format(StringResources.ConnectionOpenedLog, DateTimeOffset.UtcNow));
                }
            }
        
            public void ServerVersionGetting(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void ServerVersionGot(DbConnection connection, DbConnectionInterceptionContext<string> interceptionContext)
            { }
        
            public void StateGetting(DbConnection connection, DbConnectionInterceptionContext<ConnectionState> interceptionContext)
            { }
        
            public void StateGot(DbConnection connection, DbConnectionInterceptionContext<ConnectionState> interceptionContext)
            { } 
            #endregion
        }
        

        internal class Check
        {
            public static T NotNull<T>(T value, string parameterName)
                where T : class
            {
                if (value == null)
                {
                    throw new ArgumentNullException(parameterName);
                }
        
                return value;
            }
        }
        

        StringResources.resx:
        CommandLogCanceled          Canceled in {0} ms{1}
        CommandLogComplete          Completed in {0} ms with result: {1}
        CommandLogFailed            Failed in {0} ms with error: {1}
        ConnectionClosedLog         Closed connection at {0}
        ConnectionCloseErrorLog     Failed to close connection at {0} with error: {1}
        ConnectionOpenCanceledLog   Cancelled open connection at {0}
        ConnectionOpenedLog         Opened connection at {0}
        ConnectionOpenedLogAsync    Opened connection asynchronously at {0}
        ConnectionOpenErrorLog      Failed to open connection at {0} with error: {1}
        ConnectionOpenErrorLogAsync Failed to open connection asynchronously at {0} with error: {1}
        

        【讨论】:

          【解决方案7】:

          相当简单,但您不能使用 System.Timers.Timer 对象吗?在 EF 代码之前调用 start,在 EF 代码之后调用 end。如果您在那里有异步代码,您可以调用 .Result 并删除等待,以便同步运行代码并为调用计时。除此之外,如果您使用 SQL 探查器并从另一个值中减去一个值(计时器 - 探查器),您将了解 EF 代码执行所需的时间。

          【讨论】:

          • 我希望有一个集中的解决方案,并且每次我在任何地方使用实体框架时都不必一遍又一遍地做某事。
          • 我想您可以为每次调用实体框架创建某种包装器,但我认为您要实现的目标的复杂性肯定超过了好处。我的首选方法是在必要时计时并在开发过程中进行改进,并在没有计时器的情况下放置实时代码等。在长时间运行 5 秒以上的查询的特殊情况下,我会尝试将其放入优化的存储过程中,看看是否会减少时间。
          • @MaxRev17 好吧,根据乔纳森艾伦的回答,一般来说,这似乎有点简单......
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-25
          • 2013-03-17
          相关资源
          最近更新 更多