从.NET环境中,我可以访问由SqlCommand对象生成的完整SQL字符串吗?
注意:在调试模式下,完整的SQL字符串显示在VisualStudio中的Intellisense悬停中。
如果必须,我愿意使用反射技术。我相信这里有人知道如何做到这一点。
更新1:
我正在调用一个带有cmd.CommandType = CommandType.StoredProcedure
参数的存储过程,并且我正在尝试获取生成并运行的完整SQL。我想知道cmd.Prepare()方法在这种情况下是否有用,如果它可能将完整的字符串存储在状态字段或类似的东西中。
更新2:
根据下面的答案(和引用的)表明在准备或执行期间没有内部生成完整的SQL字符串,我做了一些使用.NET Reflector。即使是内部连接类似乎也会传递对象而不是将它们归结为字符串,例如:
内部抽象void AddPreparedCommand(SqlCommand cmd); 声明类型:System.Data.SqlClient.SqlInternalConnection 汇编:System.Data,Version = 2.0.0.0
总的来说,感谢每个人的细节水平,以证明可以做什么,并展示实际发生的事情。非常感激。我喜欢彻底的解释;他们增加了保证,并为答案提供了信任。
一个简单的循环用它们的值替换所有参数名称将为您提供类似于最终结果的东西,但是有几个问题。
有了那些,并考虑到以相同的字符开头的参数名称,如@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);
}
这里有几个类似的问题。
这个问题提供了最引人注目的答案:How to get the generated SQL-Statment from a SqlCommand-Object?
答案是:
你不能,因为它不会生成任何SQL。
参数化查询(CommandText中的查询)作为预准备语句的等效项发送到SQL Server。执行命令时,将分别处理参数和查询文本。在任何时候都不会生成完整的SQL字符串。
您可以使用SQL事件探查器来了解幕后。
命令中的CommandText
属性(或调用ToString()
)将为您提供所有SQL,但有一个小例外。它肯定会给你在调试器中看到的任何东西。请注意,这不会为您提供参数值,但它会为您提供实际命令。
唯一需要注意的是,当CommandType
是Text
时,ADO.NET框架通常(事实上,几乎总是)使用sp_executesql
来执行命令,而不是直接对连接执行命令。从这个意义上说,不可能获得执行的确切SQL。
我没试过这个,但是如果你愿意使用SMO,你可以使用捕获模式:
http://msdn.microsoft.com/en-us/library/ms162182(v=sql.120).aspx
我喜欢Jesus Ramos的答案,但我需要支持输出参数。 (我还使用了字符串构建器来生成内容。)
声明输出参数的参数
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();
}