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

时间:2008-08-29 01:31:28

标签: c# sql-server ado.net

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

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

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

约书亚

8 个答案:

答案 0 :(得分:15)

您需要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";

   // ....
}

答案 1 :(得分:9)

您可以使用SqlCommandBuilder.DeriveParameters()(请参阅SqlCommandBuilder.DeriveParameters - Get Parameter Information for a Stored Procedure - ADO.NET Tutorials)或this way,这不是那么优雅。

答案 2 :(得分:5)

虽然它不完全是你想要的,但是这里有一些示例代码,它使用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();
                }
            }
        }
    }

答案 3 :(得分:2)

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

答案 4 :(得分:2)

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

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

编辑:你太快了!!

答案 5 :(得分:1)

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

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

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

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

答案 6 :(得分:0)

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

的辅助函数
Select count(*) from information_schema.parameters 
where ...(proc name =.. param name=...) (pseudo-code)

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

答案 7 :(得分:0)

我从几年前就开始使用带有.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相关

有什么想法吗?