理解领域服务和领域操作
本文目录:
3.1 WCF Ria Services简介
3.1.1 什么是WCF Ria Services
3.1.2 WCF Ria Services如何生成客户端代码
3.1.3 如何使用WCF Ria Services
3.2 实战WCF Ria Services开发
3.2.1 使用Silverlight业务应用程序项目模板
3.2.2 使用Entity Framework创建数据访问层
3.3 创建领域操作
3.3.1 查询操作
3.3.2 查询操作命名惯例
3.3.3 插入、更新、删除操作命名惯例
3.3.4 调用操作
3.3.5 自定义操作
要使用Silverlight开发企业级的应用系统,不可避免的要与数据库打交道。当开始使用Silverlight开发企业级的瘦客户端应用时,会发现Silverlight没有提供任何的对象来直接与数据库交互。这是因为Silverlight只是一个客户端平台,被设计用来在浏览器中运行,使得客户可以在任何位置通过一个Silverlight插件来运行应用程序。很明显,让一个浏览器客户端直接访问数据库是没太多意义的,而且也是一件危险的事情(目前大多数数据库都会放在防火墙后面)。
那么Silverlight客户端如何访问数据库?Silverlight的早期版本提供了比如Web服务这样的技术,允许客户端通过调用ASP.NET Web服务来跨应用程序边界访问数据库内容。此外,WCF也是目前最流行的服务器端技术,可以使用Silverlight与WCF交互来获取数据库数据。尽管可以使用WCF服务来操作数据库,但是创建和维护WCF服务会变得有些繁琐,主要是因为它将需要很多重复性的相似的代码,当为数据库添加过滤/排序/分组/分页等功能时,会变得非常复杂,需要不断的更新服务器端的引用,并且将强制开发人员编写和维护服务器端与客户端的逻辑,比如验证和业务规则逻辑。
为了解决这些问题,且简化Silverlight应用程序访问数据库,微软引入了一个新的技术,名为WCF Ria Services(通常称为RIA服务),专门设计让Silverlight应用程序访问数据库(未来也会用于其他的表示层技术)。
3.1 WCF Ria Services简介
RIA服务是构建在WCF之上的一个层,提供了常用的模式和框架来构建基于Silverlight的数据库驱动的应用程序,以便客户端可以方便的操作服务器端的数据,也可以说RIA服务简化了处理服务器端公开的数据的能力,并且提供了在客户端和服务器端共享业务逻辑的能力,它提供了一些系列的开发模式,并且提供了一个框架来简化开发Silverlight数据驱动程序的复杂性。
3.1.1 什么是WCF Ria Services
目前,大多数的企业级应用系统,都需要大量的处理数据库数据,数据在服务器端与客户端来回传数。对于经典的C/S架构的应用程序来说,当客户端请求服务器端数据后,在客户端界面上进行了一些处理,然后将修改过的数据传送回服务器端,服务器端将用来更新数据库数据。
现今的应用系统大多采用分布式架构,整个系统分由多个功能独立的层(layers或tiers)组成,比较热门的是三层是架构,这个架构将整个系统分为表示层、业务逻辑层和数据处理层。表示层和中间的业务逻辑层不能直接访问数据库。这种架构的一个问题是,它需要一个较复杂的底层框架来有效的在各个层之间传递数据,而且同样的代码可能会在多个层之间出现(比如数据类中的验证、业务规则和业务逻辑),重复的代码需求增加了额外的工作负担,也带来了很多潜在的问题。RIA服务被设计用于解决这个问题,使得开发人员只需要专注于解决实际的业务问题而不用过于关心架构方面的问题。
RIA服务中的一部分基于数据中心的设计模式来架构Silverlight应用程序,一部分是一个提供了高级数据管理功能的框架,包含验证/授权管理和查询特性,再一部分是自动的代码生成功能。使得在一个项目中编写的代码可以同步到另一个项目(通过RIA链接)。这些功能都是基于WCF服务之上,提供了中间层和表示层之间的沟通机制,整个架构如图3.1所示。
图3.1 RIA服务系统架构
RIA服务模式的关注点主要在于中间层,它并不绑定到某一特定的数据访问技术。但是它与Entity Framework和LINQ to SQL(需要安装WCF RIA Services Toolkit工具包)结合紧密。而且RIA服务模式也并不与某一特定的表示层技术绑定,虽然目前它的主要关注点在Silverlight,对其他表示层平台仅具有有限的支持。
RIA服务是一个端到端的技术,需要完全控制应用程序的客户端与服务器端。如果你需要访问已经存在的服务或者是使用由其它团队开发的服务,RIA服务可能有点不适合。
虽然本书的重点在于使用RIA服务来公开服务器上的数据和逻辑,但是一个Silverlight应用并不是仅限于这个技术,一些其它的用来通信的技术将在后面进行介绍。在本书介绍的很多概念即便没有使用RIA服务,对于构建企业级应用程序也是非常适用的。
RIA服务的框架部分提供了大量的额外的功能来极大的简化在表示层和中间层之间的通信,包含一个数据源控件,能够在XAML中以声明的方式与服务器端通信,客户端数据变更管理,以便服务器端进行处理,在客户端编写LINQ查询,并在服务器端实际的执行查询,以及基于ASP.NET成员与角色服务的身份验证功能。
3.1.2 WCF Ria Services如何生成客户端代码
RIA服务需要服务器端和客户端项目位于同一个解决方案之中,并且通过RIA服务链接进行关联。服务器端包含公开数据的服务,客户端通过与这些服务进行通信来处理数据。
服务器端编写的服务(Services)要遵循RIA服务设计模式,在Visual Studio中RIA服务构建任务包含在客户端生成相应的代码(代码生成过程)。这简化了与服务器端的通信,它也负责在客户端生成了对应的代理数据对象,这些对象通常就是服务器间公开的所谓的“实体”。这些实体类和它们的属性上会应用了特性(Attribute),比如一些验证逻辑。并且会从服务器端直接拷贝标记为共享的服务器端代码到客户端(标记为.shared.cs的文件)。代码生成通常也被称为“投影”,因为代码直接从服务器端投影到客户端。
来看一下当使用RIA服务连接两个项目时背后发生了什么。首先使用Silverlight业务应用程序项目创建一个启用了RIA服务的应用程序,步骤如下:
(1)打开Visual Studio 2010,选择“新建项目”, 在已安装的模板中选择“Silverlight|Silverlight业务应用程序”项目,为项目命名为ShowCodeGenerateDemo,如图3.2所示。
图3.2 创建Silverlight业务应用程序
(2)在单击“确定”按钮后,Visual Studio将自动创建2个项目,一个是名为ShowCodeGenerateDemo.Web的ASP.NET服务器端项目;一个是名为ShowCodeGenerateDemo的客户端项目,这两个项目通过RIA服务链接进行了关联,可以右击Silverlight项目,在弹出的菜单中选择“属性”菜单项,从项目属性中可以看到WCF RIA服务链接,如图3.3所示。
图3.3 WCF Ria服务链接
首先在解决方案资源管理器中选中ShowCodeGenerateDemo客户端项目,单击工具栏上的“显示所有文件”铵钮(位于解决方案左数第2个按钮位置),会看到一个灰色的Generated_Code文件夹,这里就是由RIA服务构建任务生成的代码的位置,如图3.4所示。
图3.4 Silverlight客户端代码生成位置
这些代码是WEB项目中编写的,在编译Silverlight项目时会自动生成,当更改了服务器端的代码后,这些改变将立即传递到Silverlight中以便客户端进行代码编写,如果没有立即传递,也可以编辑来强制代码生成。这些生成的代码允许用户在客户端访问服务器通过RIA服务公开的操作和数据。
注意:开发人员可以查看这些生成的代码,但是不要尝试更改这些代码,因为在Silverlight项目下次被编译时将覆盖这些代码。这些生成的类是局部类,允许开发人员在客户端进行扩展以便创建仅客户端的逻辑,在本章稍后将会再次详细介绍这些生成的代码。
代码生成器能够很聪明的察觉服务器端的操作种类,它使用的逻辑将在“领域操作“中进行介绍。
开发人员可以通过为实体类的属性标记各种特性来控制客户端项目中的实体类的生成,比如可以包含或不包含哪些属性。将在修饰实体部分进行介绍。
如果要在服务器与客户端之间共享代码(代码使用RIA服务构建任务从服务器项目拷贝到客户端项目中),这些文件必须具有.shared.cs扩展名,以便于构建任务知道要从服务器上拷贝这些文件到客户端项目中。
3.1.3 如何使用WCF Ria Services
在本节来简要的预览一下使用RIA服务进行开发的过程,稍后将进行详细的介绍每一部分。
1,在Silverlight和Web项目之间创建链接。
使用RIA服务需要开发人员在构造应用程序时遵循一个模式。首先,服务端项目和客户端项目必须链接起来以便RIA服务构建任务/代码生成工器能够从服务项目中投影代码到客户端项目中去,并创建支持的对象用来与服务器交互。这意味着客户端与服务器端项目必须在同一个解决方案中。
注意:通过使用WCF RIA服务类库项目模板也可以将将实体和元数据类从Web项目中分离放到一个单独的项目中。
2,创建领域服务Domain Services
下一步是创建一个或多个领域服务,考虑领域服务非常像一个标准的WCF服务,但是遵循了一个给定的模式(由一些基本功能组成)。
注意:实际上每个领域服务都是一个标准的WCF服务,RIA服务在客户端自动生成代码投影以便与服务器端进行通信,意味着开发人员不用关注添加领域服务引用之类的工作。然而,因为领域服务是标准的WCF服务,开发人员可以将其作为一个服务引用添加到项目中(比如要为应用程序创建一个WPF或ASP.NET客户端)。
RIA服务构建任务自动在客户端项目中产生代码以便与领域服务器端进行交互,它为每个领域服务创建一个领域上下文(Domain Context),并为由领域服务公开的实体类创建了一个代理类。
3,在领域服务中创建领域操作Domain Operations
本步骤在领域服务中创建领域操作,这些操作通常用来公开数据给客户端,且接收客户端使用Create、Read、Update和Delete(CRUD)模式进行的变更。
由领域服务公开的服务器端项目的实体对象可以使用特性进行修饰(比如找定验证规则),可以直接应用特性,或者是通过一个元数据类来应用特性。开发人员中以使用.shared.cs扩展名来在客户端和服务器端共享代码。
4,使用领域服务
一旦设置了服务器端且从领域服务公开了数据,开发人员就可以将注意力放到客户端项目,以便调用在领域服务中的操作以及使用服务器端数据。开发人员可以通过创建领域上下文DomainContext的实例并调用公开的操作来与领域服务进行交互。此外,开发人员也可以使用DomainDataSource控件来使用声明式的XAML代码声明性的与领域服务交互。
实体集(Entities)通常由一个领域操作被公开,这个操作经由一个IQueryable<T>表达式或作为一个IEnumerable集合被公开。通过返回一个IQueryable<T>表达式,RIA服务框架允许开发人员在客户端写LINQ查询,以便对想要的结果实体集合进行过滤、分组和分页。RIA服务序列化LINQ查询,将表达式发送到服务器端,在表达式被执行之前,这些客户端编辑的表达式将被追加到服务器端表达式的后面。这使得集合可以在服务器端被过滤、排序、分组、分页,而不用将整个集合传回到客户端在客户端完成这些操作,这减少了数据传输量。
当这个集合在客户端被更新时,公开数据的领域上下文(DomainContext)将维护一个变更集合(Changeset),这个集合包含已经被插入、删除和更新的实体。变更集合管理是RIA服务框架提供的一个非常强大的特性,它大大简化了管理集合的复杂性,并只将更新后的记录发送回服务器。领域上下文中的SubmitChanges方法将发送实体集中的变更给服务器,这些变更将自动的调用服务器端的插入、更新、删除操作来更新相应的后台数据库。
关于上述4点的讨论涉及到了进行RIA开发的非常广义的几个方面,这几个方面涉及到进行RIA服务开发的核心概念。后面的内容将详细介绍这些概念的具体实现。
3.2 实战WCF Ria Services开发
在上一节中介绍了WCF Ria Services开发的一些概念和开发流程,本章开始将通过案例来带领大家深入理解WCF Ria Services开发的方方面面。
3.2.1 使用Silverlight业务应用程序项目模板
在上面的小节中使用Silverlight业务应用程序项目模板创建了名为ShowCodeGenerateDemo的项目,可以看到项目自动使用了RIA服务,并且提供了使用RIA服务的基本功能。对于服务器端项目来说,它包含了一个名为Services的文件夹和一个Models文件夹,如图3.5所示。
图3.5 Silverlight业务应用程序模板服务器端结构
Services文件夹包含了WCF服务内容,项目模板自动包含了2个领域服务:
AuthenticationService服务:提供身份验证服务,它整合了ASP.NET成员和角色服务向Silverlight客户端提供身份验证和授权服务。
UserRegistrationService服务:提供用来进行用户注册的标准的领域服务。
Models文件夹包含了模型内容,它包含了2个数据类,这2个类将在服务器端与客户端之间传递,在Models文件夹下面会看到一个Shared文件夹,该文件夹下面包含一个名为User.shared.cs的文件,它包含了将在客户端与服务器端进行共享的文件。
因为Silverlight客户端和Web项目已经链接(非RIA服务链接)在一起,这个链接的目的是将Silverlight应用程序生成的编译后的XAP文件自动拷贝到Web服务项目中。然而,RIA服务需要在两个项目之间创建其他的链接。RIA服务链接的目的是配置项目包含的服务,以便客户端的Silverlight应用程序可以使用其中的服务,这会使得RIA服务构建任务在Silverlight客户端生成需要的代码来与服务器端进行通信。
注意:这两个链接的不同之处在于,配置XAP拷贝的链接是在Web项目的属性页面,而配置RIA服务链接是在Silverlight的属性页面。如果项目中已经有一个ASP.NET Web服务器端项目提供RIA服务,那么可以通过在Silverlight项目属性的RIA链接下拉列表框中选择已经存在的项目来进行链接。
3.2.2 使用Entity Framework创建数据访问层
在了解了WCF RIA Servcies服务器端与客户端的关键知识点后,下面开始创建一个实例来了解这些核心的概念,使用在服务器端使用Microsoft Entity Framework创建一个数据访问层。
要完成本示例需要AdventureWorkds2008 OLTP样例数据库,可以通过如下网址进行下载:
http://msftdbprodsamples.codeplex.com/
为了获取和保存数据库数据,示例将使用Entity Framework作为数据访问层。Entity Framework(以下简称EF),EF是一个对象关系映射(ORM)工具,允许开发人员创建概念数据模型(也就是实体模型)来映射到数据库架构(比如表、视图和存储过程),开发人员可以使用LINQ to Entities来查询来查询实体数据模型。
EF将转换LINQ to Entities查询到数据库的SQL查询,然后返回在模型中定义的对象化的数据,使用EF能够让开发人员用面向对象的思想操作数据库,编辑强基于实体模型的强类型查询,更多关于EF的知识可以参考相关的资料。
使用Visual Studio 2010的向导使得开发人员可以非常轻松的创建实体模型。在ASP.NET Web服务项目上右击鼠标,选择“添加|新建项”菜单项,在已安装的模板的数据栏选择“ADO.NET实体数据模型”项。如图3.6所示。
图3.6 创建实体数据模型
单击“添加”按钮后,Visual Studio 2010将弹出创建实体模型向导,在选择模型内容页选择“从数据库生成”项,单击下一步按钮。在选择数据库连接页面创建到AdventureWorks_Data.mdf数据库的连接,如图3.7所示。
图3.7 选择数据库连接
在选择数据库对象页面,选择表节点下面的Product产品表,单击完成按钮,Visual Studio 2010将创建实体数据模型,并进入到实体设计器窗口。
3.2.3 创建领域服务
在创建了数据访问层后,接下来将创建领域服务来向客户端公开数据和操作。一个领域服务通常由一系列的CRUD操作组成,并可以包含任何其它的开发人员希望公开的操作。
1,理解领域服务生命周期
首先来观注一下领域服务的工作方式以及领域服务各种不同阶段的生命周期。客户端与领域服务交互的核心在于从服务器端获取对象的集合(或者是单个对象),然后返回已被修改的任何对象,或者是被插入到集合的其它对象来更新相应的数据库(这涉及CRUD操作)。这些已被更改的对象将被客户端标记并且发送回领域服务器端,连同应该被应用到每个对象的领域操作将被作为一个变更集Changeset(当SubmitChanges方法被调用时,SubmitChanges方法是由客户端项目创建的与领域服务相关的领域上下文对象中的一个方法)。
在领域服务中将调用Submit方法,这个方法将开启领域服务的生命周期,领域服务将依序经过如下几个阶段:
(1)变更集授权检查阶段:用来校验传送变更集的用户是否已经被授权来在该变更集上执行操作。它检查从那个变更集上被调用的每个操作是否被允许,这是通过检查定义在每个操作中的安全性规则来实现的(也就是被特性语法修饰的安全性规则)。
(2)验证变更集阶段:根据实体集上的验证特性修饰的验证规则完成变更集的服务器端验证工作。即便对象和其属性在客户端验证通过,领域服务也会依照其验证规则进行服务器端的验证,以防止有人在客户端跳过了客户端的验证规则。
(3)执行变更集阶段:当变更集已通过验证和授权后,接下来将进入到执行阶段,这个阶段将遍历变更集中的每个实体然后调用特定的领域方法(比如插入、更新和删除或领域服务中的自定义操作)。
(4)持久化变更集阶段:这是领域服务的最终阶段,也就是将数据持久化到数据库中的阶段,依据使用的数据访问层的不同而调用不同的方法,比如Entity Framework将使用SubmitChanges方法将更改提交回数据库。
这几个阶段将由领域服务自完完成,同时在领域服务中也提供了相应的方法允许开发人员进行重载来提供一些自定义的行为,这些方法是:
AuthorizeChangeSet
ValidateChangeSet
ExecuteChangeSet
PersistChangeSet
当然开发人员也可以重载Submit方法,该方法包含所有这些阶段,在每个阶段期间,将遍历变更集中的每个对象,如果在对象中的任何一个抛出了异常(不管哪个阶段),那么整个变更集提交将被取消。
2,在Visual Studio 2010中创建领域服务
如果自创建Entity Framework实体集合没有重新编辑过项目,请先编译一下服务器项目。在了解了领域服务的相关知识后,接下来将学习如何在Visual Studio中创建领域服务,步骤如下所示:
(1)右击Web服务项目的Services文件夹,从弹出的菜单中选择“添加|新建项”菜单项,从弹出的窗口中选择“Web|领域服务类”模板。命名为 ProductService.cs,如图3.8所示。
图3.8 创建领域服务类
(2)单击“添加”按钮后,将进入到“添加新的领域服务类”窗口,如图3.9所示。
图3.9 添加新的领域服务类窗口
在该对话框的可用的DataContext/ObjectContext下拉列表框中,选择前面创建的Entity Framework实体集。如果想在项目中使用POCO类型来取代使用EF或LINQ to SQL类型,需要选择<空领域服务类>选项,然后自已实现领域操作。单击“确定”按钮,Visual Studio将在Services文件夹下创建了2个cs文件,在下面的小节中将介绍图3.9中各个选项的具体作用。
3.2.4 理解添加领域服务选项
回到图3.9,实体列表允许开发人员选择想要通过领域服务公开的实体。每当一个实体被选择后,一个领域操作将被创建,这个操作将返回一个相应的实体集合给客户端,如果开发人员想让客户端可以修改实体,比如添加、修改和删除的话,选择图3.9中实体右侧的“允许编辑”复选框。对于每个允许编辑的实体,向导将创建插入、更新和删除的领域操作。
除了允许编辑这个选项需要注意外,下面看看在图3.9中的其它几个选项的作用:
1,允许客户端访问
确保“允许客户端访问”复选框被选中,这将为领域服务使用特性EnableClientAccess进行修饰,用来指定RIA服务代码生成工具应该在Silverlight客户端项目中生成代码以允许访问服务器端的领域服务。
2,公开OData终结点
这个复选框被选中后,允许将领域服务使用开放数据协议进行访问(开放数据协议(OData)是一个查询和更新数据的Web协议)。OData是对ATOM分离器义的一套扩展,用来标准化基于HTTP数据服务的创建。与REST数据服务有点相似,数据作为一个ATOM种子返回,并且提供了对于数据的CRUD操作,目前这是一个新的协义,它具有微软背景,并且微软已经将OData整合到了一系列产品中,比如SharePoint和PowerPivot。
3,产生关联的元数据类
产生元数据类这个复选框将为在实体列表中选中的每一个实体创建一个关联的元数据类。通过元数据类,程序员可以用声明性的语法为实体指定数据验证规则而不用对实体类本身进行修改,修改生成的代码是非常危险的,因为在重新生成代码时,程序员所做的修改会被覆盖,而元数据类通过在一个单独的文件中对实体类进行修改,可以避免这个问题。
每个实体都可以有一个相应的元数据类,实体中的每一个属性在元素据类中都具有一个相应的字段,通过在元数据类中使用特性修饰这些字段,RIA服务会将这些元数据的特性生成到客户端。
领域服务类向导能够为程序员选择的实体产生元数据类,这个类以.metadata.cs作为扩展名,比如对于ProductService领域服务将具有一个对应的ProductService.metadata.cs文件被创建。
当然为实体类产生元数据类也不是必须的,如果在向导中忘记了创建元数据类,可以在需要的时候手工进行创建,或者创建另一个临时的领域服务,选中那些实体,然后使用生成的元数据类并删除领域服务。
3.3 创建领域操作
领域操作是领域服务中能够被客户端进行调用的方法,这些操作通常是CRUD操作,但是程序员也能创建其他的调用操作和自定义操作。为了让RIA服务代码生成器能够了解给定的方法是哪种类型的操作,以便在客户端产生正确的代码,它使用了基于惯例的方式来进行识别,这个惯例描述了操作方法应该如何被命名以及应该遵循的签名惯例。
除此之外,开发人员也可以使用特性来显示的标识操作方法属于哪种类型的操作。实际上,程序员可能更愿意使用特性的方式来显示指明操作的类型。本节将介绍领域操作的这些细节的知识点。
3.3.1 查询操作
查询操作允许公开数据给客户端,数据可以是实体或对象集合或者是单零点的实体对象。下面通过2个方面来介绍查询操作。
1.返回实体集合
当使用向导创建了ProductServices类之后,可以看到一个返回集合的查询操作,如下代码所示:
// TODO: // Consider constraining the results of your query method. If you need additional input you can // add parameters to this method or create additional query methods with different names. // To support paging you will need to add ordering to the 'Product' query. public IQueryable<Product> GetProduct() { return this.ObjectContext.Product; }
GetProducts操作返回一个IQueryable<T>类型的表达式,该表达式由一个LINQ提供者执行后将返回Product实体的集合。在本节中使用Entity Framework作为LINQ提供者。返回一个IQueryable<T>表达式而不是一个集合对象提供了很多好处。返回一个表达式(该表达式未被LINQ提供者执行),允许RIA服务向表达式添加其它的查询条件,然后返回结果到客户端,客户端可以在表达式上指定过滤、排序、分组和分页条件,当调用查询操作时,这个LINQ查询被序列化再发送到服务器端,在查询被执行前这些条件将追加到已存在的查询后面,查询被执行后再返回查询结果到客户端。这提供了简洁、容易和优雅的方法来从客户端传递查询到服务器端来获取服务器端的数据。
查询操作可以接收参数作为其签名的一部分,开发人员能用可以使用过滤参数或者是其他的条件给查询操作。比如,下面的查询接收一个字符串来过滤结果:
public IQueryable<Product> GetProducts(string nameFilter) { return this.ObjectContext.Product.Where(p => p.Name.StartsWith(nameFilter)); }
2.返回单一实体
除了返回实体集合,程序员通常想添加一个返回单个实体的操作,这个操作包含一个参数返回与指定参数匹配的单一实体,在ProductServices中添加一个名为GetProduct的方法来返回匹配的实体,如下代码所示:
public Product GetProduct(int productID) { return ObjectContext.Product .Where(p => p.ProductID == productID) .FirstOrDefault(); }
3.3.2 查询操作命名惯例
查询操作没并无命名惯例,能接收任何参数或没有数,查询操作会被RIA服务代码生成工具隐式识别为查询操作,但是,它必须返回一个IQqueryable<T>,IEnumearble<T>或者是一个实体,否则,为方法使用Query特性进行修饰。
程序员可以为查询操作配置缓存以便节省网络资源,当数据量较少变化时,使用缓存是相当不错的选择。为了利用输出缓存,使用OutputCache特性修饰查询操作,并通过其构造函数参数配置缓存的行为,这些参数包含了数据的缓存位置以及缓存的时长。
3.3.3 插入、更新、删除操作命名惯例
实际上插入、更新和删除操作并不会被客户端显示的调用,当客户端调用SubmitChanges方法提供变更时,RIA服务将调用每个实体的插入、更新和删除操作,具体的调用基于变更集的行为。增删改操作必须接收一个实体对象作为其参数,操作将在这些实体参数是完成,并且不返回值。当命名方法时,需要遵循如下的惯例。
(1)插入操作命名必须以Insert,Create或Add开头,否则,程序员将需要为方法应用Insert特性,比如插入操作:
public void InsertProduct(Product product).
(2)更新操作命名必须以Update,Change或Modify开头,否则,程序员需要为方法应用Update特性,比如更新操作:
public void UpdateProduct(Product currentProduct).
(3)删除操作命名必须以Delete或Remove开头,否则,程序员需要应用Delete特性到方法上,比如删除操作:
public void DeleteProduct(Product product).
如果为Product实体允许在领域服务类向导中编辑的话,那么可以在生成的类中看到插入、更新和删除的方法已经被创建,如下代码所示:
//插入方法 public void InsertProduct(Product product) { if ((product.EntityState != EntityState.Detached)) { this.ObjectContext.ObjectStateManager.ChangeObjectState(product, EntityState.Added); } else { this.ObjectContext.Product.AddObject(product); } } //更新方法 public void UpdateProduct(Product currentProduct) { this.ObjectContext.Product.AttachAsModified(currentProduct, this.ChangeSet.GetOriginal(currentProduct)); } //删除方法 public void DeleteProduct(Product product) { if ((product.EntityState != EntityState.Detached)) { this.ObjectContext.ObjectStateManager.ChangeObjectState(product, EntityState.Deleted); } else { this.ObjectContext.Product.Attach(product); this.ObjectContext.Product.DeleteObject(product); } }
3.3.4 调用操作
调用操作是一种普通的领域操作,调用操作与标准的WCF服务具有相似的行为,作为一个普通规则,调用操作不会返回或处理实体,调用操作可能是初始化一个服务器行为(请求服务器端发送email)或者是向服务器请求数据,用来返回简单的类型或者是变更集跟踪不需要的一些数据(比如请求得到当前货币汇率)。
与查询操作一样,调用操作是客户端可以直接调用的方法,这些操作将被立即调用(不像自定义操作,将进行排队,然后仅到一个变更集发送到服务器端时被调用)。
尽管调用操作概念上来说与普通的WCF服务操作相同,但是它们不能返回复杂的类型,除了实体对象外。这可能是使用RIA服务所要面对的一个非常大的问题。这意味着要向客户端返回复杂的对象类型是不可能的,解决这个问题的唯一方法是用回到纯WCF通信,希望这个问题在将来能得以克服。
调用操作不需要命名或方法签名约定(惯例),实质上任何领域服务中的操作,只要不属于任何其他类型的操作分类(通过命名或方法签名)就会被RIA服务认为是一个调用操作。也可以通过为方法应用Invoke来显示将一个方法作为调用方法。调用操作的示例如下所示:
public decimal GetExchangeRate(string fromCurrency, string toCurrency) { return ExchangeRates.GetRate(fromCurrency, toCurrency); }
3.3.5 自定义操作
自定义操作用于在一个实体上完成一些行为的操作,比如,你可能有一个操作来停用一个产品,所有这些逻辑将在服务器端完成,但是当客户端使用调用操作时,会被立即执行。自定义操作不会立即执行,它会延迟执行到客户端向服务器端提交了一个变更集,可以将自定义操作看在是与插入/更新/删除相同的操作。
当客户端生成一个自定义操作时,它将作为实体的一个方法被创建,然后对那个实体发生作用,同时它也被创建在实体上下文级别,以便进行调用。
自定义方法通常是在插入或更新操作在服务器端被调用后才执行,如果一个自定义的操作在一个实体上被调用,但是随后那个实体被删除,那么自定义方法将不会被调用,而是被丢弃。
自定义操作没有命名惯例,但是它们必须没有返回值,必须接收一个实体对象作为其第1个参数(任意个数的其它参数也是被允许的)。没有什么显然的特性来标识一个自定义操作,但是相反你应该使用Update特性,设置其UsingCustomMethod属性值为True。自定义操作示例如下所示:
//自定义操作 public void DiscontinueProduct(Product product) { // Logic to discontinue the product... }