我可以从 .NET 获取 SqlCommand 对象(带有 SQL 参数)生成的完整 SQL 字符串吗?

发布于 2024-10-22 18:17:32 字数 1014 浏览 2 评论 0原文

从 .NET 环境中,我可以访问由 SqlCommand 对象?

注意:在调试模式下,完整的 SQL 字符串会显示在 VisualStudio 中的 Intellisense 悬停中。

如果必须的话,我愿意使用反射技术。我确信这里有人知道一种方法来实现它。


更新 1
我正在调用一个带有 cmd.CommandType = CommandType.StoredProcedure 参数的存储过程,并尝试获取生成并运行的完整 SQL。 我想知道 cmd 是否。Prepare() 方法可能将完整字符串存储在状态字段或类似字段中,则在这种情况下可能没有用处。


更新 2:

根据下面(和引用的)答案表明在准备或执行期间内部没有生成完整的 SQL 字符串,我使用 .NET Reflector 进行了一些探索。甚至内部连接类似乎也传递对象而不是将它们简化为字符串,例如:

internal abstract void AddPreparedCommand(SqlCommand cmd);
声明类型:System.Data.SqlClient.SqlInternalConnection
程序集:System.Data,版本=2.0.0.0


总的来说,感谢大家提供的详细信息,以证明可以做什么并显示实际发生的情况。非常感谢。我喜欢详尽的解释;它们增加了保证并为答案提供了可信度。

From the .NET environment can I get access to the full SQL string that is generated by a SqlCommand object?

Note: The full SQL string shows up in Intellisense hover, in VisualStudio, while in debug mode.

I'm willing to use reflection techniques if I must. I'm sure somebody here knows a way to get at it.


Update 1:
I'm calling a stored procedure having parameters with cmd.CommandType = CommandType.StoredProcedure and am trying to acquire the full SQL generated and run.
I wonder if the cmd.Prepare() method might not prove useful in this circumstance, if it might store the full string in a state field or something like that.


Update 2:

In light of answers below (and referenced) that indicate no complete SQL string is generated internally during preparation or execution, I did a bit of poking around using .NET Reflector. Even the internal connection classes seem to pass objects rather than boiling them down to strings, for example:

internal abstract void AddPreparedCommand(SqlCommand cmd);
Declaring Type: System.Data.SqlClient.SqlInternalConnection
Assembly: System.Data, Version=2.0.0.0


In general, thanks to everybody for the level of detail you got into to prove what can be done and show what's actually happening. Much appreciated. I like thorough explanations; they add surety and lend credence to the answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

用心笑 2024-10-29 18:17:32

一个简单的循环将所有参数名称替换为它们的值将为您提供类似于最终结果的结果,但存在几个问题。

  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);
}

A simple loop replacing all the parameter names with their values will provide you with something similar to what the end result is, but there are several problems.

  1. Since the SQL is never actually rebuilt using the parameter values, things like newlines and quotes don't need to be considered
  2. Parameter names in comments are never actually processed for their value, but left as-is

With those in place, and taking into account parameter names that starts with the same characters, like @NAME and @NAME_FULL, we can replace all the parameter names with the value that would be in the place of that parameter:

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

there is one problem left with this, however, and that is if a parameter is a string, then the SQL that initially looks like this:

SELECT * FROM yourtable WHERE table_code = @CODE

will look like this:

SELECT * FROM yourtable WHERE table_code = SOME CODE WITH SPACES

This is clearly not legal SQL, so we need to account for some parameter-types as well:

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);
}
兮颜 2024-10-29 18:17:32

这里有几个类似的问题。

针对这个问题提供了最令人信服的答案:如何从 SqlCommand-Object 获取生成的 SQL-Statment?

答案是:

你不能,因为它没有
生成任何 SQL。

参数化查询(
CommandText) 发送到 SQL Server
相当于准备好的
陈述。当您执行
命令、参数和查询
文本单独处理。在没有
完整的 SQL 字符串的时间点是
生成。

您可以使用 SQL Profiler 来获取
看看幕后。

There have been a couple of similar questions here.

The most compelling answer was provided to this question: How to get the generated SQL-Statment from a SqlCommand-Object?

and the answer was:

You can't, because it does not
generate any SQL.

The parameterized query (the one in
CommandText) is sent to the SQL Server
as the equivalent of a prepared
statement. When you execute the
command, the parameters and the query
text are treated separately. At no
point in time a complete SQL string is
generated.

You can use SQL Profiler to take a
look behind the scenes.

笑饮青盏花 2024-10-29 18:17:32

命令上的 CommandText 属性(或调用 ToString())将为您提供所有 SQL,但有一个小例外。它肯定会给您在调试器中看到的任何内容。请注意,这不会为您提供参数,但它会为您提供实际的命令。

唯一需要注意的是,当 CommandType 为 Text 时,ADO.NET 框架经常(事实上,几乎总是)使用 sp_executesql 来执行命令而不是直接针对连接执行命令。从这个意义上说,不可能获得执行的准确 SQL。

The CommandText property (or calling ToString()) on your command will give you all of the SQL, with a small exception. It will definitely give you anything you see in the debugger. Note that this won't give you parameter values, but it will give you the actual command.

The only caveat is that when CommandType is Text, the ADO.NET framework will often (in fact, almost always) use sp_executesql to execute the command rather than executing the command directly against the connection. In that sense, it's not possible to obtain the exact SQL that gets executed.

你列表最软的妹 2024-10-29 18:17:32

我还没有尝试过这个,但如果您愿意使用 SMO,您也许可以使用捕获模式:

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

I haven't tried this, but you may be able to use Capture Mode if you are willing to use SMO:

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

眼眸 2024-10-29 18:17:32

我喜欢耶稣拉莫斯的回答,但我需要输出参数的支持。 (我还使用了字符串生成器来生成内容。)

声明输出参数的参数

 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();
    }

I like Jesus Ramos answer, but I needed support for output parameters. (I also used a string builder to generate the content.)

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));
    }

Build Main Parameter Area

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 ? "" : ","));
        }
    }

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)));
    }

Full Code:

        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();
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文