Из .NET я могу получить полную строку SQL, сгенерированную объектом SqlCommand (с параметрами SQL)? - PullRequest
6 голосов
/ 18 марта 2011

Могу ли я получить доступ к полной строке SQL, сгенерированной объектом SqlCommand из среды .NET?

Примечание. Полная строка SQL отображается в подсказке Intellisense, в VisualStudio, в режиме отладки.

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


Обновление 1 :
Я вызываю хранимую процедуру, имеющую параметры с cmd.CommandType = CommandType.StoredProcedure, и пытаюсь получить полный сгенерированный и запущенный SQL. Интересно, может ли метод cmd. Prepare () оказаться бесполезным в этих обстоятельствах, если он может хранить полную строку в поле состояния или что-то в этом роде.


Обновление 2:

В свете приведенных ниже ответов (и на которые даются ссылки), которые указывают на то, что во время подготовки или выполнения внутренняя строка SQL не генерируется полностью, я немного потрудился, используя .NET Reflector. Даже классы внутреннего соединения, кажется, передают объекты, а не сводят их к строкам, например:

внутренняя абстрактная пустота AddPreparedCommand ( SqlCommand cmd );
Объявление типа: System.Data.SqlClient.SqlInternalConnection
Сборка: System.Data, версия = 2.0.0.0


В общем, спасибо всем за уровень детализации, который вы получили, чтобы доказать, что можно сделать, и показать, что на самом деле происходит. Очень признателен. Мне нравятся подробные объяснения; они добавляют уверенность и дают верность ответам.

Ответы [ 5 ]

7 голосов
/ 18 марта 2011

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

  1. Поскольку SQL никогда не перестраивается с помощьюзначения параметров, такие как переводы строк и кавычки не нужно учитывать
  2. Имена параметров в комментариях фактически никогда не обрабатываются для их значения, а остаются как есть

С этимина месте, и принимая во внимание имена параметров, которые начинаются с одинаковых символов, таких как @NAME и @NAME_FULL, мы можем заменить все имена параметров значением, которое будет находиться на месте этого параметра:

string query = cmd.CommandText;
foreach (SqlParameter p in cmd.Parameters.OrderByDescending(p => p.ParameterName.Length))
{
    query = query.Replace(p.ParameterName, p.Value.ToString());
}

Однако с этим остается одна проблема: если параметр является строкой, SQL, который изначально выглядит так:

SELECT * FROM yourtable WHERE table_code = @CODE

будет выглядеть так:

SELECT * FROM yourtable WHERE table_code = SOME CODE WITH SPACES

Это явно недопустимый SQL, поэтому нам необходимо учесть и некоторые типы параметров:

DbType[] quotedParameterTypes = new DbType[] {
    DbType.AnsiString, DbType.Date,
    DbType.DateTime, DbType.Guid, DbType.String,
    DbType.AnsiStringFixedLength, DbType.StringFixedLength
};
string query = cmd.CommandText;

var arrParams = new SqlParameter[cmd.Parameters.Count];
cmd.Parameters.CopyTo(arrParams, 0);

foreach (SqlParameter p in arrParams.OrderByDescending(p => p.ParameterName.Length))
{
    string value = p.Value.ToString();
    if (quotedParameterTypes.Contains(p.DbType))
        value = "'" + value + "'";
    query = query.Replace(p.ParameterName, value);
}
6 голосов
/ 18 марта 2011

Здесь было несколько похожих вопросов.

Наиболее убедительный ответ был дан на этот вопрос: Как получить сгенерированный SQL-объект из SqlCommand-Object?

, и ответ был:

Вы не можете, потому что он не генерирует SQL.

Параметризованный запрос (тот, что в CommandText) отправляется на SQL Server как эквивалент подготовленного оператора.Когда вы выполняете команду, параметры и текст запроса обрабатываются отдельно.В любой момент времени полная строка SQL не генерируется.

Вы можете использовать SQL Profiler, чтобы заглянуть за кулисы.

1 голос
/ 18 марта 2011

Свойство CommandText (или вызов ToString()) в вашей команде выдаст вам все SQL, за небольшим исключением. Это определенно даст вам все, что вы видите в отладчике. Обратите внимание, что это не даст вам параметр значения , но даст действительную команду.

Единственное предостережение в том, что когда CommandType равно Text, среда ADO.NET часто (фактически, почти всегда) будет использовать sp_executesql для выполнения команды, а не выполнения команды непосредственно для соединения. В этом смысле невозможно получить точный SQL, который будет выполнен.

