Có, các chuỗi SQL mã hóa cứng thành mã ứng dụng thường là một mô hình chống.
Chúng ta hãy cố gắng bỏ qua sự khoan dung mà chúng ta đã phát triển sau nhiều năm nhìn thấy điều này trong mã sản xuất. Trộn các ngôn ngữ hoàn toàn khác nhau với cú pháp khác nhau trong cùng một tệp thường không phải là một kỹ thuật phát triển mong muốn. Điều này khác với các ngôn ngữ mẫu như Dao cạo được thiết kế để mang ý nghĩa theo ngữ cảnh cho nhiều ngôn ngữ. Như Sava B. đề cập trong một bình luận bên dưới, SQL trong C # hoặc ngôn ngữ ứng dụng khác của bạn (Python, C ++, v.v.) là một chuỗi giống như bất kỳ chuỗi nào khác và không có ý nghĩa về mặt ngữ nghĩa. Điều tương tự cũng áp dụng khi trộn nhiều hơn một ngôn ngữ trong hầu hết các trường hợp, mặc dù rõ ràng có những tình huống làm như vậy là chấp nhận được, chẳng hạn như lắp ráp nội tuyến trong C, các đoạn CSS nhỏ và dễ hiểu trong HTML (lưu ý rằng CSS được thiết kế để trộn với HTML ), và những người khác.
(Robert C. Martin về pha trộn ngôn ngữ, Clean Code , Chương 17, "Mùi mã và heuristic", trang 288)
Đối với câu trả lời này, tôi sẽ tập trung SQL (như được hỏi trong câu hỏi). Các vấn đề sau có thể xảy ra khi lưu trữ SQL dưới dạng tập hợp các chuỗi tách rời:
- Cơ sở dữ liệu logic rất khó định vị. Bạn tìm kiếm gì để tìm tất cả các câu lệnh SQL của bạn? Chuỗi có "CHỌN", "CẬP NHẬT", "MERGE", v.v.?
- Việc tái cấu trúc việc sử dụng cùng một SQL hoặc tương tự trở nên khó khăn.
- Thêm hỗ trợ cho các cơ sở dữ liệu khác là khó khăn. Làm thế nào một người sẽ thực hiện điều này? Thêm câu lệnh if..then cho mỗi cơ sở dữ liệu và lưu trữ tất cả các truy vấn dưới dạng chuỗi trong phương thức?
- Các nhà phát triển đọc một tuyên bố bằng ngôn ngữ khác và bị phân tâm bởi sự thay đổi trọng tâm từ mục đích của phương thức sang chi tiết triển khai của phương thức (cách thức và nơi lấy dữ liệu).
- Mặc dù một lớp có thể không có quá nhiều vấn đề, các chuỗi SQL nội tuyến bắt đầu bị phá vỡ khi các câu lệnh trở nên phức tạp hơn. Bạn làm gì với một tuyên bố dòng 113? Đặt tất cả 113 dòng trong phương pháp của bạn?
- Làm thế nào để nhà phát triển di chuyển hiệu quả các truy vấn qua lại giữa trình soạn thảo SQL (SSMS, SQL Developer, v.v.) và mã nguồn của họ?
@
Tiền tố của C # làm cho việc này dễ dàng hơn, nhưng tôi đã thấy rất nhiều mã trích dẫn từng dòng SQL và thoát khỏi dòng mới.
"SELECT col1, col2...colN"\
"FROM painfulExample"\
"WHERE maintainability IS NULL"\
"AND modification.effort > @necessary"\
- Các ký tự thụt đầu dòng được sử dụng để căn chỉnh SQL với mã ứng dụng xung quanh được truyền qua mạng với mỗi lần thực hiện. Điều này có thể không đáng kể đối với các ứng dụng quy mô nhỏ, nhưng nó có thể tăng lên khi mức độ sử dụng của phần mềm tăng lên.
Các ORM đầy đủ (Các trình ánh xạ quan hệ đối tượng như Entity Framework hoặc Hibernate) có thể loại bỏ SQL tiêu ngẫu nhiên trong mã ứng dụng. Việc sử dụng SQL và các tệp tài nguyên của tôi chỉ là một ví dụ. Các ORM, các lớp trợ giúp, v.v ... đều có thể giúp hoàn thành mục tiêu của mã sạch hơn.
Như Kevin đã nói trong một câu trả lời trước đó, SQL trong mã có thể được chấp nhận trong các dự án nhỏ, nhưng các dự án lớn bắt đầu như các dự án nhỏ và xác suất hầu hết các đội sẽ quay lại và thực hiện đúng tỷ lệ nghịch với kích thước mã.
Có nhiều cách đơn giản để giữ SQL trong một dự án. Một trong những phương pháp mà tôi thường sử dụng là đặt từng câu lệnh SQL vào tệp tài nguyên Visual Studio, thường được đặt tên là "sql". Tệp văn bản, tài liệu JSON hoặc nguồn dữ liệu khác có thể hợp lý tùy thuộc vào công cụ của bạn. Trong một số trường hợp, một lớp riêng dành riêng cho việc liên kết các chuỗi SQL có thể là tùy chọn tốt nhất, nhưng có thể có một số vấn đề được mô tả ở trên.
Ví dụ SQL: Cái nào trông thanh lịch hơn?:
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 });
}
Trở thành
using(DbConnection connection = Database.SystemConnection()) {
var results = connection.Query<int>(sql.GetViewable, new { Id });
}
SQL luôn ở trong một tệp dễ định vị hoặc tập hợp các tệp, mỗi tệp có một tên mô tả mô tả những gì nó làm thay vì cách thực hiện, mỗi tệp có một không gian cho một nhận xét sẽ không làm gián đoạn dòng mã ứng dụng :
Phương pháp đơn giản này thực hiện một truy vấn đơn độc. Theo kinh nghiệm của tôi, các thang đo lợi ích khi sử dụng "ngoại ngữ" ngày càng tinh vi hơn.
Việc tôi sử dụng một tệp tài nguyên chỉ là một ví dụ. Các phương pháp khác nhau có thể phù hợp hơn tùy thuộc vào ngôn ngữ (SQL trong trường hợp này) và nền tảng.
Phương pháp này và các phương pháp khác giải quyết danh sách trên theo cách sau:
- Mã cơ sở dữ liệu dễ dàng xác định vị trí vì nó đã được tập trung. Trong các dự án lớn hơn, nhóm like-SQL thành các tệp riêng biệt, có thể trong một thư mục có tên
SQL
.
- Hỗ trợ cho cơ sở dữ liệu thứ hai, thứ ba, vv dễ dàng hơn. Tạo một giao diện (hoặc trừu tượng ngôn ngữ khác) trả về các câu lệnh duy nhất của cơ sở dữ liệu. Việc triển khai cho mỗi cơ sở dữ liệu trở nên ít hơn các câu lệnh tương tự như:
return SqlResource.DoTheThing;
Đúng, các triển khai này có thể bỏ qua tài nguyên và chứa SQL trong một chuỗi, nhưng một số (không phải tất cả) các vấn đề ở trên vẫn sẽ xuất hiện.
- Tái cấu trúc rất đơn giản - chỉ cần sử dụng lại cùng một tài nguyên. Thậm chí, bạn có thể sử dụng cùng một mục nhập tài nguyên cho các hệ thống DBMS khác nhau với một vài câu lệnh định dạng. Tôi làm điều này thường xuyên.
- Sử dụng ngôn ngữ thứ cấp có thể sử dụng tên mô tả, ví dụ như
sql.GetOrdersForAccount
thay vì khó hiểu hơnSELECT ... FROM ... WHERE...
- Các câu lệnh SQL được triệu tập với một dòng bất kể kích thước và độ phức tạp của chúng.
- SQL có thể được sao chép và dán giữa các công cụ cơ sở dữ liệu như SSMS và SQL Developer mà không cần sửa đổi hoặc sao chép cẩn thận. Không có dấu ngoặc kép. Không có dấu gạch chéo ngược. Trong trường hợp trình soạn thảo tài nguyên Visual Studio cụ thể, một cú nhấp chuột làm nổi bật câu lệnh SQL. CTRL + C và sau đó dán nó vào trình soạn thảo SQL.
Việc tạo SQL trong một tài nguyên là nhanh chóng, do đó có rất ít động lực để trộn việc sử dụng tài nguyên với SQL-in-code.
Bất kể phương pháp được chọn, tôi đã thấy rằng các ngôn ngữ trộn thường làm giảm chất lượng mã. Tôi hy vọng rằng một số vấn đề và giải pháp được mô tả ở đây sẽ giúp các nhà phát triển loại bỏ mùi mã này khi thích hợp.