阅读本文之前请确认你已经安装了如下软件
- Visual Studio 2008 (Express) SP1
- Silverlight 3 Tools For Visual Studio
- Microsoft Expression Blend 3 MIX 09 Preview
- .Net RIA Service Preview
本篇主要内容:
- .Net RIA Service介绍和实战
- DataPager
- DataForm
- 验证(Validation)
.Net RIA Service用来简化LOB(Line Of Business)的开发
它通过整合ASP.Net和Silverlight平台来简化传统的n层程序架构,如下图
服务器端和客户端都有应用程序逻辑层,用来处理验证信息,权限等等
服务器端的应用程序逻辑层将采用Web Service的方式将对数据库的操作
(创建,读取,更新,删除,统称CRUD操作)开放给客户端调用
但是客户端还得手动创建与服务器端连接的访问信息以及创建对应的对象类
有没有办法在服务器端创建的逻辑能够自动的在客户端也生成对应的逻辑代码呢
.Net RIA Service就是来处理这个问题
使用它将在编译的时候将在客户端生成和服务器端相对应的控制逻辑代码来提供给表现层直接调用
而不需要大家费心的去手动书写这些逻辑代码
下面我用一个实际的范例对上面的理论做一下解释
首先请确保已经安装过
- Silverlight 3 Tools For Visual Studio
.Net RIA Service Preview
1.创建一个Silverlight工程,并勾选Link to ASP.Net server project(这个只有安装了.Net RIA Service Preview才有)
2.添加一个ADO.Net Entity Data Model如下
连接到微软的范例数据库AdventureWorks.dbo
(下载Sql Server 2005的范例数据库)
选取操作的数据表,我们这里操作Product表
然后编译整个工程(这一步是必须的,一边后面的Domain Service类能够识别到这个Entity Data Model)
3.添加一个Domain Service类如下
勾选上如图所示的所有信息
其中勾选Enable Client Access是使得这个Domain Service客户端可见
而Enable editing是因为使得自动生成对Product表的插入,更新,删除等逻辑代码
而Generate associated classes for metadata将创建一个叫做ProductService.metadata.cs的类
其中可以给类中的成员添加属性用来限制其是否必须填,以及是什么格式(比如Email)等等
这个方便后面的设置验证处理(最后一节将讲到)
其中ProductService.cs类如下
1: [EnableClientAccess()]
2: public class ProductService : LinqToEntitiesDomainService<AdventureWorks_Entities>
3: {
4: public IQueryable<Product> GetProduct()
5: {
6: return this.Context.Product;
7: }
8:
9: public void InsertProduct(Product product)
10: {
11: this.Context.AddToProduct(product);
12: }
13:
14: public void UpdateProduct(Product currentProduct, Product originalProduct)
15: {
16: this.Context.AttachAsModified(currentProduct, originalProduct);
17: }
18:
19: public void DeleteProduct(Product product)
20: {
21: if ((product.EntityState == EntityState.Detached))
22: {
23: this.Context.Attach(product);
24: }
25: this.Context.DeleteObject(product);
26: }
27: }
可以给上面的类添加新的方法(比如只查询售价超过多少的方法GetProductByPrice)
编译整个工程后,将在客户端形成对应的逻辑代码(在Generated_Code\SL3Beta.Data.Web.g.cs目录下,在工程中是隐藏的)
决定服务器端哪些类和方法会在客户端相应生成的工作方式如下:
- 分析所有服务器端的Assembly
- 分析所有标有[EnableClientAccess()]属性并继承自DomainService的类
- 分析属于上面符合条件的类的公开方法并在客户端生成对应的代码
- 4.现在我们可以开始构建我们的客户端了
- 在主xaml文件中添加一个DataGrid如下:
1: <UserControl x:Class="SL3Beta.Data.MainPage"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data">
5: <StackPanel x:Name="LayoutRoot" Background="#3c3c3c">
6: <data:DataGrid MinHeight="100" MaxHeight="500" x:Name="ProductGrid"></data:DataGrid>
7: </StackPanel>
8: </UserControl>
下面一步就是从服务器端获取数据了如下
1: namespace SL3Beta.Data
2: {
3: public partial class MainPage : UserControl
4: {
5: private ProductContext _productContext = new ProductContext();
6:
7: public MainPage()
8: {
9: InitializeComponent();
10: this.ProductGrid.ItemsSource = _productContext.Products;
11: _productContext.LoadProduct();
12: }
13: }
14: }
这里的LoadProduct方法对应服务器端的GetProduct方法
(也就是所有的DomainService中的Get…在客户端中自动生成的代码的函数名变成了Load…)
5.显示结果如下所示
但是这种方法对数据库的冲击很大。因为这些数据是一次性取完的
那么又没办法分页去数据呢?
还好微软已经替我们想到了这一点,那就是使用DomainDataSource控件
在可以使用DomainDataSource之前,我们需要将这个控件添加进Toolbox下
(这个控件不是Silverlight 3 Beta自带的,而是.Net RIA Service提供的
在%ProgramFiles%\Microsoft SDKs\RIA Services\v1.0\Libraries\Silverlight的System.Windows.Ria.Controls.dll中
这里我就不讲怎么添加这个控件了,大家应该都会)
使用DomainDataSource来获取并绑定数据如下
1: <UserControl x:Class="SL3Beta.Data.MainPage"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
5: xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
6: xmlns:web="clr-namespace:SL3Beta.Data.Web">
7: <StackPanel x:Name="LayoutRoot" Background="#3c3c3c">
8: <riaControls:DomainDataSource x:Name="ProductDataSource" LoadSize="10" LoadMethodName="LoadProduct" AutoLoad="True">
9: <riaControls:DomainDataSource.DomainContext>
10: <web:ProductContext/>
11: </riaControls:DomainDataSource.DomainContext>
12: </riaControls:DomainDataSource>
13: <data:DataGrid MinHeight="100" MaxHeight="500" ItemsSource="{Binding Data,ElementName=ProductDataSource}" x:Name="ProductGrid"/>
14: </StackPanel>
15: </UserControl>
第8行的LoadSize=”10”代表每次只去数据中取10条数据,其他数据是以异步的方式取得的
获取数据的方法是LoadProduct,第13行将DomainDataSource获取的数据绑定给DataGrid
这里我们就可以将.xaml.cs文件中使用代码获取数据的方式去掉了
1: public partial class MainPage : UserControl
2: {
3: //private ProductContext _productContext = new ProductContext();
4:
5: public MainPage()
6: {
7: InitializeComponent();
8: //this.ProductGrid.ItemsSource = _productContext.Products;
9: //_productContext.LoadProduct();
10: }
11: }
这样我们已经以一种非常简洁的方式获取到了服务器端的数据了
下面一步就是如何实现分页功能了
DataPager:DataPager是在Silverlight 3才引进的新控件
其与后面要讲到的DataForm位于同一个名字空间System.Windows.Controls.Data.DataForm中
在进行分页之前,需要给DomainDataSource进行排序(不然DataPager不work,估计是个Bug)
修改DomainDataSource如下:
1: <riaControls:DomainDataSource x:Name="ProductDataSource" LoadSize="10" LoadMethodName="LoadProduct" AutoLoad="True">
2: <riaControls:DomainDataSource.DomainContext>
3: <web:ProductContext/>
4: </riaControls:DomainDataSource.DomainContext>
5: <riaControls:DomainDataSource.SortDescriptors>
6: <riaData:SortDescriptor Direction="Ascending" PropertyPath="ProductID"/>
7: </riaControls:DomainDataSource.SortDescriptors>
8: </riaControls:DomainDataSource>
添加一个DataPager到DataGrid后面:
1: <dataControls:DataPager Source="{Binding Data,ElementName=ProductDataSource}" PageSize="10"/>
这样我们就实现了分页(修改DataPager的外观可以通过给其设置不同的Template或者Style来做到)
下一步就是更新信息了,我们将引入DataForm来做这件事情
DataForm:DataForm类似ASP.Net中的DataView控件,如下
1: <dataControls:DataForm x:Name="ProductForm" AutoGenerateFields="False" AutoCommit="False" CurrentItem="{Binding SelectedItem,ElementName=ProductGrid}" Header="产品信息" CommitButtonContent="保存" CancelButtonContent="取消" ItemEditEnded="ProductForm_ItemEditEnded">
2: <dataControls:DataForm.Fields>
3: <dataControls:DataFormTextField FieldLabelContent="产品编号" Binding="{Binding ProductID}" IsReadOnly="True"/>
4: <dataControls:DataFormTextField FieldLabelContent="产品名称" Binding="{Binding Name,Mode=TwoWay}"/>
5: <dataControls:DataFormTextField FieldLabelContent="价格" Binding="{Binding ListPrice,Mode=TwoWay}"/>
6: <dataControls:DataFormDateField FieldLabelContent="开始出售日期" Binding="{Binding SellStartDate,Mode=TwoWay}" IsReadOnly="True"/>
7: <dataControls:DataFormDateField FieldLabelContent="结束出售日期" Binding="{Binding SellEndDate,Mode=TwoWay}"/>
8: </dataControls:DataForm.Fields>
9: </dataControls:DataForm>
将ProductGrid当前选中的Item绑定给DataForm来显示,其中AutoGenerateFields="False"代表将自定义显示和编辑的Fields
CommitButtonContent和CancelButtonContent分别用来设置保存按钮和取消按钮的显示内容,而第2~8行就是自定义的显示和编辑的Fields
ItemEditEnded用来表示你按下保存按钮或者取消按钮时触发的事件,如下
1: private void ProductForm_ItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
2: {
3: if (e.EditAction == DataFormEditAction.Commit)
4: {
5: ProductDataSource.SubmitChanges();
6: }
7: }
表示只有按下保存按钮才保存信息内容
验证(Validation):
在.Net RIA Service介绍和实战这一部分我曾经说过.Net RIA Service是在metadata类设置验证要求的
这是ProductService.metadata.cs这个类的原始设置
1: internal sealed class ProductMetadata
2: {
3: private ProductMetadata()
4: {
5: }
6:
7: public int ProductID;
8:
9: public string Name;
10:
11: public string ProductNumber;
12:
13: public bool MakeFlag;
14:
15: public bool FinishedGoodsFlag;
16:
17: public string Color;
18:
19: public short SafetyStockLevel;
20:
21: public short ReorderPoint;
22:
23: public Decimal StandardCost;
24:
25: public Decimal ListPrice;
26:
27: public string Size;
28:
29: public string SizeUnitMeasureCode;
30:
31: public string WeightUnitMeasureCode;
32:
33: public Nullable<Decimal> Weight;
34:
35: public int DaysToManufacture;
36:
37: public string ProductLine;
38:
39: public string Class;
40:
41: public string Style;
42:
43: public Nullable<int> ProductSubcategoryID;
44:
45: public Nullable<int> ProductModelID;
46:
47: public DateTime SellStartDate;
48:
49: public Nullable<DateTime> SellEndDate;
50:
51: public Nullable<DateTime> DiscontinuedDate;
52:
53: public Guid rowguid;
54:
55: public DateTime ModifiedDate;
56:
57: public EntityState EntityState;
58: }
我们给第五行的Name设定个限制,就是这个Name不能为空
而且其字符串长度不能超过10
修改如下:
1: [Required]
2: [StringLength(10)]
3: public string Name;客户端相应生成的代码如下
1: [DataMember()]
2: [Required()]
3: [StringLength(10)]
4: public string Name
5: {
6: get
7: {
8: return this._name;
9: }
10: set
11: {
12: if ((this._name != value))
13: {
14: this.ValidateProperty("Name", value);
15: this.RaiseDataMemberChanging("Name");
16: this._name = value;
17: this.RaiseDataMemberChanged("Name");
18: }
19: }
20: }
当你点击保存提交修改时,将会在客户端验证Name是否为空以及其字符串长度是否已经超过了10了
除了如上的验证方式,还有正则表达式验证等等,此外你还可以自定义你的验证方式
代码下载:
没有放入AdventureWorks数据库,大家自己下载配置下