Best Practice zum Abfragen von Daten von MS SQL Server in C #?

9

Was ist der beste Weg, um Daten von einem MS SQL Server in C # abzufragen?

Ich weiß, dass es keine gute Praxis ist, eine SQL-Abfrage im Code zu haben.

Ist der beste Weg, eine gespeicherte Prozedur zu erstellen und sie mit Parametern aus C # aufzurufen?

using (var conn = new SqlConnection(connStr))
using (var command = new SqlCommand("StoredProc", conn) { CommandType = CommandType.StoredProcedure }) {
   conn.Open();
   command.ExecuteNonQuery();
   conn.Close();
}
Bruno
quelle
8
"Ich weiß, dass es keine gute Praxis ist, eine SQL-Abfrage im Code zu haben." - Imo, das ist Unsinn.
Großmeister
4
Sie müssen conn.Close () nicht aufrufen, wenn Sie es in einem using-Block erstellt haben. Der Sinn des using-Blocks besteht darin, den C ++ - Stil der Destruktorbereinigung nachzuahmen. Eine elegante Reinigung aus einem zivilisierteren Zeitalter. Nicht so zufällig oder ungeschickt wie der Try-Catch-Stil.
Lord Tydus
2
Zu Ihrer Information. Gespeicherte Prozeduren sind auch "Code". Der springende Punkt bei gespeicherten Prozessen war, das Mischen von prozeduralem Code mit satzbasiertem SQL zu ermöglichen. Sie haben also unabhängig davon eine Abfrage im Code. Bei Apps mit extrem hohem Volumen ist es manchmal besser, die Logik außerhalb der Datenbank ausführen zu lassen, um eine horizontale Skalierung durch Hinzufügen von Servern zu ermöglichen. Wenn Sie eine einzelne Datenbank verwalten können, aber einen Muli-Server für die Logik, wird die Skalierung viel einfacher.
Lord Tydus
@GrandmasterB Wir haben eine beträchtliche Menge an SQL-Inline in C # (unser C # -LOC liegt jetzt bei fast 2 Millionen) - und 6 Jahre später kommt es zurück, um uns zu beißen, weil wir jetzt dieses Inline-SQL suchen müssen (wir haben kürzlich einen SQL-Experten eingestellt - also machen wir Performance-Optimierungen). Vertrauen Sie mir: Sie wissen nie, wie groß Ihre App wird und wie sich die Dinge in Zukunft ändern werden. Behalten Sie verschiedene Sprachen in verschiedenen Dateien - auch wenn Sie sie nur an Manifest-Ressourcen delegieren. Sie könnten // SQLCODEes auch - aber Sie müssen daran denken, das zu tun.
Jonathan Dickinson
1
@ JonathanDickinson, Verwenden Sie gespeicherte Prozesse, wenn Sie möchten. Ich habe oft gesagt, dass sie nützlich sind, wenn Sie unterschiedliche Codebasen haben, die mit derselben Datenbank arbeiten. Aber nur , weil sie in nützlich ist einige Umstände automatisch macht doesnt nicht sie ‚schlechte Praxis‘ mit dem ganzen Zeit . Wenn die direkte Verwendung von SQL-Anweisungen kein Problem verursacht, ist dies keine schlechte Vorgehensweise für diese App.
Großmeister

Antworten:

10

Die Verwendung gespeicherter Prozeduren ist eine Möglichkeit und wird seit vielen Jahren häufig verwendet.

Eine modernere Möglichkeit zur Interaktion mit SQL Server-Datenbanken aus C # (oder einer beliebigen .NET-Sprache) ist die Verwendung von Entity Framework. Der Vorteil von Entity Framework besteht darin, dass es eine höhere Abstraktionsebene bietet.

