Ya, hard coding string SQL ke dalam kode aplikasi umumnya adalah anti-pola.
Mari kita coba kesampingkan toleransi yang telah kita kembangkan sejak bertahun-tahun melihat ini dalam kode produksi. Mencampur bahasa yang sama sekali berbeda dengan sintaks yang berbeda dalam file yang sama pada umumnya bukan teknik pengembangan yang diinginkan. Ini berbeda dari bahasa templat seperti Razor yang dirancang untuk memberikan makna kontekstual ke beberapa bahasa. Seperti Sava B. menyebutkan dalam komentar di bawah ini, SQL dalam bahasa C # atau bahasa aplikasi lainnya (Python, C ++, dll.) Adalah string seperti yang lainnya dan secara semantik tidak berarti. Hal yang sama berlaku ketika mencampur lebih dari satu bahasa dalam banyak kasus, meskipun ada situasi yang jelas di mana hal tersebut dapat diterima, seperti perakitan inline dalam C, potongan kecil dan komprehensif CSS dalam HTML (mencatat bahwa CSS dirancang untuk dicampur dengan HTML ), dan lain-lain.
(Robert C. Martin tentang bahasa campuran, Clean Code , Bab 17, "Code Smells and Heuristics", halaman 288)
Untuk tanggapan ini, saya akan memfokuskan SQL (seperti yang ditanyakan dalam pertanyaan). Masalah-masalah berikut ini dapat terjadi ketika menyimpan SQL sebagai rangkaian string yang dipisahkan:
- Logika basis data sulit ditemukan. Apa yang Anda cari untuk menemukan semua pernyataan SQL Anda? String dengan "SELECT", "UPDATE", "MERGE", dll?
- Penggunaan refactoring dari SQL yang sama atau serupa menjadi sulit.
- Menambahkan dukungan untuk database lain sulit. Bagaimana seseorang mencapai ini? Tambahkan if..then pernyataan untuk setiap database dan simpan semua pertanyaan sebagai string dalam metode ini?
- Pengembang membaca pernyataan dalam bahasa lain dan menjadi terganggu oleh pergeseran fokus dari tujuan metode ke rincian implementasi metode (bagaimana dan dari mana data diambil).
- Sementara one-liners mungkin tidak terlalu banyak masalah, string SQL inline mulai berantakan ketika pernyataan menjadi lebih kompleks. Apa yang Anda lakukan dengan pernyataan garis 113? Masukkan semua 113 baris dalam metode Anda?
- Bagaimana pengembang efisien memindahkan pertanyaan bolak-balik antara editor SQL mereka (SSMS, SQL Developer, dll.) Dan kode sumbernya?
@
Awalan C # membuat ini lebih mudah, tapi saya telah melihat banyak kode yang mengutip setiap baris SQL dan lolos dari baris baru.
"SELECT col1, col2...colN"\
"FROM painfulExample"\
"WHERE maintainability IS NULL"\
"AND modification.effort > @necessary"\
- Lekukan karakter yang digunakan untuk menyelaraskan SQL dengan kode aplikasi di sekitarnya ditransmisikan melalui jaringan dengan setiap eksekusi. Ini mungkin tidak signifikan untuk aplikasi skala kecil, tetapi dapat bertambah seiring dengan meningkatnya penggunaan perangkat lunak.
ORM penuh (Pemetaan Objek-Relasional seperti Entity Framework atau Hibernate) dapat menghilangkan SQL yang dibumbui secara acak dalam kode aplikasi. Penggunaan SQL dan file sumber daya saya hanyalah sebuah contoh. ORM, kelas pembantu, dll. Semuanya dapat membantu mencapai tujuan kode bersih.
Seperti yang dikatakan Kevin dalam jawaban sebelumnya, SQL dalam kode dapat diterima dalam proyek-proyek kecil, tetapi proyek besar dimulai sebagai proyek kecil, dan probabilitas sebagian besar tim akan kembali dan melakukannya dengan benar sering berbanding terbalik dengan ukuran kode.
Ada banyak cara sederhana untuk menjaga SQL dalam suatu proyek. Salah satu metode yang sering saya gunakan adalah untuk menempatkan setiap pernyataan SQL ke dalam file sumber daya Visual Studio, biasanya bernama "sql". File teks, dokumen JSON, atau sumber data lain mungkin masuk akal tergantung pada alat Anda. Dalam beberapa kasus, kelas terpisah yang didedikasikan untuk mengamankan string SQL mungkin merupakan pilihan terbaik, tetapi bisa memiliki beberapa masalah yang dijelaskan di atas.
Contoh SQL: Mana yang terlihat lebih elegan ?:
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 });
}
Menjadi
using(DbConnection connection = Database.SystemConnection()) {
var results = connection.Query<int>(sql.GetViewable, new { Id });
}
SQL selalu dalam file yang mudah ditemukan atau kumpulan file, masing-masing dengan nama deskriptif yang menggambarkan apa yang dilakukannya daripada bagaimana melakukannya, masing-masing dengan ruang untuk komentar yang tidak akan mengganggu aliran kode aplikasi :
Metode sederhana ini mengeksekusi kueri soliter. Dalam pengalaman saya, skala manfaat sebagai penggunaan "bahasa asing" tumbuh lebih canggih.
Penggunaan file sumber daya oleh saya hanyalah sebuah contoh. Metode yang berbeda mungkin lebih tepat tergantung pada bahasa (SQL dalam kasus ini) dan platform.
Ini dan metode lain menyelesaikan daftar di atas dengan cara berikut:
- Kode basis data mudah ditemukan karena sudah terpusat. Dalam proyek yang lebih besar, kelompok seperti-SQL ke dalam file terpisah, mungkin di bawah folder bernama
SQL
.
- Dukungan untuk database kedua, ketiga, dll. Lebih mudah. Buat antarmuka (atau abstraksi bahasa lain) yang mengembalikan pernyataan unik setiap basis data. Implementasi untuk setiap database menjadi sedikit lebih dari pernyataan yang mirip dengan:
return SqlResource.DoTheThing;
Benar, implementasi ini dapat melewati sumber daya dan berisi SQL dalam sebuah string, tetapi beberapa (tidak semua) masalah di atas masih akan muncul.
- Refactoring sederhana - gunakan kembali sumber daya yang sama. Anda bahkan dapat menggunakan entri sumber daya yang sama untuk sistem DBMS yang berbeda sebagian besar waktu dengan beberapa pernyataan format. Saya sering melakukan ini.
- Penggunaan bahasa sekunder dapat menggunakan nama-nama deskriptif misalnya
sql.GetOrdersForAccount
daripada lebih tumpulSELECT ... FROM ... WHERE...
- Pernyataan SQL dipanggil dengan satu baris terlepas dari ukuran dan kompleksitasnya.
- SQL dapat disalin dan ditempel di antara alat basis data seperti SSMS dan SQL Developer tanpa modifikasi atau penyalinan yang cermat. Tidak ada tanda kutip. Tidak ada garis miring terbalik. Khusus untuk editor sumber daya Visual Studio, satu klik menyoroti pernyataan SQL. CTRL + C lalu tempelkan ke editor SQL.
Pembuatan SQL dalam suatu sumber daya cepat, sehingga ada sedikit dorongan untuk mencampur penggunaan sumber daya dengan SQL-in-code.
Terlepas dari metode yang dipilih, saya telah menemukan bahwa bahasa campuran biasanya mengurangi kualitas kode. Saya berharap bahwa beberapa masalah dan solusi yang dijelaskan di sini membantu pengembang menghilangkan bau kode ini bila perlu.