是否有一种简单的方法来获取.NET为参数化查询生成的“sp_executesql”查询?

背景:

如果我有以下程序

public class Program { public static void Main() { using(var connection = new SqlConnection("Server=(local);Database=Testing;Trusted_Connection=True")) using (var command = connection.CreateCommand()) { connection.Open(); command.CommandText = "UPDATE Foo set Bar = @Text"; command.Parameters.Add("@Text", SqlDbType.VarChar, 50).Value = "Hello World!"; command.ExecuteNonQuery(); } } } 

执行时,运行以下查询(根据SQL Server Profiler)

 exec sp_executesql N'UPDATE Foo set Bar = @Text',N'@Text varchar(50)',@Text='Hello World!' 

我的问题:

我想要做的是,如果我有以下

 command.CommandText = "UPDATE Foo set Bar = @Text"; command.Parameters.Add("@Text", SqlDbType.VarChar, 50).Value = "Hello World!"; string query = GenerateQuery(command); 

GenerateQuery将返回字符串

 "exec sp_executesql N'UPDATE Foo set Bar = @Text',N'@Text varchar(50)',@Text='Hello World!'" 

我能够编写一个解析器来遍历Parameters集合中的每个参数并构建字符串。 但是,在我从头开始编写这个解析器之前,.NET中是否有一些类或函数已经执行了我忽略的操作?

如果我可以访问参数的MetaType ,那么解析器就非常容易了,但我觉得使用生成应用程序中的reflection来访问.NET框架的未发布的内部API是不可能的。

格雷戈里的答案有点正确,但大多不正确。 是的,没有public方法可以调用来获取它,但是有一个private (你不能调用)确实重新打包CommandTextSqlParameterCollection作为存储过程调用sp_executesql与预格式化的参数列表name和datatypes作为该存储过程的第二个输入参数(请参阅下面有关BuildParamList的注释)。

虽然这是Microsoft源代码,但代码也是开源.NET Core项目的一部分,该项目主要是在MIT许可下发布的。 意思是,您可以复制并粘贴您需要的部分:-)。 即使代码仅在referencesource.microsoft.com上,您仍然可以从中了解您的需求,并使用它来validation您的版本是否与其function一致。

  • Microsoft.com上的原始代码: http : //referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,5400
  • 它的.NET核心版本在GitHub.com上: https : //github.com/dotnet/corefx/blob/master/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs#L2820

看起来你需要的主要是BuildParamList方法(当然,无论它调用什么):

目前,这里什么都没有。 命令对象将参数化文本和所有参数发送到SQL Server,然后SQL Server使用sp_executesql存储过程将它们结合在一起。 SQL Server .NET对象中没有任何内容可以使用参数解析查询,因此您无法提取在SQL Server上运行的内容。

即使在SQL Server中,也有像sp_prepare这样的命令,它们将准备SQL查询,但它不会返回文本。 相反,它返回带有参数的已编译查询的句柄。 我想,通过一些调查,您可以找到编译查询的位置,但使用SQL Server为您执行此类工作效率不高。 这只是因为你可以找回已编译的查询并恢复到语句。

在旧版本的SQL Server中,您可以使用sp_helptext从系统sprocs中提取文本,但它不再起作用。 它可以告诉你他们是如何做到的,但它不会比构建你自己的解析器更好。

尝试从SQL和参数创建字符串会使初始的,良好的查询方法变坏。

sp_executesql在SQL Server上创建参数化查询,如果重复调用具有相同的SQL字符串和签名(但可能是不同的参数值),则重新使用查询计划。 带有sp_executesql的SQL事件探查器输出实际上是这样发送的; 它可以在SSMS中复制和执行。 将参数值连接到SQL字符串中将为每个调用创建一个新的查询和查询计划,就像它在开始时已连接一样(包括性能损失和SQL注入风险)。

在我看来,sp_prepare和ADO.NET中的DbCommand.Prepare()已经过时,因为应用程序必须保持查询的句柄,并且只能在有限的范围内使用它(连接),而sp_executesql每次都重用查询计划无论应用程序如何获取它们,SQL和签名字符串(参数名称和类型)都是相同的。