Так, жорстке кодування рядків SQL в код програми, як правило, є анти-шаблоном.
Спробуємо відмінити толерантність, яку ми виробили за роки, коли це бачимо у виробничому коді. Змішування абсолютно різних мов з різним синтаксисом в одному файлі, як правило, не є бажаною технікою розвитку. Це відрізняється від мов шаблонів, таких як Razor, які розроблені для того, щоб надати контекстному значенню декілька мов. Як згадує Сава Б. у коментарі нижче, SQL у вашому C # або іншій мові додатків (Python, C ++ тощо) є рядком, як і будь-який інший, і семантично не має сенсу. Те ж саме стосується змішування декількох мов у більшості випадків, хоча, очевидно, є ситуації, коли це прийнятно, наприклад, вбудована вбудована мова в C, невеликі та зрозумілі фрагменти CSS в HTML (зауваживши, що CSS призначений для змішування з HTML ), та інші.
(Роберт К. Мартін про змішування мов. Чистий код , глава 17, "Код запахів та евристики", стор. 288)
Для цієї відповіді я зосередитиму увагу на SQL (як у запитанні). Наступні проблеми можуть виникнути під час зберігання SQL як а-ля-карт набору роз'єднаних рядків:
- Логіку бази даних важко знайти. Що ви шукаєте, щоб знайти всі ваші оператори SQL? Рядки з "SELECT", "UPDATE", "MERGE" тощо?
- Використання рефакторингу одного і того ж або подібного SQL стає важким.
- Додати підтримку для інших баз даних складно. Як би хтось це досяг? Додати if..then операторів для кожної бази даних і зберегти всі запити як рядки в методі?
- Розробники читають заяву іншою мовою і відволікаються на зміщення фокусу від призначення методу до деталей реалізації методу (як і звідки отримуються дані).
- Хоча одне вкладиш може бути не надто великою проблемою, вбудовані рядки SQL починають розпадатися, коли заяви стають складнішими. Що ви робите з оператором 113 рядка? Покласти всі 113 рядків у свій метод?
- Як розробник ефективно переміщує запити вперед і назад між їх редактором SQL (SSMS, SQL Developer тощо) та їх вихідним кодом?
@
Префікс C # полегшує це, але я побачив багато коду, який цитує кожну рядок SQL і виходить з нових рядків.
"SELECT col1, col2...colN"\
"FROM painfulExample"\
"WHERE maintainability IS NULL"\
"AND modification.effort > @necessary"\
- Символи відступу, які використовуються для вирівнювання SQL з оточуючим кодом програми, передаються по мережі з кожним виконанням. Це, мабуть, незначно для невеликих програм, але це може збільшуватись у міру зростання використання програмного забезпечення.
Повні ОРМ (Об'єктно-реляційні картографи, такі як Entity Framework або Hibernate) можуть усунути випадково врізаний SQL у код програми. Моє використання файлів SQL та ресурсів є лише прикладом. ORM, класи помічників тощо можуть допомогти досягти мети більш чистого коду.
Як сказав Кевін у попередній відповіді, SQL у коді може бути прийнятним для малих проектів, але великі проекти починаються як невеликі проекти, і ймовірність того, що більшість команд повернеться і зробить це правильно, часто обернено пропорційна розміру коду.
Існує багато простих способів зберегти SQL в проекті. Один із методів, якими я часто користуюся, - це розміщення кожного оператора SQL у файлі ресурсів Visual Studio, зазвичай названому "sql". Текстовий файл, документ JSON або інше джерело даних можуть бути розумними залежно від ваших інструментів. У деяких випадках окремий клас, присвячений зміцненню рядків SQL, може бути найкращим варіантом, але може мати деякі з питань, описаних вище.
Приклад SQL: Що виглядає більш елегантно ?:
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 });
}
Стає
using(DbConnection connection = Database.SystemConnection()) {
var results = connection.Query<int>(sql.GetViewable, new { Id });
}
SQL завжди знаходиться у простому для пошуку файлі чи згрупованому наборі файлів, кожен з описовим іменем, який описує, що він робить, а не як це робить, кожен з простором для коментаря, який не перериває потік коду програми :
Цей простий метод виконує одиночний запит. На мій досвід, масштаб користі, оскільки використання "іноземної мови" стає все більш досконалим.
Моє використання файлу ресурсів - лише приклад. Різні методи можуть бути більш підходящими залежно від мови (SQL у даному випадку) та платформи.
Цей та інші методи вирішують перелік вище таким чином:
- Код бази даних легко знайти, оскільки він уже централізований. У великих проектах групуйте подібний SQL в окремі файли, можливо, під папкою з назвою
SQL
.
- Підтримка баз даних другої, третьої тощо. Створіть інтерфейс (або іншу мову абстракції), який повертає унікальні заяви кожної бази даних. Реалізація для кожної бази даних стає трохи більше, ніж твердження, схожі на:
return SqlResource.DoTheThing;
Правда, ці реалізації можуть пропустити ресурс і містити SQL у рядку, але деякі (не всі) проблеми вище все ще виникають.
- Рефакторинг простий - просто повторно використовуйте той самий ресурс. Ви навіть можете використовувати один і той же запис ресурсів для різних систем СУБД протягом кількох випадків, використовуючи декілька заяв формату. Я роблю це часто.
- Використання другорядної мови може використовувати описові назви, наприклад,
sql.GetOrdersForAccount
а не більш чіткіSELECT ... FROM ... WHERE...
- Оператори SQL викликаються одним рядком незалежно від їх розміру та складності.
- SQL можна скопіювати та вставити між інструментами бази даних, такими як SSMS та SQL Developer, без змін або ретельного копіювання. Немає лапок. Ніяких зворотних нахилів в нижній частині. У конкретному редакторі ресурсів Visual Studio одним клацанням миші виділяється оператор SQL. CTRL + C, а потім вставити його в редактор SQL.
Створення SQL в ресурсі швидке, тому невеликий поштовх для змішування використання ресурсу з SQL-кодом.
Незалежно від обраного методу, я виявив, що змішування мов зазвичай знижує якість коду. Я сподіваюся, що деякі проблеми та рішення, описані тут, допомагають розробникам усунути цей запах коду, коли це доречно.