Najlepsza praktyka do wysyłania zapytań do danych z MS SQL Server w C #?

9

Jaki jest najlepszy sposób na zapytanie danych z MS SQL Server w C #?

Wiem, że nie jest dobrą praktyką mieć zapytanie SQL w kodzie.

Czy to najlepszy sposób na utworzenie procedury składowanej i wywołanie jej z C # z parametrami?

using (var conn = new SqlConnection(connStr))
using (var command = new SqlCommand("StoredProc", conn) { CommandType = CommandType.StoredProcedure }) {
   conn.Open();
   command.ExecuteNonQuery();
   conn.Close();
}
Bruno
źródło
8
„Wiem, że zapytanie SQL nie jest dobrą praktyką w kodzie”. - imo, to nonsens.
GrandmasterB
4
Nie musisz wywoływać conn.Close (), jeśli utworzyłeś go w bloku używającym. Istotą używanego bloku jest naśladowanie stylu czyszczenia niszczyciela w stylu C ++. Eleganckie sprzątanie od bardziej cywilizowanego wieku. Nie tak losowy lub niezdarny jak styl try-catch.
Lord Tydus
2
Do Twojej wiadomości Procedury przechowywane są również „kodowane”. Chodziło przede wszystkim o to, aby procenty przechowywane były mieszane z kodem SQL w stylu opartym na zestawie. Więc niezależnie od tego będziesz mieć zapytanie w kodzie. W przypadku bardzo dużych aplikacji czasami lepiej jest wykonywać logikę poza bazą danych, aby umożliwić skalowanie w poziomie przez dodanie serwerów. Jeśli możesz utrzymać pojedynczy DB, ale muli-serwer dla logiki, skalowanie staje się znacznie łatwiejsze.
Lord Tydus
@GrandmasterB mamy znaczną ilość wbudowanego SQL w C # (nasz C # LOC jest obecnie blisko 2 milionów) - i 6 lat później wraca, aby nas ugryźć, ponieważ teraz musimy wytropić ten wbudowany SQL (niedawno zatrudniliśmy eksperta SQL - dlatego wprowadzamy poprawki wydajności). Zaufaj mi: nigdy nie wiesz, jak duża będzie Twoja aplikacja i jak zmieni się sytuacja w przyszłości. Zachowaj różne języki w różnych plikach - nawet jeśli delegujesz je w celu manifestowania zasobów. Ty też // SQLCODEmożesz - ale musisz o tym pamiętać.
Jonathan Dickinson
1
@JonathanDickinson, Jeśli chcesz, użyj zapisanych procedur. Powiedziałem wiele razy, że są one użyteczne, gdy masz różne bazy kodu działające na tej samej bazie danych. Ale tylko dlatego, że są użyteczne w niektórych okolicznościach, nie oznacza automatycznie, że nie stosuje się ich przez cały czas jako „złą praktykę” . Jeśli bezpośrednie użycie instrukcji SQL nie powoduje problemu, nie jest to „zła praktyka” dla tej aplikacji.
GrandmasterB

Odpowiedzi:

10

Korzystanie z procedur przechowywanych jest jednym ze sposobów i jest szeroko stosowane od wielu lat.

Bardziej nowoczesnym sposobem interakcji z bazami danych SQL Server z C # (lub dowolnego języka .NET) jest użycie Entity Framework. Zaletą Entity Framework jest to, że zapewnia wyższy poziom abstrakcji.

