先給關注dlinq的朋友們道歉,最近工作實在忙,沒有時間來寫blog。從本節開始,我們講dlinq文法咯。我們先從select子句講起。看下面的例子。 var q =
from c in db.Customers
select c.ContactName;
這是一個最簡單的dlinq查詢語句,查詢得到連絡人的名字。在這裡,我需要提醒下大家的是,像這個語句只是個聲明,dlinq並沒有真正把資料取出來,只有當你需要該資料的時候,它才會幫你去取,這就是消極式載入(deferred loading)。如果,你想在聲明的時候就希望dlinq幫你取到資料,你可以使用ToList() 或ToArray()方法。如上例。
var q = (from c in db.Customers
select c.ContactName).ToArray();
或
var q = (from c in db.Customers
select c.ContactName).ToList();
在這裡,我還要提醒大家一點。dlinq返回的結果集是對象的集合,不是資料的。
在dlinq執行的時候,它會先將上面的標準查詢轉換成dlinq的API(也有人叫級連方法),比如,下面語句
var q =
from c in db.Customers
where c.City == "London"
select c;
就會先被轉化成 var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 也就是說,這兩個語句是等價的。而後,dlinq會解析影射檔案,根據dlinq的query語句,自動產生sql語句,並把sql送到sql server伺服器,根據返回的資料集,建立相應的對象。在這裡,你可能會對c=>c感到非常陌生。這是Lambda運算式(expression),你可以理解c為結果集裡的任一對象,這對象的類型是和你結果集裡元素類型是一致的 。這裡理解起來可能困難。我們一起來理解下資料即是對象的概念。我相信這會幫我們理解Lambda運算式。
在dlinq之前,在java領域有Hibernate,在net領域有NHibernate技術,來實現object/relational 持久和查詢服務。dlinq其實質上,是在吸收了眾多技術的基礎上,比他們更加強大的工具。資料即對象的含義有兩層。第一,資料結構(表結構)即是類。可以描述為Table Schema--Class。第二,表裡的資料即是變數,描述為Data--object(variable)。那麼,我們在來理解Lambda運算式可能就容易些。剛才我們已經說了,var q = db.Customers.Where(c=>c.City== "London").Select(c=>c);將會返回Customers對象的集合,也就說,這個集合的每個元素就是一個Customer。Lambda運算式是對c# 2.0中的anonymous methods(匿名方法)的擴充。它更加簡化匿名方法的實現形式。這裡的c是一種隱式的聲明,編譯器會自動推斷它的實際類型,也可以顯示聲明,比如, var q = db.Customers.Where((Customer c) => c.City == "London").ToList(); Lambda運算式用=>符號跟隨一個運算式,這個運算式,需要返回一個類型,其實質就是一個方法返回一個類型。它只是更加簡潔的匿名方法。然後,where等操作符用它返回的這個類型做為參數。關於Lambda運算式的具體實現,我會在進階部分詳細講解。這裡不再贅述。
有一點要提醒大家的是,標準的查詢語句,必須是select語句在最後,而級連運算式,各種操作符的位置並不是很重要。比如var q = db.Customers.Where(c=>c.City== "London").Select(c=>c); 可以寫成var q = db.Customers.Select(c=>c).Where(c=>c.City== "London");它們兩個是一樣的,但是,標準查詢就不可以換位子,select語句必須在最後。雖然在級連運算式,各種操作符的位置並不是很重要,但是他們還是有區別的。特別是在使用匿名類後,區別很明顯。但萬變不離其宗,我們只要記住,下一個操作符總是在上一個操作符所篩選的資料集的基礎上進行篩選。這點,我會在以後的blog中,更加詳細的說明。
在select語句中,另一個痛點是匿名類。比如列子
var q =
from c in db.Customers
select new {c.ContactName, c.Phone};
其實不光在select操作中有匿名類,其他動作中也有。讓我們一起來理解下匿名類。上面的語句與
var q = db.Customers.Select(c=>new {c,ContactName,c.Phone});是等價的。匿名類是c# 3.0中新出現的特性。其實質是編譯器根據使用者定義,自動產生一個匿名的類,幫使用者實現臨時變數的儲存。注意,是臨時變數。大量使用匿名類會使程式可讀性降低。匿名類還依賴於另外一個特性,就是在c# 3.0可以支援根據property來建立對象。比如,有類
public class Person
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
以前,我們只可以用建構函式來建立其對象,現在在3.0中支援用property來建立,即,可以用
var d = new Person { Name = "s" }; 來建立對象。在這裡,你可能還對var類型產生疑問。你可能以為c#3.0和javascript一樣是弱類型的。其實var並不是c#3.0的類型,它是編譯器的關鍵字,編譯器根據實際變數的傳回型別,自動推斷類型。那麼var c = null; 是無法編譯通過,因為編譯不知道null代表那個類型。所以,c#3.0還是強型別的。
現在3.0可以支援用property來建立對象了,那麼就有了匿名類的出現。比如,var d = new { Name = "s" };編譯器自動產生一個有property叫做Name的匿名類,然後按這個類型分配記憶體,並初始化對象。在這個地方,還有個問題,比如,var d = new { "s" };是編譯不通過的。因為,編譯器不知道匿名類中的property的名字。但是,如果, string c = "d"; var d = new { c}; 則是可以通過編譯的。編譯器會建立一個叫做匿名類帶有叫c的property。
在dlinq中,比如new {c,ContactName,c.Phone});這裡出現ContactName和Phone都是我們在影射檔案中定義的和表中欄位相對應的property。編譯器在取會資料並建立對象時,會建立一個匿名類,這個類有兩個屬性,為ContactName和Phone,然後根據資料初始化對象。匿名類還有另外一種形式。
var q =
from e in db.Employees
select new {Name = e.FirstName + " " + e.LastName, Phone = e.HomePhone};
這種形式和第一種不同的是,編譯器會重新命名property的名字。當然也可以把兩種形式組合起來。
var q =
from p in db.Products
select new {p.ProductID, HalfPrice = p.UnitPrice / 2};
第一個屬性的名字不會變,第二個會被重新命名。
好,就先講這幾個,下節我會介紹幾個更複雜的用法。