與眾不同 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(10, 99);
            product.UnitPrice = 6.66m;
            product.Discontinued = false;

            var vm = App.Current.Resources["ODataViewModel"] as MyViewModel;
            vm.AddProduct(product);
        }
    }
}



OK
[源碼下載]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM