Tables、Blobs 及び Queues の利用

新規 Windows Phone Cloud Application の作成の箇所で、Windows Azure Storage のサポートにチェックを入れた場合、Windows Azure に格納されている、Tables、Blobs、Queues、にアクセスすることができます。そのためのステップは以下の通りです。

フリックまたはパンで、画面を左側に移動して、Tables Pivot アイテムに遷移します。そうすると、Windows Azure Storage の中で、利用可能な Table がわかります。 アプリケーションバーの中にあるプラスボタン (clip_image0016_thumb_thumb_thumb) をクリックすると、新テーブルが追加でき、また、Table 名の隣にある削除ボタン(clip_image0028_thumb_thumb_thumb)をクリックすると、 当該 Table を削除できます。

clip_image0036_thumb2_thumb_thumb

ここのソースコードを見てみましょう。Pivot アプリケーションで、MVVM を採用して開発されているため、処理はおもに ViewModel フォルダにまとまっています。この中では、上記のボタンの動き、NewTable()、DeleteTable()、あたりのメソッドを確認しておいてください。

   1: namespace WPCloudApp5.Phone.ViewModel
   2: {
   3:     using System;
   4:     using System.Globalization;
   5:     using System.Linq;
   6:     using System.Windows;
   7:     using System.Windows.Threading;
   8:     using Microsoft.Phone.Shell;
   9:     using Microsoft.Samples.WindowsPhoneCloud.StorageClient;
  10:  
  11:     internal enum TableAction
  12:     {
  13:         None = 0,
  14:         Create = 1,
  15:         Delete = 2
  16:     }
  17:  
  18:     public class TablesPageViewModel : TableBaseViewModel<TableServiceSchema>
  19:     {
  20:         private const string IconsRootUri = "/Toolkit.Content/";
  21:  
  22:         private TableAction action = TableAction.None;
  23:         private TableServiceSchema currentTable = null;
  24:  
  25:         public TablesPageViewModel()
  26:             : this(App.CloudClientFactory, Deployment.Current.Dispatcher)
  27:         {
  28:         }
  29:  
  30:         public TablesPageViewModel(ICloudClientFactory cloudClientFactory, Dispatcher dispatcher)
  31:             : base(cloudClientFactory, dispatcher)
  32:         {
  33:         }
  34:  
  35:         public override string TableName
  36:         {
  37:             get { return "Tables"; }
  38:         }
  39:  
  40:         public void NewTable()
  41:         {
  42:             var newTableName = string.Format(CultureInfo.InvariantCulture, "Table{0}", DateTime.Now.ToString("yyyyMMddTHHmmss", CultureInfo.InvariantCulture));
  43:             this.currentTable = new TableServiceSchema(newTableName);
  44:             this.action = TableAction.Create;
  45:  
  46:             this.Message = "Creating new table...";
  47:             this.IsLoading = true;
  48:  
  49:             try
  50:             {
  51:                 this.Context.AddTable(this.currentTable);
  52:                 this.Context.BeginSaveChanges(this.OnBeginSaveChanges, null);
  53:             }
  54:             catch (Exception exception)
  55:             {
  56:                 var errorMessage = StorageClientExceptionParser.ParseDataServiceException(exception).Message;
  57:  
  58:                 this.IsLoading = false;
  59:                 this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", errorMessage);
  60:             }
  61:         }
  62:  
  63:         public void DeleteTable(TableServiceSchema table)
  64:         {
  65:             this.currentTable = table;
  66:             this.action = TableAction.Delete;
  67:  
  68:             this.Message = "Deleting table...";
  69:             this.IsLoading = true;
  70:  
  71:             try
  72:             {
  73:                 // In the case the table was previously detached in a failed operation.
  74:                 if (!this.Context.Entities.Any(e => e.Entity == this.currentTable))
  75:                 {
  76:                     this.Context.AttachTo("Tables", this.currentTable);
  77:                 }
  78:  
  79:                 this.Context.DeleteObject(this.currentTable);
  80:                 this.Context.BeginSaveChanges(this.OnBeginSaveChanges, null);
  81:             }
  82:             catch (Exception exception)
  83:             {
  84:                 var errorMessage = StorageClientExceptionParser.ParseDataServiceException(exception).Message;
  85:  
  86:                 this.IsLoading = false;
  87:                 this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", errorMessage);
  88:             }
  89:         }
  90:  
  91:         public void OnBeginSaveChanges(IAsyncResult asyncResult)
  92:         {
  93:             this.Dispatcher.BeginInvoke(
  94:                 () =>
  95:                 {
  96:                     try
  97:                     {
  98:                         this.Context.EndSaveChanges(asyncResult);
  99:  
 100:                         // Update the Table collection that is binded in the page.
 101:                         if (this.action == TableAction.Create)
 102:                         {
 103:                             this.Table.Add(this.currentTable);
 104:                         }
 105:                         else if (this.action == TableAction.Delete)
 106:                         {
 107:                             this.Table.Remove(this.currentTable);
 108:                         }
 109:  
 110:                         this.Message = "Changes saved successfully.";
 111:                     }
 112:                     catch (Exception exception)
 113:                     {
 114:                         this.Message = string.Format(
 115:                             CultureInfo.InvariantCulture,
 116:                             "Error: {0}",
 117:                             StorageClientExceptionParser.ParseDataServiceException(exception).Message);
 118:  
 119:                         // Detach from the Context the table that produced the failed operation.
 120:                         this.Context.Detach(this.currentTable);
 121:                     }
 122:                     finally
 123:                     {
 124:                         this.IsLoading = false;
 125:                     }
 126:                 });
 127:         }
 128:  
 129:         protected override void PopulateApplicationBarButtons(IApplicationBar applicationBar)
 130:         {
 131:             var refreshButton = new ApplicationBarIconButton(new Uri(string.Format(CultureInfo.InvariantCulture, "{0}{1}", IconsRootUri, "appbar.refresh.rest.png"), UriKind.Relative)) { Text = "refresh" };
 132:             refreshButton.Click += (s, e) => this.LoadTable();
 133:  
 134:             var addButton = new ApplicationBarIconButton(new Uri(string.Format(CultureInfo.InvariantCulture, "{0}{1}", IconsRootUri, "appbar.add.rest.png"), UriKind.Relative)) { Text = "add table" };
 135:             addButton.Click += (s, e) => this.NewTable();
 136:  
 137:             applicationBar.Buttons.Add(refreshButton);
 138:             applicationBar.Buttons.Add(addButton);
 139:         }
 140:     }
 141: }

