与众不同 windows phone (29) - Communication(通信)之与 OData 服务通信

原文:与众不同 windows phone (29) - Communication(通信)之与 OData 服务通信

[索引页]
[源码下载]

与众不同 windows phone (29) - Communication(通信)之与 OData 服务通信

作者:webabcd

介绍
与众不同 windows phone 7.5 (sdk 7.1) 之通信

  • 与 OData 服务通信

示例
1、服务端(采用 Northwind 示例数据库)
NorthwindService.svc.cs

/*
* 提供 OData 服务,详细说明请参见 WCF Data Services 的相关文章:http://www.cnblogs.com/webabcd/category/181636.html
*/ 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 Web.Communication
{
// 出现异常时,返回详细的错误信息
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class NorthwindService : DataService<NorthwindEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
// 全部实体全部权限
config.SetEntitySetAccessRule("*", EntitySetRights.All);
// 指定服务所支持的 OData 协议的最大版本
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
}

2、客户端(ViewModel 层)
MyViewModel.cs

/*
* 调用 OData 服务的 ViewModel 层
*
* DataServiceCollection<T> - OData 服务的实体数据集合,添加、删除和更新数据时都会提供通知,继承自 ObservableCollection<T>
* Count - 实体数据集合中的实体数
* CollectionChangedCallback - 实体集合更改时调用的委托
* EntityChangedCallback - 实体更改时调用的委托
* Continuation - 是否还有下一页数据
* LoadAsync(IQueryable<T> query) - 通过指定的查询异步加载数据
* CancelAsyncLoad() - 取消异步加载请求
* LoadNextPartialSetAsync() - 加载下一页数据
* Detach() - 停止跟踪集合中的全部实体
* LoadCompleted - 异步数据加载完成后触发的事件
*
*
* DataServiceState - 服务状态
* Serialize(DataServiceContext context, Dictionary<string, Object> collections) - 序列化 DataServiceContext 对象以及页面的相关数据
* 注:引用 OData 服务后,会自动生成一个继承自 DataServiceContext 的代理类,如果需要序列化的话,则必须手动为其加上 [System.Runtime.Serialization.DataContract]
* Deserialize() - 反序列化,返回 DataServiceState 类型的数据
* Context - 相关的 DataServiceContext 对象
* RootCollections - 相关的数据
*
*
* 注:
* OData 服务的相关查询参数参见:http://msdn.microsoft.com/en-us/library/gg309461.aspx
*/ using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes; using System.ComponentModel;
using Demo.NorthwindContext;
using System.Data.Services.Client;
using System.Linq;
using System.Collections.Generic; namespace Demo.Communication.ODataClient.ViewModel
{
public class MyViewModel : INotifyPropertyChanged
{
// 引用过来的 OData 服务
private NorthwindEntities _context;
// OData 服务地址
private Uri _northwindUri = new Uri("http://localhost:15482/Communication/NorthwindService.svc/"); // 相关数据,DataServiceCollection<T> 继承自 ObservableCollection<T>
public DataServiceCollection<Category> Categories { get; private set; } public void LoadData()
{
_context = new NorthwindEntities(_northwindUri); // 实例化 DataServiceCollection<Category>,并注册相关事件
Categories = new DataServiceCollection<Category>(_context);
Categories.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(Categories_LoadCompleted); // 获取全部 Categories 数据,并且一次请求带上 Categories 的所有 Products
DataServiceQuery<Category> query = _context.Categories.Expand("Products");
Categories.LoadAsync(query); NotifyPropertyChanged("Categories");
} void Categories_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
// 如果服务端有分页,则继续获取直至获取完全部数据
if (Categories.Continuation != null)
Categories.LoadNextPartialSetAsync();
}
else
{
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
MessageBox.Show(e.Error.ToString());
});
}
} // 新增产品数据
public void AddProduct(Product product)
{
_context.AddToProducts(product); Categories.Single(p => p.CategoryID == product.CategoryID).Products.Add(product); SaveChanges();
} // 删除产品数据
public void DeleteProduct(Product product)
{
_context.DeleteObject(product); Categories.Single(p => p.CategoryID == product.CategoryID).Products.Remove(product); SaveChanges();
} // 更新产品数据
public void UpdateProduct(Product product)
{
_context.UpdateObject(product); SaveChanges();
} public void SaveChanges()
{
// 异步向 OData 服务提交最近的数据变化
_context.BeginSaveChanges(OnChangesSaved, _context);
} private void OnChangesSaved(IAsyncResult result)
{
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
_context = result.AsyncState as NorthwindEntities; try
{
// 完成数据提交
_context.EndSaveChanges(result);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
});
} // 一个异步获取数据的例子,此 Demo 没用到此方法
public void GetData()
{
// 查找 Products 里 ProductID 为 1 的数据
var query = _context.CreateQuery<Product>("Products").AddQueryOption("$filter", "ProductID eq 1"); // 异步请求 OData 服务
query.BeginExecute(p =>
{
var myQuery = p.AsyncState as DataServiceQuery<Product>; try
{
// 获取请求结果
var result = myQuery.EndExecute(p).First();
// 更新数据
_context.UpdateObject(result);
// 将更新后的结果提交到 OData 服务
SaveChanges();
}
catch (Exception ex)
{
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
MessageBox.Show(ex.ToString());
});
}
}, query);
} // 序列化当前数据,Deactivated 保存此值到应用程序状态,参见 App.xaml.cs
public string GetState()
{
var dict = new Dictionary<string, object>();
dict["categories"] = Categories; return DataServiceState.Serialize(_context, dict);
} // 恢复数据,当从 tombstone 状态进入 Activated 时,则从应用程序状态中恢复数据,参见 App.xaml.cs
public void RestoreState(string appState)
{
Dictionary<string, object> dict; if (!string.IsNullOrEmpty(appState))
{
DataServiceState state = DataServiceState.Deserialize(appState); // 获取相关数据
var context = state.Context as NorthwindEntities;
dict = state.RootCollections;
DataServiceCollection<Category> categories = dict["Categories"] as DataServiceCollection<Category>; // 恢复相关数据
_context = context;
Categories = categories;
Categories.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(Categories_LoadCompleted);
NotifyPropertyChanged("Categories");
}
} public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

