【原文地址】ASP.NET Dynamic Data Preview Available
【原文发表日期】Thursday, April 10, 2008 12:44 AM
几个月前,我们推出了ASP.NET 3.5 Extensions的预览版,该版本内含一堆今年稍后会发布的新特性(包括ASP.NET AJAX方面的改进,ASP.NET MVC,ASP.NET Silverlight支持,以及ASP.NET动态数据)。
该预览版中的ASP.NET动态数据(Dynamic Data)支持对一个很酷的新特性提供了初览,该特性允许你很快地建造使用LINQ to SQL或LINQ to Entities对象模型的数据驱动的网站。 ASP.NET动态数据允许你自动地根据你的ORM数据模型的元数据动态地生成功能完整的数据输入和报表网页。除了支持动态渲染模式外,它还允许你使用任何你想要的HTML或代码来覆盖和定制任何视图模板(给你完整的体验控制)。
ASP.NET动态数据预览
今天,我们发布了ASP.NET动态数据的更新版,你可以在这里了解相关详情和下载。
这个新的动态数据预览版现在可以与标准的内置ASP.NET数据控件(包括GridView,ListView,FormView,DetailsView等)协作了,动态数据支持允许这些控件自动地处理外键关系。例如,在一个GridView上,你现在将得到外键字段值的友好名称的自动显示,以及在编辑模式下这些值的自动下拉框选择支持:
新的动态数据支持还能根据你在数据模型类上设置的约束提供自动的UI验证支持(同时支持客户端和服务器端)。例如,如果数据库中的一个字段在大小上限于50个字符,并且被标记为不能为空,ASP.NET动态数据就会自动地将合适的UI控件校验器加到UI页上以强制这个约束。如果你在LINQ to SQL或LINQ to Entities数据模型类里改变了约束,UI就会自动地反应这些变动,并且在下一个web请求中强制新的约束。
除了标准的数据模型元数据外,你还可以声明自定义的元数据以进一步控制验证和对象界面的默认显示。
你既能在LINQ to SQL中,也能在LINQ to Entities中使用上面所有的特性。
Visual Studio动态数据项目向导(Project Wizard)
除了核心的ASP.NET动态数据运行时支持外,VS web工具开发团队今天还发布了新的动态数据项目向导的第一个预览版,该向导允许你很快地开始一个数据驱动的网站。该向导允许你选择数据库,然后选择你想用来建造 LINQ to SQL数据模型的数据表,视图和存储过程:
在创建数据模型之后,该向导允许你轻松地选择动态数据驱动的模板网页来建造界面:
然后你能选择每个网页上支持的数据插入、编辑、更新界面的类型:
在你点击“完成”之后,它会用你的数据模型类和数据界面网页配置生成一个项目。你可以在这里了解该向导的有关详情,以及观看一个实战示范的screencast。
如何入门
你可以在 这里了解这个新的动态数据预览版的详情以及下载它在本地运行。
你可以观看David Ebbo在MIX 08上做的动态数据讲座以了解其工作原理的细节。也看一下Scott Hunter这里的screencast,以及Brad Millington这里的screencast。 David这里还有一个贴子,讨论十二月份预览版与今天的预览版本之间的变动。
你可以在www.asp.net上这里的论坛上问问题或提交反馈。
希望本文对你有所帮助,
Scott
【原文地址】New ASP.NET Dynamic Data Support
【原文发表日期】 Friday, December 14, 2007 12:41 AM
上个周末发布的ASP.NET 3.5 Extensions CTP预览版包括了一堆很棒的新特性。
其中一个新特性是我们称之为“ASP.NET Dynamic Data Support(动态数据支持)”的东西。简单地说,这个东西允许你极其快速地建造可以使用LINQ to SQL(在将来LINQ to Entities)对象模型的数据驱动的网站,还允许你不用手工建造任何网页。
看它在实战中的例子的最佳方式是观看David Ebbo的精彩的17分钟屏播:
你也可以按下面的步骤来轻松上手,开始使用动态数据支持:
第一步: 创建ASP.NET动态数据网站
在安装ASP.NET 3.5 Extensions CTP预览版之后,你可以使用VS 2008或免费的Visual Web Developer 2008 Express版来创建启用ASP.NET动态数据支持的新网站项目或新Web应用项目:
这会创建一个可用的新网站,其中包括了一些默认的模板文件,以及你可以用来定制模板文件观感(look and feel)的CSS样式表:
第二步: 添加LINQ to SQL数据模型
LINQ to SQL是.NET 3.5中内置的O/RM(对象关系映射器),它允许你使用.NET类来对关系数据库建模。然后你可以使用LINQ来对数据库进行查询,以及在其中更新、插入和删除数据。 LINQ to SQL完全支持事务,视图和存储过程。它还提供了简易方式来将数据验证和业务逻辑规则集成进你的数据模型。
开始使用ASP.NET动态数据网站的最简便的方式是向你的项目中添加一个新的LINQ to SQL数据模型(右击->添加新项->LINQ to SQL类)。这会调出LINQ to SQL类设计器。从你的服务器管理器中把数据库数据表拖拉到设计器表面,为你的数据库数据定义(schema)来建立一些类(包括其间的关系):
第三步: 在项目中启用动态模板
在默认情形下,ASP.NET动态数据项目提供了内置支持,为你的LINQ to SQL和LINQ to Entities数据模型创建自动的“scaffolding(脚手架)”视图。动态数据项目包括了一些模板页和用户控件,可以自动地创建内置的浏览、编辑、选择和删除数据的用户界面,这些界面是根据你加到项目里的LINQ to SQL或者LINQ to Entities数据模型,在运行时动态构建的。
为启用这个自动scaffolding的支持,打开你项目的web.config文件,找一下其中的dynamicData节,把它的enableTemplates属性设为true:
注: 这个自动scaffolding特性在默认情形下为安全的原因是关掉的(我们要你明确地设置来启用这个特性)。
注: 就象你在上面看到的那样,ASP.NET动态数据配置节还提供了可选的URL映射支持,该支持允许你将你的动态scaffold网页定制成你想要的任何URL格式。
第四步: 运行你的网站
按F5运行你的项目,默认情形下,你会被转到default.aspx网页,上面列出了你数据模型中的所有数据表对象:
注: default.aspx主页是项目中的一个文件,如果你不喜欢这个网页的样子,你可以打开它,编辑成你想要的样子。
点击上面列表中的任何数据表名称,会把你转到一个动态数据网页,其上列出了对应数据表中的数据:
默认的数据视图网页自动创建了有效的数据导航界面,这些界面包括了内置的数据分页和编辑支持(其中启用了ASP.NET AJAX):
数据视图网页自动推断出关联对象间的关系,提供了自动的过滤支持:
数据视图网页还自动地显示你的对象间的关系,允许用户轻松地在其间导航。例如,注意,产品的Category字段允许我转到特定产品的分类细节网页上去:
数据视图网页还提供了自动选择,编辑和删除支持。注意下面,在行内编辑模式下,他们会自动推断出友好的外键名称(允许你编辑“Category”,从下拉框中挑一个名称,而不是指定一个“CategoryID”整数):
所有这些都是自动工作的,而不用创建任何自定义的.aspx 网页或者编写任何自定义代码。所有的web用户界面都是根据你的LINQ to SQL或LINQ to Entities数据模型动态生成的。
第五步: 进一步定制网站
对你的ORM数据模型现成提供上面所有的功能,对让你的网站很快开始运作大有好处。在实现基本的网站体验之后,你可以进行完全的定制,无论是数据验证/业务逻辑层次还是用户界面层次。
你可以添加标准的LINQ to SQL数据模型和业务验证规则到你的数据模型中,以执行约束条件。动态数据网页会自动执行这逻辑,提供基于这逻辑的适当的错误信息和验证用户界面。
你也可以定制任何数据网页中的用户界面,动态数据项目允许你定制网站上所有数据网页的默认观感,以及定制用在网站上的所有字段类型的默认观感。你可以通过对创建新的动态数据项目时加到“/App_Shared”文件夹中的默认模板文件做编辑来实现这2种定制:
只要编辑上面的任何文件就可定制默认的数据网页视图或默认的数据字段的外观。
然后,你还可以通过在项目中明确地添加一个映射到某个特定数据视图网页的.aspx网页,来覆盖对应的用户界面。例如,我可以往项目中加一个 \Products\ListDetails.aspx 来覆盖默认产品的 ListDetails 行为:
然后,你可以往网页上添加任何标准的ASP.NET数据控件。你还可以在网页上使用ASP.NET 3.5 Extensions CTP预览版中发布的有动态数据意识的ASP.NET数据控件。这些控件允许你完全定制用户界面的观感,同时还使用新的动态元数据提供器(dynamic meta-data provider)来推断你的数据模型类间的关系和的元数据,以提供默认行为。
有关详情
上面的示范只涉及了你可以通过ASP.NET动态数据项目来实现的几个场景和功能。要了解详情的话:
- 观看David的网播
- 看一下ASP.NET动态数据的快速上手文档
- 订阅David Ebbo, Scott Hunter, 和Marcin Dobosz的博客
- 访问ASP.NET动态数据论坛问问题
想了解有关LINQ to SQL,以及如何创建数据模型类的详情,参阅我以前写的下列博客帖子(【译注】博客园的韩现龙已经完成了其中的8篇,不久会在这里贴出来):
你还可以观看www.asp.net网站上此处的精彩LINQ和 LINQ to SQL “How do I?(该如何做)” 录像。
希望本文对你有所帮助,
Scott
【原文地址】Using LINQ to SQL (Part 1)
【原文发表日期】 Saturday, May 19, 2007 12:41 AM
在过去的几个月中我写了一系列涵盖了VS和.NET Framework Orcas版中的一些新特性的帖子,下面是这些帖子的链接:
以上的语言特性帮助数据查询成为一等编程概念。我们称这个总的查询编程模型为“LINQ”--它指的是.NET语言级集成查询。
开发者可以在任何的数据源上使用LINQ。他们可以在他们选择的编程语言中表达高效的查询行为,选择将查询结果转换或构形成他们想要的任何格式,然后非常方便地操作这个结果集。有LINQ功能的语言可以提供完全的类型安全和查询表达式的编译时检查,而开发工具则可以提供在编写LINQ代码时完全的智能感知,调试,和丰富的重构支持。
LINQ支持一个非常丰富的的扩展性模型,该模型将有助于对不同的数据源生成不同的高效运算因子(domain-specific operators)。.NET Framework的Orcas版本将发布一些内置库,这些库提供了针对对象(Objects),XML,和数据库的LINQ支持。
什么是LINQ to SQL?
LINQ to SQL 是随.NET Framework Orcas版一起发布的O/RM(对象关系映射)实现,它允许你用.NET 的类来对一个关系型数据库建模。然后你可以用LINQ对数据库进行查询,以及进行更新/插入/删除数据的操作。
LINQ to SQL完全支持事务,视图和存储过程。它还提供了一种把数据验证和业务逻辑规则结合进你的数据模型的便利方式。
使用LINQ to SQL对数据库建模:
Visual Studio Orcas版中提供了一个LINQ to SQL设计器,该设计器提供了一种简易的方式,将数据库可视化地转换为LINQ to SQL对象模型。我下一篇博客将会更深入一些来介绍怎么使用该设计器(你可以观看我一月份时录制的这个关于如何从头开始创建LINQ to SQL模型的录像)。
通过LINQ to SQL设计器,我可以方便地设计出如下所示的Northwind数据库的示例模型:
上图定义了四个实体类:Product, Category, Order 和 OrderDetail。每个类的属性都映射到数据库中相应表的字段,类实体的每个实例代表了数据表中的一行记录。
在上图中,四个实体类间的箭头代表了不同实体间的关联/关系。它们主要是根据数据库中的主键/外键关系生成的。设计器上的箭头的指向表明了该关系是一对一还是一对多的关系。基于此,强类型的属性将会被加入到实体类中。例如,上边的Category类和Product类之间有一个“一对多”的关系。这意味着Category类将有一个"Products"属性,该属性代表了在该类中所有的产品对象的集合。而Product类将会有一个"Category"属性来指向一个Category类的实例,该Category类的实例表明了了产品所属的类别。
上图中LINQ to SQL设计器的右侧列出了与我们的数据库模型交互的存储过程。在上边的例子中,我添加了一个“GetProductsByCategory”存储过程。它有一个categoryID作为输入参数,返回一个产品实体序列作为结果集。下面的代码将展示如何调用该存储过程。
理解DataContext类
当你点击LINQ to SQL设计器上的“保存"按钮时,Visual Studio将会保存我们建立的代表了实体和数据库关系的各个类。针对加入到我们的解决方案的每一个LINQ to SQL设计器文件,同时也会生成一个自定义的DataContext类。这个DataContext类是我们从数据库中查询实体或者进行更改操作的主要渠道。生成的DataContext类将含有一些属性,对应于我们在数据库中建了模的每个数据表,以及一些方法,对应于我们添加的每个存储过程。
例如,下图就是基于我们上边设计的模型而生成的的NorthwindDataContext类:

