如何用参数替换的SQL捕获? (.NET,SqlCommand)

时间:2010-05-19 17:43:19

标签: .net sql vb.net ado.net sqlcommand

如果在参数替换后有一种简单的方法可以获得完整的SQL语句?即,我想保留该程序运行的所有SQL的日志文件。

或者,如果我想这样做,我是否只想摆脱参数,并用一个大字符串完成旧学校的整个查询?

简单示例:我想捕获输出:

SELECT subcatId FROM EnrollmentSubCategory WHERE catid = 1

..来自此代码:

    Dim subCatSQL As String = "SELECT subcatId FROM EnrollmentSubCategory WHERE catid = @catId"
    Dim connectionString As String = "X"
    Dim conn As New SqlConnection(connectionString)
    If conn.State = ConnectionState.Closed Then
        conn.Open()
    End If
    Dim cmd As New SqlCommand(subCatSQL, conn)
    With cmd
        .Parameters.Add(New SqlParameter("@catId", SqlDbType.Int, 1)) 
    End With

    Console.WriteLine("Before: " + cmd.CommandText)
    cmd.Prepare()
    Console.WriteLine("After: " + cmd.CommandText)

我曾假设Prepare()会做替换,但显然不是。

思考?建议吗?提前致谢。

7 个答案:

答案 0 :(得分:6)

不,.Prepare不这样做,事实上Command中没有任何内容。参数值永远不会替换为实际的命令字符串。它被单独发送到DB,这有几个原因:

  • 这允许sqlserver(或其他dbs)缓存查询的查询计划,并在下次重用它。每次向数据库发送不同的commandtext字符串时,数据库每次都必须开发一个计划。
  • 通过将参数发送到数据库分区,可以防止sql注入攻击。

除非您使用的是真正旧的数据库(sql server 7?),否则.Prepare()不是必需的,实际上并没有为您做任何事情。它曾经有用,因为它会在服务器上编译查询,但现在自动完成。我很长时间没有使用.Prepare()

Hmmmmm。看here似乎.Prepare()仍然为你做了一些事情:如果任何值大于参数定义的长度,.Prepare()会截断该值,这样当你执行时你不会得到错误和查询成功。凉爽。

答案 1 :(得分:5)

我通常会在项目中添加一个实用程序函数,给定一个DbCommand对象(OracleCommand的父类,SqlCommand等)将记录查询和参数值。它可能看起来像这样:

Public Shared Sub LogQuery(ByRef cmd As DbCommand)
    If cmd.CommandText Is Nothing Or cmd.CommandText.Length = 0 Then
        Return
    End If

    Dim logFile As CLog = New CLog("sql.log")
    Dim msg As New StringBuilder

    msg.AppendLine("*** Query ***")
    msg.AppendLine(cmd.CommandText)
    msg.AppendLine("*** End Query ***")
    msg.AppendLine("*** Parameters ***")

    For Each p As DbParameter In cmd.Parameters
        msg.AppendLine(String.Format("{0}: {1}", p.ParameterName, p.Value.ToString()))
    Next

    msg.AppendLine("*** End Parameters ***")

    logFile.WriteLog(msg.ToString() & System.Environment.NewLine)
End Sub

当我写这篇文章时,我发现它不是单独记录参数值,而是可以执行一些String.Replace()来将参数值替换为查询然后记录它:

For Each p As DbParameter In cmd.Parameters
    msg = msg.Replace(p.ParameterName, p.Value.ToString())
Next

答案 2 :(得分:2)

不替换参数。 sql原样,参数本身作为参数传递给sp_executesql系统存储过程

答案 3 :(得分:1)

发送到SQL Server的内容不是用值替换的变量,而是一个准备好的语句,这样就可以重用

在SQL Server中打开探查器,你会看到正在运行的是不是SELECT subcatId FROM EnrollmentSubCategory WHERE catid = 1

答案 4 :(得分:0)

这是recently asked here。正如其他答案所指出的那样,这些参数是在带外发送的。

如果您想在服务器上执行此操作,可能是您要用于捕获的Profiler,您可以create a trace and use it as a startup job

答案 5 :(得分:0)

您可以围绕System.Data.SqlClient提供程序构建一个包装器(*例如,在配置文件中注册的提供程序为...... * providerName="System.Data.SqlClient")。基本上是一个拦截代理,您可以访问通过提供程序的所有信息,并可以虹吸您需要的内容,聚合和/或丰富它并将其发送到日志。这是一个更先进的,但打开了捕获一系列信息的大门,可以作为一个单独的关注层插入/替换/删除。

答案 6 :(得分:0)

尝试使用CommandAsSql NuGet软件包