【原文地址】Tip/Trick: Optimizing ASP.NET 2.0 Web Project Build Performance with VS 2005
【原文发表日期】Friday, September 22, 2006 11:47 AM
这个帖子讨论在Visual Studio 2005中开发web项目时,如何优化其Build性能。如果你正受缓慢Build的煎熬,或者想知道怎么提高Build速度,请继续读下去。
VS 2005 网站项目和VS 2005 Web应用项目简介
VS 2005 支持两种项目模型:VS 2005 网站项目(Web Site Projects) 和 VS 2005 Web应用项目(Web Application Projects)。
VS 2005 网站项目是VS 2005刚发行时内置的项目模型,它提供了一个基于无项目 (project-less)的模型来开发web应用,该模型使用了ASP.NET 2.0在运行时使用的同个动态编译系统。而VS 2005 Web应用项目则是今春早些时候发布的一个完全支持的可下载项目模型,它提供了一个使用了基于MSBuild的build系统的项目模型,可以把一个项目中的所有编码编译成单个程序集 (与VS 2003类似,但又少了VS 2003 web项目中涉及FrontPage服务器扩展,IIS依赖性,以及其他问题的种种限制)。想进一步了解VS 2005 Web应用项目的话,请参考我发表在 http://webproject.scottgu.com网站上的系列教程。注意,对VS 2005 Web应用项目的支持将包含在VS 2005 SP1中,这样以后就不用另外下载了。
VS 2005网站项目和VS 2005 Web应用项目两个模型在将来的Visual Studio版本中将会继续被完全支持。我们发现,有些人喜欢一个模型,憎恶另外一个模型,反之亦然。从特性(feature)的角度看,没有“最佳的模型选择”这一说,完全取决于你个人的爱好和你们的团队协作机制(team dynamics)来决定哪个模型最适于你。譬如,许多的企业开发人员喜欢VS 2005 Web应用项目模型,因为它提供了更多的build控制和团队集成支持,而许多的web开发人员则喜欢VS 2005网站项目模型,因为它的“即存即行(just hit save)”的动态模型和灵活性。
有2篇文章,你也许会发现在决定采用哪个模型时有用,这篇MSDN上的白皮书内含对这两个模型的比较, 而Rick Strahl的《Web应用项目和Web 部署项目发布了》一文对不同选项的利弊做了一个很好的讨论。
想从VS 2005 网站项目模型迁移到VS 2005 Web应用项目模型的话,请按这个C#或VB版的教程里示范的步骤做。
那么哪个项目模型Build起来快些呢?
在对项目做full build时,VS 2005 Web 应用项目模型编译起项目来会比VS 2005 网站项目模型快很多。full build是指对项目里的每一个类和网页要做编译或重新编译的情形,要么因为你在build菜单里选了Rebuild,要么因为你修改了一个依赖的类库项目或者/app_code子目录里的编码,然后点击了build或按了ctrl-shift-b来编译解决方案。
在这些“全部重新编译(full rebuild)”的情形下,VS 2005 Web 应用项目编译比VS 2005 网站项目快很多有几个原因。主要原因是,跟在VS 2003中一样,VS 2005 Web 应用项目只编译你的页面的后台编码(code-behind)以及你项目中的其他类文件。它既不分析也不编译你的.aspx 页面内的内容/控件/行内(inline)编码,这意味着,它不需要parse那些文件。坏处是,这也意味着,在编译过程中,它不会检查那些文件是否有错(而VS 2005 网站项目则会识别其中的任何错误)。好处是,这使得编译极其快。
那么,这是否意味着你总是应该在大型项目的情形下使用VS 2005 Web 应用项目模型来得到最快的build 时间呢?不,不一定。VS 2005 网站项目的一个很好的特性是对“按需编译(on demand compilation)”的支持。这在你对所依赖的文件做了变化(dependent changes)后,让你避免常规地重新编译整个项目,而是只需要对那些你正在编辑的页面做重新编译,而且这个编译是按需进行的。这会给你的解决方案带来build性能上的极大改进,而且也会在开发非常大的项目时给予你非常好的工作流程。如果你想要在保持网站模型灵活性的同时改进build性能,我强烈推荐使用这个模型。
下面几节提供了针对VS 2005 网站项目模型和VS 2005 Web 应用项目模型优化技术方面的特定教程,包括我上面描述的“按需编译(on demand compilation)” build 选项。
优化VS 2005 网站项目Build时间的特定技巧和诀窍
在使用VS 2005 网站项目模型时,按下述步骤做的话,你就可以极大地改进build性能:
1) 确认你没有遭受我称之为“程序集引用冲突(Dueling Assembly References)”的问题。我在这个博客帖子里描述了如何发觉和解决这个问题。如果你曾经在build时看到编译过程好像在“正验证网站(Validating Web Site)”这个编译阶段停顿了(意即,输出窗口里超过几秒钟都没有输出)的话,你非常可能是遇上这个问题了。使用上述这个博客帖子里概述的技术来解决这个问题。
2) 把在/app_code子目录里的文件的数目保持在很小的范围。如果你最后在这个目录里有一大堆类文件的话,我建议在你的VS解决方案里另建一个类库项目,把这些类移到这个类库项目里去,因为类库项目比/app_code子目录里的类编译快多了。如果在/app_code目录里只有小数量的文件的话,这通常不是个问题,但你有很多目录或数十个文件,那么把这些文件移到一个单独的类库项目里,然后在你的网站项目里引用这个类库项目,你将能改进build速度。另一个需要知道的事情是,每次你在VS HTML设计器里从源码视图转换到设计视图时,设计器会在设计表面加载之前引起/app_code目录的编译。原因是,这样你就可以在设计器里使用在/app_code目录里定义的控件。如果你没有/app_code目录,或者里面没几个文件,那么页面设计器加载起来就会极快,因为它不需要先做很大的编译工作。
3) 在你的网站项目中激活“按需编译(on-demand compilation)”。右击你的网站项目,打开项目属性页。在左边点击Build区,打开项目的build设置。在设置里把“F5启动操作(F5 Start Action)”从“Build Web Site(网站)”改成“Build Page(页面)”或“No Build (不Build)”选项。然后,确认清除了“把网站当作解决方案一部分来Build (Build Web site as part of solution)”复选框:
在你点击ok接受设置改动后,你将处于“按需编译”模式下。这意味着,在上面的对话框你选了“Build Page(页面)”的话,你编辑了一个页面,按F5(以调试模式运行)或Ctrl-F5(以非调试模式运行)时,解决方案将跟以前一样,编译所有的类库项目,然后编译 /app_code 目录以及Global.asax文件,然后,不再对网站里所有的页面做重新核实,而是只核实你手头的网页,以及这个页面引用的任何用户控件。在含有大量网页的大型甚至中型项目中,这很明显会带来极大的性能改观。注意,ASP.NET 会自动重新编译你在运行时访问的任何其他页面或控件,这样,你运行的应用程序用的总是最新的编码,而不必担心老的编码在运行。你也可以选“No Build”,省略IDE里page级的验证,很明显会进一步提高整个过程的build速度。我建议你对这两个选项都尝试一下,看你喜欢哪个。
不选“把网站当作解决方案一部分来Build (Build Web site as part of solution)”复选框,你会发现Ctrl-Shift-B键(触发build解决方案)会继续编译所有的类库项目,但不会重新build你网站项目里的所有页面。在此场景下,你依然会得到完整的intellisense支持,这样你不会失去任何设计时的支持。在打开页面时你也会继续得到警告/出错的弯曲的下划线提示(warning/error squiggles)。如果你需要对没有打开的页面,或对所有的页面强迫做重新编译的话,你可以使用Visual Studio的Build菜单里的Build Page或Build Web Site菜单选项:
这让你来控制你要核实你网站上的哪些页面以及什么时候核实,可以极大地改进build性能。我推荐做的一个诀窍是,在你的环境里添加一个快捷键,允许你很快地触发Build Page操作,以避免使用鼠标和菜单。你可以这么做,选择工具->定制菜单选项,然后在定制对话框的左下方点击“键盘”按钮。这会打开一个对话框,让你选择VS Build.BuildPage命令,然后与你想要的任何键组合相关联:
设置完毕后,在任何页面上,你可以打入Ctrl-Shift-P (或者你设置的任何键),会引起VS编译任何改动过的类库项目(效果跟Ctrl-Shift-B一样),然后核实/app_code目录里的所有类,然后重新build当前项目里你手头正编辑的页面或用户控件(以及被引用的母板页或所使用的用户控件)。
应用上述步骤之后,你应该发现你的build性能和灵活性大为提高,而且你对build什么时候发生有完全的控制。
优化VS 2005 Web应用项目Build时间的特定技巧和诀窍
如果你使用VS 2005 Web 应用项目模型的话,下面是几个你可以考虑的优化手段:
1) 如果你有一个很大的项目,或者与很多其他开发人员在一同开发一个项目,那么你也许要考虑把这个项目分成多个子web项目。我不见得会为性能的原因推荐这么做(除非你有成千上万个页面,否则效果不大),但它有时会有助于管理一个大项目。请阅读我以前写的这个关于怎么建立子web项目的帖子来了解怎么使用这个手段。
2) 考虑给你的解决方案添加一个VS 2005 Web部署项目来作深层的校验(verification)。在上面我提到,使用VS 2005 Web 应用项目的一个弊处是,它只编译你的页面的后台(code-behind)源码,并不对实际的.aspx标识符做进一步的校验,所以它会错过你在.aspx 页面里写错了tag的那些情形。这提供了与VS 2003 同等级别的校验支持(这样你也没有损失什么),但没有网站项目模型那么深入。有一个方法,你仍旧可以在VS 2005 Web 应用项目模型里得到网站项目模型那个级别的校验,就是添加一个VS 2005 Web部署项目到你的解决方案里(Web部署项目既可以和网站项目模型,也可以和web应用项目模型合作使用)。你可以配置它只在build你解决方案的release和staging版本时才运行,以避免在开发时build受影响,然后在发布你的应用前,用它来同时提供对你的内容和源码的深层的校验。
优化任何VS 2005项目Build时间的常用技巧和诀窍
在编译项目/解决方案时有性能问题时,我建议检查的几样东西:(注:当我听说新的技巧时,我会不断地添加到这个列单上,所以,以后请不时回来查看一下新内容):
1) 提防Virus Checkers,Spy-Bots和Search/Indexing工具
VS频繁访问文件系统,很明显地,每次它编译时,都需要重新parse一个项目里变动过的任何文件。有一个问题,我见到不少人报告过的,是在病毒扫描程序,spy-bot 检测程序,或者桌面 search indexing工具过分密切监测一个内含项目的目录的情形下造成的,因为它们不断地变更这些文件的时间戳(timestamp)。它们并不改变文件的内容,但它们确是改变最后访问的时间戳,而VS也使用这个时间戳。这就会造成一个模式:你对某个文件做了一个变动,重新build,然后在后台(background),这些病毒/搜寻工具进去,重新搜寻/检查这个文件,然后把这个文件标记为改动过了,从而导致 VS重新把它编译一遍。如果你看到build性能问题,检查一下是不是这个问题,以及考虑禁止别的程序扫描你在工作的目录。我也见过报告说,某些Spybot工具会导致VS 调试时极其缓慢,所以你也许也应该确认一下你的问题是否跟那些工具有关。
2) 关闭Windows Forms 设计器选项中的AutoToolboxPopulate
VS 2005里有个会导致VS自动把作为你解决方案一部分一起编译的任何控件加载到工具箱的选项。这是个在开发控件时非常有用的特性,因为当你编译时,VS会自动更新它们。但我看到几个报告,报告人说这个选项在某些情形下会导致VS编译时花非常长的时间(几乎象死机一样)。注意,这同时适用于Windows Forms和 Web 项目。想禁止这个选项的话,选择工具->选项菜单,然后勾销Windows Forms Designer/General/AutoToolboxPopulate复选框。(见相关议题:http://forums.asp.net/1108115/ShowPost.aspx)
3) 检查哪些第三方工具包正在Visual Studio中运行
有很多很棒的第三方VS工具包你可以插入Visual Studio。这些工具可以带来很高的生产力,而且提供成堆的功能。但有时我也看到有些性能和系统稳定性问题是受了它们的影响,特别是在使用了这些工具包的早期(或者beta)版本的情形下(你应该总是留心什么时候工具包制造商会更新这些版本)。如果你看到与性能和稳定性有关的问题的话,你也许要尝试一下卸载任何工具包来看是否有作用。如果有作用的话,你可以跟第三方工具包制造商合作识别问题所在。
Visual Basic之Build性能HotFix
Visual Basic产品组刚发布了涉及大型VB项目编译性能问题的几个hotfixes。你可以从这个博客帖子里了解到如何立即得到这些hotfixes。Visual Basic产品组还有一个直通email 地址,vbperf@microsoft.com,你可以用它来直接联系他们,假如你遇到性能问题的话。
希望本文对你有所帮助,
Scott
【原文地址】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>
<h1>Aggregate Value Samples</h1>
<div>
<b>Farthest Distance City:</b>
<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后台代码文件:
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 != "USA"
& 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";
}
}
注意,上面最后两个例子使用了新的Lambda表达式(Lambda Expression)支持-这些表达式允许我们通过譬如象 委托这样的代码段在数据之上做进一步的操作,从而计算出一个结果来。你也可以用之来建立你自己的.NET查 询操作(例如:你可以建立一些特定领域的查询来计算运费或者收入税)。所有的对象都是强类型的,而且支 持智能感知和编译时检查。
上面示例的输出如下所示:
第六步:匿名类型(Anonymous Types)
LINQ能够利用的另一个C#和VB新特性之一就是对“匿名类型”的支 持。这允许你不需明确声明对象模型就能很容易地创建和使用内联的类型结构,因为类型可以通过数据的初始 化推断出来。这在使用LINQ查询“自定义构形(custom shape)”数据时非常的有 用。
例如,考虑这样一个场景:你正在处理一个具有许多属性的数据库或者强类型的集合-但是你只关心其中少数的 几个字段。与创建和处理整个类型相比,仅返回你所需要的字段将会更加有用些。我们来新建一 个"step6.aspx"文件来实现以上操作:
<%@ 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>
在我们的后台代码文件中我们将编写一个使用匿名类型的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子句中返回一个"location"对象,我们通过新建一个具有City和Distance 两个属性的匿名类型来实现。这两个属性的类型是根据它们初始化时赋与的值来自动确定的,在这里是一个是 string,另一个是int。将其绑定到GridView时,将产生如下输出:
第七步:匿名类型续
前面的示例展示了一个使用匿名类型来自定 义LINQ查询输出的基本例子。下面的示例提供了一个更复杂和更实际的场景。它把我们的城市列表转换成一个分层的结果集合──我们将使用一个匿名类型来对结果按国家分组,这个匿名类型包含了一个国家名称,一个城 市详细信息的子集合和在这个国家中所有城市距离的总和,这距离之和将通过第五步中示范过的lambda表达式 来计算:
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的模版列中添加了一个"Cities"列,并且在其中添加了一个<asp:bulletedlist>控件 (一个新的ASP.NET 2.0自带控件)来绑定在上面用LINQ查询所得到的分层结果。生成的输出如下所 示:
注意,所有上面的绑定语法和层次绑定在现在的ASP.NET 2.0中是完全支持的,所以,你可以在现有的程序中使 用这些技术。新颖(我也认为非常酷)之处,是匿名类型和LINQ提供的数据构形功能,这个功能使得在ASP.NET 控件里绑定分层数据非常容易。
下一步
上面所有的例子操作的都是本地内存中的集合数据。他们展示了你如何在.NET对象模型中使用LINQ,包括那些你 自己创建的类型。
在我将来的有关LINQ的文章中,我将深入讨论LINQ,利用新的DLINQ支持使用上面提到的技术来处理关系数据库 ,和通过新的XLINQ支持来处理XML文件和结构。LINQ项目的好处在于,在所有的应用中,其句法和概念都是一 样的,这样,你一旦学会使用LINQ对一个数组或集合做查询,你也就知道了在处理数据库甚至XML文件时所需 的所有概念。
例如,假如你使用DLINQ生成了Northwinds数据库中供应商(Suppliers)和产品( Products)表相对应的.NET类型 (注:你不需要编写任何代码就可以实现),那么要获取分层的数据结果,并且将其绑定到GridView上,你只 要写下面这个编码就可以了(注意:我们使用了跟前面的例子一样的数据构形技术,只从数据库中取得两列数 据,并且自动地把每个供应商和其对应的产品组合成一个层次结构的结 果):
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 == "USA"
& nbsp; orderby x.Country
& nbsp; select new {
& nbsp; & nbsp; x.CompanyName,
& nbsp; & nbsp; x.Country,
& nbsp; & nbsp; x.Products
& nbsp; };
GridView1.DataBind();
}
}
不需要额外的SQL语句和代码──这些就是实现高效获取和组装层次数据所需的所有代码(注意:只取出了需要的 列和行的数据-DLINQ可以使用LINQ的远程函数支持因而我们没必要持久化或者取出所有数据库表或者一行中 的所有列)。而且这些都是类型安全的,同样具有完全的编译时检查,智能感知和调试支 持。
更棒的是,接入一个新的LINQ提供器(DLINQ和XLINQ是两例)的机制是完全公开的──因此那些已经建立或者使用现 有数据提供程序(例如:O/R数据库映射)的开发人员可以很容易的无缝地把他们的实现和LINQ整合起来。一 旦你了解了LINQ,你就知道了开发LINQ所需的所有的基本知识。
总结
本文对一些即将到来的非常酷的技术做了一个比较粗略的概述。你可以从这儿下载LINQ五月份CTP版来尝试一下。你也可以 在这儿下载并运行我在上面所建 示例的.zip文件。
希 望本文对你有所帮助,
Scott
(
蓝天译)