So zitieren Sie von Microsoft ( https://msdn.microsoft.com/en-us/data/jj590134 ):

Mit dem ADO.NET Entity Framework können Entwickler Datenzugriffsanwendungen erstellen, indem sie anhand eines konzeptionellen Anwendungsmodells programmieren, anstatt direkt anhand eines relationalen Speicherschemas zu programmieren. Ziel ist es, den Code- und Wartungsaufwand für datenorientierte Anwendungen zu verringern. Entity Framework-Anwendungen bieten die folgenden Vorteile:

  • Anwendungen können im Hinblick auf ein anwendungsorientierteres konzeptionelles Modell arbeiten, einschließlich Typen mit Vererbung, komplexen Elementen und Beziehungen.
  • Anwendungen werden von fest codierten Abhängigkeiten von einem bestimmten Datenmodul oder Speicherschema befreit.
  • Zuordnungen zwischen dem konzeptionellen Modell und dem speicherspezifischen Schema können sich ändern, ohne den Anwendungscode zu ändern.
  • Entwickler können mit einem konsistenten Anwendungsobjektmodell arbeiten, das verschiedenen Speicherschemata zugeordnet werden kann, die möglicherweise in verschiedenen Datenbankverwaltungssystemen implementiert sind.
  • Mehrere konzeptionelle Modelle können einem einzelnen Speicherschema zugeordnet werden.
  • Die Unterstützung für sprachintegrierte Abfragen (LINQ) bietet eine Syntaxvalidierung zur Kompilierungszeit für Abfragen anhand eines konzeptionellen Modells.

Die Verwendung eines ORM gegenüber gespeicherten Prozeduren beinhaltet Kompromisse, insbesondere in Bezug auf die Sicherheit und wo sich die Logik befindet.

Der "klassische" Ansatz für die Entwicklung mit SQL Server besteht darin, dass sich die Anwendungslogik in gespeicherten Prozeduren und Programmen befindet, denen nur Sicherheitsrechte zum Ausführen gespeicherter Prozeduren erteilt wurden, und keine Tabellen direkt aktualisiert werden. Das Konzept hier ist, dass gespeicherte Prozeduren die Geschäftslogikschicht für die Anwendung (en) sind. Obwohl die Theorie stichhaltig ist, ist sie aus verschiedenen Gründen in Ungnade gefallen und wurde durch die Implementierung der Geschäftslogik in einer Programmiersprache wie C # oder VB ersetzt. Gute Anwendungen werden immer noch mit einem abgestuften Ansatz implementiert, einschließlich der Trennung von Bedenken usw., folgen jedoch eher einem Muster wie MVC.

Ein Nachteil der Implementierung von Logik im ORM anstelle der Datenbank ist das einfache Debuggen und Testen von Datenintegritätsregeln durch die für die Datenbank Verantwortlichen (DA oder DBA). Nehmen wir das klassische Beispiel für die Überweisung von Geld von Ihrem Scheck auf ein Sparkonto. Es ist wichtig, dass dies als atomare Arbeitseinheit erfolgt, dh als Teil einer Transaktion. Wenn diese Art der Übertragung nur über eine gespeicherte Prozedur erfolgen darf, ist es für den Staatsanwalt und die Prüfer relativ einfach, die gespeicherte Prozedur zu überprüfen.

Wenn dies andererseits über ein ORM wie Entity Framework erfolgt und in der Produktion festgestellt wird, dass in seltenen Fällen Geld aus der Prüfung entnommen, aber nicht in das Debuggen von Einsparungen gesteckt wird, kann dies weitaus komplexer sein, insbesondere wenn möglicherweise mehrere Programme beteiligt sind. Dies wäre höchstwahrscheinlich ein Randfall, der möglicherweise besondere Hardwareprobleme mit sich bringt, die in einer bestimmten Reihenfolge auftreten müssen usw. Wie testet man dies?

JonnyBoats
quelle
Oder ein anderes ORM. Es gibt viele von ihnen mit verschiedenen Vor- und Nachteilen im Vergleich zu EF.
Svick
8
Das Auflisten von Nachteilen von ORMs würde diese Antwort nützlicher machen.
Den
6

Tatsächlich ist die grundlegende Behauptung umstritten - es gibt Kompromisse zwischen SQL im Code oder Code in der Datenbank (wohin Sie mit gespeicherten Prozeduren gehen).

Das Ergebnis ist, dass es kein einziges "Bestes" gibt. Dies können Sie nicht verallgemeinern, da Sie auf jedem Weg einen Kompromiss eingehen (Sie erhalten Vorteile, führen aber auch Einschränkungen ein).

Wenn zwischen Ihrer Anwendung und Ihrer Datenbank eine Eins-zu-Eins-Korrespondenz besteht, spielt dies keine Rolle. Wenn Sie andererseits eine große Kerndatenbank haben, die von einer erheblichen Anzahl von Anwendungen gemeinsam genutzt wird, wird die Durchsetzung der Konsistenz innerhalb der Datenbank zwischen diesen Anwendungen viel wichtiger.

Wichtiger ist es, sich um die Architektur und Schichtung Ihrer Anwendung zu kümmern. Wenn Sie eine geeignete Datenzugriffsschicht verwenden, sollten Sie einen Großteil Ihrer Anwendung von dieser Entscheidung isolieren, unabhängig davon, ob Sie Abfragen erstellen, unabhängig davon, ob Sie Abfragen erstellen oder gespeicherte Prozeduren verwenden.


Ich werde frei daran arbeiten, an relativ kleinen Projekten mit kleinen Teams (1-3 Entwickler) zu arbeiten - die Verwendung gespeicherter Prozeduren ist für mich mehr Mühe als es wert ist, da angesichts der Art unserer Anwendungen (und meiner Fähigkeiten?), Die neu bereitgestellt werden Code ist im Allgemeinen viel einfacher als das Aktualisieren des Schemas (selbst wenn ich Code habe, der das Aktualisieren des Schemas relativ einfach macht), und ich kann Geschäftsregeln mithilfe eines allgemeinen Datenzugriffscodes durchsetzen. Dies ist eindeutig ein klassisches Beispiel für "Ihre Laufleistung kann variieren".

Murph
quelle
2
+1 für "no single best", -1 für die Bereitstellung von Code ist einfacher als die Bereitstellung gespeicherter Prozessänderungen, +1 für die Auswahl eines Ansatzes, der für Ihre App sinnvoll ist und die DAL-Isolation gewährleistet - also +1.
Joel Brown
Ich habe das Bereitstellungsbit mit "für mich" qualifiziert, da dies für mich durchweg der Fall war (insbesondere für Webanwendungen) - trotz allem, was in diesem Bereich möglicherweise etwas umgeschrieben wird.
Murph
+1, was am besten ist, hängt von der Anwendung und der Situation ab.
Großmeister
1
Ich denke, es hängt von den Bedingungen ab, unter denen Sie arbeiten. Wenn Sie nicht über DBAs verfügen, die Sie von der Produktion abhalten, kann es erschreckend einfach sein, einen gespeicherten Ersatzprozess in der Datenbank abzulegen. Ohne Produktionskontrollen kann es auch viel zu einfach sein, neues Markup, Skript und sogar neuen kompilierten Code in eine zentralisierte App zu kopieren.
Joel Brown
@ JoelBrown - Genau - ich nehme an, nein, ich bin sicher, ich habe dort eine ganze Reihe von Annahmen getroffen. Erstens ist mein Schema versioniert (grob, aber effektiv). Ich bin kein dba, aber ich bin klug genug zu wissen, dass das eigene Schema konsistent sein muss. Ich arbeite hauptsächlich an Webanwendungen und Sie können nur durch einen Besuch des Servers zu den Datenbanken gelangen, während ich die Bereitstellung von Apps (nur) auf (mehr oder weniger) einen einzigen Knopfdruck reduziert habe ... um Schemaaktualisierungen in diese zu integrieren Die Bereitstellung ist auf der Liste, aber ich habe immer festgestellt, dass Schema-Updates stressiger sind als Code-Updates (einfaches Rollback?)
Murph
4

Solange Sie Ihre Eingaben parametrisiert haben, ist jeder Ansatz gültig. Ein Großteil der Abfragen in Code-Argumenten stammt aus der schlechten alten Zeit, als viele Bibliotheken Sie gezwungen haben, Ihre Anweisungen aneinander zu hängen, und daher kamen SQL-Injection-Angriffe.

Rechnung
quelle
1
Reicht die Parametrierung aus? Müssen Sie nicht auch desinfizieren?
StuperUser
Das hängt von Ihrer Quelle und Ihrem Zweck ab. Ich habe seine Frage wörtlich genommen, er ist mit einigen Parametern schreibgeschützt. In den meisten Fällen ist das Schlimmste, was Sie mit schmutzigen Eingaben tun würden, wenn sie richtig parametrisiert werden, eine Typausnahme oder keine Ergebnisse mit schlechten Eingaben. Das Einfügen ist anders und muss normalerweise validiert werden, es sei denn, Sie behandeln die Quelle als maßgeblich.
Bill
0

Best Practice ist hier wirklich übertrieben - es gibt viele gute Möglichkeiten, dies zu tun, und die, die Sie auswählen, sollte wirklich davon abhängen, was Ihre App ist und was Sie tun müssen. Das heißt, es gibt nur zwei Dinge, die Sie wirklich falsch machen können:

  • Wie @Bill hervorhebt, sollten Sie Ihre Abfragen immer parametrisieren. Das Erstellen von Strings ist ein einfacher Vektor für die SQL-Injection sowie für alle Arten von schwer auffindbaren Fehlern. Viel klügere Leute haben herausgefunden, wie man SQL tupelisiert und entkommt, damit Sie es nicht selbst herausfinden müssen.

  • Schließen Sie Ihre Verbindungen. Der beste Weg ist, alles mit einer using-Anweisung zu verpacken, aber try / catch / finally ist auch cool, wenn das Ihr Boot schwimmt. Aber stellen Sie immer sicher, dass Sie eine Verbindung wie ein billiges Auto verwenden - fahren Sie hart und schnell und entfernen Sie sie schnell.

Die andere Praxis, für die ich vehement argumentieren würde, ist, dass Sie sicherstellen sollten, dass Sie Ihren Datenzugriffscode an so wenigen Stellen wie möglich konzentrieren. Wir erlauben Front-End-Webanwendungen nicht, einen direkten Verweis auf System.Data.SqlClient zu enthalten, um diese Einschränkung durchzusetzen.

Wyatt Barnett
quelle