注意 : PushUserEndpoints、secMembership、UserPrivileges 及び secRole の各 Tableは、(この前の投稿でも中身を見た通り)、Toolkit サービスが内部的に使っているものです。したがって、それらのどれかをもし削除しようとすると、権限がないというメッセージとともに、エラーが発生します。

次に、フリックまたはパンで、画面を左側に移動して、Sample Data Pivot アイテムに遷移します。SampleData Table で、行が使用可能なのがわかります。もしこのテーブルが、それまでAzure Table ストレージに存在していなかった場合には、今回初めて(新規アプリケーションの作成により)生成されたものです。

Sample data Pivot アイテムの中で、アプリケーションバーの中にあるプラスボタン (clip_image0017_thumb_thumb_thumb) をクリックして、新しい行を追加します。もちろん、既に入っている行を編集したり、削除したりもできます。

clip_image0044_thumb2_thumb_thumb

ここで、サンプルコードを見てみましょう。SampleDataDetailsPageViewModel.cs に処理が書いてあります。まずは、SampleDataTablePageViewModel.cs の LoadTable() の処理です。

   1: public override void LoadTable()
   2:         {
   3:             if (!sampleDataTableCreated)
   4:             {
   5:                 try
   6:                 {
   7:                     this.cloudTableClient.CreateTableIfNotExist(
   8:                         this.TableName,
   9:                         r =>
  10:                         {
  11:                             if (this.Dispatcher != null)
  12:                             {
  13:                                 this.Dispatcher.BeginInvoke(() => this.HandleTableCreationResponse(r));
  14:                             }
  15:                             else
  16:                             {
  17:                                 this.HandleTableCreationResponse(r);
  18:                             }
  19:                         });
  20:                 }
  21:                 catch (Exception exception)
  22:                 {
  23:                     var errorMessage = StorageClientExceptionParser.ParseDataServiceException(exception).Message;
  24:  
  25:                     this.IsLoading = false;
  26:                     this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", errorMessage);
  27:                 }
  28:             }
  29:             else
  30:             {
  31:                 base.LoadTable();
  32:             }
  33:         }