Cytat z Microsoft ( https://msdn.microsoft.com/en-us/data/jj590134 ):

ADO.NET Entity Framework umożliwia programistom tworzenie aplikacji dostępu do danych poprzez programowanie w oparciu o koncepcyjny model aplikacji zamiast programowania bezpośrednio w relacyjnym schemacie przechowywania. Celem jest zmniejszenie ilości kodu i konserwacji wymaganej dla aplikacji zorientowanych na dane. Aplikacje Entity Framework zapewniają następujące korzyści:

  • Aplikacje mogą działać w oparciu o bardziej koncepcyjny model koncepcyjny, w tym typy z dziedziczeniem, złożone elementy i relacje.
  • Aplikacje są wolne od zakodowanych na stałe zależności od konkretnego silnika danych lub schematu pamięci.
  • Odwzorowania między modelem koncepcyjnym a schematem specyficznym dla pamięci masowej można zmieniać bez zmiany kodu aplikacji.
  • Programiści mogą pracować ze spójnym modelem obiektowym aplikacji, który można odwzorować na różne schematy przechowywania, ewentualnie zaimplementowane w różnych systemach zarządzania bazami danych.
  • Wiele modeli koncepcyjnych można zmapować na pojedynczy schemat pamięci.
  • Obsługa zapytań zintegrowanych z językiem (LINQ) zapewnia sprawdzanie poprawności składni w czasie kompilacji dla zapytań w modelu koncepcyjnym.

Zastosowanie ORM kontra Procedury składowane wiąże się z kompromisami, szczególnie pod względem bezpieczeństwa i miejsca, w którym znajduje się logika.

„Klasyczne” podejście do programowania w SQL Server polega na tym, że logika aplikacji znajduje się w procedurach przechowywanych, a programy mają tylko uprawnienia bezpieczeństwa do wykonywania procedur przechowywanych, a nie bezpośrednio aktualizują tabele. Chodzi tutaj o to, że procedury przechowywane są warstwą logiki biznesowej dla aplikacji. Chociaż teoria jest słuszna, z różnych powodów zwykle popada w niełaskę, zastępując ją logiką biznesową w języku programowania, takim jak C # lub VB. Dobre aplikacje są nadal wdrażane przy użyciu wielopoziomowego podejścia, w tym rozdzielania problemów itp., Ale częściej są zgodne z wzorem takim jak MVC.

Wadą implementacji logiki w ORM zamiast w bazie danych jest łatwość debugowania i testowania reguł integralności danych przez osoby odpowiedzialne za bazę danych (DA lub DBA). Weźmy klasyczny przykład przelewu pieniędzy z czeku na konto oszczędnościowe, ważne jest, aby uczynić to jako atomową jednostkę pracy, innymi słowy umieszczoną w transakcji. Jeśli tego rodzaju przeniesienie jest dozwolone tylko poprzez procedurę przechowywaną, DA i audytorzy mogą stosunkowo łatwo ocenić jakość przechowywanej procedury.

Jeśli z drugiej strony odbywa się to za pomocą ORM, takiego jak Entity Framework, a podczas produkcji okazuje się, że w rzadkich przypadkach pieniądze są sprawdzane, ale nie wkładane w debugowanie oszczędności może być znacznie bardziej skomplikowane, szczególnie jeśli potencjalnie zaangażowanych jest wiele programów. Najprawdopodobniej byłby to przypadek skrajny, być może obejmujący szczególne problemy sprzętowe, które muszą wystąpić w określonej sekwencji itp. W jaki sposób można to sprawdzić?

JonnyBoats
źródło
Lub inne ORM. Jest ich wiele, z różnymi zaletami i wadami w porównaniu z EF.
svick
8
Wymienienie wad ORM sprawiłoby, że ta odpowiedź byłaby bardziej przydatna.
Den
6

W rzeczywistości podstawowe twierdzenie jest dyskusyjne - istnieją kompromisy między sposobem SQL w kodzie lub kodem w bazie danych (tam, gdzie zmierzasz z procedurami przechowywanymi).

Rezultatem jest to, że nie ma jednego „najlepszego”, nie jest to coś, co można uogólnić, ponieważ niezależnie od tego, którą drogą pójdziesz, pójdziesz na kompromis (zyskujesz korzyści, ale także wprowadzasz ograniczenia).

Jeśli istnieje zgodność między aplikacją a bazą danych, to tak naprawdę nie ma to znaczenia. Z drugiej strony, jeśli duża baza danych jest współużytkowana przez znaczną liczbę aplikacji, wymuszanie spójności w bazie danych między tymi aplikacjami staje się znacznie ważniejsze.

Co ważniejsze, musisz martwić się architekturą i warstwami aplikacji - biorąc pod uwagę odpowiednią warstwę dostępu do danych, niezależnie od tego, czy korzystasz z ORM, takich jak Entity Framework lub NHibernate, czy robisz coś bardziej bezpośredniego, powinien izolować większość aplikacji od tej decyzji, niezależnie od tego, czy budujesz zapytania lub użyj procedur przechowywanych.


Wolę pracować nad stosunkowo małymi projektami w małych zespołach (1-3 programistów) - korzystanie z procedur przechowywanych jest dla mnie większym problemem niż jest to warte, ponieważ biorąc pod uwagę naturę naszych aplikacji (i mojego zestawu umiejętności?) Wdrażanie nowych kod jest na ogół znacznie łatwiejszy niż aktualizacja schematu (nawet pozwalając na posiadanie kodu, który sprawia, że ​​aktualizacja schematu jest stosunkowo prosta) i jestem w stanie egzekwować reguły biznesowe za pomocą wspólnego kodu dostępu do danych. Jest to wyraźnie klasyczny przykład „Your Mileage May Vary”.

Murph
źródło
2
+1 za „brak pojedynczego najlepszego”, -1 za wdrożenie kodu jest łatwiejsze niż wdrożenie zapisanych zmian proc, +1 za wybór podejścia, które ma sens dla Twojej aplikacji i zapewnia izolację DAL - więc +1.
Joel Brown
Zrobiłem zakwalifikować wdrażana trochę z „dla mnie”, bo to ma dla mnie niezmiennie sprawa (szczególnie dla aplikacji internetowych) - dla wszystkich, że może znieść trochę przepisać w tym obszarze.
Murph
+1, co jest najlepsze, zależy od zastosowania i sytuacji.
GrandmasterB
1
Myślę, że to zależy od warunków, w których pracujesz. Jeśli nie masz DBA blokujących cię z produkcji, upuszczenie zamienionego przechowywanego proc do bazy danych może być przerażająco łatwe. W tym przypadku bez kontroli produkcji upuszczanie nowego znacznika, skryptu, a nawet nowego skompilowanego kodu do scentralizowanej aplikacji może być również zbyt łatwe.
Joel Brown
@JelBrown - Dokładnie - przypuszczam, że nie, jestem pewien, że podjąłem tam całą masę założeń. Po pierwsze, mój schemat jest kontrolowany pod względem wersji (w prymitywny, ale skuteczny sposób), nie jestem dba, ale jestem wystarczająco inteligentny, aby wiedzieć, że czyjś schemat musi być spójny. Pracuję głównie na aplikacjach internetowych i nie można dostać się do baz danych, chyba że odwiedzam serwer, podczas gdy (po prostu) ograniczyłem wdrażanie aplikacji do (mniej więcej) jednego kliknięcia przycisku ... integrując aktualizacje schematu z tym wdrożenie znajduje się na liście, ale zawsze uważałem, że aktualizacje schematu są bardziej stresujące niż aktualizacje kodu (łatwość wycofania?)
Murph
4

Dopóki sparametryzujesz swoje dane wejściowe, każde podejście jest poprawne. Wiele z braków zapytań w argumentach kodu pochodziło ze złych, dawnych czasów, kiedy wiele bibliotek zmusiło cię do ciągłego dołączania instrukcji razem i stąd pochodziły ataki SQL Injection.

Rachunek
źródło
1
Czy wystarczająca jest parametryzacja? Nie musisz też dezynfekować?
StuperUser
zależy to od twojego źródła i celu. Podjąłem jego pytanie dosłownie, jest on tylko do odczytu z pewnymi parametrami. w typowych przypadkach najgorsze, co zrobiłbyś z brudnymi danymi wejściowymi, gdyby odpowiednio sparametryzowano, uzyskać wyjątek typu lub brak wyników przy złym wprowadzeniu. Wstawianie jest inne i zwykle wymaga sprawdzania poprawności, chyba że traktujesz źródło jako wiarygodne.
Bill
0

Najlepsza praktyka jest tutaj naprawdę przesadzona - istnieje wiele dobrych sposobów, a wybór, który wybierzesz, powinien zależeć od tego, co jest twoja aplikacja i co musisz zrobić. To powiedziawszy, są tylko dwie rzeczy, które możesz zrobić naprawdę źle:

  • Jak wskazuje @Bill, zawsze należy sparametryzować swoje zapytania. Tworzenie ciągów jest łatwym wektorem do wstrzykiwania SQL, a także wszelkiego rodzaju trudnych do wyśledzenia błędów. Znacznie mądrzejsi ludzie wymyślili, jak tupelize i uciec sql, więc nie musisz sam tego wymyślać.

  • Zamknij połączenia. Najlepszym sposobem jest zawinięcie wszystkiego w instrukcję, ale spróbuj / catch / wreszcie jest też fajny, jeśli to unosi twoją łódź. Ale zawsze upewnij się, że korzystasz z połączenia takiego jak tani samochód - prowadź go mocno i szybko i szybko się go pozbądź.

Inną praktyką, o którą opowiadam się stanowczo, jest to, że powinieneś skoncentrować swój kod dostępu do danych w jak najmniejszej liczbie miejsc. Nie zezwalamy aplikacjom internetowym frontonu na bezpośrednie odniesienie do System.Data.SqlClient, aby pomóc w egzekwowaniu tego zastrzeżenia.

Wyatt Barnett
źródło