先給關注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};
第一個屬性的名字不會變,第二個會被重新命名。
好,就先講這幾個,下節我會介紹幾個更復雜的用法。