【原文地址】Using LINQ with ASP.NET (Part 1)
【原文发表日期】Sunday, May 14, 2006 9:49 PM
最近使我激动不已的新鲜事之一就是LINQ系列技术的出现,包括LINQ,DLINQ,XLINQ和不久后的其他技术。
LINQ将被完全集成到代号为Orcas的下个版本Visual Studio中,而且它也包含了一些非常酷的框架和 工具支持,包括完全的智能感知和可视化设计器支持。你可以在这儿下载上周发布的LINQ五月份CTP版。这个CTP版 本的亮点就是它能在VS 2005上运行,使你能够立即开始深入研究LINQ。它实现了很多用户的反馈(例 如:在DLINQ中添加了对存储过程的支持),并且包含了一个内置的ASP.NET网站项目模板来帮助你在ASP.NET 中使用它(注意:你也可以在VS 2005 Web Application Project 中使用LINQ)。
我将在接下来的几周中发表一系列文章来介绍怎样在ASP.NET工程 中使用LINQ/DLINQ/XLINQ。下面这第一个走过场的示范将帮助你了解一些LINQ重要的基本概念。你可以下 载LINQ五月份CTP版,然后随着文章的进行逐步输入相应代码(在下面我会列出所有代码),或者你也可以 在这儿下载并运行我所做示例的 完整.zip文件(注意:你仍然需要下载LINQ五月份版来运行.zip文件中的示 例)。
注意:C#和VB都完全支持LINQ,DLINQ和XLINQ。在下面的示例中 我将使用C#。
第0步:建立一个C# LINQ ASP.NET网站
建立一个能使用LINQ/DLINQ/XLINQ和新的C#3.0语言特性的ASP.NET网站,在VS中选择文件->新建网站然后选 择"LINQ ASP.NET Web Site Template":
默认会创建一个如下所示的网站 工程:
注意它在\bin文件夹中引入了一些LINQ程序集。它同样在web.config文件中添加了一些配置以告诉VS和ASP.NET 使用C# 3.0编译器来编译和运行程序:
<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版本并肩运行, 因此你无需担心安装LINQ会破坏VS或ASP.NET。
第一步:建立第一个使用了LINQ的ASP.NET页 面
新建一个叫Step1.aspx的新页面。添加一个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>
然后在后台代码文件中我们将编写经典的“hello world”LINQ示例-包括对一列字符串的搜索和排 序:
using System;
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 = { "London", "Amsterdam", "San Francisco", "Las Vegas",
& nbsp; "Boston", "Raleigh", "Chicago", "Charlestown",
& nbsp; "Helsinki", "Nice", "Dublin" };
GridView1.DataSource = from city in cities
& nbsp; where city.Length > 4
& nbsp; orderby city
& nbsp; select city.ToUpper();
GridView1.DataBind();
}
}
在上面的示例中,我列出了一组我今年一月到五月所去过的城市的名称。然后我用LINQ查询表达式(query expression)对这个数组进行操作。这个查询表达式返回名字多于4个字符的所有城市,然后按照城市名 称的字母进行排序并把名字转换为大写。
LINQ查询返回如下类型:IEnumerable<T>-"select"子句选择的对象类型决定了这里 的<T>的类型。因为上面例子中"city"是一个字符串,所以类型安全的结果是一个如下所示的基于泛型 的集合:
IEnumerable<string> result = from city in cities
& nbsp; where city.Length > 4
& nbsp; orderby city
& nbsp; select city.ToUpper();
因为ASP.NET控件能绑定到任何的IEnumerable集合,所以我们可以很容易的把LINQ查询结果绑定到GridView中, 然后调用DataBind()方法来生成如下的页面输出:
注意,除了可以使用上面的GridView控件外,我也可以使用 <asp:repeater>, <asp:datalist>, <asp:dropdownlist>, 或者任何其他ASP.NET的列表 控件(可以是产品自带或者开发人员自己开发的控件)。在这些示例中我只使用了<asp:gridview>-但 是你们可以使用任何其他的控件。
第二步:使用功能更丰富的集合
搜索一个数组的字符串并没多大意思,虽然有时候很有用。如果我们能对自己的功能更丰富的那些集合中搜索将 会更有趣。好消息是,LINQ使这些变得很简单。例如,为了更好记录我去过的地方,我在我的工程中建立了一 个叫"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; }
}
}
它公开了三个属性来表示国家、城市名称和到西雅图的距离。然后我新建一个包含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 Seattle" DataField="Distance" />
</Columns>
</asp:GridView>
</form>
</body>
</html>
然后我建立一个Location对象集合来绑定到Grid中,后台代码文件如下所示:
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="London", Distance=4789, Country="UK" },
& nbsp; & nbsp; new Location { City="Amsterdam", Distance=4869, Country="Netherlands" },
& nbsp; & nbsp; new Location { City="San Francisco", Distance=684, Country="USA" },
& nbsp; new Location { City="Las Vegas", Distance=872, Country="USA" },
& nbsp; & nbsp; new Location { City="Boston", Distance=2488, Country="USA" },
& nbsp; & nbsp; new Location { City="Raleigh", Distance=2363, Country="USA" },
& nbsp; & nbsp; new Location { City="Chicago", Distance=1733, Country="USA" },
& nbsp; & nbsp; new Location { City="Charleston", Distance=2421, Country="USA" },
& nbsp; & nbsp; new Location { City="Helsinki", Distance=4771, Country="Finland" },
& nbsp; & nbsp; new Location { City="Nice", Distance=5428, Country="France" },
& nbsp; & nbsp; new Location { City="Dublin", Distance=4527, Country="Ireland" }
& nbsp; };
GridView1.DataSource = from location in cities
& nbsp; where location.Distance > 1000
& nbsp; orderby location.Country, location.City
& nbsp; select location;
GridView1.DataBind();
}
}
上面的后台代码展示了几个非常酷的特性。首先是C# 3.0新的简便的构造器写法,在创建对象的同时,还可以同 时设置这些对象的属性的值::
new Location { City="London", Distance=4789, Country="UK" }
这在实例化和同时添加对象到集合中的情形下非常有用,以及在 后面将用到的匿名类型的情形中也非常有用。注意到我这次并没有用数组,而是用了一个类型为Location的基 于泛型的List集合。LINQ支持对任何的IEnumerable<T>集合执行查询,所以你可以使用现有的任何泛型 或者非泛型的对象集合。
在下面的LINQ查询中我返回了距离西雅图超过100英里的城市的集合。我还选择了对查询进行先国家后城市名称 的排序操作。这个LINQ查询的结果的类型是由location变量来确定下来的─在这里,其类型 是Location:
IEumerable<Location> result = from location in cities
& nbsp; where location.Distance > 1000
& nbsp; orderby location.Country, location.City
& nbsp; select location;
当我把结果绑定到GridView中将会得到如下结果:
第三步:稍微重构一下City集合
因为我们将在好几个示例中重用这个城市集合,我决定把它封装到一个"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="London", Distance=4789, Country="UK" },
& nbsp; & nbsp; new Location { City="Amsterdam", Distance=4869, Country="Netherlands" },
& nbsp; & nbsp; new Location { City="San Francisco", Distance=684, Country="USA" },
& nbsp; & nbsp; new Location { City="Las Vegas", Distance=872, Country="USA" },
& nbsp; & nbsp; new Location { City="Boston", Distance=2488, Country="USA" },
& nbsp; & nbsp; new Location { City="Raleigh", Distance=2363, Country="USA" },
& nbsp; & nbsp; new Location { City="Chicago", Distance=1733, Country="USA" },
& nbsp; & nbsp; new Location { City="Charleston", Distance=2421, Country="USA" },
& nbsp; & nbsp; new Location { City="Helsinki", Distance=4771, Country="Finland" },
& nbsp; new Location { City="Nice", Distance=5428, Country="France" },
& nbsp; & nbsp; new Location { City="Dublin", Distance=4527, Country="Ireland" }
& nbsp; & nbsp; };
return cities;
}
}
}
这使我只需要编写如下的代码就能得到跟上面同样的结果:
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很酷之处就是它是强类型的。这意味着,
1) 你的所有的查询都会进行编译时检查。不像现在的SQL语句,你只有到运行时才会发现你的错误所 在。这意味着你在开发时就可以检查你的代码的正确性,例如,如果我把上面的"distance"误写成 了"distanse",编译器将为我捕获到这个错误。
2) 当你写LINQ查询的时候你将在VS或免费的Visual Web Developer中获得智能感知的提示。这不仅加 快了编码的输入速度,而且使我们在处理无论简单还是复杂的集合和数据源对象模型时都变得非常容 易。
第四步:使用.NET的标准查询操作符做Skip和Take操 作
LINQ支持许多内置的标准查询操作。如果你在类之前加入"using System.Query"语句你就可以在代码 中使用这些操作。例如,如果我要列出第2远到第6远的城市,我就可以使用象下面这样的编 码:
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();
}
}
注意我是怎么通过距离的远近来对结果进行排序的。然后我使 用Skip操作来跳过第一个城市,然后使用Take操作来只返回5个结 果。
NET标准查询操作的真正强大之处在于,这些操作不是写死 的(hard-coded ),任何开发人员都可以添加新的或替换其中的操作。这就可以支持实现非常强有力的特定 域(domain specific)操作。例如,当你在DLINQ里使用Skip和Take操作时,DLINQ实际上是把这些操作转换成 服务器端分页的后台SQL逻辑,这样,只有少量的记录从数据库返回,不管数据表中是否有十几万条数据。这 意味着我们可以在大量关系数据之上很轻易地实现高效的web数据分页。注意:在LINQ正式发行之前,你可以 使用这里提到的技术。
第五步:.NET的标准查询操作续
除了可以返回数据集之外,我们可以使用.NET标准查询操作来返回单个或者统计数据结果。下面的例子演示了怎 么做:
<%@ Page Language="C#" CodeFile="Step5.aspx.cs" Inherits="Step5" %>
<html>
<body>
<form id="form1" runat="server">
<div