0 голосов
/ 05 марта 2018

Мне нравится ответ Иисуса Рамоса, но мне нужна была поддержка выходных параметров.(Я также использовал построитель строк для генерации содержимого.)

Объявление параметров для выходных параметров

 foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
    {
        // todo : I only needed a couple of types supported, you could add addition types
        string dbtype = string.Empty;
        switch (p.DbType)
        {
            case DbType.Guid:
                dbtype = "uniqueidentifier";
                break;
            case DbType.Int16:
            case DbType.Int64:
            case DbType.Int32:
                dbtype = "int";
                break;
            case DbType.String:
                dbtype = "varchar(max)";
                break;
        }

        query.Append(string.Format(" Declare {0}_ {1}\n", p.ParameterName, dbtype));
    }

Создание области основных параметров

foreach (SqlParameter p in arrParams)
    {
        bool isLast = p == last;
        string value = p.Value.ToString();
        if (quotedParameterTypes.Contains(p.DbType))
            value = "'" + value + "'";
        if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
        {
            query.Append(string.Format("{0} = {0}_ out{2}\n", p.ParameterName, value, isLast ? "" : ","));
        }
        else
        {
            query.Append(string.Format("{0} = {1}{2}\n", p.ParameterName, value, isLast ? "" : ","));
        }
    }

Результаты вывода списка параметров

foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
    {
        query.Append(string.Format(" select {0}_ {1}\n", p.ParameterName, p.ParameterName.Substring(1)));
    }

Полный код:

        public static string GetProcedureDebugInformation(SqlCommand cmd, [System.Runtime.CompilerServices.CallerMemberName] string caller = null, [System.Runtime.CompilerServices.CallerFilePath] string filePath = null, [System.Runtime.CompilerServices.CallerLineNumber] int? lineNumber = null)
    {
        // Collection of parameters that should use quotes
        DbType[] quotedParameterTypes = new DbType[] {
            DbType.AnsiString, DbType.Date,
            DbType.DateTime, DbType.Guid, DbType.String,
            DbType.AnsiStringFixedLength, DbType.StringFixedLength
        };

        // String builder to contain generated string
        StringBuilder query = new StringBuilder();

        // Build some debugging information using free compiler information
        query.Append(filePath != null ? filePath : ""
        + (lineNumber.HasValue ? lineNumber.Value.ToString() : "")
        + (lineNumber.HasValue || !string.IsNullOrWhiteSpace(filePath) ? "\n\n" : ""));
        query.Append("\n\n");

        var arrParams = new SqlParameter[cmd.Parameters.Count];
        cmd.Parameters.CopyTo(arrParams, 0);

        // Declare Parameter for output parameters
        foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
        {
            // todo : I only needed a couple of types supported, you could add addition types
            string dbtype = string.Empty;
            switch (p.DbType)
            {
                case DbType.Guid:
                    dbtype = "uniqueidentifier";
                    break;
                case DbType.Int16:
                case DbType.Int64:
                case DbType.Int32:
                    dbtype = "int";
                    break;
                case DbType.String:
                    dbtype = "varchar(max)";
                    break;
            }

            query.Append(string.Format(" Declare {0}_ {1}\n", p.ParameterName, dbtype));
        }

        // Set Exec Text
        query.Append(string.Format("\n exec {0}\n", cmd.CommandText));
        var last = arrParams.LastOrDefault();

        //Build Main Parameter Area
        foreach (SqlParameter p in arrParams.OrderByDescending(p => p.ParameterName.Length))
        {
            bool isLast = p == last;
            string value = p.Value.ToString();
            if (quotedParameterTypes.Contains(p.DbType))
                value = "'" + value + "'";
            if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
            {
                query.Append(string.Format("{0} = {0}_ out{2}\n", p.ParameterName, value, isLast ? "" : ","));
            }
            else
            {
                query.Append(string.Format("{0} = {1}{2}\n", p.ParameterName, value, isLast ? "" : ","));
            }
        }

        // List Output Parameter results
        foreach (SqlParameter p in arrParams.Where(x => x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput))
        {
            query.Append(string.Format(" select {0}_ {1}\n", p.ParameterName, p.ParameterName.Substring(1)));
        }

        return query.ToString();
    }
0 голосов
/ 28 октября 2014

Я не пробовал этого, но вы можете использовать режим захвата, если вы хотите использовать SMO:

http://msdn.microsoft.com/en-us/library/ms162182(v=sql.120).aspx

...