Tak, stałe kodowanie ciągów SQL w kodzie aplikacji jest ogólnie anty-wzorcem.
Spróbujmy odłożyć na bok tolerancję, którą wypracowaliśmy od lat postrzegania tego w kodzie produkcyjnym. Mieszanie całkowicie różnych języków z różną składnią w tym samym pliku nie jest na ogół pożądaną techniką programowania. Różni się to od języków szablonów, takich jak Razor, które mają nadawać kontekstowe znaczenie wielu językach. Jak wspomina Sava B. w komentarzu poniżej, SQL w twoim C # lub innym języku aplikacji (Python, C ++ itp.) Jest ciągiem jak każdy inny i jest semantycznie bez znaczenia. To samo dotyczy w większości przypadków mieszania więcej niż jednego języka, choć oczywiście są sytuacje, w których jest to dopuszczalne, takie jak wbudowanie w C, małe i zrozumiałe fragmenty CSS w HTML (zauważ, że CSS jest zaprojektowany do mieszania z HTML ), i inni.
(Robert C. Martin o mieszaniu języków, Clean Code , rozdział 17, „Zapachy i heurystyka kodu”, strona 288)
Dla tej odpowiedzi skupię się na SQL (jak zadano w pytaniu). Podczas przechowywania SQL jako zestawu oddzielonych ciągów mogą wystąpić następujące problemy:
- Logika bazy danych jest trudna do zlokalizowania. Czego szukasz, aby znaleźć wszystkie swoje instrukcje SQL? Ciągi znaków z „SELECT”, „UPDATE”, „MERGE” itp.?
- Refaktoryzacja użycia tego samego lub podobnego SQL staje się trudna.
- Dodanie obsługi innych baz danych jest trudne. Jak można to osiągnąć? Dodać instrukcje if..then dla każdej bazy danych i przechowywać wszystkie zapytania jako ciągi w metodzie?
- Deweloperzy czytają instrukcje w innym języku i rozpraszają ich przesunięcie punktu ciężkości z celu metody na szczegóły implementacji metody (sposób i miejsce pobierania danych).
- Podczas gdy jednowierszowe mogą nie stanowić większego problemu, wbudowane ciągi SQL zaczynają się rozpadać, gdy instrukcje stają się bardziej złożone. Co robisz z zestawieniem wiersza 113? Umieścić wszystkie 113 wierszy w metodzie?
- W jaki sposób programista skutecznie przenosi zapytania tam iz powrotem między swoim edytorem SQL (SSMS, SQL Developer itp.) A kodem źródłowym?
@
Prefiks C # ułatwia to, ale widziałem dużo kodu, który cytuje każdą linię SQL i unika znaku nowej linii.
"SELECT col1, col2...colN"\
"FROM painfulExample"\
"WHERE maintainability IS NULL"\
"AND modification.effort > @necessary"\
- Znaki wcięcia używane do wyrównania kodu SQL z otaczającym kodem aplikacji są przesyłane przez sieć przy każdym wykonaniu. Jest to prawdopodobnie nieistotne w przypadku małych aplikacji, ale może się sumować w miarę wzrostu użytkowania oprogramowania.
Pełne ORM (obiekty mapujące obiektowo-relacyjne, takie jak Entity Framework lub Hibernacja) mogą wyeliminować losowo zaszyty SQL w kodzie aplikacji. Moje użycie SQL i plików zasobów jest tylko przykładem. ORM, klasy pomocnicze itp. Mogą pomóc w osiągnięciu celu czyszczenia kodu.
Jak Kevin powiedział we wcześniejszej odpowiedzi, kod SQL może być akceptowalny w małych projektach, ale duże projekty zaczynają się jako małe projekty, a prawdopodobieństwo, że większość zespołów wróci i zrobi to poprawnie, jest często odwrotnie proporcjonalne do rozmiaru kodu.
Istnieje wiele prostych sposobów utrzymywania SQL w projekcie. Jedną z metod, których często używam, jest umieszczenie każdej instrukcji SQL w pliku zasobów Visual Studio, zwykle o nazwie „sql”. Plik tekstowy, dokument JSON lub inne źródło danych może być uzasadnione w zależności od narzędzi. W niektórych przypadkach najlepszą opcją może być osobna klasa poświęcona tworzeniu łańcuchów SQL, ale może ona zawierać niektóre z wyżej opisanych problemów.
Przykład SQL: Który wygląda bardziej elegancko ?:
using(DbConnection connection = Database.SystemConnection()) {
var eyesoreSql = @"
SELECT
Viewable.ViewId,
Viewable.HelpText,
PageSize.Width,
PageSize.Height,
Layout.CSSClass,
PaginationType.GroupingText
FROM Viewable
LEFT JOIN PageSize
ON PageSize.Id = Viewable.PageSizeId
LEFT JOIN Layout
ON Layout.Id = Viewable.LayoutId
LEFT JOIN Theme
ON Theme.Id = Viewable.ThemeId
LEFT JOIN PaginationType
ON PaginationType.Id = Viewable.PaginationTypeId
LEFT JOIN PaginationMenu
ON PaginationMenu.Id = Viewable.PaginationMenuId
WHERE Viewable.Id = @Id
";
var results = connection.Query<int>(eyesoreSql, new { Id });
}
Staje się
using(DbConnection connection = Database.SystemConnection()) {
var results = connection.Query<int>(sql.GetViewable, new { Id });
}
SQL zawsze znajduje się w łatwym do zlokalizowania pliku lub zgrupowanym zestawie plików, każdy z opisową nazwą, która opisuje, co robi, a nie jak to robi, każdy z miejscem na komentarz, który nie zakłóci przepływu kodu aplikacji :
Ta prosta metoda wykonuje pojedyncze zapytanie. Z mojego doświadczenia wynika, że korzyści są skalowane, ponieważ użycie „języka obcego” staje się coraz bardziej wyrafinowane.
Moje użycie pliku zasobów jest tylko przykładem. Różne metody mogą być bardziej odpowiednie w zależności od języka (w tym przypadku SQL) i platformy.
Ta i inne metody rozwiązują powyższą listę w następujący sposób:
- Kod bazy danych jest łatwy do zlokalizowania, ponieważ jest już scentralizowany. W większych projektach grupuj jak-SQL w osobne pliki, być może w folderze o nazwie
SQL
.
- Obsługa drugiej, trzeciej itd. Baz danych jest łatwiejsza. Utwórz interfejs (lub abstrakcję innego języka), który zwraca unikalne instrukcje dla każdej bazy danych. Implementacja dla każdej bazy danych staje się niewiele więcej niż stwierdzeniem podobnym do:
return SqlResource.DoTheThing;
To prawda, że te implementacje mogą pomijać zasób i zawierać SQL w ciągu, ale niektóre (nie wszystkie) problemy powyżej nadal będą się pojawiać.
- Refaktoryzacja jest prosta - wystarczy ponownie użyć tego samego zasobu. Możesz nawet użyć tego samego wpisu zasobu dla różnych systemów DBMS przez kilka instrukcji formatu. Robię to często.
- Używanie języka dodatkowego może używać nazw opisowych, np.
sql.GetOrdersForAccount
Zamiast bardziej rozwartychSELECT ... FROM ... WHERE...
- Instrukcje SQL są przywoływane z jedną linią, niezależnie od ich wielkości i złożoności.
- SQL można kopiować i wklejać między narzędziami baz danych, takimi jak SSMS i SQL Developer, bez modyfikacji lub starannego kopiowania. Brak znaków cudzysłowu. Brak końcowych ukośników odwrotnych. W przypadku edytora zasobów Visual Studio jedno kliknięcie podświetla instrukcję SQL. CTRL + C, a następnie wklej go do edytora SQL.
Tworzenie SQL w zasobie jest szybkie, więc nie ma większego impulsu do mieszania wykorzystania zasobów z SQL-in-code.
Niezależnie od wybranej metody odkryłem, że mieszanie języków zwykle obniża jakość kodu. Mam nadzieję, że niektóre opisane tutaj problemy i rozwiązania pomogą deweloperom wyeliminować ten zapach kodu, gdy jest to właściwe.