新Orcas語言特性-查詢句法_oracle

來源:互聯網
上載者:User

【原文地址】New "Orcas" Language Feature: Query Syntax
【原文發表日期】 Saturday, April 21, 2007 2:12

上個月我開始了一個貼子系列,討論作為Visual Studio和.NET架構Orcas版本一部分發布的一些新的VB和C#語言特性。下面是該系列的前三篇貼子的連結:

  • 自動屬性,對象初始化器,和集合初始化器
  • 擴充方法
  • Lambda運算式

今天的貼子要討論另一個基礎性的新語言特性:查詢句法(Query Syntax)。

什麼是查詢句法(Query Syntax)?

查詢句法是使用標準的LINQ查詢運算子來表達查詢時一個方便的聲明式簡化寫法。該句法能在代碼裡表達查詢時增進可讀性和簡潔性,讀起來容易,也容易讓人寫對。Visual Studio 對查詢句法提供了完整的intellisense和編譯時間檢查支援。

在底下,C#和VB編譯器則把查詢句法的運算式翻譯成明確的方法調用代碼,這樣的代碼利用了Orcas中的新的擴充方法和Lambda運算式語言特性。

查詢句法的例子:

在我以前的語言系列貼子裡,我示範了你可以象下面這樣聲明一個Person類:

 

然後我們可以使用下面這樣的代碼,用一些個人資訊來產生一個List<Person>集合執行個體,然後使用查詢句法來對該集合做一個LINQ查詢,只取出那些姓(last name)的首字母為G的人,按名字(first name)來排序(升序):

上面查詢句法的運算式在語意上與下面明確使用LINQ擴充方法和Lambda運算式的代碼是等同的:

使用查詢句法方法的好處是,結果會是稍微容易讀寫些,這在運算式變得更繁複時尤其如此。

查詢句法 - 理解from和select子句:

在C#中,每個查詢運算式的句法從from子句開始,以select或group子句結束。from子句表示你要查詢什麼資料。select子句則表示你要返回什麼資料,且應該以什麼構形返回。

譬如,讓我們再來看一下我們對List<Person>集合的查詢:

在上面的程式碼片段裡,"from p in people"表示了我要對"people" 這個集合做一個LINQ查詢,我將用參數"p"代表我正查詢的輸入序列的每個項。我們將參數命名為"p" 這個事實是無關緊要的,我完全可以很容易地將其命名為"o", "x", "person"或我想要的任何名字。

在上面的程式碼片段裡,語句結尾的"select p"子句表示,作為查詢的結果,我要返回一個Person對象的IEnumerable序列。這是因為"people"集合包含了Person類型的對象,而參數p則代表了輸入序列中的Person對象。因此,該查詢句法運算式的結果資料類型是IEnumerable<Person>。

假如不是返回Person對象,我想返回該集合中的人的名字,我可以把查詢改寫成這樣:

注意上面我不再說"select p",而是說"select p.FirstName"。這表示我不想返回一串Person對象,而是想返回一串字串,由Person對象的FirstName屬性(該屬性是個字串)填充而來。 因此,該查詢句法運算式的結果類型是 IEnumerable<string>。

針對資料庫的查詢句法的例子

LINQ的妙處在於,我可以針對任何資料類型使用完全一樣的查詢句法。譬如,我可以使用Orcas提供的新LINQ到SQL對象關係映射器支援,對SQL伺服器的Northwind資料庫進行建模,產生下面這些類(請觀看我這裡的錄影來學習該如何?):

在上面定義好類模型之後(以及它與資料庫間的映射關係),然後我就可以寫個查詢句法的運算式取出那些單價大於99元的產品:

在上面的程式碼片段裡,我表示我要對NorthwindDataContext類的Products表進行一個LINQ查詢,NorthwindDataContext類是由Visual Studio orcas的ORM設計器產生的。"select p"表示我要返回匹配我的查詢的一串Product對象,因此,該查詢句法運算式的結果資料類型是IEnumerable<Product>。

就象前面List<Person>查詢句法的例子一樣,C# 編譯器會把我們的聲明式查詢句法翻譯成明確的擴充方法調用(使用Lambda運算式作為參數)。在上面的LINQ到SQL的例子的情形下,這些Lambda運算式會被轉化成SQL命令,然後在SQL伺服器上做運算(這樣,只有那些匹配查詢條件的Product記錄行會返回到我們的應用中)。促成這個Lambda->SQL 轉化的機制的細節可見於我的Lambda運算式部落格貼子的"Lambda運算式樹狀架構"部分。

