如何从SQL Server中的存储过程检索参数列表

使用C#和System.Data.SqlClient,有没有办法在实际执行之前检索属于SQL Server上的存储过程的参数列表?

我有一个“多环境”场景,其中有相同数据库架构的多个版本。 环境的示例可以是“开发”,“分段”和“生产”。 “开发”将有一个版本的存储过程,“Staging”将有另一个版本。

我想要做的就是在传递一个值并调用存储过程之前validation参数是否存在。 避免使用SqlException而不是必须捕获它对我来说是一个加分。

约书亚

您可以使用SqlCommandBuilder.DeriveParameters()(请参阅SqlCommandBuilder.DeriveParameters – 获取存储过程的参数信息 – ADO.NET教程 )或者这种方式不是那么优雅。

您需要SqlCommandBuilder.DeriveParameters(SqlCommand)方法。 请注意,它需要额外的数据库往返,因此它的性能有点显着。 您应该考虑缓存结果。

一个示例电话:

using (SqlConnection conn = new SqlConnection(CONNSTRING)) using (SqlCommand cmd = new SqlCommand("StoredProc", conn)) { cmd.CommandType = CommandType.StoredProcedure; SqlCommandBuilder.DeriveParameters(cmd); cmd.Parameters["param1"].Value = "12345"; // .... } 

虽然它不完全是您想要的,但这里有一些示例代码,它使用SqlConnection.GetSchema()方法返回与数据库关联的所有存储过程,然后返回每个存储过程的所有参数名称和类型。 下面的示例只是将其加载到变量中。 请注意,这也会返回所有“系统”存储过程,这可能是不可取的。

史蒂夫

  public void LoadProcedureInfo() { SqlConnection connection = new SqlConnection(); ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["ConnectionString"]; connection.ConnectionString = settings.ConnectionString; connection.Open(); DataTable procedureDataTable = connection.GetSchema("Procedures"); DataColumn procedureDataColumn = procedureDataTable.Columns["ROUTINE_NAME"]; if (procedureDataColumn != null) { foreach (DataRow row in procedureDataTable.Rows) { String procedureName = row[procedureDataColumn].ToString(); DataTable parmsDataTable = connection.GetSchema("ProcedureParameters", new string[] { null, null, procedureName }); DataColumn parmNameDataColumn = parmsDataTable.Columns["PARAMETER_NAME"]; DataColumn parmTypeDataColumn = parmsDataTable.Columns["DATA_TYPE"]; foreach (DataRow parmRow in parmsDataTable.Rows) { string parmName = parmRow[parmNameDataColumn].ToString(); string parmType = parmRow[parmTypeDataColumn].ToString(); } } } } 

您可以使用SqlCommandBuilder对象,并调用DeriveParameters方法。

基本上你需要传递一个命令,设置为调用你的存储过程,它将命中数据库来发现参数,并在SqlCommand的Parameters属性中创建适当的参数

编辑:你太快了!!

 SqlCommandBuilder.DeriveParameters(command) 

这句话做了我需要的。

以下是我解决此问题的完整代码示例。

 Public Sub GetLogEntriesForApplication(ByVal settings As FilterSettings, Optional ByVal RowGovernor As Integer = -1) Dim command As New SqlCommand("GetApplicationActions", New SqlConnection(m_environment.LoggingDatabaseConnectionString)) Dim adapter As New SqlDataAdapter(command) Using command.Connection With command .Connection.Open() .CommandType = CommandType.StoredProcedure SqlCommandBuilder.DeriveParameters(command) With .Parameters If settings.FilterOnLoggingLevel Then If .Contains("@loggingLevel") Then .Item("@loggingLevel").Value = settings.LoggingLevel End If End If If settings.FilterOnApplicationID Then If .Contains("@applicationID") Then .Item("@applicationID").Value = settings.ApplicationID End If End If If settings.FilterOnCreatedDate Then If .Contains("@startDate") Then .Item("@startDate").Value = settings.CreatedDate.Ticks End If End If If settings.FilterOnEndDate Then If .Contains("@endDate") Then .Item("@endDate").Value = settings.EndDate.Ticks End If End If If settings.FilterOnSuccess Then If .Contains("@success") Then .Item("@success").Value = settings.Success End If End If If settings.FilterOnProcess Then If settings.Process > -1 Then If .Contains("@process") Then .Item("@process").Value = settings.Process End If End If End If If RowGovernor > -1 Then If .Contains("@topRows") Then .Item("@topRows").Value = RowGovernor End If End If End With End With adapter.TableMappings.Clear() adapter.TableMappings.Add("Table", "ApplicationActions") adapter.TableMappings.Add("Table1", "Milestones") LogEntries.Clear() Milestones.Clear() adapter.Fill(m_logEntryData) End Using End Sub 

Mark拥有DeriveParameters的最佳实现。 正如他所说,请确保像本教程中一样缓存。

但是,我认为这是解决数据库sproc版本化原始问题的危险方法。 如果要通过添加或删除参数来更改过程的签名,则应执行以下操作之一:

  • 通过使用默认值(对于新的参数)或通过简单地忽略参数(对于已删除的参数)以向后兼容的方式编码。 这可确保您的客户端代码始终可以调用任何版本的存储过程。
  • 按名称显式地对过程进行版本化(因此您将拥有my_proc和my_proc_v2)。 这可确保您的客户端代码和sprocs保持同步。

依靠DeriveParameters来validation你正在使用的sproc的哪个版本似乎是错误的工具,恕我直言。

所有这些ADO.NET解决方案都要求代码库代表您查询数据库的元数据。 如果你打算在任何时候获得性能,也许你应该只编写一些调用的辅助函数

 Select count(*) from information_schema.parameters where ...(proc name =.. param name=...) (pseudo-code) 

或者甚至可以根据您获得的参数列表生成参数。 此技术适用于多个版本的MS SQL,有时还适用于其他ANSI SQL数据库。

几年以来,我一直在使用带有.NET 1.1和2.0的DeriveParameters,并且每次都像魅力一样工作。

现在我正在使用.NET 3.5进行我的第一次任务,并且发现并且令人惊讶:DeriveParameters使用SqlDbType“Variant”创建所有参数,而不是正确的SqlDbTypes。 这在尝试使用数字参数执行SP时会创建SqlException,因为SQL Server 2005说sql-variant类型不能被隐式转换为int(或smallint或numeric)值。

我刚刚使用.NET CF 2.0和SQL Server 2000测试了相同的代码,并按预期工作,为每个参数分配正确的SqlDbType。

我已经针对SQL Server 2005数据库测试了.NET 2.0应用程序,因此不是与SQL Server相关的问题,所以它必须与.NET 3.5相关

有任何想法吗?