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 クライアントの実行については、だいぶ詳しくセッションで実演しましたので、ぜひ試してみてください。
また環境構築については、下記のエントリーをご参照ください。
また、ASP.NET Web APIバックエンドと、Windows ストアアプリでの CRUD 処理については、下記のエントリーをご参照ください。ソリューションファイルもダウンロード可能です。
2.オフラインサポートと Xamarin iOS 対応
オフラインサポートは、セッションでご紹介した Windows ストアアプリについては、下記のエントリーをご参照ください。ソリューションファイルもダウンロード可能です。
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以上あったポストですので(笑)、ぜひご覧になってください。
iOS/Android に関しては、別途、コードもご紹介したいと思います。もしすぐに知りたい方は、近いものが、 弊社エバンジェリスト松崎のブログ にありますので、ご覧ください。
以上です。来期もまたよろしくお願いします(^^)!
鈴木章太郎