【问题标题】:In EF 4.1 DbContext how to trace generated SQL在 EF 4.1 DbContext 中如何跟踪生成的 SQL
【发布时间】:2011-08-01 23:16:35
【问题描述】:

我想知道如何在 LinqToSql 中跟踪生成的 SQL,如 DataContext。

我也在 Jaroslaw Kowalski 的博客上看过关于 EFProviderWrapper 解决方案的文章,但是它是基于 ObjectContext,不适用于 DbContext。

有人知道如何在 DbContext 中执行此操作吗?

谢谢。

【问题讨论】:

  • 这其实是一个很好的问题。
  • 这是features 之一,Microsoft 希望您花更多的钱来获得 (Visual Studio Ultimate)。不过,有一些 3rd 方库可以让它变得更容易。

标签: sql logging entity-framework-4 entity-framework-4.1 trace


【解决方案1】:

这比用值替换参数的解决方案(@kmk)略有改进。此解决方案改为声明并分配参数:

    public static string ToSqlString<TEntity>(this IQueryable<TEntity> queryable) where TEntity : class
    {
        StringBuilder parametersBuilder = new StringBuilder();

        try
        {
            var dbQuery = queryable as DbQuery<TEntity>;

            // 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 oq = oqProp.GetValue(iq, null);

            var objectQuery = oq as ObjectQuery<TEntity>;

            var sqlString = objectQuery.ToTraceString();

            foreach (var objectParam in objectQuery.Parameters)
            {
                SqlMetaData metadata = SqlMetaData.InferFromValue(objectParam.Value, objectParam.Name);
                string sqlType = metadata.TypeName + (metadata.SqlDbType == SqlDbType.NVarChar ? "(" + metadata.MaxLength + ")" : String.Empty);

                parametersBuilder.AppendFormat("declare @{0} {1} = '{2}'", objectParam.Name, sqlType, objectParam.Value);
                parametersBuilder.AppendLine();
            }

            parametersBuilder.AppendLine();
            return parametersBuilder.ToString() + sqlString;
        }
        catch (Exception)
        {
            //squash it and just return ToString
            return queryable.ToString();
        }
    }

【讨论】:

    【解决方案2】:

    执行您的代码,然后运行此查询以查看最后执行的 SQL。

    SELECT deqs.last_execution_time AS [Time], dest.TEXT AS [Query]
    FROM sys.dm_exec_query_stats AS deqs
    CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest
    ORDER BY deqs.last_execution_time DESC
    

    【讨论】:

      【解决方案3】:

      对于不想拉入第三方库,而只是寻找包含参数的 SQL(并且不会因所有反射而感到不安)的任何人,此扩展方法从DbQuery 并在实现参数后返回 ToTraceString 回字符串。如果失败,则从 IQueryable 返回无参数 ToString:

          public static string ToSqlString<TEntity>(this IQueryable<TEntity> queryable) where TEntity : class
          {
              try
              {
                  var dbQuery = queryable as DbQuery<TEntity>;
      
                  // 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);
      
                  // get the ObjectQuery internal variable from the IInternalQuery object
                  var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
      
                  var oq = oqProp.GetValue(iq);
      
                  var objectQuery = oq as ObjectQuery<TEntity>;
      
                  var sqlString = objectQuery.ToTraceString();
      
                  foreach (var objectParam in objectQuery.Parameters)
                  {
                      if (objectParam.ParameterType == typeof(string) || objectParam.ParameterType == typeof(DateTime) || objectParam.ParameterType == typeof(DateTime?))
                      {
                          sqlString = sqlString.Replace(string.Format("@{0}", objectParam.Name), string.Format("'{0}'", objectParam.Value.ToString()));
                      }
                      else if (objectParam.ParameterType == typeof(bool) || objectParam.ParameterType == typeof(bool?))
                      {
                          bool val;
                          if (Boolean.TryParse(objectParam.Value.ToString(), out val))
                          {
                              sqlString = sqlString.Replace(string.Format("@{0}", objectParam.Name), string.Format("{0}", val ? 1 : 0));
                          }
      
                      }
                      else
                      {
                          sqlString = sqlString.Replace(string.Format("@{0}", objectParam.Name), string.Format("{0}", objectParam.Value.ToString()));
                      }
                  }
      
                  return sqlString;
              }
              catch (Exception)
              {
                  //squash it and just return ToString
                  return queryable.ToString();
              }
          }
      

      【讨论】:

      • 参数超过10个时,这个方案有问题。假设一个参数称为 p10。当你用它的值替换 p1 时,你会得到无效的 SQL。
      【解决方案4】:

      我在这里找到了 ObjectContext 的 EFTracingProvider 扩展:

      http://efwrappers.codeplex.com/

      但该示例是针对 ObjectContext 而不是 DbContext,以使其与 DbContext 一起使用 在构造函数中执行以下操作:

      Public Sub New()
        MyBase.New(EFTracingProviderUtils.CreateTracedEntityConnection("MyDbConnection"), True)
        Dim context As ObjectContext = CType(Me, IObjectContextAdapter).ObjectContext
        context.EnableTracing()
      End Sub
      

      哦,记得设置配置:

      <?xml version="1.0" encoding="utf-8"?>
      <configuration>
        <system.diagnostics>
          <sources>
            <source name="EntityFramework.MyDbConnection" switchValue="All" />
          </sources>
       </system.diagnostics>
      

      然后将所有 SQL 跟踪到即时窗口。

      【讨论】:

        【解决方案5】:

        使用DbContextDbSet&lt;T&gt; 最简单的方法就是在您构建的IQueryable 上使用ToString()。例如:

        var query = context.Blogs.Include(b => b.Posts)
                           .Where(b => b.Title == "AnyTitle");
        
        string sql = query.ToString();
        

        sql 包含执行查询时将向数据库发出的 SQL 命令。

        【讨论】:

        • 这仅在您进行搜索时才有帮助(返回 IQueryable)。如果您正在添加、修改和删除实体,您如何查看该 sql?
        • @codemonkey:是的,仅适用于查询。我认为没有任何内置方法可以使用 DbContext 跟踪 all SQL 语句。
        • 我来这里是为了问@codemonkey 的问题。我发现的一切都表明前者,后者还没有运气。
        • @Slaima: 但我如何通过 DBContext 从 SaveChanges() 语句记录生成的 Sql?
        【解决方案6】:

        您可以使用ObjectQuery.ToTraceString 方法查看存储命令(例如SQL 语句)。 MSDN 上的How To 将向您展示如何使用它。

        请注意,在许多情况下,您可以将 IQueryable(来自 Linq 扩展方法的返回类型,如 IQueryable.Where)转换为 ObjectQuery,以便您可以访问 ToTraceString 方法。

        【讨论】:

        【解决方案7】:

        MVC-Mini-Profiler是一个强大的工具,不仅是trace生成sql,还是profiling工具。

        Using mvc-mini-profiler database profiling with Entity Framework Code First

        【讨论】:

          【解决方案8】:

          我使用 SQL Server 分析器工具来准确查看已创建的 SQL。还有http://efprof.com/,不过价格挺高的。

          【讨论】:

          • 谢谢,我知道那个工具。我正在寻找 DIY 解决方案。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-30
          • 2011-06-17
          相关资源
          最近更新 更多