続いて、SampleDataDetailsPageViewModel.cs の DeleteSampleData()メソッドの処理を見てみてください。こちらも、Microsoft.Samples.WindowsPhoneCloud.StorageClient のインターフェースである ITableServiceContext を実装したオブジェクトである Context を使ってCRUD操作をしています。上記と同じ動きですね。

   1: public void DeleteSampleData()
   2:         {
   3:             this.Message = "Deleting...";
   4:             this.IsSaving = true;
   5:  
   6:             this.SampleData.PropertyChanged -= this.OnUpdateSampleData;
   7:  
   8:             try
   9:             {
  10:                 this.context.DeleteObject(this.SampleData);
  11:                 this.context.BeginSaveChanges(this.OnBeginSaveChanges, null);
  12:             }
  13:             catch (Exception exception)
  14:             {
  15:                 var errorMessage = StorageClientExceptionParser.ParseDataServiceException(exception).Message;
  16:  
  17:                 this.IsSaving = false;
  18:                 this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", errorMessage);
  19:             }
  20:         }

こちらも、Microsoft.Samples.WindowsPhoneCloud.StorageClient のICloudTableClientインターフェースを実装したオブジェクトを使って操作を行っています。これにより、各メソッドでの操作を抽象化し、単純化させることができます。要は、最初の新規作成~Wizardの中で、ストレージ名と、キーだけ渡しておけば、あとはCRUD 操作ができるわけです。

次に、フリックまたはパンで、画面を左側に移動して、List Blobs Pivot アイテムに遷移します。そして、アプリケーションバーの中にある、カメラボタン(clip_image0056_thumb_thumb_thumb) をクリックします。Windows Phone のカメラが起動します。スクリーン右上にあるボタンをクリックして、写真を撮り、決定ボタンを押します。これにより、Upload Picture ページに遷移します。

clip_image0068_thumb2_thumb_thumb

Upload Picture ページの中で、Blobに名前を付けて、Upload を行います。 もし、期待通りにすべて動いてくれれば、メッセージボックスが出現し、”Image was successfully uploaded”と出るでしょう。OK を押して、メイン Pivot ページに遷移します。

clip_image0076_thumb2_thumb_thumb

ここの部分のソースコードを見てみましょう。UploadPhotoPageViewModel.csから、UploadPhoto() メソッドをご覧ください。

   1: public void UploadPhoto(Action<string> successCallback, Action<string> failureCallback)
   2:         {
   3:             this.IsUploading = true;
   4:             this.blobClient.Upload(
   5:                 this.BlobName,
   6:                 this.PhotoStream,
   7:                 r => this.dispatcher.BeginInvoke(
   8:                         () =>
   9:                         {
  10:                             this.IsUploading = false;
  11:  
  12:                             if (r.Exception == null)
  13:                             {
  14:                                 if (successCallback != null)
  15:                                 {
  16:                                     successCallback.Invoke(string.Format(CultureInfo.InvariantCulture, "Image file {0} successfully uploaded!", this.BlobName));
  17:                                 }
  18:                             }
  19:                             else
  20:                             {
  21:                                 if (failureCallback != null)
  22:                                 {
  23:                                     failureCallback.Invoke(string.Format(CultureInfo.InvariantCulture, "Error: {0}", r.Exception.Message));
  24:                                 }
  25:                             }
  26:                         }));
  27:         }

ここでも、blobClient というオブジェクトを使って、そのメソッドのUpload を使って処理が行われています。このblobClient も、Microsoft.Samples.WindowsPhoneCloud.StorageClient のICloudTableClientインターフェースを実装したオブジェクトです。これにより、各メソッドでの操作を抽象化し、単純化させることができます。こちらも、最初の新規作成~Wizardの中で、ストレージ名と、アクセスキーだけ渡しておけば、あとは操作ができるというわけです。

続いて、List Blobs Pivot ページの中で、リスト表示をするために、list blobs ボタンを押し、リスティングします (prefix を入力するとフィルタリングができます)。先ほど、撮影し Upload した画像のサムネールを、Blob へのリンクの形で見ることができます。このリンクをクリックすると、IE9 が起動し、画像を表示します。

clip_image0088_thumb2_thumb_thumb