LINQ to SQL 代码例子
用LINQ to SQL 设计器对我们的数据库建模之后,我们就可以很方便地编写代码对数据库进行操作了。下边是一些展示了常见的数据库操作的代码例子:
1) 从数据库中查询Products
下面的代码用LINQ to SQL 查询语法来获取Product对象的IEnumerable序列。注意代码是如何通过Product/Category关系来仅查出那些类别是"Beverages"的产品的:
C#:
VB:
2) 更新数据库中的一条产品记录
下面的代码示范了如何从数据库中查询出单一的一条产品记录,更新它的价格,然后将改动保存至数据库:
C#:
VB:
注意:VB在Orcas Beta1中尚不支持Lambda。但是在Beta2中,它就会支持了--那时代码就会能写得更为简洁一些。
3) 向数据库中插入一条新的Category和两条新的Products
下面的代码示范了如何生成一个新的分类,然后生成两条和该分类相关联的产品,然后将这三条记录保存到数据库中。
注意下边,我不用手工去维护主/外键关系,取而代之的是,我只向分类对象的“Products”集合中添加了两个Product记录,然后把该Category对象添加到DataContext的“Categories”集合中,LINQ to SQL将知道自动为我持久适当的PK/FK的关系。
C#
VB:
4) 从数据库中删除Products
下面的代码示范了如何从数据库中删除所有的玩具产品:
C#:
VB:
5) 调用存储过程
下面的代码示范了如何不使用LINQ查询语法,而是通过调用我们在上面向数据模型中添加的“GetProductsByCategory”存储过程来查询Product实体。注意,一旦我查询出了Product结果集,我可以更新/删除它们,然后再调用 db.SubmitChanges()来将这些更新提交到数据库。
C#:
VB:
6) 在服务器端分页查询Products
下面的代码示范了如何通过LINQ查询语法实现高效的服务器端数据库分页查询。通过使用下面的Skip()和Take()操作符,我们从数据库中只查询出从200行开始的10条记录:
C#:
VB:
总结
LINQ to SQL提供了一种很棒的、干净利索的方法来为你的应用程序来建立数据层。一旦你定义了数据模型,你就可以方便而且有效地对它进行查询,插入,更新和删除。
希望以上的介绍和代码例子刺激了你的胃口,希望了解到更多东西。在接下来的几周里我会在该系列中更具体地探讨LINQ to SQL。
希望本文对你有所帮助,
Scott
(博客园 韩现龙译)
【原文地址】 New "Orcas" Language Feature: Anonymous Types
【原文发表日期】 Tuesday, May 15, 2007 7:02 AM
在过去的2个月里,我发表了一系列贴子,讨论作为Visual Studio和.NET框架Orcas版本一部分发布的一些新的语言特性。这里是这个系列里前4个贴子的链接:
今天的贴子讨论我这个语言系列的最后一个新特性:匿名类型。
什么是匿名类型(Anonymous Types)?
匿名类型是C#和VB的方便语言特性,它允许开发人员在代码内简明地定义行内CLR类型,而不用显式地对类型定义一个正式的类声明。
匿名类型在使用LINQ做查询,转换/投影/构形数据时尤其有用。
匿名类型的例子
在我以前的查询句法贴子里,我示范了你可以通过投影来转换数据。这个LINQ的强有力的特性允许你对一个数据源(不管这个数据源是数据库,XML文件还是内存中的集合)做查询操作,然后对查询数据的结果构形成与原先数据源不同的结构或格式。
在我以前的查询句法贴子里,我定义了一个用来代表我转换过后的产品数据的MyProduct类。通过显式地定义MyProduct类,我就有了一个正式的CLR类型契约,我可以很容易地用它来把我自定义结构的产品结果在web服务间或我的应用解决方案中的多个类和程序集间传递。
但有的时候,我只想要在我当前的代码范围内查询和操作数据,我不想要另外正式地定义一个类来代表我的数据,才可以操作数据。在这种情形下,匿名类型非常有用,因为它们允许你在你的代码内,简明地定义一个新类型在行内使用。
例如,假设我使用Orcas中的LINQ到SQL对象关系映射器设计器对Northwind数据库建模,生成下列的类:
然后我就可以使用下列代码来对数据库里的产品数据进行查询,使用LINQ的投影/转换功能将数据结果定制构形成与上面的Product类有所不同的东西。但不是用一个显式定义的MyProduce类来代表从数据库获取的数据行,而是用匿名类型的特性来隐式地定义一个含4个属性的新类型来代表我定制构形的数据,象这样:
在上面的代码里,作为LINQ表达式select子句的一部分,我声明了一个匿名类型,然后由编译器自动生成带4个属性(Id, Name, UnitPrice 和 TotalRevenue)的匿名类型,这些属性的名称和类型是从查询的构形中推断出来的。
然后我使用了C#中的var这个新关键词来指代从LINQ表达式返回的这个匿名类型的 IEnumerable<T> 序列,还在后面代码的foreach语句里,对这个序列进行循环时,用var来指代其中的每个匿名类型实例。
尽管这个句法给了我动态语言一样的灵活性,我还保留了强类型语言的好处 - 包括 Visual Studio中的编译时检查和代码intellisense支持。例如,注意上面,我是如何对返回的产品序列做foreach的,对从LINQ查询推断出的带自定义属性的匿名类型,我还能得到完整的代码intellisense和编译检查。
理解var关键词
Orcas中的C#引进了var这个新关键词,在声明局部变量时可用于替代类型名。
在第一次看见var这个新关键词时,大家常有的一个错误认识是,这是个后期绑定或者无类型的变量引用(譬如,Object类型的引用或象Javascript中后期绑定的对象引用)。这并不正确,var关键词总是生成强类型的变量引用。不是要求开发人员显式地定义变量的类型,var这个关键词而是告诉编译器在变量最先声明时,从用来初始化变量的表达式推断出变量的类型。
var这个关键词可以用来引用C#的任何类型(意即它可用于匿名类型和显式定义的类型)。实际上,理解var这个关键词的最容易的方法是看一下几个将其用于常见显式类型的例子。譬如,我可以象下面这样使用var这个关键词来声明三个变量:
编译器会根据初始赋值推断出name,age和male变量的类型,在这个例子中,分别是字符串,整数和布尔值。这意味着,编译器会生成与下面代码完全一样的IL:
实际上,CLR根本不知道你使用了var这个关键词,从它的角度来看,上面2个代码例子绝对没有区别。第一个版本只不过是由编译器提供的节省开发人员几下键击的语法糖而已,让编译器做苦力推断出和声明类型名称。
除了使用var这个关键词替代内置的数据类型外,很明显地,你也可以将它用于你定义的任何自定义类型。例如,回到我以前博客贴子中的LINQ查询投影例子,这个投影使用了用来数据构形的显式的MyProduct类型,我可以用var这个关键词将其改写为:
重要注意事项:虽然我在上面使用了var这个关键词,我并没将其用于匿名类型。我的LINQ查询还是使用了MyProduct这个类型来对返回的数据做了构形,这意味着var products声明是IEnumerable<Product> products的速记而已。同样地,在foreach语句中我定义的var p变量不过是MyProduct p的速记而已。
var关键词的重要规则
因为var这个关键词产生强类型的变量声明,编译器需要能够根据它的用法推出其类型。这意味着,在用它来声明变量时,你总是需要做个初始赋值。编译器会产生一个编译错误,如果你不这么做的话:
声明匿名类型
至此,我们介绍了var这个关键词,我们可以开始用它来指代匿名类型了。
C#中的匿名类型是使用与我语言系列第一个博客贴子里讨论过的对象初始化句法同样的句法来定义的。其区别是,不是作为初始化语法的一部分来声明类型名称,而是在实例化匿名类型时,你将new关键词后面的类型名称省略掉:
编译器会分析上面的句法,自动定义一个带有4个属性的新的标准CLR类型。这4个属性的类型是根据赋给的初始值的类型来决定的。例如,在上面的例子中,Id属性被赋值了一个整数,所以编译器将生成一个类型为整数的属性。
匿名类型的实际CLR名称是由C#编译器自动生成的。CLR本身并不知道匿名类型和非匿名类型间的区别,所以两者的运行时语义是绝对完全一样的。Bart De Smet在这里写有一篇很好的博客贴子,对此做了详细讨论,如果你想知道匿名类型的确切类命名模式以及生成的IL的话。
注意上面,当你在匿名类型上键入"product." 时,你依然在Visual Studio中得到编译时检查和完整的intellisense。还注意一下,intellisense描述是如何表明它是个AnonymousType(匿名类型)的,但依然提供了完全的属性声明信息,如红线圈出的文字所示。
使用匿名类型做分层构形
匿名类型可以带来便利的一个强有力的场景是,可以用最小量的代码来轻易地对数据做分层构形投影。
例如,我可以编写下面这样的LINQ表达式,对Northwind数据库中价格大于50美元的所有产品进行查询,然后将返回的产品用以产品的ReorderLevel(库存重订购水平)来排序的一个分层结构来构形(使用了LINQ查询句法支持的group into子句):
将上面的代码在ASP.NET中运行时,我将得到浏览器显示的下列输出:
同样地,我也可以根据JOIN结果来做分层构形。例如,下面的代码生成了一个新匿名类型,它带有几个标准的产品字段属性,以及一个含有客户对特定产品所做的最新5个订单的分层的子集合属性:
注意到我是如何利落地访问分层数据的。在上面,我对产品查询进行循环,然后细钻到每个产品的最新5个订单的。你可以看到,到处都有完整的intellisense和编译时检查,即使是匿名类型上订单细节的嵌套子集合中的对象的属性上也都有。
数据绑定匿名类型
就象我在贴子前面提到的那样,从CLR的立场来说,匿名类型和显式定义的类型间绝对毫无区别。匿名类型和var关键词纯属节省代码量的“语法糖”,其运行时语义跟显式定义的类型是完全一样的。
此外,这意味着,所有的标准.NET类型反射特性,对匿名类型也是工作的,即意味着,象绑定到UI控件的特性同样工作。例如,假如我要显示我前面的分层LINQ查询的结果,我可以象下面这样在一个.aspx网页里定义一个 <asp:gridview> 控件:
上面的.aspx 含有一个gridview,它有2个标准的boundfield列,一个含有嵌套的 <asp:bulletedlist> 控件的模板字段列,我将用这个嵌套控件来显示产品的分层订单细节的子结果集。
然后,我可以编写下面这个LINQ代码来对数据库做一个分层查询,然后将定制构形的数据结果绑定到GridView来显示:
因为GridView支持对任何 IEnumerable<T> 序列的绑定,使用反射获取属性值,它对我上面使用的匿名类型依然工作。
在运行时,上面的代码会产生一个产品细节以及产品最新的订单数量的分层列表的简单网格,象这样:
很明显地,你可以把这个报表做得更丰富,更漂亮,但希望你能从中了解到,现在对数据库做分层查询是多么的容易,可以对返回的结果做你要的构形,然后对结果用编程手法操作或将其绑定到UI控件上。
结语
匿名类型是个很方便的语言特性,允许开发人员在代码内简明地定义行内CLR类型,而不用提供一个正式的类定义声明。虽然它们可以在很多场合下使用,但在使用LINQ查询和转换/构形数据时尤其有用。
这个贴子结束了我5个部分的Orcas语言系列。以后,我会发表更多的LINQ贴子,来示范如何在实战上使用所有这些新语言特性来做常见的数据访问操作(定义数据模型,查询,更新,使用存储过程,验证等等)。但我想先把这个5个部分的语言系列完成,这样我们在我将来的贴子里深入探讨时,你才会真正理解所用的底层语言构造。
希望本文对你有所帮助,
Scott
【原文地址】New "Orcas" Language Feature: Query Syntax
【原文发表日期】 Saturday, April 21, 2007 2:12
上个月我开始了一个贴子系列,讨论作为Visual Studio和.NET框架Orcas版本一部分发布的一些新的VB和C#语言特性。下面是该系列的前三篇贴子的链接:
今天的贴子要讨论另一个基础性的新语言特性:查询句法(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
【原文地址】Working with Data in ASP.NET 2.0
【原文发表日期】 Saturday, April 14, 2007 11:08 AM
最近,有一堆关于如何在ASP.NET 2.0应用中操作数据的精彩文章和教程在网上发表了。本贴子着重说一下其中的几个:
在ASP.NET 2.0中操作数据教程系列
Scott Mitchell最近完成了为 www.asp.net 网站撰写的《在ASP.NET 2.0中操作数据 (Working with Data in ASP.NET 2.0)》教程系列。该教程系列包含了超过75个数据教程,内容超过750页!所有的教程都有VB和C#版本,还提供了PDF格式供下载。如果你还没有读过这些教程的话,我非常建议你去这里看一下。
【译注】 ASP.NET MVP朱晔主持翻译了该系列,参考 Scott Mitchell的ASP.NET2.0数据指南中文版索引。
下面是该教程的目录和指向已经发表的文章的链接:
Scott的系列中不久将发表的还有18个教程,它们讨论的内容是:
- 缓存 (4个教程)
- 数据和站点地图提供器 (1个教程)
- 操作批数据 (包括事务; 4个教程)
- 高级数据访问层场景 (9个教程).
你可以订阅这个RSS feed,以在这些教程发表时了解相关更新。
使用ASP.NET 2.0的数据源控件
除了在www.asp.net发表上面的文章外,Scott Mitchell 还在优秀的ASP.NET网站www.4GuysFromRolla.com上撰写一些关于使用ASP.NET 2.0数据源控件的精彩数据文章。
如果你没访问过www.4GuysFromRolla.com的话,我非常建议你去浏览一下,该网站拥有成堆的精彩内容,都是免费的。你也可以在这里订阅已发表的4GuysFromRolla 文章的RSS feed。如果你是ASP.NET开发人员的话,你绝对应该这么做了。
下面是4GuysFromRolla网站上ASP.NET 2.0数据源控件系列的最新文章的指针:
SubSonic 2.0 Beta 3
SubSonic 2.0 Beta 3 已经发布了。SubSonic 是个完全免费的强大的工具,可以帮助你快速建立数据驱动的网站。它包含了一个DAL构造器,允许你快速地为你的数据创建强类型的集合和对象模型 (它是根据你网站的数据定义在编译时建立的),以及根据数据自动创建UI的丰富的scaffolding支持。
Jon Galloway 对一些新的SubSonic 2.0特性作了简短的总结,你可以在这里阅读一下。
如果你将参加2个星期后的MIX大会的话,一定要去跟Rob Conery打声招呼,他是SubSonic的主要架构师,他也将和Mono项目的Miguel de Icaza以及其他人一起列席MIX大会的一个精彩的开源座谈会的专家小组。
在ASP.NET中使用NHibernate的最佳实践, 1.2 版本
Billy McCafferty这个月早些时候更新了他的很受欢迎的《在ASP.NET中使用NHibernate的最佳实践》一文。你可以在这里阅读最新的版本。
这篇文章讨论了在用备受欢迎的(完全免费的)ORM的.NET版本建立企业级数据层时需要考虑的一些核心的东西。也留心一下计划于7月份出版的《实战NHibernate (NHibernate in Action)》一书。
希望本文对你有所帮助,
Scott
【原文地址】New "Orcas" Language Feature: Lambda Expressions
【原文发表日期】 Sunday, April 08, 2007 4:21 PM
上个月我开始了一个贴子系列,讨论作为Visual Studio和.NET框架Orcas版本一部分发布的一些新的VB和C#语言特性。下面是这个系列的前2篇贴子:
今天的贴子讨论另一个基础性的新语言特性:Lambda表达式。
什么是Lambda表达式?
随VS 2005发布的C#2.0引进了匿名方法的概念,允许在预期代理(delegate)值的地方用“行内(in-line)”代码块(code blocks)来做替代。
Lambda表达式为编写匿名方法提供了更简明的函数式的句法,但结果却在编写LINQ查询表达式时变得极其有用,因为它们提供了一个非常紧凑的而且类安全的方式来编写可以当作参数来传递,在以后作运算的函数。
Lambda表达式的例子:
在我以前的扩展方法博客贴子里,我演示了你如何可以象下面这样声明一个简单的Person类:
然后,我示范了你可以如何使用一些值来生成一个List<Person>集合的实例,然后使用由LINQ提供的新的Where和Average扩展方法来返回集合中的人的一个子集,以及计算这个集合中的人的平均年龄:
上面高亮标记的红色 p => 表达式就是Lambda表达式。在上面的例子里,我用第一个lambda来指定获取特定人时所用的过滤条件,用第二个lambda来指定在计算平均年龄时该用Person对象的哪个值。
详解Lambda表达式
理解Lambda表达式最容易的方法是把它们设想成编写简明的行内方法的方式。譬如,我上面编写的例子可以使用C#2.0的匿名方法来编写,象这样:
上面两个匿名方法都接受一个Person类型的参数。第一个匿名方法返回一个布尔值,表示Person的LastName是否是Guthrie,第二个匿名方法返回一个整数值(返回那个人的年龄)。我们前面使用的lambda表达式的作用是一样的,两个表达式都接受一个Person类型的参数。第一个lambda表达式返回一个布尔值,第二个返回一个整数。
在C#里,一个lambda表达式在句法上是写成一个参数列表,随后是 => 符号,随后是表达式在调用时要运算的表达式或者语句块:
params => expression
所以,当我们编写这样的lambda表达式时:
p => p.LastName == "Guthrie"
我们是想表示,我们在定义的Lambda接受一个参数p,要运行的代码表达式返回p.LastName的值是否等于“Guthrie”。 我们将参数命名为p是不相干的,我也可以很容易地将其命名为o,x,foo,或者我想要的任何名字。
不象匿名方法要求参数类型是明确地指明的,Lambda表达式允许省略参数类型,而允许它们根据用法来推断出类型。譬如,当我编写 p=>p.LastName == "Guthrie" 这个lambda表达式时,编译器推断出p参数属于Person类型,因为当前的Where扩展方法的对象是个范型的List<Person>集合。
Lambda参数的类型可以在编译时和被Visual Studio的intellisense引擎推断出来,这意味着在编写lambda时你将获得完全的intellisense 和编译时检查。譬如,注意当我在下面健入 p. 时,Visual Studio Orcas是如何提供intellisense完成的,因为它知道 p 是 Person类型:
注: 假如你要给一个Lambda表达式明确地声明参数的类型的话,你可以在Lambda参数表里的参数名字前声明参数类型,象这样:
针对框架开发人员的高级内容:Lambda表达式树 (Lambda Expression Trees)
从一个框架开发人员(framework developer)的角度来看,使得Lambda表达式特别强有力的事情之一是,它们既可以以基于IL的方法的形式被编译成代码代理(code delegate),或者也可以编译成一个表达式树(expression tree)对象,然后在运行时用来分析,转换或者优化表达式。
能将Lambda表达式编译成一个表达式树对象是个强大无比的机制,将促成许多使用场景,包括使用能提供编译时句法检查和VS intellisense的统一的查询语言来建立支持丰富数据查询的高性能对象映射器(无论是关系数据库,活动目录,还是web服务)之能力。
从Lambda表达式到代码代理 (Code Delegates)
上面的Where扩展方法是个将Lambda表达式编译成代码代理(code delegate)的例子(意即它是编译成IL的,可以以代理的形式调用)。支持象上面那样过滤任何IEnumerable集合的Where()扩展方法可以使用下面这样的扩展方法代码来实现:
上面的Where()扩展方法接受一个 Func<T, bool> 类型的过滤参数,该参数是个接受一个类型为T的参数,返回一个布尔值表示条件是否满足的方法之代理。当我们把Lambda表达式作为一个参数传递给这个Where() 扩展方法时,C#编译器会将我们的Lambda表达式编译成IL方法代理(这里,<T> 将是Person),然后我们的Where()方法可以调用来计算某个给定条件是否被满足了。
从Lambda表达式到表达式树
当我们要想针对类似我们的列表集合一样的内存中的数据做运算时,把lambda表达式编译成代码代理是恰如其分的。但考虑一下你想要查询数据库里的数据的情形(下面的代码是使用Orcas中内置的LINQ到SQL对象关系映射器写成的) :
这里,我要从数据库里取出一串强类型的Product对象,我向Where()扩展方法表示,要通过一个Lambda表达式来做过滤。
我绝对不想要看到发生的是,从数据库里取回所有的产品记录,将它们放在一个局部的集合里,然后在内存里对它运行Where()扩展方法来进行过滤。这么做效率极其不高,对大数据库的扩缩性将是极差的。而我希望的是,LINQ到SQL的ORM将我上面的Lambda过滤条件翻译成SQL表达式,然后在远程的数据库里进行过滤性查询。那样的话,我只返回那些符合查询条件的记录,这样的数据库查询效率是非常高的。
框架开发人员可以通过声明他们的Lambda表达式参数是个Expression<T>类型,而不是Func<T>类型来取得这样的结果。这会导致Lambda表达式参数被编译成一个我们可以在运行时拆开和分析的表达式树:
注意上面我是怎么把我们在先前用过的同样的 p=>p.LastName == "Guthrie" Lambda表达式,但这次将其赋值给一个 Expression<Func<Person, bool>> 变量,而不是Func<Person,bool> 变量。编译器不会产生IL,而是会指派一个表达式树对象,然后我作为一个框架开发人员就可以用它来对相应的Lambda表达式进行分析,按我想要的方式对其进行运算(譬如,我可以挑出表达式中的类型,名字和值等)。
在LINQ到SQL的情形下,它会将这个Lambda过滤语句翻译成标准的关系SQL语句,来对数据库进行操作(从逻辑上来说,一个“SELECT * from Products where UnitPrice < 55”语句)。
IQueryable<T> 接口
为帮助框架开发人员建立可查询的数据提供器,LINQ提供了 IQueryable<T> 接口。这个接口实现了标准的LINQ扩展方法查询运算符,提供了一个更便利的方式来实现对一个复杂的表达式树的处理(譬如,象下面这样,我用了3个不同的扩展方法,2个lambda来从数据库取回10个产品的情形):
想阅读一些关于如何使用 IQueryable<T> 来建立自定义的LINQ数据提供器的精彩博客系列的话,请看一下下面这些别人写的精彩博客贴子:
结语
希望上面的贴子内容对如何考虑和使用Lambda表达式提供了基本的理解。当与Orcas中System.Linq命名空间下提供的内置标准查询扩展方法结合使用时,它们提供了一个非常好的方式来对任何类型的数据进行查询和交互,同时还保持了对完整的编译时检查和intellisense的支持。
通过利用由Lambda提供的对表达式树的支持,以及 IQueryable<T> 接口,构建数据提供器的框架开发人员可以确保开发人员编写的干净的编码,对任何数据源(无论是数据库,XML文件,内存中的对象,web服务,LDAP系统等)运行起来速度快而且效率高。
在下几个星期里,我将完成这个从理论的层次上讨论新核心语言概念的语言系列,然后转到讨论一些极其实用的实战例子(特别是针对数据库和XML文件使用LINQ的场景)。
希望本文对你有所帮助,
Scott
【原文地址】New "Orcas" Language Feature: Extension Methods
【原文发表日期】 Tuesday, March 13, 2007 2:27 AM
上个星期,我发表了我准备写的讨论一些新的VB和C#语言特性的系列博客贴子的第一篇,这些新语言特性是将于今年晚些时候发布的Visual Studio和.NET框架Orcas版的一部分。
我的上一个博客贴子讨论了自动属性,对象初始化器和集合初始化器等新特性。如果你还没有读过这个帖子的话,请在这里阅读。今天的贴子讨论一个VB和C#中都具有的,重要得多的新特性:扩展方法 (Extension Methods)。
什么是扩展方法 (Extension Methods)?
扩展方法允许开发人员往一个现有的CLR类型的公开契约(contract)中添加新的方法,而不用生成子类或者重新编译原来的类型。扩展方法有助于把今天动态语言中流行的对duck typing的支持之灵活性,与强类型语言之性能和编译时验证融合起来。
扩展方法促成了好多有用的使用场景,并使在作为Orcas一部分发布的.NET版本中引进的非常强大的LINQ查询框架成为可能。
简单的扩展方法例子:
有没有想过要检查一个字符串变量是否是个合法的电子邮件地址? 在今天,你大概需要通过调用一个单独的类(或许通过一个静态方法)来实现检查该字符串变量是否合法。譬如,象这样:
string email = Request.QueryString["email"];
if ( EmailValidator.IsValid(email) ) {
}
而使用C#和VB中的新“扩展方法”语言特性的话,我则可以添加一个有用的“IsValidEmailAddress()”方法到string类本身中去,该方法返回当前字符串实例是否是个合法的字符串。然后我可以把我的代码重写一下,使之更加干净,而且更具描述性,象这样:
string email = Request.QueryString["email"];
if ( email.IsValidEmailAddress() ) {
}
我们是怎么把这个新的IsValidEmailAddress()方法添加到现有的string类里去的呢?我们是通过定义一个静态的类型,带有我们的“IsValidEmailAddress”这个静态的方法来实现的,象下面这样:
public static class ScottGuExtensions
{
public static bool IsValidEmailAddress(this string s)
{
Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
return regex.IsMatch(s);
}
}
注意,上面的静态方法在第一个类型是string的参数变量前有个“this”关键词,这告诉编译器,这个特定的扩展方法应该添加到类型为“string”的对象中去。然后在IsValidEmailAddress()方法实现里,我可以访问调用该方法的实际string实例的所有公开属性/方法/事件,取决于它是否是合法电子邮件地址来返回true/false。
在我的代码里把这个特定的扩展方法的实现添加到string实例,我只要使用标准的“using”语句来引入含有该扩展方法的实现的命名空间:
using ScottGuExtensions;
然后编译器就会在任何string上正确地定位IsValidEmailAddress()方法。在公开发行的Orcas三月份的CTP中的C#和VB在Visual Studio代码编辑器里对扩展方法提供了完整的intellisense支持。所以,当我在一个字符串变量上点击“.”关键词时,我的扩展方法现在就会出现在intellisense的下拉框里:
VB和C#编译器也会很自然地给与你对所有扩展方法用法的编译时的检查,这意味着你会得到一个编译时错误,假如你键错或者错用一个扩展方法的话。
[感谢David Hayden,是他在去年的一个老帖子 里第一个示范了我在上面使用的这个IsValidEmailAddress使用场景。]
扩展方法使用场景续...
利用扩展方法这个新特性来给个别类型添加方法给开发人员开辟了许多有用的扩展性使用场景。但使得扩展方法非常强有力的是,它们不仅能够应用到个别类型上,也能应用到.NET框架中任何基类或接口上。这允许开发人员建立种种可用于整个.NET框架的丰富的可组合的框架层扩展。
譬如,考虑这样一个场景,我想要一个既容易,描述性又强的方式来检查一个对象是否已经包含在一个对象集合或数组里。我可以定义一个简单的.In(集合)扩展方法,我想把它添加到.NET框架中的所有对象上,我可以在C#里这么来实现这个“In()”扩展方法:
注意上面我是如何声明扩展方法的第一个参数的:“this object o”。这表明,这个扩展方法应该适用于继承于基类System.Object的所有类型,这意味著我可以在.NET中的每个对象上使用它。
上面这个“In”方法的实现允许我检查一个指定的对象是否包含在作为方法参数传入的一个IEnumerable序列中。因为所有的.NET集合和数组都实现了IEnumerable接口,现在我拥有了一个有用的,描述性强的方法来检查一个任意的对象是否属于任何.NET集合或数组。
然后我就可以使用这个“In()”方法来看一个特定的字符串是否在一个字符串数组中:
我也可以用它来检查一个特定的ASP.NET控件是否在一个容器控件集合里:
我甚至可以将其用在象整数这样的标量数据类型上:
注意上面,你甚至可以在象整数值42这样的基本数据类型值上使用扩展方法。因为CLR支持数值类型的自动boxing/unboxing,扩展方法可以直接使用在数值和其他标量数据类型上。
你大概可以开始从上面的例子中看出,扩展方法可以促成一些非常丰富和描述性强的扩展性使用场景。当使用于.NET中常见的基类和接口上时,他们可以促成一些非常好的特定于某个领域(domain specific)的框架和组合使用场景。
内置的System.Linq扩展方法
一个在Orcas时段随.NET发布的内置的扩展方法库是一套允许开发人员对任何数据进行查询的非常强有力的查询扩展方法。这些扩展方法实现位于新的 System.Linq 命名空间之下,定义了标准的查询操作符扩展方法,可以为.NET开发人员用来轻松地查询XML,关系数据库,.NET 对象, 和任何其他数据结构类型。
下面是使用这些查询扩展方法的扩展性模型的几个好处:
1) 它允许一个可用于所有数据类型(数据库,XML文件,内存中的对象,以及web-services等)的共同的查询编程模型和语法。
2) 它是可以组合的,允许开发人员轻松地往查询语法中添加新的方法/操作符。譬如,我们可以将我们自定义的“In()”方法与为LINQ所定义的标准的“Where()”方法作为一个单独查询的一部分一起使用。我们自定义的In()方法看上去就跟由System.Linq命名空间提供的标准方法一样。
3) 它是可扩展的,允许与任何数据提供器类型一起使用。譬如,任何一个象NHibernate或LLBLGen这样现有的ORM引擎可以实现LINQ的标准查询操作符来允许对他们现有的ORM实现和映射引擎实现LINQ查询。这允许开发人员学会一个查询数据的共同方式,然后对种类繁多的丰富数据存储实现使用同样的技能。
我将在下几个星期里对LINQ作更多的示范,但想留给你几个例子,这些例子展示了如何对不同类型的数据使用几个内置的LINQ查询扩展方法:
使用场景一:对内存中的.NET对象使用LINQ扩展方法
假定我们象这样定义了代表“Person”的类:
然后我可以使用新的对象初始化器和集合初始化器特性创建和填充一个“people”集合,象这样:
然后我可以使用由System.Linq提供的标准的“Where()”扩展方法来获取这个集合中FirstName的首字符是"S"的那些“Person”对象,象这样:
上面这个新的 p => 语法是“Lambda表达式”的一个例子,是对C# 2.0匿名方法支持的更简明的发展,允许我们通过一个实参来轻松地表达查询过滤(在这个情形下,我们表示我们只想要返回一串firstname属性的首字符是“S”字母的Person对象) 。上面这个查询然后就会返回包含2个对象的序列,Scott 和 Susanne。
我也可以利用由System.Linq提供的新的“Average” 和“Max”扩展方法编写代码来决定我的集合里的人的平均年龄,以及年龄最大的人,象这样:
使用场景二:对XML文件使用LINQ扩展方法
你手工在内存里创建一个硬写(hard-coded)的数据集合大概是很少见的。更有可能的是,你会从一个XM文件,数据库,或web服务里获取数据。
假定我们在硬盘上有一个XML文件,包含下面这样的数据:
很明显地,我可以使用现有的 System.Xml APIs 来装载这个XML文件进一个DOM,然后访问它,或者使用一个层次较低的XmlReader API ,自己对之手工分析。或者,在 Orcas中,我现在也可以使用支持标准的LINQ扩展方法的System.Xml.Linq 实现(即 XLINQ),更优雅地分析和处理XML。
下面的代码例子展示了如何使用LINQ来获取所有包含一个子节点的值的首字母为“S”的<person> XML元素:
注意,它使用了跟内存中对象例子中一模一样的 Where() 扩展方法。现在它返回一个“XElement”元素序列,XElemen是没有类型的XML节点元素。或者我也可以重写查询表达式,通过LINQ的 Select() 扩展方法来构造数据形状,提供一个使用了新的对象初始化器句法的Lambda 表达式来填充同样的“Person”类,跟我们第一个内存中的集合的例子一样:
上面的代码会做需要打开,分析,和过滤XML,然后返回一个强类型的Person对象序列所有的工作,不需要什么映射或持久的文件来映射数值,我只是在上面的LINQ查询式里直接指明了从XML到对象的构形而已。
我也可以和前面一样使用同样的Average() 和 Max() LINQ扩展方法来计算XML文件中<person>元素的平均年龄,以及最大年龄,象这样:
我不用手工分析XML文件,XLINQ 不仅可以为我处理分析,它在估算LINQ表达式时,也可以使用低层的XMLReader,而不是使用DOM来分析文件。这意味着它是迅速之极,而且不分配很多内存。
使用场景三:对数据库使用LINQ扩展方法
假定我们拥有一个SQL数据库,内含一个叫“People”的表,具有下列数据定义:
我可以使用Visual Studio中新的LINQ到SQL的所见即所得(WYSIWYG) ORM设计器,快速地创建一个映射到数据库的“Person”类:
然后我可以使用我先前用于对象和XML文件同样的LINQ Where() 扩展方法,从数据库中获取firstname的首字符为“S”的强类型“Person”对象序列:
注意,查询句法与对象和XML场景中的一模一样。
然后我也可以使用与前面一样的 LINQ Average() 和Max() 扩展方法来从数据库里获取平均和最大值,象这样:
要使上面代码例子工作,你自己不需编写任何SQL代码。Orcas中提供的LINQ到SQL对象关系映射器会处理获取,跟踪,和更新映射到你的数据库数据定义和存储过程的对象。你只要使用任何LINQ扩展方法对结果进行过滤和构形即可,LINQ到SQL会执行获取数据所需的SQL代码(注意,上面的 Average和Max 扩展方法很明显地不会从数据表中返回所有的数据行,它们会使用TSQL的聚合函数来计算数据库中的值,然后只返回一个标量值)。
请观看我一月份制作的一个录像,演示了LINQ到SQL如何显著地改进了Orcas中的数据生产力。录像中,你也可以看到新的LINQ到SQL的所见即所得ORM设计器的实战示范,以及对数据模型编写LINQ代码时代码编辑器提供的完整的 intellisense。
结语
希望上面的帖子给了你一个对扩展方法工作原理的基本理解,以及你能够利用它们来实现的一些酷扩展性方式。跟任何扩展性机制一样,我要告诫你别一开始就滥建新的扩展方法。不能因为你有一个闪亮的新榔头,就意味着世界上所有的东西突然都变成钉子了!
想着手开始尝试扩展方法的话,我建议你先探究一下Orcas中System.Linq命名空间中提供的标准查询操作符。这些操作符提供了对任何数组,集合,XML流,或关系数据库做丰富的查询的支持,可以极大地改进你操作数据时的生产力。我认为你会发现它们极大地减小了你要在你应用中编写的代码量,允许你编写非常干净和描述性强的语句。它们也允许你在你编码中得到查询逻辑自动的intellisense 和编译时检查。
在下几个星期里,我将继续这个关于Orcas中新语言特性的系列,探讨匿名类和类的推断(Type Inference),还会讨论Lambda的细节和其他酷特性。很明显地,我还会地更多地讨论LINQ。
希望本文对你有所帮助,
Scott
【原文地址】Free SQL Server Training Videos (and other good data tutorial pointers)
【原文发表日期】 Thursday, March 01, 2007 9:54 PM
今天在 www.asp.net 网站上,我们发布了一个注重于SQL Server 2005 Express 的新的精彩的《“我怎么做?”》录像系列,你可以下载和使用,是完全免费的。
系列中包括了13个专业录像,从最基本的开始,然后探讨数据表/数据定义(schema) 的设计,使用数据类型,创建存储过程,使用报表服务和商业智能引擎,对你的数据启用全文搜索等等。我猜,即使是大多数富有经验的开发人员也许也能从报表服务和全文搜索录像中学到一些新东西呢:
你可以在这里免费下载或在线观看这些SQL培训录像。
万一你错过了我写的关于把 SQL Express 或 SQL Server数据库上传到远程主机环境中的帖子的话,我强烈建议你回去读一下这2个贴子:
去年夏天早先时候,我曾贴出过一个很受欢迎的贴子,里面提供了你可以下载,在你应用中使用的100多个数据库数据定义的例子:
最后,也许是最重要的,一定要去看一下Scott Mitchell 这里的极棒的ASP.NET 数据教程系列 ,内含超过50个无比精彩的ASP.NET中常见的数据场景的教程(同时提供了VB 和C# 编码):
希望本文对你有所帮助,
Scott
【原文地址】 Video: Using LINQ with ASP.NET in VS "Orcas" (Part 1)
【原文发表日期】 Sunday, January 28, 2007 11:28 PM
我今年的一个目标是在博客中多用录像。我的看法是这样,很多时候,用来示范某样东西是怎么工作的,较之于比文字,录像是更好的媒介,也可以帮你避免由阅读我的一些非常长的博客帖子而带来的沉闷,单调,乏味。。。
在下几个月里,我会尝试着每隔一个星期就各种主题创制一个新录像。很多这些录像会涉及对随Visual Studio Orcas而来的一些新的特性的初览,Visual Studio Orcas是将于今年晚些时候发行的新版本。至于今天的录像,我决定着重介绍 LINQ,我认为LINQ是随Orcas而来的最激动人心的特性之一。
在高的层面上看,LINQ使得查询的概念成为.NET中一等(first class)的编程概念。通过使用LINQ,你可以轻松地查询关系数据库,XML文件,以及任何普通的.NET对象。你也可以轻松地在LINQ中使用你自己的数据源抽象来为各种数据提供器提供丰富的域模型(domain models)。想看一个精彩的例子的话,参考Fabrice的LINQ到Amazon的实现,这个例子支持了对通过 web-service 呈示的Amazon数据的LINQ查询。 作为 Orcas 发布的一部分,VB 和 C# 两者都提供了与LINQ丰富的语言级的集成,包括完全的 Intellisense,编译时检查和调试支持。
观看或者下载录像
第一个录像从一个新项目开始,然后示范对一个SQL数据库使用LINQ来建立一个简单的数据报表网页的基本知识,内含对从多个数据表计算出来的聚合值,以及效率高的服务器端数据库中分页的支持。
你可以在这里在线观看这个录像(总长度为24分钟)。或者如果你想保存在本地观看的话,你也可以在这里下载一个 .zip版本。注意,它也许会花30-60秒钟来做缓存,因为这是个大的录像。万一我的服务器请求太多不堪重负的话,我会在这个星期的晚些时候更新网址,并将它发布到一个微软的录像服务器上去。
想了解LINQ的详细信息的话,包括我自己的“使用LINQ建立数据驱动的ASP.NET应用”讲座的讲义和演示代码,请查看一下这里一个我以前的帖子。至于其他的ASP.NET 2.0技巧,诀窍和教程,请查看我的ASP.NET技巧/诀窍汇总列表网页。
这个录像里讨论的主题细节
这个录像里的示范举例说明了几个新特性:
1) 对新的所见即所得(WYSIWYG)HTML设计器一些功能的简短示范(包括分割界面和对新的CSS管理器的简短介绍,我将来会在另外的录像里对两者做详细讨论):
2) 如何使用VS Orcas中的LINQ到SQL的新ORM设计器来创建模型数据的类:
3) 如何在VS Orcas代码编缉器里使用新的LINQ Intellisense支持来得到对查询的完全的Intellisense和编译支持:
4) 如何使用 LINQ到SQL 来创建结合了Product实体数据和从Product实体相关联的OrderDetail实体中计算出来的聚合单元和收入数值的数据报表。这个数据报表使用了服务器端的数据库分页,每次有效率地只获取10行记录(这样,对一个十万行的产品表,不管你的报表指向哪个页,你每次只从数据库获取10行数据):
将来的录像
在将来的录像里,我会对LINQ做一些更深入的探讨,在这个录像里我只是刚触及到表面而已。其他的部分将包含这些内容:
- 类型推断和关键词 var (以及为什么是如此地酷)
- 使用 LINQ到SQL 支持插入,更新和删除操作的情形
- LINQ数据模型里的验证规则和逻辑
- 在 LINQ到SQL 中使用存储过程
- LINQ到SQL 中对JOIN的更丰富的支持
- LINQ到XML 和 LINQ到对象
- 使用新的 LINQDataSource 控件和其他ASP.NET数据控件轻松地创建 Web UI
很明显地,我也将有更多的ASP.NET和与web有关的录像,包括:
- 新的所见即所得(WYSIWYG)的HTML设计器和CSS支持
- 客户端JavaScript Intellisense
- JavaScript调试
- ASP.NET AJAX 扩展器(Extender)支持
- 数据库备份和上传集成
- 单元测试
- 多目标(Multi-targeting)支持(示范如何使用VS Orcas来build ASP.NET 2.0项目,而不用更新你的框架和服务器)
希望本文对你有所帮助,
Scott
附注:如果你有其他题目或主题要我用录像来作详细探讨的话,请把它们贴在这个贴子的评论部分。