查詢句法 - 理解where和orderby子句:

在一個查詢句法運算式開頭的"from" 子句和結尾的"select"子句之間,你可以使用最常見的LINQ查詢運算子來過濾和轉換你在查詢的資料。兩個最常用的子句是"where"和"orderby"。這兩個子句處理對結果集的過濾和排序。

譬如,要從Northwind資料庫裡返回按字母降序排列的分類名稱列表,過濾條件是只包括那些含有5個以上產品的分類,我們可以編寫下面這樣的查詢句法來用LINQ到SQL對我們的資料庫做查詢:

在上面的運算式裡,我們加了 "where c.Products.Count > 5" 子句來表示我們只要那些含有5個以上產品的分類。這利用了資料庫中產品和分類間的LINQ到SQL的ORM映射的關聯。在上面的運算式中,我也加了"order by c.CategoryName descending"子句來表示我要將結果集按名稱降序排列。

LINQ到SQL然後就會在使用這個運算式查詢資料庫時,產生下列SQL:

Select [t0].[CategoryName] FROM [dbo].[Categories] AS [t0]
Where ((
    Select COUNT(*)
    FROM [dbo].[Products] AS [t1]
    Where [t1].[CategoryID] = [t0].[CategoryID]
)) > 5
ORDER BY [t0].[CategoryName] DESC

注意,LINQ到SQL很聰明,只返回了我們所需的單個欄位(分類名稱), 而且它是在資料庫層做了所有的過濾和排序,使得該查詢效率非常高。

查詢句法 - 用投影(Projection)來轉換資料

先前我指出的一個要點是,"select" 子句表示了你要返回的資料,以及這個資料的構形是什麼。

譬如,假如你有個象下面這樣的"select p" 子句,這裡p的類型是Person,然後,它就會返回一串Person對象:

LINQ和查詢句法提供的一個非常強大的功能是允許你定義跟被查詢的資料分開的新的類型,然後用新的類型來控制查詢返回的資料的形狀和結構。

譬如,假設我們定義了一個新的AlternatePerson類,內含一個FullName屬性,而不是我們原先的Person類內的分開的FirstName和LastName屬性:

然後我就可以使用下面的LINQ查詢句法來查詢我原先的List<Person>集合,用下面的查詢句法將結果轉換成一串AlternatePerson對象:

注意看,我們是如何在上面的運算式裡的"select"子句裡,使用我的語言系列的第一個貼子裡討論過的新的對象初始化器句法來建立新的AlternatePerson執行個體,同時設定它的屬性的。也注意我是如何串連我們原先Person類的FirstName和LastName屬性,然後將其賦值給FullName屬性的。

對資料庫使用查詢句法投影

這個投影特性在操作從象資料庫這樣一個遠端資料提供器那裡取回的資料時,會變得難以置信地有用,因為它提供給我們一個優雅的方式,來表示我們的ORM應該從資料庫實際取回哪些資料欄位。

譬如,假設我用了LINQ到SQL的ORM提供器對Northwind資料庫建模,產生下面這些類:

通過編寫下面這個LINQ查詢,我告訴LINQ到SQL我要返回一串Product對象:

填充Product類所需的所有欄位都將作為上面查詢的一部分從資料庫中返回,由LINQ到SQL orM執行的raw SQL看上去象下面這樣:

Select [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID],
              [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock],
              [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]
FROM [dbo].[Products] AS [t0]
Where [t0].[UnitPrice] > 99

