Wie kann ich eine Liste von Parametern aus einer gespeicherten Prozedur in SQL Server abrufen?
-
09-06-2019 - |
Frage
Gibt es mit C# und System.Data.SqlClient eine Möglichkeit, eine Liste von Parametern abzurufen, die zu einer gespeicherten Prozedur auf einem SQL Server gehören, bevor ich sie tatsächlich ausführe?
Ich habe ein Szenario mit mehreren Umgebungen, in dem es mehrere Versionen desselben Datenbankschemas gibt.Beispiele für Umgebungen könnten „Entwicklung“, „Staging“ und „Produktion“ sein.„Entwicklung“ wird eine Version der gespeicherten Prozedur haben und „Staging“ wird eine andere haben.
Ich möchte lediglich überprüfen, ob ein Parameter vorhanden ist, bevor ich ihm einen Wert übergebe und die gespeicherte Prozedur aufrufe.Diese SqlException zu vermeiden, anstatt sie abfangen zu müssen, ist für mich ein Plus.
Joshua
Lösung
Sie können SqlCommandBuilder.DeriveParameters() verwenden (siehe SqlCommandBuilder.DeriveParameters – Parameterinformationen für eine gespeicherte Prozedur abrufen – ADO.NET-Tutorials) oder es gibt Hier entlang was nicht so elegant ist.
Andere Tipps
Du willst das SqlCommandBuilder.DeriveParameters(SqlCommand) Methode.Beachten Sie, dass hierfür ein zusätzlicher Roundtrip zur Datenbank erforderlich ist und es daher zu erheblichen Leistungseinbußen kommt.Sie sollten erwägen, die Ergebnisse zwischenzuspeichern.
Ein Beispielaufruf:
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";
// ....
}
Obwohl es nicht genau das ist, was Sie wollen, finden Sie hier einen Beispielcode, der die Methode SqlConnection.GetSchema() verwendet, um alle mit einer Datenbank verknüpften gespeicherten Prozeduren und anschließend alle Parameternamen und -typen für jede gespeicherte Prozedur zurückzugeben.Das folgende Beispiel lädt dies einfach in Variablen.Beachten Sie, dass dadurch auch alle gespeicherten Systemprozeduren zurückgegeben werden, was möglicherweise unerwünscht ist.
Steve
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();
}
}
}
}
Sie können das SqlCommandBuilder-Objekt verwenden und die DeriveParameters-Methode aufrufen.
Im Grunde müssen Sie ihm einen Befehl übergeben, der so eingerichtet ist, dass er Ihren gespeicherten Prozess aufruft, und er greift auf die Datenbank zu, um die Parameter zu ermitteln und die entsprechenden Parameter in der Parameters-Eigenschaft des SqlCommand zu erstellen
BEARBEITEN: Ihr seid alle zu schnell!!
SqlCommandBuilder.DeriveParameters(command)
Diese Aussage tut, was ich brauche.
Hier ist ein vollständiges Codebeispiel für die Art und Weise, wie ich dieses Problem gelöst habe.
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 hat die beste Implementierung von DeriveParameters.Wie er sagte, stellen Sie sicher, dass Sie den Cache mögen dieses Tutorial.
Ich denke jedoch, dass dies eine gefährliche Möglichkeit ist, Ihr ursprüngliches Problem der Datenbank-Sproc-Versionierung zu lösen.Wenn Sie die Signatur einer Prozedur durch Hinzufügen oder Entfernen von Parametern ändern möchten, sollten Sie einen der folgenden Schritte ausführen:
- Codieren Sie abwärtskompatibel, indem Sie Standardwerte verwenden (für neue Parameter) oder indem Sie einfach einen Parameter ignorieren (für gelöschte Parameter).Dadurch wird sichergestellt, dass Ihr Clientcode immer jede Version Ihrer gespeicherten Prozedur aufrufen kann.
- Versionieren Sie die Prozedur explizit nach Namen (damit Sie my_proc und my_proc_v2 haben).Dadurch wird sichergestellt, dass Ihr Client-Code und Ihre Sprocs synchron bleiben.
Sich auf DeriveParameters zu verlassen, um zu überprüfen, welche Version des Sproc Sie verwenden, scheint meiner Meinung nach das falsche Tool für den Job zu sein.
Alle diese ADO.NET-Lösungen fordern die Codebibliothek auf, die Metadaten der Datenbank in Ihrem Namen abzufragen.Wenn Sie diesen Leistungseinbruch trotzdem in Kauf nehmen wollen, sollten Sie vielleicht einfach ein paar Hilfsfunktionen schreiben, die aufrufen
Select count(*) from information_schema.parameters
where ...(proc name =.. param name=...) (pseudo-code)
Oder generieren Sie Ihre Parameter vielleicht sogar basierend auf der Parameterliste, die Sie zurückerhalten.Diese Technik funktioniert mit mehreren Versionen von MS SQL und manchmal auch anderen ANSI SQL-Datenbanken.
Ich verwende DeriveParameters jetzt seit ein paar Jahren mit .NET 1.1 und 2.0 und es hat jedes Mal wunderbar funktioniert.
Jetzt arbeite ich an meiner ersten Aufgabe mit .NET 3.5 und habe gerade eine hässliche Überraschung gefunden:DeriveParameters erstellt alle Parameter mit SqlDbType „Variant“ und nicht mit den richtigen SqlDbTypes.Dies führt zu einer SqlException, wenn versucht wird, SPs mit numerischen Parametern auszuführen, da SQL Server 2005 angibt, dass SQL-Variantentypen nicht implizit in int-Werte (oder smallint-Werte oder numerische Werte) konvertiert werden können.
Ich habe gerade denselben Code mit .NET CF 2.0 und SQL Server 2000 getestet und wie erwartet funktioniert, indem ich jedem Parameter den richtigen SqlDbType zugewiesen habe.
Ich habe .NET 2.0-Apps mit SQL Server 2005-Datenbanken getestet, es handelt sich also nicht um ein SQL Server-bezogenes Problem, es muss also etwas mit .NET 3.5 zusammenhängen
Irgendwelche Ideen?