Silverlight 3 Beta 新特性解析(5) - Data篇
前提条件:
阅读本文之前请确认你已经安装了如下软件
- 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介绍和实战:
.Net RIA Service用来简化LOB(Line Of Business)的开发
它通过整合ASP.Net和Silverlight平台来简化传统的n层程序架构,如下图
服务器端和客户端都有应用程序逻辑层,用来处理验证信息,权限等等
服务器端的应用程序逻辑层将采用Web Service的方式将对数据库的操作
(创建,读取,更新,删除,统称CRUD操作)开放给客户端调用
但是客户端还得手动创建与服务器端连接的访问信息以及创建对应的对象类
有没有办法在服务器端创建的逻辑能够自动的在客户端也生成对应的逻辑代码呢
.Net RIA Service就是来处理这个问题
使用它将在编译的时候将在客户端生成和服务器端相对应的控制逻辑代码来提供给表现层直接调用
而不需要大家费心的去手动书写这些逻辑代码
下面我用一个实际的范例对上面的理论做一下解释
首先请确保已经安装过
1.创建一个Silverlight工程,并勾选Link to ASP.Net server project(这个只有安装了.Net RIA Service Preview才有)
2.添加一个ADO.Net Entity Data Model如下
连接到微软的范例数据库AdventureWorks.dbo
选取操作的数据表,我们这里操作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: }
- 分析所有服务器端的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: }
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>
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>
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: }
代码下载:
没有放入AdventureWorks数据库,大家自己下载配置下
出处:http://ibillguo.cnblogs.com/
本文版权由作者和博客园共同所有,转载请注明出处