文章目錄
4.8 執行手動查詢
有很多原因決定你會手動寫查詢。或許由EF產生的SQL太慢,又或許執行起來浪費了太多資源。另一種情況可能是當你動態產生一個如此複雜的查詢,建立SQL代碼比使用LINQ to Entities簡單。
在這種情況下,你自己可以使用ObjectContext類的ExecuteStoreQuery<T>建立一個SQL命令。它允許你發出任意的查詢並將查詢結果映射到類。它的使用如下:
var details = ctx.ExecuteStoreQuery<OrderDetail>("Select * from OrderDetail");
就是這麼簡單。查詢被執行,列自動對應到實體。關於映射階段有一個小小的提示:它繞過了EDM,而是使用另外一種基於屬性-列名匹配的機制。這一行為
這個映射解決方案的有一定的局限性:
1.如果一個屬性跟相應的列名稱不同,映射就不會執行並拋出異常。你可以很容易的通過在查詢中使用AS SQL子句重新命名列解決這個問題。
2.不能映射到有複雜屬性的實體,因為沒有辦法將複雜屬性內的名稱和列進行匹配。
當然,你可以映射返回的資料到任何類型的類,不僅僅是那些定義在EDM中的。假設,你建立了一個類OrderDetailProjection,有Quantity,UnitPrice和Discount屬性。你可以寫一個查詢,從OrderDetail表中取出Quantity, UnitPrice和 Discount列,並將結果映射到OrderDetailProjection。
public class OrderDetailProjection{ public int Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Discount { get; set; }}
var details = ctx.ExecuteStoreQuery<OrderDetailProjection> ("Select quantity, unitprice, discount from OrderDetail");
注意:
通過ExecuteStoreQuery<T>,你也可以啟動預存程序。但是我們不建議這樣做,因為EF本身就支援預存程序並且提供了比ExecuteStoreQuery<T>更多的選項。
如果查詢帶有參數,你可以使用ExecuteStoreQuery<T>的重載方法,它接受參數列表。這可能會非常棘手,所以讓我們看一看。
4.8.1 帶參數的查詢
當初EF團隊設計如何傳遞參數時,有很多的選擇。通過選擇,現在有兩種巨集指令引數的方式。
1.使用帶有編號清單,跟String.Format方法一樣。
2.使用ADO.NET文法
下面,我們詳細看一下這兩種方式。
使用帶編號的列表
這是最簡單的辦法。在SQL查詢中,將數字放在花括弧中代表參數。然後,你在方法的第二個參數中傳遞參數。參數既可以是簡單的數值也可以是DbParameter類的執行個體。這裡有一些建議,要牢記:
1.如果你使用DbParameter執行個體,你必須使用與資料庫Provider相應的具體類型。例如,如果對SQL Server就必須使用SqlParameter,對OLE DB就必須使用OleDbParameter。如果你使用其他的執行個體,在運行時會得到InvalidCastException。
2.如果你有多個參數,你不能混用DbParameter執行個體和純值。你必須選擇其一。否則,在運行時會得到InvalidOperationException。
3.如果你使用簡單的數值作為參數,它們傳過來的順序必須和它們在查詢中的順序一樣。
使用簡單數值傳遞參數
var names = ctx.ExecuteStoreQuery<string> ("SELECT name FROM company WHERE shippingcity = {0} and billingcity = {1}", "New York", "Seattle");
使用SqlParameter傳遞參數
var p0 = new SqlParameter("p0", DbType.String) { Value = "New York" };var p1 = new SqlParameter("p1", DbType.String) { Value = "Seattle" };var names = ctx.ExecuteStoreQuery<string>("SELECT name FROM company WHERE shippingcity = {0} and billingcity = {1}", p0, p1);
正如你所見,這裡沒有什麼特別困難的。只要注意我們前面提到的陷阱即可。
注意:
即使此文法可能會導致你認為可以通過SQL注入攻擊的影響,但絕對不是這種情況。參數往往作為安全的方式傳遞給資料庫。
現在,讓我們繼續談談使用傳統的參數
使用傳統的參數
通過經典的ADO.NET寫查詢時,你通常寫這樣的東西表達參數:
SELECT * FROM table WHERE id = @id
這種文法對SQL Server provider是有效。如果你使用OLE DB provider,你必須問號(?)代替@paramname。
這種方法使用ExecuteStoreQuery<T>方法仍然完全有效。不是將數字放在花括弧中,而是放置參數。其他都一樣。參數的值仍然可以作為簡單數值或參數傳遞過來。
使用簡單數值傳遞參數
var names = ctx.ExecuteStoreQuery<string> ("SELECT name FROM company WHERE shippingcity = @p0 and billingcity = @p1", "New York", "Seattle");
使用SqlParameter傳遞參數
var p0 = new SqlParameter("p0", DbType.String) { Value = "New York" };var p1 = new SqlParameter("p1", DbType.String) { Value = "Seattle" };var names = ctx.ExecuteStoreQuery<string>("SELECT name FROM company WHERE shippingcity = @p0 and billingcity = @p1", p0, p1);
如果將這段代碼和前面的代碼比較,你會發現只是SQL代碼中參數聲明發生了改變。剩下的仍然是相同的,也就是說,沒有更多的東西需要學習。
到目前為止,我們已經介紹了如何編寫功能強大的查詢。現在,我們換個角度思考:你有多少相關的實體需要查詢?是直接檢索出所有相關的實體,還是根據代碼需要。這顯然是一個擷取(fetching)問題,它幾乎獨立與你寫的任何查詢。