Как я могу получить список параметров из хранимой процедуры в SQL Server - PullRequest
15 голосов
/ 29 августа 2008

Используя C # и System.Data.SqlClient, есть ли способ получить список параметров, которые принадлежат хранимой процедуре на SQL Server, прежде чем я фактически ее выполню?

У меня есть сценарий «нескольких сред», когда существует несколько версий одной и той же схемы базы данных. Примерами сред могут быть «Разработка», «Подготовка» и «Производство». У «Development» будет одна версия хранимой процедуры, а у «Staging» будет другая.

Все, что я хочу сделать, это проверить, что параметр будет там, перед передачей ему значения и вызовом хранимой процедуры. Избегать этого SqlException вместо того, чтобы ловить его, - плюс для меня.

Джошуа

Ответы [ 8 ]

15 голосов
/ 29 августа 2008

Требуется метод 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";

   // ....
}
9 голосов
/ 29 августа 2008

Вы можете использовать SqlCommandBuilder.DeriveParameters () (см. SqlCommandBuilder.DeriveParameters - Получить информацию о параметрах для хранимой процедуры - ADO.NET Tutorials ) или есть таким образом , который не как элегантно.

5 голосов
/ 29 августа 2008

Хотя это не совсем то, что вам нужно, вот пример кода, который использует метод 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();
                }
            }
        }
    }
2 голосов
/ 29 августа 2008

Вы можете использовать объект SqlCommandBuilder и вызвать метод DeriveParameters.

Обычно вам нужно передать ему команду, которая настроена для вызова вашего хранимого процесса, и он попадет в БД, чтобы обнаружить параметры, и создать соответствующие параметры в свойстве Parameters SqlCommand

РЕДАКТИРОВАТЬ: Ты слишком быстро !!

2 голосов
/ 29 августа 2008
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
1 голос
/ 29 августа 2008

Марк имеет лучшую реализацию DeriveParameters. Как он сказал, убедитесь, что вы кешируете как в этом уроке .

Тем не менее, я думаю, что это опасный способ решения исходной проблемы версионирования sproc базы данных. Если вы собираетесь изменить подпись процедуры, добавив или удалив параметры, вам необходимо выполнить одно из следующих действий:

  • Кодирование обратно совместимым способом, используя значения по умолчанию (для новых параметров) или просто игнорируя параметр (для удаленных параметров). Это гарантирует, что ваш клиентский код всегда может вызывать любую версию вашей хранимой процедуры.
  • Явно создайте версию процедуры по имени (поэтому у вас будут my_proc и my_proc_v2). Это гарантирует, что ваш клиентский код и sprocs будут синхронизированы.

Опираясь на DeriveParameters, чтобы проверить, какую версию sproc вы используете, кажется, не тот инструмент для работы, ИМХО.

0 голосов
/ 01 октября 2009

Пару лет назад я использую DeriveParameters с .NET 1.1 и 2.0, и каждый раз работал как чары.

Сейчас я работаю над своим первым заданием с .NET 3.5, и только что обнаружил и ужасный сюрприз: DeriveParameters создает все параметры с помощью SqlDbType «Variant», а не с собственными SqlDbTypes. Это создает исключение SqlException при попытке выполнить SP с числовыми параметрами, поскольку SQL Server 2005 говорит, что sql-вариантные типы нельзя неявно преобразовать в значения типа int (или smallint, или numeric).

Я только что протестировал один и тот же код с .NET CF 2.0 и SQL Server 2000 и работал, как и ожидалось, назначая правильный SqlDbType для каждого параметра.

Я тестировал приложения .NET 2.0 на базе данных SQL Server 2005, поэтому проблема не связана с SQL Server, поэтому она должна быть связана с .NET 3.5

Есть идеи?

0 голосов
/ 23 января 2009

Все эти решения ADO.NET просят библиотеку кода запросить метаданные базы данных от вашего имени. Если вы все-таки понесете это снижение производительности, возможно, вам стоит написать несколько вспомогательных функций, которые вызывают

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

Или, может быть, даже сгенерировать ваши параметры на основе списка параметров, который вы получите обратно. Этот метод будет работать с несколькими версиями MS SQL и иногда с другими базами данных ANSI SQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...