3、客户端(View 层)
App.xaml

<Application
x:Class="Demo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:odataViewModel="clr-namespace:Demo.Communication.ODataClient.ViewModel"> <Application.Resources> <odataViewModel:MyViewModel x:Key="ODataViewModel" /> </Application.Resources> </Application>

App.xaml.cs

        private void Application_Activated(object sender, ActivatedEventArgs e)
{
// 从 tombstone 状态返回,则恢复相关数据,参见:“调用 OData 服务”
if (!e.IsApplicationInstancePreserved)
{
if (PhoneApplicationService.Current.State.ContainsKey("categoriesState"))
{
var vm = App.Current.Resources["ODataViewModel"] as MyViewModel;
string categoriesState = PhoneApplicationService.Current.State["categoriesState"] as string; vm.RestoreState(categoriesState);
}
}
} private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
// 保存相关数据,参见:“调用 OData 服务”
var vm = App.Current.Resources["ODataViewModel"] as MyViewModel;
PhoneApplicationService.Current.State["categoriesState"] = vm.GetState();
}

Demo.xaml

<phone:PhoneApplicationPage
x:Class="Demo.Communication.ODataClient.Demo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="696" d:DesignWidth="480"
shell:SystemTray.IsVisible="True" DataContext="{Binding Source={StaticResource ODataViewModel}}"
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"> <phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="item">
<ListBox ItemsSource="{Binding Products}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="460" HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions> <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding ProductName}" />
<TextBlock Text="{Binding UnitPrice}" Margin="10 0 0 0" />
</StackPanel> <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right">
<Button x:Name="btnUpdate" Content="更新" Click="btnUpdate_Click" />
<Button x:Name="btnDelete" Content="删除" Click="btnDelete_Click" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
<DataTemplate x:Key="header">
<TextBlock Text="{Binding CategoryName}" />
</DataTemplate>
</phone:PhoneApplicationPage.Resources> <Grid x:Name="LayoutRoot" Background="Transparent">
<controls:Pivot x:Name="pivot"
Title="产品列表"
ItemTemplate="{StaticResource item}"
HeaderTemplate="{StaticResource header}"
ItemsSource="{Binding Categories}">
</controls:Pivot>
</Grid> <phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar Mode="Default" IsVisible="True">
<shell:ApplicationBarIconButton x:Name="btnAd" IconUri="/ApplicationBarDemo/Assets/appbar.add.rest.png" Text="添加" Click="btnAd_Click" />
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar> </phone:PhoneApplicationPage>

Demo.xaml.cs

/*
* 调用 OData 服务的 View 层
*/ 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 Microsoft.Phone.Controls; using Demo.Communication.ODataClient.ViewModel;
using Demo.NorthwindContext; namespace Demo.Communication.ODataClient
{
public partial class Demo : PhoneApplicationPage
{
public Demo()
{
InitializeComponent(); this.Loaded += new RoutedEventHandler(Demo_Loaded);
} void Demo_Loaded(object sender, RoutedEventArgs e)
{
// 加载数据
var vm = App.Current.Resources["ODataViewModel"] as MyViewModel;
vm.LoadData();
} private void btnDelete_Click(object sender, RoutedEventArgs e)
{
var product = (sender as Button).DataContext as Product; // 删除指定的产品数据
var vm = App.Current.Resources["ODataViewModel"] as MyViewModel;
vm.DeleteProduct(product);
} private void btnUpdate_Click(object sender, RoutedEventArgs e)
{
// 更新指定的产品数据
var product = (sender as Button).DataContext as Product;
product.UnitPrice = 8.88m; var vm = App.Current.Resources["ODataViewModel"] as MyViewModel;
vm.UpdateProduct(product);
} private void btnAd_Click(object sender, EventArgs e)
{
// 在指定的类别下添加一个新的产品数据
var category = pivot.SelectedItem as Category; Product product = new Product();
product.CategoryID = category.CategoryID;
product.ProductName = "测试" + new Random().Next(, );
product.UnitPrice = 6.66m;
product.Discontinued = false; var vm = App.Current.Resources["ODataViewModel"] as MyViewModel;
vm.AddProduct(product);
}
}
}

OK
[源码下载]

上一篇:mysql 开发基础系列20 事务控制和锁定语句(上)


下一篇:docker的安装及使用