6/27 "C# を使い倒す!クロス プラットフォーム アプリ開発とクラウド連携の新潮流" のMobile Services セッションフォローアップ

皆様、こんにちは!

2014/6/27(金) 日本マイクロソフトで行われた インフラジスティックス・ジャパン様、エクセルソフト様、との共同セミナー "C# を使い倒す!クロス プラットフォーム アプリ開発とクラウド連携の新潮流" では、多数の皆様のご来場ありがとうございました。簡単ではありますが、Mobile Services セッションのフォローアップをさせてください。なお、弊社は7月から新年度なので、私のブログとしては今年度最後の投稿となります。

スライドは下記にあります。

Microsoft azure mobile services 概要と xamarin との連携 from Shotaro Suzuki

1.概要部分とリソース

Mobile Services の概要や、Node.jsバックエンドでのXamarin クライアントの実行については、だいぶ詳しくセッションで実演しましたので、ぜひ試してみてください。

また環境構築については、下記のエントリーをご参照ください。

https://blogs.msdn.com/b/shosuz/archive/2014/06/10/5-29-30-de-code-sv-007-microsoft-azure-mobile-services-5.aspx

また、ASP.NET Web APIバックエンドと、Windows ストアアプリでの CRUD 処理については、下記のエントリーをご参照ください。ソリューションファイルもダウンロード可能です。

https://blogs.msdn.com/b/shosuz/archive/2014/05/31/5-29-30-de-code-sv-007-microsoft-azure-mobile-services-1.aspx

https://blogs.msdn.com/b/shosuz/archive/2014/06/04/5-29-30-de-code-sv-007-microsoft-azure-mobile-services-2.aspx

2.オフラインサポートと Xamarin iOS 対応

オフラインサポートは、セッションでご紹介した Windows ストアアプリについては、下記のエントリーをご参照ください。ソリューションファイルもダウンロード可能です。

https://blogs.msdn.com/b/shosuz/archive/2014/06/06/5-29-30-de-code-sv-007-microsoft-azure-mobile-services-3.aspx

Xamarin.iOS 対応については、セッションと懇親会でお話した通り、ストアアプリの場合とロジックはほとんど一緒ですので、プラットフォーム固有の部分のみ参考にしていただければと思います。下記の手順で試してみてください。

準備するもの:

Xamarin.iOS オフラインの利用

  • Azure Managerment Portal 上で新規 Mobile Service を作成するか既存の者を使い、Xamarin quickstart for iOS をダウンロード

  • プロジェクトをVisual Studio で開いて、Components フォルダにある Azure Mobile Services SDK への参照を削除

  • NuGet パッケージマネージャーを使い、Mobile Services SQLiteStore のプレリリースをインストール

        1: install-package WindowsAzure.MobileServices.SQLiteStore -Pre
    
  • 参照フォルダで、System.IO, System.Runtime、System.Threading.Tasks への参照を削除

QSTodoService.cs の編集
  • using 句の追加

        1: using Microsoft.WindowsAzure.MobileServices; 
        2: using Microsoft.WindowsAzure.MobileServices.Sync; 
    
  • todoTable メンバーのタイプを IMobileServicesSyncTable に変更

        1: IMobileServiceSyncTable<ToDoItem> todoTable; 
    
  • QSTodoService のコンストラクタの中でtodoTable の初期化メソッドを変更:

        1: todoTable = client.GetSyncTable <ToDoItem> ();
    
  • QSTodoService のコンストラクタの2行目に下記を追加:

        1: SQLitePCL.CurrentPlatform.Init();
    
  • InitializeAsync メソッドの追加

        1: public async Task InitializeStoreAsync()
        2: {
        3:     string path = "test1.db";
        4:     var store = new MobileServiceSQLiteStore(path);
        5:     store.DefineTable<ToDoItem>();
        6:     await client.SyncContext.InitializeAsync(store);
        7: }
    
  • SyncAsync メソッドの追加

        1: public async Task SyncAsync()
        2: {
        3:     try
        4:     {
        5:         await this.client.SyncContext.PushAsync();
        6:         await this.todoTable.PullAsync();
        7:     }
        8:     catch (MobileServiceInvalidOperationException e)
        9:     {
       10:         Console.Error.WriteLine(@"Sync Failed: {0}", e.Message);
       11:     }
       12: }
    
QSTodoListViewController.cs の編集
  • ViewDidLoad() メソッドで todoService の初期化の後に InitializeStoreAsync() の呼び出しを追加

        1: public override async void ViewDidLoad ()
        2: {
        3:     base.ViewDidLoad ();
        4:  
        5:  
        6:     todoService = QSTodoService.DefaultService;
        7:     await todoService.InitializeStoreAsync();
        8:     ...    // more code
        9: }
    
  • AddRefreshControl メソッドで RefreshAsync を呼び出す前に SyncAsync を呼び出すように変更

        1: RefreshControl.ValueChanged += async (sender, e) => {
        2:     await todoService.SyncAsync();
        3:     await RefreshAsync();
        4: }; 
    
