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

Posted

技术标签:

【中文标题】如何从 SQL Server 中的存储过程中检索参数列表【英文标题】:How can I retrieve a list of parameters from a stored procedure in SQL Server 【发布时间】:2010-09-07 05:16:29 【问题描述】:

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

我有一个“多环境”场景,其中存在同一个数据库架构的多个版本。环境的示例可能是“开发”、“暂存”和“生产”。 “开发”将有一个版本的存储过程,而“暂存”将有另一个版本。

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

约书亚

【问题讨论】:

【参考方案1】:

您可以使用 SqlCommandBuilder.DeriveParameters()(请参阅 SqlCommandBuilder.DeriveParameters - Get Parameter Information for a Stored Procedure - ADO.NET Tutorials)或者使用不太优雅的 this way。

【讨论】:

David Hayden 网站的链接(第一个链接)不存在,而且似乎不是从他的旧博客迁移过来的。【参考方案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

【讨论】:

【参考方案3】:

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

现在我正在使用 .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 相关

有什么想法吗?

【讨论】:

刚刚发现这个问题是与 CompactFramework 相关的问题。刚刚在 WindowsForms 应用程序中运行测试并按预期工作,但从 SmartDEvice 项目运行完全相同的代码,将所有参数返回为“变体”。 :(【参考方案4】:

所有这些 ADO.NET 解决方案都要求代码库代表您查询数据库的元数据。如果你无论如何都要对性能造成影响,也许你应该编写一些调用

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

或者甚至可以根据您返回的参数列表生成您的参数。这种技术适用于多个版本的 MS SQL,有时也适用于其他 ANSI SQL 数据库。

【讨论】:

【参考方案5】:

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

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

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

依靠 DeriveParameters 来验证您使用的存储过程的版本似乎是错误的工具,恕我直言。

【讨论】:

【参考方案6】:

虽然它并不完全符合您的要求,但这里有一些示例代码,它使用 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();
                
            
        
    

【讨论】:

【参考方案7】:

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

   // ....

【讨论】:

【参考方案8】:

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

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

编辑:你太快了!!

【讨论】:

以上是关于如何从 SQL Server 中的存储过程中检索参数列表的主要内容,如果未能解决你的问题,请参考以下文章

如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?

从 SQL Server 中检索各种存储过程的数据的单一方法,如何处理参数?

使用 C# 和存储过程从 SQL Server 检索 VarChar(MAX)

如何在 SQL Server 2008 中检索已删除的存储过程、函数、表

如何从 SQL Server 存储过程中的 3 列中获取 MAX 值?

如何从 SQL Server Management Studio 中的表生成 CRUD 存储过程