稳扎稳打Silverlight(56) - 4.0通信之与 WCF Data Services 进行通信
稳扎稳打Silverlight(56) - 4.0通信之与 WCF Data Services 进行通信
作者:webabcd
介绍
Silverlight 4.0 对 WCF Data Services 的支持:
- DataServiceCollection<T> - 继承自 ObservableCollection<T>,用于将 WCF Data Services 提供的数据绑定到 Silverlight 控件上,当数据发生变化时可提供通知
在线DEMO
http://www.cnblogs.com/webabcd/archive/2010/08/09/1795417.html
示例
演示 Silverlight 4.0 与 WCF Data Services 之间的通信
1、服务端
MyWCFDataService.svc.cs
代码
/*
* WCF Data Services - 用于提供基于 OData 协议的数据服务
* OData 协议 - 开放数据协议(Open Data Protocol),其基于 REST-ful 协议,参考:http://www.odata.org/
*/
using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel.Web;
using System.Web;
namespace Silverlight40.Web.Service
{
public class MyWCFDataService : DataService<Model.NorthwindEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
// 指定对 Products, Categories, Orders 启用服务端分页,页大小为 3
config.SetEntitySetPageSize("Products", 3);
config.SetEntitySetPageSize("Categories", 3);
config.SetEntitySetPageSize("Orders", 3);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
}
* WCF Data Services - 用于提供基于 OData 协议的数据服务
* OData 协议 - 开放数据协议(Open Data Protocol),其基于 REST-ful 协议,参考:http://www.odata.org/
*/
using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel.Web;
using System.Web;
namespace Silverlight40.Web.Service
{
public class MyWCFDataService : DataService<Model.NorthwindEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
// 指定对 Products, Categories, Orders 启用服务端分页,页大小为 3
config.SetEntitySetPageSize("Products", 3);
config.SetEntitySetPageSize("Categories", 3);
config.SetEntitySetPageSize("Orders", 3);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
}
2、客户端
Demo.xaml
代码
<navigation:Page x:Class="Silverlight40.WCFDataServices.Demo"
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"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
Title="Demo Page">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<!--
用于演示通过 WCF Data Services 实现数据的增删改查
-->
<StackPanel Orientation="Horizontal">
<Button Name="btnGetData" Content="获取数据" Margin="5" Click="btnGetData_Click" />
</StackPanel>
<TextBlock Text="产品类别列表:" Margin="5" />
<sdk:DataGrid Name="gridCategory" Margin="5" Height="200" Width="400" AutoGenerateColumns="False" ItemsSource="{Binding}"
SelectionChanged="gridCategory_SelectionChanged">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="类别ID" Binding="{Binding CategoryID}" IsReadOnly="True" />
<sdk:DataGridTextColumn Header="类别名称" Binding="{Binding CategoryName}" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<TextBlock Text="产品列表:" Margin="5" />
<sdk:DataGrid Name="gridProduct" Margin="5" Height="200" Width="400" AutoGenerateColumns="False" ItemsSource="{Binding}" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="产品ID" Binding="{Binding ProductID}" IsReadOnly="True" />
<sdk:DataGridTextColumn Header="产品名称" Binding="{Binding ProductName}" />
<sdk:DataGridTextColumn Header="单价" Binding="{Binding UnitPrice}" IsReadOnly="True" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<Button Name="btnSave" Content="保存" Margin="5" Width="400" Click="btnSave_Click" />
<!--
用于演示 WCF Data Services 的服务端分页
-->
<TextBlock Text="订单列表:" Margin="5,20,5,5" />
<sdk:DataGrid Name="gridOrder" Margin="5" Height="200" Width="400" IsReadOnly="True" AutoGenerateColumns="False" ItemsSource="{Binding}" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="订单ID" Binding="{Binding OrderID}" />
<sdk:DataGridTextColumn Header="客户ID" Binding="{Binding CustomerID}" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<TextBlock Name="txtCurrentPageIndex" />
<Button Name="btnPrev" Content="上一页" Margin="5" Width="400" />
<Button Name="btnNext" Content="下一页" Margin="5" Width="400" />
</StackPanel>
</Grid>
</navigation:Page>
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"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
Title="Demo Page">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<!--
用于演示通过 WCF Data Services 实现数据的增删改查
-->
<StackPanel Orientation="Horizontal">
<Button Name="btnGetData" Content="获取数据" Margin="5" Click="btnGetData_Click" />
</StackPanel>
<TextBlock Text="产品类别列表:" Margin="5" />
<sdk:DataGrid Name="gridCategory" Margin="5" Height="200" Width="400" AutoGenerateColumns="False" ItemsSource="{Binding}"
SelectionChanged="gridCategory_SelectionChanged">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="类别ID" Binding="{Binding CategoryID}" IsReadOnly="True" />
<sdk:DataGridTextColumn Header="类别名称" Binding="{Binding CategoryName}" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<TextBlock Text="产品列表:" Margin="5" />
<sdk:DataGrid Name="gridProduct" Margin="5" Height="200" Width="400" AutoGenerateColumns="False" ItemsSource="{Binding}" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="产品ID" Binding="{Binding ProductID}" IsReadOnly="True" />
<sdk:DataGridTextColumn Header="产品名称" Binding="{Binding ProductName}" />
<sdk:DataGridTextColumn Header="单价" Binding="{Binding UnitPrice}" IsReadOnly="True" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<Button Name="btnSave" Content="保存" Margin="5" Width="400" Click="btnSave_Click" />
<!--
用于演示 WCF Data Services 的服务端分页
-->
<TextBlock Text="订单列表:" Margin="5,20,5,5" />
<sdk:DataGrid Name="gridOrder" Margin="5" Height="200" Width="400" IsReadOnly="True" AutoGenerateColumns="False" ItemsSource="{Binding}" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="订单ID" Binding="{Binding OrderID}" />
<sdk:DataGridTextColumn Header="客户ID" Binding="{Binding CustomerID}" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<TextBlock Name="txtCurrentPageIndex" />
<Button Name="btnPrev" Content="上一页" Margin="5" Width="400" />
<Button Name="btnNext" Content="下一页" Margin="5" Width="400" />
</StackPanel>
</Grid>
</navigation:Page>
Demo.xaml.cs
代码
/*
* 演示 Silverlight 4.0 与 WCF Data Services 之间的通信
*
* DataServiceCollection<T> - 继承自 ObservableCollection<T>。用于将 WCF Data Services 提供的数据绑定到 Silverlight 控件上,当数据发生变化时可提供通知
* LoadAsync(IQueryable<T>) - 从数据服务上异步加载 IQueryable<T> 数据到 DataServiceCollection<T> 中(在 LoadCompleted 触发之前不能再调用此方法)
* LoadAsync() - 当 DataServiceCollection<T> 表示为某实体的导航属性时,从数据服务上异步加载该数据(在 LoadCompleted 触发之前不能再调用此方法)
* LoadCompleted - 异步加载完成后所触发的事件
* LoadCompletedEventArgs - LoadCompleted 事件的事件参数
* LoadCompletedEventArgs.QueryOperationResponse - 获取数据服务对改查询操作的响应
* QueryOperationResponse.Error - 引发的异常
* QueryOperationResponse.Headers - HTTP 的响应头
* QueryOperationResponse.Query - 对应的 DataServiceQuery(DataServiceQuery.RequestUri - 请求数据服务的 URI) 对象
* QueryOperationResponse.StatusCode - HTTP 的响应代码
* Continuation - 返回一个 DataServiceQueryContinuation<T> 对象,该对象封装了当前查询结果的下一页的 URI
* DataServiceQueryContinuation<T>.NextLinkUri - 当前查询结果的下一页的 URI
* LoadNextPartialSetAsync() - 加载下一页数据到 DataServiceCollection<T> 中
*
* Silverlight 通过 ObservableCollection<T> 与 ADO.NET Data Services 1.0 进行通信详见 http://www.cnblogs.com/webabcd/archive/2009/03/12/1409281.html
* WCF Data Services(ADO.NET Data Services 1.5)新特性详见 http://www.cnblogs.com/webabcd/archive/2010/06/11/1756071.html
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
using Silverlight40.MyWCFDataServiceReference;
using System.Data.Services.Client;
namespace Silverlight40.WCFDataServices
{
public partial class Demo : Page
{
private NorthwindEntities _context;
private DataServiceCollection<Category> _categories;
public Demo()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// 指定数据服务地址
_context = new NorthwindEntities(new Uri("Service/MyWCFDataService.svc", UriKind.Relative));
_categories = new DataServiceCollection<Category>();
_categories.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(_categories_LoadCompleted);
// 演示如何通过 WCF Data Services 做服务端分页
ServerPagingDemo();
}
// 开始异步加载 Categories 数据
private void btnGetData_Click(object sender, RoutedEventArgs e)
{
gridCategory.DataContext = null;
gridProduct.DataContext = null;
// 加载 Categories 表中的全部数据
var query = from c in _context.Categories select c;
_categories.LoadAsync(query);
btnGetData.IsEnabled = false;
}
// 异步加载 Categories 数据完成
void _categories_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
if (_categories.Continuation != null)
{
// 在数据服务中 Categories 被服务端分页了,所以需要不断调用 LoadNextPartialSetAsync() ,以便加载全部的 Categories 数据
_categories.LoadNextPartialSetAsync();
}
else
{
gridCategory.DataContext = _categories;
gridCategory.SelectedIndex = 0;
btnGetData.IsEnabled = true;
}
}
else
{
MessageBox.Show(e.Error.ToString());
btnGetData.IsEnabled = true;
}
}
// 开始异步加载指定 Category 下的 Products 数据
private void gridCategory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Category category = gridCategory.SelectedItem as Category;
if (category != null)
{
if (category.Products.Count == 0)
{
category.Products.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(Products_LoadCompleted);
try
{
// 加载实体的导航属性数据
category.Products.LoadAsync();
}
catch (InvalidOperationException ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
gridProduct.DataContext = category.Products;
}
}
}
// 异步加载 Products 数据完成
void Products_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
DataServiceCollection<Product> products = sender as DataServiceCollection<Product>;
if (products.Continuation != null)
{
try
{
products.LoadNextPartialSetAsync();
}
catch (InvalidOperationException ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
gridProduct.DataContext = products;
}
}
else
{
MessageBox.Show(e.Error.ToString());
}
}
// 异步保存 Categories 中的被更新的数据(包括增删改),由于 Products 是 Category 的导航属性,所以相关的 Products 的数据的更新也会被保存
private void btnSave_Click(object sender, RoutedEventArgs e)
{
try
{
/*
* NorthwindEntities - 继承自 DataServiceContext,在 VS 中引用服务后会自动生成这个对象
* DataServiceContext.BeginSaveChanges() - 将有变化的数据提交至数据服务端进行更新
* DataServiceContext.EndSaveChanges() - 获取 BeginSaveChanges() 操作的结果,返回一个 DataServiceResponse 类型的对象
*/
_context.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, null);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
// 数据被异步保存后
private void OnChangesSaved(IAsyncResult result)
{
bool error = false;
this.Dispatcher.BeginInvoke(() =>
{
try
{
DataServiceResponse response = _context.EndSaveChanges(result);
foreach (ChangeOperationResponse changeResponse in response)
{
if (changeResponse.Error != null)
error = true;
}
if (!error)
{
MessageBox.Show("数据更新成功");
}
else
{
MessageBox.Show("数据更新失败");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
});
}
private int _pageIndex = 0;
private int _pageSize = 3;
// 演示如何通过 WCF Data Services 做服务端分页
private void ServerPagingDemo()
{
BindOrder();
btnNext.Click += (s, args) =>
{
_pageIndex++;
BindOrder();
};
btnPrev.Click += (s, args) =>
{
_pageIndex--;
BindOrder();
};
}
private void BindOrder()
{
txtCurrentPageIndex.Text = "当前页索引:" + _pageIndex.ToString();
var query = from o in _context.Orders select o;
query = query.Skip(_pageIndex * _pageSize).Take(_pageSize);
DataServiceCollection<Order> orders = new DataServiceCollection<Order>();
orders.LoadAsync(query);
orders.LoadCompleted += (s, args) =>
{
gridOrder.DataContext = orders;
};
}
}
}
* 演示 Silverlight 4.0 与 WCF Data Services 之间的通信
*
* DataServiceCollection<T> - 继承自 ObservableCollection<T>。用于将 WCF Data Services 提供的数据绑定到 Silverlight 控件上,当数据发生变化时可提供通知
* LoadAsync(IQueryable<T>) - 从数据服务上异步加载 IQueryable<T> 数据到 DataServiceCollection<T> 中(在 LoadCompleted 触发之前不能再调用此方法)
* LoadAsync() - 当 DataServiceCollection<T> 表示为某实体的导航属性时,从数据服务上异步加载该数据(在 LoadCompleted 触发之前不能再调用此方法)
* LoadCompleted - 异步加载完成后所触发的事件
* LoadCompletedEventArgs - LoadCompleted 事件的事件参数
* LoadCompletedEventArgs.QueryOperationResponse - 获取数据服务对改查询操作的响应
* QueryOperationResponse.Error - 引发的异常
* QueryOperationResponse.Headers - HTTP 的响应头
* QueryOperationResponse.Query - 对应的 DataServiceQuery(DataServiceQuery.RequestUri - 请求数据服务的 URI) 对象
* QueryOperationResponse.StatusCode - HTTP 的响应代码
* Continuation - 返回一个 DataServiceQueryContinuation<T> 对象,该对象封装了当前查询结果的下一页的 URI
* DataServiceQueryContinuation<T>.NextLinkUri - 当前查询结果的下一页的 URI
* LoadNextPartialSetAsync() - 加载下一页数据到 DataServiceCollection<T> 中
*
* Silverlight 通过 ObservableCollection<T> 与 ADO.NET Data Services 1.0 进行通信详见 http://www.cnblogs.com/webabcd/archive/2009/03/12/1409281.html
* WCF Data Services(ADO.NET Data Services 1.5)新特性详见 http://www.cnblogs.com/webabcd/archive/2010/06/11/1756071.html
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
using Silverlight40.MyWCFDataServiceReference;
using System.Data.Services.Client;
namespace Silverlight40.WCFDataServices
{
public partial class Demo : Page
{
private NorthwindEntities _context;
private DataServiceCollection<Category> _categories;
public Demo()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// 指定数据服务地址
_context = new NorthwindEntities(new Uri("Service/MyWCFDataService.svc", UriKind.Relative));
_categories = new DataServiceCollection<Category>();
_categories.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(_categories_LoadCompleted);
// 演示如何通过 WCF Data Services 做服务端分页
ServerPagingDemo();
}
// 开始异步加载 Categories 数据
private void btnGetData_Click(object sender, RoutedEventArgs e)
{
gridCategory.DataContext = null;
gridProduct.DataContext = null;
// 加载 Categories 表中的全部数据
var query = from c in _context.Categories select c;
_categories.LoadAsync(query);
btnGetData.IsEnabled = false;
}
// 异步加载 Categories 数据完成
void _categories_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
if (_categories.Continuation != null)
{
// 在数据服务中 Categories 被服务端分页了,所以需要不断调用 LoadNextPartialSetAsync() ,以便加载全部的 Categories 数据
_categories.LoadNextPartialSetAsync();
}
else
{
gridCategory.DataContext = _categories;
gridCategory.SelectedIndex = 0;
btnGetData.IsEnabled = true;
}
}
else
{
MessageBox.Show(e.Error.ToString());
btnGetData.IsEnabled = true;
}
}
// 开始异步加载指定 Category 下的 Products 数据
private void gridCategory_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Category category = gridCategory.SelectedItem as Category;
if (category != null)
{
if (category.Products.Count == 0)
{
category.Products.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(Products_LoadCompleted);
try
{
// 加载实体的导航属性数据
category.Products.LoadAsync();
}
catch (InvalidOperationException ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
gridProduct.DataContext = category.Products;
}
}
}
// 异步加载 Products 数据完成
void Products_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
DataServiceCollection<Product> products = sender as DataServiceCollection<Product>;
if (products.Continuation != null)
{
try
{
products.LoadNextPartialSetAsync();
}
catch (InvalidOperationException ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
gridProduct.DataContext = products;
}
}
else
{
MessageBox.Show(e.Error.ToString());
}
}
// 异步保存 Categories 中的被更新的数据(包括增删改),由于 Products 是 Category 的导航属性,所以相关的 Products 的数据的更新也会被保存
private void btnSave_Click(object sender, RoutedEventArgs e)
{
try
{
/*
* NorthwindEntities - 继承自 DataServiceContext,在 VS 中引用服务后会自动生成这个对象
* DataServiceContext.BeginSaveChanges() - 将有变化的数据提交至数据服务端进行更新
* DataServiceContext.EndSaveChanges() - 获取 BeginSaveChanges() 操作的结果,返回一个 DataServiceResponse 类型的对象
*/
_context.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, null);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
// 数据被异步保存后
private void OnChangesSaved(IAsyncResult result)
{
bool error = false;
this.Dispatcher.BeginInvoke(() =>
{
try
{
DataServiceResponse response = _context.EndSaveChanges(result);
foreach (ChangeOperationResponse changeResponse in response)
{
if (changeResponse.Error != null)
error = true;
}
if (!error)
{
MessageBox.Show("数据更新成功");
}
else
{
MessageBox.Show("数据更新失败");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
});
}
private int _pageIndex = 0;
private int _pageSize = 3;
// 演示如何通过 WCF Data Services 做服务端分页
private void ServerPagingDemo()
{
BindOrder();
btnNext.Click += (s, args) =>
{
_pageIndex++;
BindOrder();
};
btnPrev.Click += (s, args) =>
{
_pageIndex--;
BindOrder();
};
}
private void BindOrder()
{
txtCurrentPageIndex.Text = "当前页索引:" + _pageIndex.ToString();
var query = from o in _context.Orders select o;
query = query.Skip(_pageIndex * _pageSize).Take(_pageSize);
DataServiceCollection<Order> orders = new DataServiceCollection<Order>();
orders.LoadAsync(query);
orders.LoadCompleted += (s, args) =>
{
gridOrder.DataContext = orders;
};
}
}
}
OK
[源码下载]