【原文發(fā)表日期】Sunday, May 14, 2006 9:49 PM 最近使我激動(dòng)不已的新鮮事之一就是LINQ系列技術(shù)的出現(xiàn),包括LINQ,DLINQ,XLINQ和不久后的其他技術(shù)。 LINQ將被完全集成到代號(hào)為Orcas的下個(gè)版本Visual Studio中,而且它也包含了一些非??岬目蚣芎?工具支持,包括完全的智能感知和可視化設(shè)計(jì)器支持。你可以在這兒下載上周發(fā)布的LINQ五月份CTP版。這個(gè)CTP版 本的亮點(diǎn)就是它能在VS 2005上運(yùn)行,使你能夠立即開始深入研究LINQ。它實(shí)現(xiàn)了很多用戶的反饋(例 如:在DLINQ中添加了對(duì)存儲(chǔ)過程的支持),并且包含了一個(gè)內(nèi)置的ASP.NET網(wǎng)站項(xiàng)目模板來幫助你在ASP.NET 中使用它(注意:你也可以在VS 2005 Web Application Project 中使用LINQ)。 我將在接下來的幾周中發(fā)表一系列文章來介紹怎樣在ASP.NET工程 中使用LINQ/DLINQ/XLINQ。下面這第一個(gè)走過場的示范將幫助你了解一些LINQ重要的基本概念。你可以下 載LINQ五月份CTP版,然后隨著文章的進(jìn)行逐步輸入相應(yīng)代碼(在下面我會(huì)列出所有代碼),或者你也可以 在這兒下載并運(yùn)行我所做示例的 完整.zip文件(注意:你仍然需要下載LINQ五月份版來運(yùn)行.zip文件中的示 例)。
注意:C#和VB都完全支持LINQ,DLINQ和XLINQ。在下面的示例中 我將使用C#。 第0步:建立一個(gè)C# LINQ ASP.NET網(wǎng)站 建立一個(gè)能使用LINQ/DLINQ/XLINQ和新的C#3.0語言特性的ASP.NET網(wǎng)站,在VS中選擇文件->新建網(wǎng)站然后選 擇"LINQ ASP.NET Web Site Template": 默認(rèn)會(huì)創(chuàng)建一個(gè)如下所示的網(wǎng)站 工程:
注意它在\bin文件夾中引入了一些LINQ程序集。它同樣在web.config文件中添加了一些配置以告訴VS和ASP.NET 使用C# 3.0編譯器來編譯和運(yùn)行程序: <system.codedom> <compilers> <compiler language="c#;cs;csharp" & nbsp; extension=".cs" type="Microsoft.CSharp.CSharp3CodeProvider, CSharp3CodeDomProvider"/> </compilers> </system.codedom> 注意C# 3.0編譯器和CodeDOM提供器可以和C# 2.0版本并肩運(yùn)行, 因此你無需擔(dān)心安裝LINQ會(huì)破壞VS或ASP.NET。 第一步:建立第一個(gè)使用了LINQ的ASP.NET頁 面
新建一個(gè)叫Step1.aspx的新頁面。添加一個(gè)GridView控件到頁面中,如下所 示: <%@ Page Language="C#" CodeFile="Step1.aspx.cs" Inherits="Step1" %>
<html> <body> <form id="form1" runat="server"> <div> <h1>City Names</h1> <asp:GridView ID="GridView1" runat="server"> </asp:GridView> </div> </form> </body> </html> 然后在后臺(tái)代碼文件中我們將編寫經(jīng)典的“hello world”LINQ示例-包括對(duì)一列字符串的搜索和排 序:
using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Query; public partial class Step1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string[] cities = { " & nbsp; " & nbsp; " GridView1.DataSource = from city in cities & nbsp; where city.Length > 4 & nbsp; orderby city & nbsp; select city.ToUpper(); GridView1.DataBind(); } }
在上面的示例中,我列出了一組我今年一月到五月所去過的城市的名稱。然后我用LINQ查詢表達(dá)式(query expression)對(duì)這個(gè)數(shù)組進(jìn)行操作。這個(gè)查詢表達(dá)式返回名字多于4個(gè)字符的所有城市,然后按照城市名 稱的字母進(jìn)行排序并把名字轉(zhuǎn)換為大寫。 LINQ查詢返回如下類型:IEnumerable<T>-"select"子句選擇的對(duì)象類型決定了這里 的<T>的類型。因?yàn)樯厦胬又?city"是一個(gè)字符串,所以類型安全的結(jié)果是一個(gè)如下所示的基于泛型 的集合: IEnumerable<string> result = from city in cities & nbsp; where city.Length > 4 & nbsp; orderby city & nbsp; select city.ToUpper();
因?yàn)锳SP.NET控件能綁定到任何的IEnumerable集合,所以我們可以很容易的把LINQ查詢結(jié)果綁定到GridView中, 然后調(diào)用DataBind()方法來生成如下的頁面輸出: 注意,除了可以使用上面的GridView控件外,我也可以使用 <asp:repeater>, <asp:datalist>, <asp:dropdownlist>, 或者任何其他ASP.NET的列表 控件(可以是產(chǎn)品自帶或者開發(fā)人員自己開發(fā)的控件)。在這些示例中我只使用了<asp:gridview>-但 是你們可以使用任何其他的控件。 第二步:使用功能更豐富的集合 搜索一個(gè)數(shù)組的字符串并沒多大意思,雖然有時(shí)候很有用。如果我們能對(duì)自己的功能更豐富的那些集合中搜索將 會(huì)更有趣。好消息是,LINQ使這些變得很簡單。例如,為了更好記錄我去過的地方,我在我的工程中建立了一 個(gè)叫"Location"的簡單類: using System; public class Location { // Fields private string _country; private int _distance; private string _city; // Properties public string Country { get { return _country; } set { _country = value; } } public int Distance { get { return _distance; } set { _distance = value; } } public string City { get { return _city; } set { _city = value; } } } 它公開了三個(gè)屬性來表示國家、城市名稱和到西雅圖的距離。然后我新建一個(gè)包含GridView控件的Step3.aspx頁 面,其中GridView定義了三列,如下所示: <%@ Page Language="C#" CodeFile="Step2.aspx.cs" Inherits="Step2" %>
<html> <body> <form id="form1" runat="server"> <h1>Cities and their Distances</h1> <asp:GridView ID="GridView1" AutoGenerateColumns="false" runat="server"> <Columns> <asp:BoundField HeaderText="Country" DataField="Country" /> <asp:BoundField HeaderText="City" DataField="City" /> <asp:BoundField HeaderText="Distance from </Columns> </asp:GridView>
</form> </body> </html> 然后我建立一個(gè)Location對(duì)象集合來綁定到Grid中,后臺(tái)代碼文件如下所示: using System; using System.Collections.Generic; using System.Web; using System.Query; public partial class Step2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { List<Location> cities = new List<Location>{ & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City="Nice", Distance=5428, Country=" & nbsp; & nbsp; new Location { City=" & nbsp; }; GridView1.DataSource = from location in cities & nbsp; where location.Distance > 1000 & nbsp; orderby location.Country, location.City & nbsp; select location; GridView1.DataBind(); } } 上面的后臺(tái)代碼展示了幾個(gè)非常酷的特性。首先是C# 3.0新的簡便的構(gòu)造器寫法,在創(chuàng)建對(duì)象的同時(shí),還可以同 時(shí)設(shè)置這些對(duì)象的屬性的值:: new Location { City="
這在實(shí)例化和同時(shí)添加對(duì)象到集合中的情形下非常有用,以及在 后面將用到的匿名類型的情形中也非常有用。注意到我這次并沒有用數(shù)組,而是用了一個(gè)類型為Location的基 于泛型的List集合。LINQ支持對(duì)任何的IEnumerable<T>集合執(zhí)行查詢,所以你可以使用現(xiàn)有的任何泛型 或者非泛型的對(duì)象集合。 在下面的LINQ查詢中我返回了距離西雅圖超過100英里的城市的集合。我還選擇了對(duì)查詢進(jìn)行先國家后城市名稱 的排序操作。這個(gè)LINQ查詢的結(jié)果的類型是由location變量來確定下來的─在這里,其類型 是Location: IEumerable<Location> result = from location in cities & nbsp; where location.Distance > 1000 & nbsp; orderby location.Country, location.City & nbsp; select location; 當(dāng)我把結(jié)果綁定到GridView中將會(huì)得到如下結(jié)果: 第三步:稍微重構(gòu)一下City集合 因?yàn)槲覀儗⒃诤脦讉€(gè)示例中重用這個(gè)城市集合,我決定把它封裝到一個(gè)"TravelOrganizer"類中,如下所 示: using System; using System.Collections.Generic; public class TravelOrganizer { public List<Location> PlacesVisited { get { List<Location> cities = new List<Location>{ & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; new Location { City=" & nbsp; new Location { City="Nice", Distance=5428, Country=" & nbsp; & nbsp; new Location { City=" & nbsp; & nbsp; }; return cities; } } }
這使我只需要編寫如下的代碼就能得到跟上面同樣的結(jié)果: using System; using System.Collections.Generic; using System.Web; using System.Web.UI; using System.Query; public partial class Step3 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { TravelOrganizer travel = new TravelOrganizer(); GridView1.DataSource = from location in travel.PlacesVisited & nbsp; where location.Distance > 1000 & nbsp; orderby location.Country, location.City & nbsp; select location; GridView1.DataBind(); } } LINQ很酷之處就是它是強(qiáng)類型的。這意味著, 1) 你的所有的查詢都會(huì)進(jìn)行編譯時(shí)檢查。不像現(xiàn)在的SQL語句,你只有到運(yùn)行時(shí)才會(huì)發(fā)現(xiàn)你的錯(cuò)誤所 在。這意味著你在開發(fā)時(shí)就可以檢查你的代碼的正確性,例如,如果我把上面的"distance"誤寫成 了"distanse",編譯器將為我捕獲到這個(gè)錯(cuò)誤。 2) 當(dāng)你寫LINQ查詢的時(shí)候你將在VS或免費(fèi)的Visual Web Developer中獲得智能感知的提示。這不僅加 快了編碼的輸入速度,而且使我們?cè)谔幚頍o論簡單還是復(fù)雜的集合和數(shù)據(jù)源對(duì)象模型時(shí)都變得非常容 易。 第四步:使用.NET的標(biāo)準(zhǔn)查詢操作符做Skip和Take操 作 LINQ支持許多內(nèi)置的標(biāo)準(zhǔn)查詢操作。如果你在類之前加入"using System.Query"語句你就可以在代碼 中使用這些操作。例如,如果我要列出第2遠(yuǎn)到第6遠(yuǎn)的城市,我就可以使用象下面這樣的編 碼: using System; using System.Web.UI; using System.Query; public partial class Step4 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { TravelOrganizer travel = new TravelOrganizer(); GridView1.DataSource = (from location in travel.PlacesVisited & nbsp; orderby location.Distance descending & nbsp; select location).Skip(1).Take(5); GridView1.DataBind(); } } 注意我是怎么通過距離的遠(yuǎn)近來對(duì)結(jié)果進(jìn)行排序的。然后我使 用Skip操作來跳過第一個(gè)城市,然后使用Take操作來只返回5個(gè)結(jié) 果。 NET標(biāo)準(zhǔn)查詢操作的真正強(qiáng)大之處在于,這些操作不是寫死 的(hard-coded ),任何開發(fā)人員都可以添加新的或替換其中的操作。這就可以支持實(shí)現(xiàn)非常強(qiáng)有力的特定 域(domain specific)操作。例如,當(dāng)你在DLINQ里使用Skip和Take操作時(shí),DLINQ實(shí)際上是把這些操作轉(zhuǎn)換成 服務(wù)器端分頁的后臺(tái)SQL邏輯,這樣,只有少量的記錄從數(shù)據(jù)庫返回,不管數(shù)據(jù)表中是否有十幾萬條數(shù)據(jù)。這 意味著我們可以在大量關(guān)系數(shù)據(jù)之上很輕易地實(shí)現(xiàn)高效的web數(shù)據(jù)分頁。注意:在LINQ正式發(fā)行之前,你可以 使用這里提到的技術(shù)。 第五步:.NET的標(biāo)準(zhǔn)查詢操作續(xù) 除了可以返回?cái)?shù)據(jù)集之外,我們可以使用.NET標(biāo)準(zhǔn)查詢操作來返回單個(gè)或者統(tǒng)計(jì)數(shù)據(jù)結(jié)果。下面的例子演示了怎 么做: <%@ Page Language="C#" CodeFile="Step5.aspx.cs" Inherits="Step5" %>
<html> <body> <form id="form1" runat="server"> <div> <h1>Aggregate Value Samples</h1> <div> <b>Farthest <asp:Label ID="MaxCityNameTxt" runat="server" Text="Label"></asp:Label> <asp:Label ID="MaxCityDistanceTxt" runat="server" Text="Label"></asp:Label> </div> <div> <b>Total Travel Distance (outside of US):</b> <asp:Label ID="TotalDistanceTxt" runat="server" Text="Label"></asp:Label> </div> <div> <b>Average Distance:</b> <asp:Label ID="AverageDistanceTxt" runat="server" Text="Label"></asp:Label> </div> </div> </form> </body> </html> Step5.aspx.cs后臺(tái)代碼文件: using System; using System.Collections.Generic; using System.Web.UI; using System.Query; public partial class Step5 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { TravelOrganizer travel = new TravelOrganizer(); // // Calculate farthest city away Location farthestCity = (from location in travel.PlacesVisited & nbsp; & nbsp; orderby location.Distance descending & nbsp; & nbsp; select location).First(); MaxCityNameTxt.Text = farthestCity.City; MaxCityDistanceTxt.Text = "(" + farthestCity.Distance + " miles)"; // // Calculate total city distances of all cities outside US int totalDistance = (from location in travel.PlacesVisited & nbsp; where location.Country != " & nbsp; select location).Sum(loc => loc.Distance); TotalDistanceTxt.Text = totalDistance + " miles"; // // Calculate average city distances of each city trip double averageDistance = travel.PlacesVisited.Average(loc => loc.Distance); AverageDistanceTxt.Text = averageDistance + " miles"; } } 注意,上面最后兩個(gè)例子使用了新的Lambda表達(dá)式(Lambda Expression)支持-這些表達(dá)式允許我們通過譬如象 委托這樣的代碼段在數(shù)據(jù)之上做進(jìn)一步的操作,從而計(jì)算出一個(gè)結(jié)果來。你也可以用之來建立你自己的.NET查 詢操作(例如:你可以建立一些特定領(lǐng)域的查詢來計(jì)算運(yùn)費(fèi)或者收入稅)。所有的對(duì)象都是強(qiáng)類型的,而且支 持智能感知和編譯時(shí)檢查。 上面示例的輸出如下所示: 第六步:匿名類型(Anonymous Types) LINQ能夠利用的另一個(gè)C#和VB新特性之一就是對(duì)“匿名類型”的支 持。這允許你不需明確聲明對(duì)象模型就能很容易地創(chuàng)建和使用內(nèi)聯(lián)的類型結(jié)構(gòu),因?yàn)轭愋涂梢酝ㄟ^數(shù)據(jù)的初始 化推斷出來。這在使用LINQ查詢“自定義構(gòu)形(custom shape)”數(shù)據(jù)時(shí)非常的有 用。 例如,考慮這樣一個(gè)場景:你正在處理一個(gè)具有許多屬性的數(shù)據(jù)庫或者強(qiáng)類型的集合-但是你只關(guān)心其中少數(shù)的 幾個(gè)字段。與創(chuàng)建和處理整個(gè)類型相比,僅返回你所需要的字段將會(huì)更加有用些。我們來新建一 個(gè)"step6.aspx"文件來實(shí)現(xiàn)以上操作: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Step6.aspx.cs" Inherits="Step6" %>
<html> <body> <form id="form1" runat="server"> <div> <h1>Anonymous Type</h1> <asp:GridView ID="GridView1" runat="server"> </asp:GridView> </div> </form> </body> </html> 在我們的后臺(tái)代碼文件中我們將編寫一個(gè)使用匿名類型的LINQ查詢,如下所 示: using System; using System.Web.UI; using System.Query; public partial class Step6 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { TravelOrganizer travel = new TravelOrganizer(); GridView1.DataSource = from location in travel.PlacesVisited & nbsp; orderby location.City & nbsp; select new { & nbsp; & nbsp; City = location.City, & nbsp; & nbsp; Distance = location.Distance & nbsp; }; GridView1.DataBind(); } } 注意,我們并沒有像上面一樣從select子句中返回一個(gè)"location"對(duì)象,我們通過新建一個(gè)具有City和Distance 兩個(gè)屬性的匿名類型來實(shí)現(xiàn)。這兩個(gè)屬性的類型是根據(jù)它們初始化時(shí)賦與的值來自動(dòng)確定的,在這里是一個(gè)是 string,另一個(gè)是int。將其綁定到GridView時(shí),將產(chǎn)生如下輸出: 第七步:匿名類型續(xù) 前面的示例展示了一個(gè)使用匿名類型來自定 義LINQ查詢輸出的基本例子。下面的示例提供了一個(gè)更復(fù)雜和更實(shí)際的場景。它把我們的城市列表轉(zhuǎn)換成一個(gè)分層的結(jié)果集合──我們將使用一個(gè)匿名類型來對(duì)結(jié)果按國家分組,這個(gè)匿名類型包含了一個(gè)國家名稱,一個(gè)城 市詳細(xì)信息的子集合和在這個(gè)國家中所有城市距離的總和,這距離之和將通過第五步中示范過的lambda表達(dá)式 來計(jì)算: using System; using System.Web.UI; using System.Query; public partial class Step7 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { TravelOrganizer travel = new TravelOrganizer(); GridView1.DataSource = from location in travel.PlacesVisited & nbsp; group location by location.Country into loc & nbsp; select new { & nbsp; & nbsp; Country = loc.Key, & nbsp; & nbsp; Cities = loc, & nbsp; & nbsp; TotalDistance = loc.Sum(dist => dist.Distance) & nbsp; }; GridView1.DataBind(); } } 我們.aspx頁面中的GridView是這樣定義的: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Step7.aspx.cs" Inherits="Step7" %>
<html> <body> <form id="form1" runat="server"> <div> <h1>Groupings with Anonymous Classes</h1> <asp:GridView ID="GridView1" AutoGenerateColumns="false" runat="server"> <Columns> <asp:BoundField HeaderText="Country" DataField="Country" /> <asp:TemplateField HeaderText="Cities"> & nbsp; <ItemTemplate> & nbsp; & nbsp; <asp:BulletedList ID="BulletedList1" runat="server" & nbsp; & nbsp; DataSource='<%#Eval("Cities")%>' DataValueField="City"/> & nbsp; & nbsp; </ItemTemplate> </asp:TemplateField> <asp:BoundField HeaderText="Total Distance" DataField="TotalDistance" /> </Columns> </asp:GridView> </div> </form> </body> </html> 注意,我在GridView的模版列中添加了一個(gè)"Cities"列,并且在其中添加了一個(gè)<asp:bulletedlist>控件 (一個(gè)新的ASP.NET 2.0自帶控件)來綁定在上面用LINQ查詢所得到的分層結(jié)果。生成的輸出如下所 示: 注意,所有上面的綁定語法和層次綁定在現(xiàn)在的ASP.NET 2.0中是完全支持的,所以,你可以在現(xiàn)有的程序中使 用這些技術(shù)。新穎(我也認(rèn)為非??幔┲帲悄涿愋秃蚅INQ提供的數(shù)據(jù)構(gòu)形功能,這個(gè)功能使得在ASP.NET 控件里綁定分層數(shù)據(jù)非常容易。
下一步 上面所有的例子操作的都是本地內(nèi)存中的集合數(shù)據(jù)。他們展示了你如何在.NET對(duì)象模型中使用LINQ,包括那些你 自己創(chuàng)建的類型。 在我將來的有關(guān)LINQ的文章中,我將深入討論LINQ,利用新的DLINQ支持使用上面提到的技術(shù)來處理關(guān)系數(shù)據(jù)庫 ,和通過新的XLINQ支持來處理XML文件和結(jié)構(gòu)。LINQ項(xiàng)目的好處在于,在所有的應(yīng)用中,其句法和概念都是一 樣的,這樣,你一旦學(xué)會(huì)使用LINQ對(duì)一個(gè)數(shù)組或集合做查詢,你也就知道了在處理數(shù)據(jù)庫甚至XML文件時(shí)所需 的所有概念。
例如,假如你使用DLINQ生成了Northwinds數(shù)據(jù)庫中供應(yīng)商(Suppliers)和產(chǎn)品( Products)表相對(duì)應(yīng)的.NET類型 (注:你不需要編寫任何代碼就可以實(shí)現(xiàn)),那么要獲取分層的數(shù)據(jù)結(jié)果,并且將其綁定到GridView上,你只 要寫下面這個(gè)編碼就可以了(注意:我們使用了跟前面的例子一樣的數(shù)據(jù)構(gòu)形技術(shù),只從數(shù)據(jù)庫中取得兩列數(shù) 據(jù),并且自動(dòng)地把每個(gè)供應(yīng)商和其對(duì)應(yīng)的產(chǎn)品組合成一個(gè)層次結(jié)構(gòu)的結(jié) 果): using System; using System.Query; public partial class Data_Data2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Northwind db = new Northwind(); GridView1.DataSource = from x in db.Suppliers & nbsp; where x.Country == " & nbsp; orderby x.Country & nbsp; select new { & nbsp; & nbsp; x.CompanyName, & nbsp; & nbsp; x.Country, & nbsp; & nbsp; x.Products & nbsp; }; GridView1.DataBind(); } } 不需要額外的SQL語句和代碼──這些就是實(shí)現(xiàn)高效獲取和組裝層次數(shù)據(jù)所需的所有代碼(注意:只取出了需要的 列和行的數(shù)據(jù)-DLINQ可以使用LINQ的遠(yuǎn)程函數(shù)支持因而我們沒必要持久化或者取出所有數(shù)據(jù)庫表或者一行中 的所有列)。而且這些都是類型安全的,同樣具有完全的編譯時(shí)檢查,智能感知和調(diào)試支 持。 更棒的是,接入一個(gè)新的LINQ提供器(DLINQ和XLINQ是兩例)的機(jī)制是完全公開的──因此那些已經(jīng)建立或者使用現(xiàn) 有數(shù)據(jù)提供程序(例如:O/R數(shù)據(jù)庫映射)的開發(fā)人員可以很容易的無縫地把他們的實(shí)現(xiàn)和LINQ整合起來。一 旦你了解了LINQ,你就知道了開發(fā)LINQ所需的所有的基本知識(shí)。 總結(jié) 本文對(duì)一些即將到來的非??岬募夹g(shù)做了一個(gè)比較粗略的概述。你可以從這兒下載LINQ五月份CTP版來嘗試一下。你也可以 在這兒下載并運(yùn)行我在上面所建 示例的.zip文件。 |
|