ここでソースコードを見てみます。ListBlobsPageViewModel.cs の中の、ListBlobs() ですね。

   1: public void ListBlobs()
   2:         {
   3:             this.IsListing = true;
   4:             this.Message = "Listing blobs...";
   5:  
   6:             try
   7:             {
   8:                 this.blobClient.ListBlobsWithPrefix(
   9:                     this.Prefix,
  10:                     this.UseFlatBlobListing,
  11:                     r =>
  12:                     {
  13:                         if (this.dispatcher != null)
  14:                         {
  15:                             this.dispatcher.BeginInvoke(() => this.UpdateListBlobs(r));
  16:                         }
  17:                         else
  18:                         {
  19:                             this.UpdateListBlobs(r);
  20:                         }
  21:                     });
  22:             }
  23:             catch (Exception exception)
  24:             {
  25:                 var errorMessage = StorageClientExceptionParser.ParseDataServiceException(exception).Message;
  26:  
  27:                 this.IsListing = false;
  28:                 this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", errorMessage);
  29:             }
  30:         }

ここでも、blobClient は、Microsoft.Samples.WindowsPhoneCloud.StorageClient のICloudTableClientインターフェースを実装したオブジェクトです。こちらも、各メソッドでの操作を抽象化し、単純化させることができます。

左向きの矢印 (clip_image0098_thumb_thumb_thumb).を使って、アプリケーションに戻ります。続いて Queues Pivot ページの中で、リスト表示をするために、list queus ボタンを押し、Windows Azure ストレージにある 利用可能な queues をリスティングします (prefix を入力するとフィルタリングができます)。

clip_image0106_thumb2_thumb_thumb

Queues Pivot アイテムの中で、アプリケーションバーの中にあるプラスボタン (clip_image0018_thumb_thumb_thumb) をクリックし、新しい Queue を追加します ( Queue 名の隣にある削除ボタン(clip_image0029_thumb_thumb_thumb)も利用可能です)。

clip_image0118_thumb2_thumb_thumb

それぞれのソースコードを見てみましょう。ListQueuesPageViewModel.cs にある、NewQueue() 、および、DeleteQueue()、がそれです。

NewQueue()

   1: public void NewQueue()
   2: {
   3:     var newQueueName = string.Format(CultureInfo.InvariantCulture, "Queue{0}", DateTime.Now.ToString("yyyyMMddTHHmmss", CultureInfo.InvariantCulture)).ToLowerInvariant();
   4:  
   5:     this.Message = "Creating new queue...";
   6:     this.IsLoading = true;
   7:  
   8:     try
   9:     {
  10:         var currentQueue = this.queueClient.GetQueueReference(newQueueName);
  11:         currentQueue.Create(
  12:             creationResult =>
  13:             {
  14:                 if (this.dispatcher != null)
  15:                 {
  16:                     this.dispatcher.BeginInvoke(() => this.HandleQueueCreationResult(currentQueue, creationResult));
  17:                 }
  18:                 else
  19:                 {
  20:                     this.HandleQueueCreationResult(currentQueue, creationResult);
  21:                 }
  22:             });
  23:     }
  24:     catch (Exception exception)
  25:     {
  26:         this.IsLoading = false;
  27:         this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", exception.Message);
  28:     }
  29: }

DeleteQueue()

   1: public void DeleteQueue(ICloudQueue queue)
   2: {
   3:     this.Message = "Deleting queue...";
   4:     this.IsLoading = true;
   5:  
   6:     try
   7:     {
   8:         var currentQueue = this.queueClient.GetQueueReference(queue.Name);
   9:         currentQueue.Delete(
  10:             deletionResult =>
  11:             {
  12:                 if (this.dispatcher != null)
  13:                 {
  14:                     this.dispatcher.BeginInvoke(() => this.HandleQueueDeletionResult(currentQueue, deletionResult));
  15:                 }
  16:                 else
  17:                 {
  18:                     this.HandleQueueDeletionResult(currentQueue, deletionResult);
  19:                 }
  20:             });
  21:     }
  22:     catch (Exception exception)
  23:     {
  24:         this.IsLoading = false;
  25:         this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", exception.Message);
  26:     }
  27: }

ここでもやはり、Microsoft.Samples.WindowsPhoneCloud.StorageClient にある、ICloudQueue インターフェースを実装した queueClient オブジェクトのメソッドにより、簡単に Queue に対するCRUDを行っています。

