有没有一种简单的方法来获取 .NET 为参数化查询生成的 "sp_executesql" 查询?
Is there a easy way to get the "sp_executesql" query .NET generates for a parametrized query?
背景:
如果我有下面的程序
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
会 return 字符串
"exec sp_executesql N'UPDATE Foo set Bar = @Text',N'@Text varchar(50)',@Text='Hello World!'"
我有能力编写一个解析器来遍历 Parameters
集合中的每个参数并构建字符串。然而,在我开始从头开始编写这个解析器之前,.NET 中是否有一些 class 或函数已经执行了我忽略的这个操作?
如果我可以访问参数的 MetaType,编写解析器将非常容易,但我觉得在生产应用程序中使用反射访问未发布的内部 API' .NET 框架的 s。
目前,这里没有任何内容。命令对象将参数化文本和所有参数发送到 SQL 服务器,然后服务器使用 sp_executesql 存储过程将它们结合起来。 SQL Server .NET 对象中没有使用参数解析查询的任何内容,因此您无法在 SQL Server 上提取 运行。
即使在 SQL 服务器中,您也有像 sp_prepare 这样的命令,它会准备 SQL 查询,但不会 return 文本。相反,它 return 是已编译查询的句柄,带有参数。我想,通过一些调查,您可以找到编译查询的位置,但是使用 SQL 服务器为您完成此类工作效率不高。只有当您可以取回已编译的查询并恢复到该语句时才会这样。
在旧版本的 SQL 服务器中,您可以使用 sp_helptext 从系统存储过程中提取文本,但它不再起作用。它可以向您展示他们是如何做到的,但这并不比构建您自己的解析器更好。
尝试从 SQL 和参数创建字符串会使初始的良好查询方法变坏。
sp_executesql 在 SQL 服务器上创建参数化查询,如果重复调用具有相同的 SQL 字符串和签名(但可能有不同的参数值),则重新使用查询计划.带有sp_executesql的SQL Profiler输出实际上是这样发送的;它可以在 SSMS 中复制和执行。将参数值连接到 SQL 字符串中将为每次调用创建一个新的查询和查询计划,就好像它在开始时就已连接一样(包括性能损失和 SQL 注入风险)。
sp_prepare 和 ADO.NET 中的 DbCommand.Prepare() 在我看来已经过时了,因为应用程序必须保留查询的句柄并且只能在有限的范围内使用它作用域(连接),而 sp_executesql 在 SQL 和签名字符串(参数名称和类型)相等时重新使用查询计划,无论应用程序如何获取它们。
Gregory 的回答有一点点正确,但大部分都不正确。是的,没有 public
方法可以调用来获取它,但是有 private
一个(你不能调用)确实重新打包了 CommandText
和 SqlParameterCollection
作为对 sp_executesql
的存储过程调用,使用预先格式化的参数名称和数据类型列表作为该存储过程的第二个输入参数(请参阅下面关于 BuildParamList
的注释)。
虽然这是微软的源代码,但该代码也是主要在 MIT license 下发布的开源 .NET Core 项目的一部分。也就是说,您可以复制并粘贴您需要的部分 :-)。即使代码仅在 referencesource.microsoft.com 上,您仍然可以从中了解您需要的内容,并使用它来验证您的版本在功能上是否与其一致。
- Microsoft.com 上的原始代码位于:http://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,5400
- .NET Core 版本在 GitHub.com 上:https://github.com/dotnet/corefx/blob/master/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs#L2820
看来您主要需要的是 BuildParamList
方法(当然还有它调用的任何方法):
背景:
如果我有下面的程序
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
会 return 字符串
"exec sp_executesql N'UPDATE Foo set Bar = @Text',N'@Text varchar(50)',@Text='Hello World!'"
我有能力编写一个解析器来遍历 Parameters
集合中的每个参数并构建字符串。然而,在我开始从头开始编写这个解析器之前,.NET 中是否有一些 class 或函数已经执行了我忽略的这个操作?
如果我可以访问参数的 MetaType,编写解析器将非常容易,但我觉得在生产应用程序中使用反射访问未发布的内部 API' .NET 框架的 s。
目前,这里没有任何内容。命令对象将参数化文本和所有参数发送到 SQL 服务器,然后服务器使用 sp_executesql 存储过程将它们结合起来。 SQL Server .NET 对象中没有使用参数解析查询的任何内容,因此您无法在 SQL Server 上提取 运行。
即使在 SQL 服务器中,您也有像 sp_prepare 这样的命令,它会准备 SQL 查询,但不会 return 文本。相反,它 return 是已编译查询的句柄,带有参数。我想,通过一些调查,您可以找到编译查询的位置,但是使用 SQL 服务器为您完成此类工作效率不高。只有当您可以取回已编译的查询并恢复到该语句时才会这样。
在旧版本的 SQL 服务器中,您可以使用 sp_helptext 从系统存储过程中提取文本,但它不再起作用。它可以向您展示他们是如何做到的,但这并不比构建您自己的解析器更好。
尝试从 SQL 和参数创建字符串会使初始的良好查询方法变坏。
sp_executesql 在 SQL 服务器上创建参数化查询,如果重复调用具有相同的 SQL 字符串和签名(但可能有不同的参数值),则重新使用查询计划.带有sp_executesql的SQL Profiler输出实际上是这样发送的;它可以在 SSMS 中复制和执行。将参数值连接到 SQL 字符串中将为每次调用创建一个新的查询和查询计划,就好像它在开始时就已连接一样(包括性能损失和 SQL 注入风险)。
sp_prepare 和 ADO.NET 中的 DbCommand.Prepare() 在我看来已经过时了,因为应用程序必须保留查询的句柄并且只能在有限的范围内使用它作用域(连接),而 sp_executesql 在 SQL 和签名字符串(参数名称和类型)相等时重新使用查询计划,无论应用程序如何获取它们。
Gregory 的回答有一点点正确,但大部分都不正确。是的,没有 public
方法可以调用来获取它,但是有 private
一个(你不能调用)确实重新打包了 CommandText
和 SqlParameterCollection
作为对 sp_executesql
的存储过程调用,使用预先格式化的参数名称和数据类型列表作为该存储过程的第二个输入参数(请参阅下面关于 BuildParamList
的注释)。
虽然这是微软的源代码,但该代码也是主要在 MIT license 下发布的开源 .NET Core 项目的一部分。也就是说,您可以复制并粘贴您需要的部分 :-)。即使代码仅在 referencesource.microsoft.com 上,您仍然可以从中了解您需要的内容,并使用它来验证您的版本在功能上是否与其一致。
- Microsoft.com 上的原始代码位于:http://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,5400
- .NET Core 版本在 GitHub.com 上:https://github.com/dotnet/corefx/blob/master/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs#L2820
看来您主要需要的是 BuildParamList
方法(当然还有它调用的任何方法):