【IT168专稿】ADO.NET数据服务是随同微软.NET框架3.5 SP1的推出一同正式发行的一个框架,主要目的是为了简化数据中心型Web服务开发。ADO.NET数据服务框架,提供了一套完整的库,还有集成于Visual Studio中的工具,以及一套定义良好的模式,从而使得以服务方式基于数据模型级提供相关CRUD操作变得与简单的HTTP操作一样得容易。
ADO.NET数据服务定义了一个协议,用于与基于HTTP以及当前流行的AtomPub和JSON标准的数据服务进行通信。除了支持这些标准之处, ADO.NET数据服务协议还定义了数据承载的格式及资源的寻址模式。由于它的开放与可互操作设计,各种各样的客户端和服务器已开始接纳ADO.NET数据服务协议。总之,基于ADO.NET数据服务,无论是在开发者方面还是在应用者方面都正在不断壮大。
从多方面来看,ADO.NET数据服务协议模拟了微软.NET RIA服务应用程序中客户端与服务器端层间的通信技术。微软的目标是进一步密切这两种技术间的协同操作,从而使得.NET RIA服务可以使用与ADO.NET数据服务一样的协议。这样一来,对两种技术都有所收益:微软.NET RIA服务应用程序都能够与ADO.NET数据服务的相关内容进行交互(利用现有的客户端技术及开发工具),基于ADO.NET数据服务的服务也能够利用.NET RIA服务提供的业务逻辑模式,丰富的UI控件以及端对端的开发经验。
根据微软提供的消息,目前上述两种技术间的协作仍处于发展阶段。就目前情况,我们可以实现这两种技术的如下协作开发:
(1) 扩展现有的微软.NET RIA服务应用程序—通过添加一个ADO.NET数据服务端点,以支持基于ADO.NET数据服务协议的交互操作。
(2) 使用微软.NET RIA服务模式扩展现有的ADO.NET数据服务以添加业务逻辑支持。
本文中的示例仅提供针对上述第一种情况的应用展示,即如何把ADO.NET数据服务端点添加一个现有的微软.NET RIA服务应用程序中。另外,由于目前上述两种技术的交互仍处于发展阶段,所以在实际开发环境下还不推荐使用这种解决方案。
1.创建Silverlight 3示例工程
(1)打开Visual Studio 2008,选择“文件|新建|项目”菜单命令,打开“新建项目”对话框。
(2)选择“Silverlight Application”模板,创建一个Silverlight 3项目,并命名为ProductsWithDataService。
(3)单点“确定”按钮,进入到下一步以选择silverlight应用的宿主网站。从“web project type”下拉列表框中选择“ASP.NET Web Application Project”。选中对话框中最下面的“Enable .NET RIA Services”复选按钮,这样便把RIA框架支持添加到当前解决方案中。
至此,我们创建了两个工程:
(1) ProductsWithDataService—此工程中包含了Silverlight代码,这个工程称为客户端工程,这是我们创建的应用程序的客户端层。
(2) ProductsWithDataService.Web—此工程中包含了ASP.NET web应用程序代码,这个工程称为服务器端工程,这是我们创建的应用程序的中间层。
至此,一个基本的集成了.NET RIA Services的Silverlight 3示例工程框架生成完毕。
2.在Web工程上添加ADO.NET实体数据模型
(1)右键单击web工程ProductsWithDataService.Web,在弹出菜单中选择“添加|新建项”命令。在随后出现的“添加新项”对话框中选择“ADO.NET Entity Data Model”模板,命名为AdventureWorks.edmx,最后单击“添加”按钮退出。
【提示】为了读者调试方便,我们使用微软提供的SQL Server 2008示例数据库AdventureWorks。在服务器资源管理器中添加AdventureWorks示例数据库连接的操作在此不再赘述,而是直接假设用户创建好了这一连接。
(2)在随后的ADO.NET实体数据模型向导中,选择“从数据库生成”并单击“下一步”按钮。
(3)在接下来的“选择您的数据连接”对话框中,选择已有的AdventureWorks数据库连接,并勾选最下方的“将Web.config中的实体连接设置另存为”复选按钮,并改名为AdventureWorks2008Entities。
(4)在接下来的“选择数据库对象”对话框中,仅选择一个表格Product并输入模型命名空间为AdventureWorks2008Model。最后单击“完成”按钮退出ADO.NET实体数据模型,并保存生成的文件。
(3)最后,选择菜单“生成|重新生成解决方案”。
3.使用域服务封装业务逻辑
(1)仍然右键单击web工程ProductsWithDataService.Web,在弹出菜单中选择“添加|新建项”命令。在随后出现的“添加新项”对话框中选择“Domain Service Classes”模板添加一个域服务,输入名称ProductsDomain.csl,最后单击“添加”按钮退出。
(2)在随后出现的“Add New Domain Service Class”对话框。在对话框中选择Product实体,并确保选择了“Enable Editing”复选按钮。最后,再选择对话框最下部的“Generate associated classes for metadata”复选按钮,以保证在元数据中生成相关联的类。
(3)最后,选择菜单“生成|重新生成解决方案”。
随后打开的ProductsDomain服务类自动生成了针对Product实体集的基本CRUD操作的雏形(换句话说,在实际开发中,我们可以添加自定义的逻辑来重构这里的查询,插入,更新以及删除操作)。而且注意到,因为在上面我们勾选了“Enable client access”复选按钮,所以系统自动在类ProductsDomain的前面添加了一个[EnableClientAccess]属性;这意味着,此类将把自己的方法暴露给客户层使用。
至此,我们已经搭起一个基本的Silverlight应用的服务器端框架。接下来,我们讨论Silverlight用户界面相关编程。
4.Silverlight用户界面及基本后台代码编程
其实,从分层角度来看,就客户端Silverlight项目这边来看,也可进一步分为若干子层。例如,.xaml文件可看作最上面的展示层,相应的后台代码文件.xaml.cs则又是一层。
(一)XAML设计
Silverlight 3项目自动生成的主页面文件为MainPage.xaml。现在,打开此文件,并输入如下标记代码:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="Auto" d:DesignHeight="Auto">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<data:DataGrid x:Name="productGrid" AutoGenerateColumns="true" Grid.Row="0" />
</Grid>
</UserControl>
上面的XAML代码非常简单,不再赘述。
相应的后台代码文件MainPage.xaml.cs的内容如下所示:
//下面是新添加的命名空间引用
using ProductsWithDataService.Web;
using System.Windows.Ria.Data;
namespace ProductsWithDataService
{
public partial class MainPage : UserControl
{
ProductsDomain _domain = new ProductsDomain();
public MainPage()
{
InitializeComponent();
this._domain.Load(this._domain.GetProductQuery());
this.productGrid.ItemsSource = _domain.Products;
}
}
}
注意,上面代码仅实现了基本的产品信息数据查询与加载,不再赘述。
现在运行应用程序,你将得到如下所示的快照:
上面的代码仅仅通过一个网格控件显示所有表格数据。
接下来,我们将在前面界面中添加一个“保存”按钮,以便当用户更改完网格控件中的数据后,可以通过它保存到服务器端。
返回到文件MainPage.xaml中,紧接着DataGrid控件下部添加如下代码:
<Button Content="保存修改结果" Click="SaveChanges_Click" />
</StackPanel>
下面给出的是相应于上面保存按钮的Click事件处理器函数:
{
SubmitOperation submitOp = this._domain.SubmitChanges();
submitOp.Completed += delegate
{
if (submitOp.Error == null)
{
MessageBox.Show("已经成功保存到服务器端。");
}
else
{
MessageBox.Show(string.Format("发生保存错误: {0}", submitOp.Error.ToString()));
}
};
}
现在,重新构建并运行应用程序。之后,在网格中修改某个数据,并单击保存按钮,然后再试着刷新示例页面并使数据重新加载,你会注意到上面的修改的确被保存到了服务端。
至此,我们已经拥有了一个具备基本功能的应用程序。接下来,我们可以基于微软.NET RIA服务提供的机制进一步丰富这个应用程序。例如,在实体类型上添加校验属性及定制校验代码,或者提供基于DataPager和DataForm等控件的更丰富的用户接口,等等。但是,我们的兴趣点不在于此。
从下一节开始,我们将讨论如何把一个ADO.NET数据服务接口添加到现有的应用程序中。
5.添加ADO.NET数据服务
至此,我们的示例应用程序的客户端和服务器已经可以利用.NET RIA服务的现有机制进行通信了。
在本节中,我们将增加一个ADO.NET数据服务,并将分析ADO.NET数据服务协议的传输情况。下面,我们将修改客户端层以便使用ADO.NET数据服务协议。
为了把ADO.NET数据服务添加到应用程序中,请执行下列步骤:
(1) 右键单击解决方案资源管理器中的ProductsWithDataService.Web项目,选择“添加—新项目”。
(2) 在“添加新项目”对话框中,选择“Domain ADO.NET Data Service”,将它命名为ProductsDataService.svc,然后单击“添加”按钮,如下图所示。
这将把ADO.NET数据服务的框架添加到项目中,并将打开ProductsDataService.cs文件,其中已经包含了基本的支持服务的代码。
为了使ADO.NET数据服务正确运行,我们需要对系统自动生成的服务代码作一些修改。首先,我们需要把服务与一个数据源关联起来。在本例中,我们将使用我们的域名服务类—ProductsDomain,作为数据源。ProductsDomain提供了针对Product实体集的CRUD方法支持。通过配置ADO.NET数据服务工作于ProductsDomain类中,我们将把这些CRUD方法暴露为遵循ADO.NET数据服务协议的HTTP操作。
在配置完数据源后,我们还必须指定服务的访问控制规则。在本文例子中,我们将开放服务的完全的读/写访问支持。在实际应用开发中,则应当更谨慎地控制访问权限。
接下来,请继续遵循下面的步骤实现把ADO.NET数据服务与类ProductsDomain相关联,并且配置好Product实体集以支持读/写访问:
(3) 打开ProductsDataService.svc.cs文件,注意到服务类ProductsDataService派生自DataService,而且在系统自动生成的代码中,泛型参数T已不存在,代之以TODO注释—提示你加以修改(其实Visual Studio语法感知也已经提示你了—下面的“错误提示”窗口中显示语法错误:“应输入类型”,见下图):
(4) 使用前面创建的域服务类ProductsDomain取代上面的TODO注释部分。其中,服务类的声明应当类似于下面形式:
(5) 在InitializeService()方法中,你会注意到还有一个TODO注释。现在,你使用下面代码取代注释中的内容:
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
}
至此,我们就有了一个ADO.NET数据服务。此服务把从域服务类返回的数据暴露为一组可使用URI寻址的资源并且使用我们在类ProductsDomain中实现的CRUD方法映射HTTP方法。
在下面的步骤中,我们来试用一下上面创建的数据服务,以更好地理解这种功能及相应的传输格式。
6.观察运行结果(1)
现在,重新构建并运行示例工程。
在浏览器中,请导航到ProductsDataService.svc(预览此文件即可)。结果如下图所示:
注意到,图中显示了服务文档及基于AtomPub标准显示的内容。这个文档指出,我们拥有一个且仅有一个名字为Product的集合,而且可以通过一个同名字(Product)的相对的URL进行访问。
接下来,我们进行的第二个试验是把上面的URL更改为http://localhost:1623/ProductsDataService.svc/Product/,然后按回车。
如果再试用下面几种URL:
http://localhost:1623/ProductsDataService.svc/Product(1)/或者http://localhost:1623/ProductsDataService.svc/Product(1)/Name,则浏览器提示是打开或是另存储相应的文件,选择使用浏览器打开,则显示的是第一个实体对应字段内容,或第一个实体Name字段的名称。
在上面每一种查询情况下,ADO.NET数据服务都生成了一个对于ProductsDomain类方法GetProduct()的调用。
注意,上面表达的URI格式都是ADO.NET数据服务协议中特别规定的。因此,任何理解这种协议的客户端都能够访问ProductsDomain类提供的数据。
上面举例仅是ADO.NET数据服务查询的极其简单的情形,其实ADO.NET数据服务还能够使用JSON串行化数据,以及读/写访问(使用HTTP POST,PUT和DELETE方法)服务器端数据。MSDN中提供了有关ADO.NET数据服务的非常细致的介绍,在此不再赘述。
7.从客户端访问ADO.NET数据服务协议
在上一节中,我们已经成功地把一个ADO.NET数据服务添加到我们的应用程序中,并且简单地从浏览器中分析了数据服务的调用方式。但是,到目前为止,Silverlight应用程序的客户端部分还没法与这种服务基于ADO.NET数据服务协议进行通信。在这一节,我们将修改现有的Silverlight客户端应用程序使之能够使用上述新式协议。
使用ADO.NET数据服务协议需要修改系统自动生成的客户端ProductsDomain类。然后,微软.NET RIA服务会基于我们在服务器端创建的域服务类ProductsDomain而自动生成客户端类ProductsDomain。通过对服务器端的DomainService类修饰以特殊属性,我们便能指示微软.NET RIA服务生成需要的客户端代码,于是可以基于此代码进一步使用ADO.NET数据服务协议。然后,我们仅需要对Silverlight代码作一些简单的修改便可以使用这种新生成的客户端类了。下面给出上述描述的具体实现步骤:
(1)打开ProductsWithDataService.Web工程中的ProductsDomain.cs文件。
(2)添加如下命名空间引用:
(3) 然后把下列属性添加到类ProductsDomain声明之前,紧接着现有的 [EnableClientAccess]属性的下面:
(4) 在解决方案资源管理器中,右单击客户端应用程序ProductsWithDataService,并选择“添加引用”菜单项。
(5) 在随后的“添加引用”对话框中单击“浏览”选项卡,导航到%ProgramFiles%\Microsoft SDKs\RIA Services\v1.0\Libraries\Silverlight目录下,并选择下列如下两个程序集:
a. System.Windows.Ria.Atom.dll
b. System.Data.Services.Client.Protocol.dll
(6) 重新生成解决方案。此时,你应当发现一些构建错误,这是因为前面我们应用Atom DomainIdentifier属性后由微软.NET RIA服务自动生成的客户端类与我们最初书写的代码不一致所导致的。
下面,我们将修正上述错误:
(7) 打开MainPage.xaml.cs文件,添加如下命名空间引用:
using System.Data.Services.Client.Protocol;
(8) 修改MainPage类中的_domain成员为如下形式:
8.观察运行结果(2)
现在,重新生成解决方案,并运行应用程序。
此时,你会注意到程序运行正常,而且在功能上如前面并无两样。但是,其区别在于,所有的“区别”并不为我们所知—一切是由系统自动为我们生成的。通过第八节中的操作,一个Silverlight客户端就能够使用ADO.NET数据服务协议“直接”与数据服务进行通信了。数据服务会自动把它接收到的HTTP请求翻译成针对ProductsDomain类中的适当的CRUD方法调用。
9.小结
在本文中,我们首先概括分析了微软.NET RIA服务与ADO.NET数据服务两个子框架交互的重要意义,然后通过一个简单的例子讨论了把ADO.NET数据服务集成到一个Silverlight 3 RIA应用程序中所涉及到的步骤及注意的问题。但是,根据微软提示,本文中两种框架的交互方案仍处于发展阶段,所以,尚不能把它作为一种成熟的方案应用于实际开发环境下。有兴趣的读者可基于本文提示对上述问题作进一步探讨