Как захватить SQL с подставленными параметрами? (.NET, SqlCommand)

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

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

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

ВЫБРАТЬ подкатегорию ИЗ 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 () выполнит замены, но, видимо, нет.

Мысли? Совет? Заранее спасибо.


person Bryan    schedule 19.05.2010    source источник


Ответы (7)


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

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

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

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

person Patrick Karcher    schedule 19.05.2010
comment
Спасибо за все ответы - я выбрал это в качестве ответа, потому что он лучше всего объясняет рассуждения. Спасибо всем! - person Bryan; 19.05.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
person Cory Grimster    schedule 19.05.2010
comment
Думаю, я тоже буду этим заниматься - по сути, он выполняет то, что я искал. - person Bryan; 24.05.2010

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

person Ben Robinson    schedule 19.05.2010

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

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

person SQLMenace    schedule 19.05.2010

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

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

person Cade Roux    schedule 19.05.2010

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

person JoeGeeky    schedule 19.05.2010

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

person George Birbilis    schedule 13.06.2019