ToDoItem.cs の編集
  • 下記の using 句を追加

        1: using Microsoft.WindowsAzure.MobileServices; 
    
  • 下記のメンバー変数を ToDoItem クラスに追加

        1: [Version]
        2: public string Version { get; set; }
        3:  
        4:  
        5: public override string ToString()
        6: {
        7:     return "Text: " + Text + "\nComplete: " + Complete + "\n";
        8: }
    
アプリケーションの実行

アプリを実行すると、リストビューは空になるはずです。これは、サービス側のデータベースを読みに行っておらず、ローカルのデータベースのみ参照しているためです。リフレッシュ操作をすると、SyncAsync メソッドが呼ばれ、これにより Mobile Services 側のデータベースからローカルの SQLite ストレージにデータが読み込まれます。

オプション : コンフリクトのハンドリング

懇親会で質問を戴きましたので、ちょっと触れておきますね。下記の通り実装してみてください。

-

**QSTodoService.cs を開き、InitializeAsync の最後の行を、Sync ハンドラを指定するように変更**

  
  

  

``` 
    1: await client.SyncContext.InitializeAsync(store, new ToDoSyncHandler());  
```

  

  
  • 新しい Sync ハンドラを追加
    1: using System;
    2: using System.Net.Http;
    3: using System.Threading.Tasks;
    4: using System.Collections.Generic;
    5: using Microsoft.WindowsAzure.MobileServices;
    6: using Microsoft.WindowsAzure.MobileServices.Sync;
    7: using Newtonsoft.Json.Linq;
    8: using MonoTouch.UIKit;
    9: using Microsoft.WindowsAzure.MobileServices.SQLiteStore;
   10:  
   11: class ToDoSyncHandler : MobileServiceSyncHandler
   12: {
   13:     const string LOCAL_VERSION = "Use local version";
   14:     const string SERVER_VERSION = "Use server version";
   15:  
   16:     public override async Task<JObject> ExecuteTableOperationAsync(IMobileServiceTableOperation operation)
   17:     {
   18:         MobileServicePreconditionFailedException error;
   19:  
   20:         do
   21:         {
   22:             error = null;
   23:             try
   24:             {
   25:                 return await operation.ExecuteAsync();
   26:             }
   27:             catch (MobileServicePreconditionFailedException ex)
   28:             {
   29:                 error = ex;
   30:             }
   31:  
   32:             if (error != null)
   33:             {
   34:                 var localItem = operation.Item.ToObject<ToDoItem>();
   35:                 var serverValue = error.Value;
   36:  
   37:                 var dialog = new UIAlertView("Conflict between local and server versions",
   38:                 "How do you want to resolve this conflict?\n\n" + "Local item: \n" + localItem +
   39:                 "\n\nServer item:\n" + serverValue.ToObject<ToDoItem>(), null, "Cancel", LOCAL_VERSION, SERVER_VERSION);
   40:  
   41:                 var clickTask = new TaskCompletionSource<int>();
   42:                 dialog.Clicked += (sender, e) =>
   43:                 {
   44:                     clickTask.SetResult(e.ButtonIndex);
   45:                 };
   46:  
   47:                 dialog.Show();
   48:  
   49:                 int command = await clickTask.Task;
   50:                 if (command == 1)
   51:                 {
   52:                     // Overwrite the server version and try the operation again by continuing the loop
   53:                     operation.Item[MobileServiceSystemColumns.Version] = serverValue[MobileServiceSystemColumns.Version];
   54:                     continue;
   55:                 }
   56:                 else if (command == 2)
   57:                 {
   58:                     return (JObject)serverValue;
   59:                 }
   60:                 else
   61:                 {
   62:                     operation.AbortPush();
   63:                 }
   64:             }
   65:         } while (error != null);
   66:  
   67:         return null;
   68:     }
   69: }

アプリケーションの実行

手動でコンフリクトの状況を実現するには、ローカルでの変更を同期して、Mobile Services 側にある item を変更する必要があります。他のクライアントアプリ(Windows ストアアプリでも、他の REST クライアントでも、SQL Server Management Studio でもかまいません)から行います。 その後、ローカルで item を変更し、リフレッシュ操作をします。もし、コンフリクトハンドラを実装していれば、どのようにこのコンフリクトを解決するか(サービス側を保持するかクライアント側を保持するか)、選択するダイアログが表示されるはずです。

3.Microsoft Azure Active Directory との連携

こちらについては、このエントリーで詳しく解説してあります。初日のページビューが3,000以上あったポストですので(笑)、ぜひご覧になってください。

https://blogs.msdn.com/b/shosuz/archive/2014/06/09/5-29-30-de-code-sv-007-microsoft-azure-mobile-services-4.aspx

iOS/Android に関しては、別途、コードもご紹介したいと思います。もしすぐに知りたい方は、近いものが、 弊社エバンジェリスト松崎のブログ にありますので、ご覧ください。

以上です。来期もまたよろしくお願いします(^^)!

鈴木章太郎