在一些情境下,我不需要也不用所有這些欄位,我可以定義一個下面這樣的新的MyProduct類,只擁有Product類具有的部分屬性,以及一個Product類並不具有的額外屬性,TotalRevenue (註: 對那些不熟悉C#的,Decimal?句法表示我們的UnitPrice屬性是個nullable值):

 

然後我就可以使用下面這個查詢,使用查詢句法的投影功能來構造我要從資料庫返回的資料的形狀:

這表明,不是返回一串Product對象,我要MyProduct對象,我只要其中三個屬性被賦值,LINQ到SQL就會很聰明地調整要執行的raw SQL語句,從資料庫只返回那三個需要的產品欄位:

Select [t0].[ProductID], [t0].[ProductName], [t0].[UnitPrice]
FROM [dbo].[Products] AS [t0]
Where [t0].[UnitPrice] > 99

為炫耀起見,我也可以填充MyProduct類的第四個屬性,即TotalRevenue屬性。我要這個值等於我們產品目前的銷售額的總量。這個值在Northwind資料庫中並沒有作為一個預先算好的欄位而存在。而是,你需要在Products表和Order Details表間做一個關聯,然後計算出一個給定產品對應的所有的Order Detail 行的總量。

非常酷的是,我可以在Product類的OrderDetails關聯上使用LINQ的 Sum 這個擴充方法,編寫一個作為我的查詢句法投影一部分的乘法Lambda運算式,來計算這個值:

LINQ到SQL就會非常聰明地使用下面這個SQL在SQL資料庫裡做運算:

Select [t0].[ProductID], [t0].[ProductName], [t0].[UnitPrice], (
        Select SUM([t2].[value])
        FROM (
                 Select [t1].[UnitPrice] * (CONVERT(Decimal(29,4),[t1].[Quantity])) AS [value], [t1].[ProductID]
                 FROM [dbo].[Order Details] AS [t1]
                 ) AS [t2]
        Where [t2].[ProductID] = [t0].[ProductID]
        ) AS [value]
FROM [dbo].[Products] AS [t0]
Where [t0].[UnitPrice] > 99

查詢句法 - 理解順延強制(Deferred Execution)和使用ToList() 和ToArray()

在預設情形下,查詢句法運算式的結果的類型是IEnumerable<T>。在上面的例子裡,你會注意到所有的查詢句法賦值是給IEnumerable<Product>, IEnumerable<string>, IEnumerable<Person>, IEnumerable<AlternatePerson>, 和 IEnumerable<MyProduct> 變數的。

IEnumerable<T>介面的一個很好的特徵是,實現它們的對象可以把實際的查詢運算延遲到開發人員第一次試圖對傳回值進行迭代(這是通過使用最早在VS 2005中C# 2.0 中引進的yield構造來達成的)時才進行。LINQ和查詢句法運算式利用了這個特性,將查詢的實際運算延遲到了你第一次對傳回值進行迴圈時才進行。假如你對IEnumerable<T>的結果從不進行迭代的話,那麼查詢根本就不會執行。

譬如,考慮下面這個LINQ到SQL的例子:

不是在查詢句法運算式聲明的時候,而是在我們第一次試圖對結果進行迴圈(上面紅箭頭標誌的地方),才會去訪問資料庫以及取出填充Category對象所需的值。

這個延遲運算的行為結果變得非常有用,因為它促成了一些把多個LINQ查詢和運算式鏈在一起的強有力的組合情境。譬如,我們可以把一個運算式的結果餵給另一個運算式,然後通過延遲運算,允許象LINQ 到SQL這樣的ORM根據整個運算式樹狀架構來最佳化raw SQL。我將在以後的一個部落格貼子裡對這樣的情境做示範說明。

如何立刻對查詢句法運算式做運算

如果你不要延遲查詢運算,而是要對它們立刻就執行運算,你可以使用內建的ToList() 和ToArray() 運算子來返回一個包括了結果集的List<T>或者數組。

譬如,要返回一個基於範型的 List<T> 集合的話:

要返回一個數組的話:

在上面兩種情形下,會立刻訪問資料庫,填充Category對象。

結語

查詢句法在使用標準的LINQ查詢運算子來表達查詢時,提供了非常方便的聲明式簡化寫法。它提供的句法可讀性非常高,可以針對任何類型的資料(記憶體中的集合,數組,XML內容,以及象資料庫這樣的遠端資料提供器,web服務等等)進行查詢。一旦你熟悉這個句法後,你可以在任何地方應用這個知識。

在不遠的將來,我將結束本語言系列的最後一部分,該部分將討論新的匿名型別特性。然後我將轉而討論在實際應用中使用所有這些語言特性的一些非常實用的例子(特別是針對資料庫和XML檔案使用LINQ的例子)。

希望本文對你有所協助,

Scott

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.