Ответ 1
Вы можете использовать SqlCommandBuilder.DeriveParameters() (см. SqlCommandBuilder.DeriveParameters - Получить информацию о параметрах для хранимой процедуры - Учебники ADO.NET) или там этот путь, который не так изящен.
Используя С# и System.Data.SqlClient, есть способ получить список параметров, которые принадлежат хранимой процедуре на SQL Server, прежде чем я ее действительно выполнил?
У меня есть сценарий "multi-environment", где есть несколько версий одной и той же схемы базы данных. Примерами сред могут быть "Разработка", "Стадия" и "Производство". "Разработка" будет иметь одну версию хранимой процедуры, а "Staging" будет иметь другую.
Все, что я хочу сделать, это проверить, будет ли параметр присутствовать перед передачей ему значения и вызовом хранимой процедуры. Избегая того, что исключение SqlException вместо того, чтобы его поймать, является плюсом для меня.
Joshua
Вы можете использовать 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(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
Вы можете использовать объект SqlCommandBuilder и вызвать метод DeriveParameters.
В основном вам нужно передать ему команду, которая предназначена для вызова сохраненного proc, и она попадет в БД для обнаружения параметров и создаст соответствующие параметры в свойстве Parameters для SqlCommand
EDIT: вы слишком быстро!
Mark имеет лучшую реализацию DeriveParameters. По его словам, убедитесь, что вы кешируете, как в этот учебник.
Однако, я думаю, что это опасный способ решения вашей исходной проблемы управления версиями базы данных sproc. Если вы собираетесь изменить подпись процедуры, добавив или удалив параметры, вы должны сделать одно из следующих действий:
Опираясь на DeriveParameters, чтобы проверить, какая версия sproc, которую вы используете, кажется неправильным инструментом для работы, IMHO.
Все эти решения ADO.NET обращаются к библиотеке кода с запросом на метаданные базы данных от вашего имени. Если вы так или иначе столкнетесь с этим успехом, возможно, вам стоит просто написать некоторые вспомогательные функции, которые вызывают
Select count(*) from information_schema.parameters
where ...(proc name =.. param name=...) (pseudo-code)
Или, возможно, даже сгенерировать ваши параметры на основе списка параметров, который вы вернетесь. Этот метод будет работать с несколькими версиями MS SQL, а иногда и с другими базами данных ANSI SQL.
Я использую DeriveParameters с .NET 1.1 и 2.0 через пару лет и каждый раз работал как шарм.
Теперь я работаю над своим первым назначением с .NET 3.5 и просто нашел и уродливый сюрприз: DeriveParameters создает все параметры с помощью SqlDbType "Вариант", вместо этого правильные 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
Любые идеи?