次に、前のステップで作成したばかりのQueue の名前をクリックして、Queue details ページに遷移します。Queue details ページの中で、テキストボックスに、”message 1” と入力し Queue をクリックします。続いて、テキストボックスに、”message 2” と入力し Queue を再度クリックします。そうすると、Queueにメッセージを入れることができます。

続いて、Dequeue ボタンを2回クリックすることにより、Queue に入れたメッセージを正しい順序で取り出すことができます。

clip_image0124_thumb2_thumb_thumb

ここで当該部分のソースコードを見てみましょう。同じく QueueDetailsPageViewModel.cs の QueueMessage() メソッド及び DequeMessage() メソッドの箇所になります。

QueueMessage()

   1: public void QueueMessage()
   2: {
   3:     this.Message = "Queing message...";
   4:     this.IsBusy = true;
   5:     try
   6:     {
   7:         this.Queue.AddMessage(
   8:             new CloudQueueMessage { AsBytes = Encoding.UTF8.GetBytes(this.QueueMessageContent) },
   9:             r => this.dispatcher.BeginInvoke(
  10:                 () =>
  11:                 {
  12:                     this.Message = r.Exception == null
  13:                         ? "Message successfully queued!"
  14:                         : string.Format(CultureInfo.InvariantCulture, "Error: {0}", r.Exception.Message);
  15:  
  16:                     this.IsBusy = false;
  17:                 }));
  18:     }
  19:     catch (Exception exception)
  20:     {
  21:         var errorMessage = StorageClientExceptionParser.ParseDataServiceException(exception).Message;
  22:  
  23:         this.IsBusy = false;
  24:         this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", errorMessage);
  25:     }
  26: }

DequeueMessage()

   1: public void DequeueMessage()
   2: {
   3:     this.Message = "Dequeing message...";
   4:     this.IsBusy = true;
   5:     try
   6:     {
   7:         this.Queue.GetMessage(
   8:             s =>
   9:             {
  10:                 if (s.Exception == null)
  11:                 {
  12:                     if (s.Response == null)
  13:                     {
  14:                         this.dispatcher.BeginInvoke(
  15:                             () =>
  16:                             {
  17:                                 this.Message = "Queue is empty";
  18:                                 this.IsBusy = false;
  19:                             });
  20:                     }
  21:                     else
  22:                     {
  23:                         this.Queue.DeleteMessage(
  24:                             s.Response,
  25:                             r => this.dispatcher.BeginInvoke(
  26:                                 () =>
  27:                                 {
  28:                                     if (r.Exception == null)
  29:                                     {
  30:                                         this.CloudQueueMessages.Add(s.Response);
  31:                                         this.Message = "Message successfully dequeued!";
  32:                                     }
  33:                                     else
  34:                                     {
  35:                                         this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", r.Exception.Message);
  36:                                     }
  37:  
  38:                                     this.IsBusy = false;
  39:                                 }));
  40:                     }
  41:                 }
  42:                 else
  43:                 {
  44:                     this.dispatcher.BeginInvoke(
  45:                         () =>
  46:                         {
  47:                             this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", s.Exception.Message);
  48:                             this.IsBusy = false;
  49:                         });
  50:                 }
  51:             });
  52:     }
  53:     catch (Exception exception)
  54:     {
  55:         var errorMessage = StorageClientExceptionParser.ParseDataServiceException(exception).Message;
  56:  
  57:         this.IsBusy = false;
  58:         this.Message = string.Format(CultureInfo.InvariantCulture, "Error: {0}", errorMessage);
  59:     }
  60: }

ここでも、Microsoft.Samples.WindowsPhoneCloud.StorageClient にある、ICloudQueue インターフェースを実装した Queue オブジェクトのメソッドにより、簡単に Queue に対するCRUDを行っています。

Windows Phone Emulator のWindows ボタン(clip_image013_thumb_thumb_thumb) をクリックして、Start メニューに戻ります。

ユーザーメニュー及び権限管理について

ここはセッション内でデモをしていませんが、追加でご紹介しておきます。Web ブラウザーの方に戻り、User メニューオプションをクリックして開きます。新しいユーザー(ACSで認証した際にRegisiter したユーザー)が作成されており、デフォルトで、TablesBlobsQueues および SQL Azure に対する権限が付与されていることがわかります。

clip_image0144_thumb2_thumb_thumb

