我一直都不喜歡在訪問數據庫時采用拼接SQL的方法,原因有以下幾點:
1. 不安全:有被SQL注入的風險。
2. 可能會影響性能:每條SQL語句都需要數據庫引擎執行[語句分析]之類的開銷。
3. 影響代碼的可維護性:SQL語句與C#混在一起,想修改SQL就得重新編譯程序,而且二種代碼混在一起,可讀性也不好。
所以我通常會選擇【參數化SQL】的方法去實現數據庫的訪問過程,而且會將SQL語句與項目代碼(C#)分離開。
不過,有些人可能會說:我的業務邏輯很復雜,Where中的過慮條件不可能事先確定,因此不拼接SQL還不行。
看到這些缺點,ORM用戶可能會認為:使用ORM工具就是終極的解決方案。
是的,的確ORM可以解決這些問題。
但是,解決方案并非只有ORM一種,還有些人就是喜歡寫SQL呢。
所以,這篇博客不是寫給ORM用戶的,而是寫給所有喜歡寫SQL語句的朋友。
CPQuery是什么?
看到博客的標題,你會不會想:CPQuery是什么?
下面是我的回答:
1. CPQuery 是一個縮寫:Concat Parameterized Query
2. CPQuery 可以讓你繼續使用熟悉的拼接方式來寫參數化的SQL
3. CPQuery 是我設計的一種解決方案,它可以解決拼接SQL的前二個缺點。
4. CPQuery 也是這個解決方案中核心類型的名稱。
希望大家能記住CPQuery這個名字。
CPQuery適合哪些人使用?
答:適合于喜歡手寫SQL代碼的人,尤其是當需要寫動態查詢時。
參數化的SQL語句
對于需要動態查詢的場景,我認為:拼接SQL或許是必需的,但是,你不要將數值也拼接到SQL語句中嘛,或者說,你應該拼接參數化的SQL來解決你遇到的問題。
說到【拼接參數化SQL】,我想解釋一下這個東西了。
這個方法的實現方式是:拼接SQL語句時,不要把參數值拼接到SQL語句中,在SQL語句中使用占位符參數,具體的參數值通過ADO.NET的command.Parameters.Add()傳入。現在流行的ORM工具應該都會采用這個方法。
我認為參數化的SQL語句可以解決本文開頭所說的那些問題,尤其是前二個。對于代碼的維護問題,我的觀點是:如果你硬是將SQL與C#混在一起,那么參數化的SQL語句也是沒有辦法的。如果想解決這個問題,你需要將SQL語句與項目代碼分離,然后可以選擇以配置文件或者存儲過程做為保存那些SLQ語句的容器。
所以,參數化的SQL并不是萬能的,代碼的可維護性與技術的選擇無關,與架構的設計有關。任何優秀的技術都可能寫出難以維護的代碼來,這就是我的觀點。
改造現有的拼接語句
還是說動態查詢,假設我有這樣一個查詢界面:
顯然,在設計程序時,不可能知道用戶會輸入什么樣的過濾條件。
因此,喜歡手寫SQL的人們通常會這樣寫查詢:
復制代碼 代碼如下:
var query = "select ProductID, ProductName from Products where (1=1) ";
if( p.ProductID > 0 )
query = query + " and ProductID = " + p.ProductID.ToString();
if( string.IsNullOrEmpty(p.ProductName) == false )
query = query + " and ProductName like '" + p.ProductName + "'";
if( p.CategoryID > 0 )
query = query + " and CategoryID = " + p.CategoryID.ToString();
if( string.IsNullOrEmpty(p.Unit) == false )
query = query + " and Unit = '" + p.Unit + "'";
if( p.UnitPrice > 0 )
query = query + " and UnitPrice >= " + p.UnitPrice.ToString();
if( p.Quantity > 0 )
query = query + " and Quantity >= " + p.Quantity.ToString();
復制代碼 代碼如下:
var query = "select ProductID, ProductName from Products where (1=1) ".AsCPQuery(true);
if( p.ProductID > 0 )
query = query + " and ProductID = " + p.ProductID.ToString();
if( string.IsNullOrEmpty(p.ProductName) == false )
query = query + " and ProductName like '" + p.ProductName + "'";
if( p.CategoryID > 0 )
query = query + " and CategoryID = " + p.CategoryID.ToString();
if( string.IsNullOrEmpty(p.Unit) == false )
query = query + " and Unit = '" + p.Unit + "'";
if( p.UnitPrice > 0 )
query = query + " and UnitPrice >= " + p.UnitPrice.ToString();
if( p.Quantity > 0 )
query = query + " and Quantity >= " + p.Quantity.ToString();
復制代碼 代碼如下:
新聞熱點
疑難解答