Как захватить SQL с параметрами, подставленными в? (.NET, SqlCommand) - PullRequest
4 голосов
/ 19 мая 2010

Если есть простой способ вернуть законченный оператор SQL после замены параметра? Т.е. я хочу сохранить лог-файл всех SQL, которые запускает эта программа.

Или, если я хочу сделать это, я просто хочу избавиться от параметров и выполнить весь запрос по-старому, в одной большой строке?

Простой пример: я хочу захватить вывод:

ВЫБЕРИТЕ подкатегорию ОТ EnrollmentSubCategory ГДЕ 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 ]

6 голосов
/ 19 мая 2010

Нет, .Prepare этого не делает, и фактически ничего в Команде не делает. Значения параметров никогда не подставляются в фактическую командную строку. Он отправляется в БД отдельно, что хорошо по нескольким причинам:

  • Это позволяет sqlserver (или другим базам данных) кэшировать план запроса для вашего запроса и использовать его в следующий раз. Когда в базу данных каждый раз отправляется другая строка командного текста, база данных должна каждый раз разрабатывать план.
  • Посылая параметры в отсеки БД, существует естественная защита от атак инъекций SQL.

Если вы не используете действительно старую базу данных (sql server 7?), .Prepare() не требуется и фактически ничего для вас не делает. Раньше было полезно, потому что он собирал запрос на сервере, но теперь это делается автоматически. Я давно не пользуюсь .Prepare().

Hmmmmm. Если посмотреть здесь , кажется, .Prepare() все еще что-то для вас делает: если какие-либо значения превышают длину, определенную параметром, .Prepare () усекает значение, поэтому при выполнении вы не получите ошибка и запрос завершается успешно. круто.

5 голосов
/ 19 мая 2010

Я обычно добавляю служебную функцию к своим проектам, которая, учитывая объект 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 голосов
/ 19 мая 2010

Параметры не подставляются. SQL как есть, и сами параметры передаются как параметры в системную хранимую процедуру sp_executesql

1 голос
/ 19 мая 2010

То, что отправляется на SQL Server, - это не переменные, заменяемые значениями, а подготовленный оператор, поэтому его можно использовать повторно

Откройте профилировщик в SQL Server, и вы увидите, что выполняется не SELECT subcatId FROM EnrollmentSubCategory WHERE catid = 1

0 голосов
/ 13 июня 2019

Попробуйте пакет CommandAsSql NuGet

0 голосов
/ 19 мая 2010

Вы можете создать оболочку вокруг System.Data.SqlClient Provider (* Например, поставщик, зарегистрированный в файле конфигурации как ... * providerName="System.Data.SqlClient"). По сути, для перехвата прокси у вас будет доступ ко всей информации, проходящей через Провайдера, и вы сможете откачивать то, что вам нужно, собирать и / или обогащать ее и отправлять в журналы. Это немного более продвинутый, но открывает дверь для сбора всего спектра информации и может быть вставлен / заменен / удален в качестве отдельного слоя проблемы.

0 голосов
/ 19 мая 2010

Это было , недавно спросили здесь . Как указывают другие ответы, параметры отправляются вне диапазона.

Profiler - это, вероятно, то, что вы хотите использовать для захвата, если вы хотите сделать это на сервере, и вы можете создать трассировку и использовать ее в качестве задания при запуске .

...