注意 : このWeb サイトの administrator ユーザー (admin) は、このWindows Azure Mobile Cloud アプリケーションが、ASP.NET Membership 認証を使う場合だけ、表示されます。この場合には、当該 admin ユーザーもまた有効なアプリケーションユーザーとなります。また、SQL のカラムが表示されるのは、このアプリケーションが、SQL Azure データベースを使用すると構成された場合だけです。

ここで、追加されたユーザーの、TablesBlobsと Queues のチェックボックスを外してみましょう。

clip_image015_thumb2_thumb_thumb

次に、Tables メニューのオプションをクリックします。このページでは、Windows Azure Tables ストレージに入っている Table につき、個別のユーザーに、権限をあたえることができます。

clip_image0166_thumb2_thumb_thumb

次に、Queues メニューのオプションをクリックします。このページでは、Windows Azure Tables ストレージに入っている Queue につき、個別のユーザーに、権限をあたえることができます。

clip_image0174_thumb2_thumb_thumb

Windows Phone Emulator に戻り、WAT Windows Phone アイコンをクリックして、再度アプリケーションを開きます。フリックまたはパンにより左側に遷移して、tables Pivot アイテムに遷移し、次いで、sample data Pivot アイテムに遷移します。そうすると、エラーメッセージが出ているのがわかります。いずれも、Table 使用権限がありません、という内容です。

clip_image018_thumb2_thumb_thumb

続いて、フリックまたはパンにより左側に遷移して、list blobs Pivot アイテムに遷移し、次いで、list blobs をクリックします。そうすると、エラーメッセージが出ているのがわかります。Blob 使用権限がありません、という内容です。

clip_image019_thumb2_thumb_thumb

これまた同様に、フリックまたはパンにより左側に遷移して、queues Pivot アイテムに遷移し、次いで、list queues をクリックします。そうすると、エラーメッセージが出ているのがわかります。Queue 使用権限がありません、という内容です。

clip_image020_thumb2_thumb_thumb

SQL Azure データベースの利用

この点も、セッション内ではデモをしていませんが、追加でご説明しておきます。もし、このアプリケーションを作成する際に、Creating a New Windows Phone Cloud Application Wizard のセクションで、SQL Azure データベースをサポートするように構成した場合、SQL Azure データベースの上に配置されている OData サービスにアクセスすることができます。下記のステップに従って、この特徴を見てみましょう。

注意 : このアプリケーションは、Entity Framework 4.1 Code First を使用して、必要なテーブルを作成するように設定されています。デフォルトでは、一つのデータベースが SQL Azure の中に作成され、その名前は、テンプレートのWizardが作成している場合は、ベースとなるプロジェクトと同じ名前になります。そこで、モデルが変更された際に、Entity Framework 4.1 Code First を使用して、必要なデータベースを再作成するには、必要となってくるのが、Persist Security Info=True を、当該データベースへの connection string に追加することです。この設定は、必ずしもプロダクション環境にそぐわないので、アプリケーションの配布の際には削除を検討してください。

フリックまたはパンにより左側に遷移して、sql azure data Pivot アイテムに遷移します。そうすると、SqlSampleData Table が確認できます。

clip_image021_thumb3_thumb_thumb

Windows Phone Emulator の Windows ボタン(clip_image0131_thumb_thumb_thumb) をクリックして、Start メニューに遷移します。Web ブラウザーに切り替えて、User メニューをクリックします。

clip_image0145_thumb2_thumb_thumb

このアプリケーションユーザーの SQL チェックボックスのチェックを外します。

clip_image022_thumb2_thumb_thumb

Windows Phone Emulator に切り替え、WAT Windows Phone アイコンをクリックして、再びアプリケーションを開きます。フリックまたはパンにより左側に遷移して、sql azure data Pivot アイテムに遷移します。そうすると、エラーメッセージが出ているのがわかります。SQL Azure 使用権限がありません、という内容です。

clip_image023_thumb2_thumb_thumb

以上で、セッションでご紹介した、スクラッチからのWindows Azure Toolkit for Windows Phone 開発編は完了です。

いかがでしたでしょうか?このようにデフォルトでも、かなりのことができるテンプレートに仕上がっていますので、ぜひこれをうまく利用して、皆様の Windows Phone x Windows Azure ソリューションを迅速に開発して戴ければ!と思う次第です。

次のエントリーは、Windows Azure への展開について、注意点を簡単に纏めます。

その次のエントリーでは、Windows Azure Toolkit for iOS による開発について書く予定です。

鈴木 章太郎