2009年3月22日日曜日

初めてのAzure Storage

そういえばAzureのInviteが来てから、なんにも遊んでないことを思い出した。手を動かしてみよう。

Building Web Applications with Windows Azure - MIX Videos

ちょうどいい具合のビデオ。Azureがどういうものかは分かってるっていうことでそこは省略。ビデオの通りのデモを作るってだけの単純なエントリ。だけど、どこで何をしなきゃいけないのかは分かるようになると思われる。

まずは、Visual StudioでWebRoleプロジェクトを作成。これで8割完成。もちろん最新のSDK&Toolsを入れて無いとダメっす。

Download details: Windows Azure SDK Download details: Windows Azure Tools for Microsoft Visual Studio March 2009 CTP

Azure StorageとSDSと2種類のストレージサービスがあるけど、SDSは今回関係無いのであしからず。SDKを入れるとローカルでAzureプロジェクトが動くようにDevelopment Fabric(ローカル実行環境)とDevelopment Storage(ローカルストレージ)がインストールされます。今回はWeb実行環境はローカルなんだけど、ストレージはローカルストレージを使わずにAzure Storageを直接使うという内容なので、Development Storageは停止状態でOKです。

Azure Service Developer Portal

Azureのポータルでまずはストレージを作成。とりあえず初めてなので"FirstWeb"という名前にしてみた。

azurestorage1

後はビデオ通り。と、ここでエントリが終わってもつまんないから、とりあえずは手順を。

csdefにConfigurationSettingsを追加。

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="CloudService2" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole">
  <InputEndpoints>
    <!-- Must use port 80 for http and port 443 for https when running in the cloud -->
    <InputEndpoint name="HttpIn" protocol="http" port="80" />
  </InputEndpoints>
  <ConfigurationSettings>
    <Setting name="AccountName" />
    <Setting name="AccountSharedKey" />
    <Setting name="TableStorageEndpoint" />
  </ConfigurationSettings>
</WebRole>
</ServiceDefinition>
これに合わせるように、cscfgにはvalueをセットしたものを書き込みます。もう、ここまでで9割完成。

次にモデルクラスを作成。エンティティ自体はPartition keyとRow keyさえあれば、あとはどんなスキームでもいいのでサンプル通りのメッセージクラスを作成。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Samples.ServiceHosting.StorageClient;
using System.Data.Services.Client;

namespace CloudService2_WebRole
{
public class ShortMessage : TableStorageEntity
{
  public string Message {get;set;}

  public ShortMessage(){}
  public ShortMessage(string pk, string rk, string message):base(pk,rk)
  {
    Message = message;
  }
}

public class MessageContext : TableStorageDataServiceContext
{
  public DataServiceQuery<ShortMessage> MessageTable
  {
    get
    {
      return CreateQuery<ShortMessage>("MessageTable");
    }
  }
}
}

テーブルの行に相当するエンティティはTableStorageEntityクラスから派生させる。自分でPartition KeyとRow Key書けばいらないけど、この実装が一番簡単。ストレージで管理したいテーブルのコンテキストをTableStorageDataServiceContextから派生。ここまではもう、こうする物だと覚えておこう。自分で全部実装してもいいけど、せっかくStorageClientのサンプルライブラリが提供されてるんだからそれを使う。.NET RIA Servicesに付いてくるサンプルはまた違う実装を使ってるので、なんならそっちを使う(Microsoft.Azure.StorageClient)っていうのも面白いかもね。中身はRESTfulな実装になってるから好みの実装で。

Development Storageを使う場合、"Create Test Storage Tables"を実行すればローカルにテーブルが作成されて簡単でいいんだけど、クラウドのAzure Storageを使う場合、クラウド内にちゃんとテーブル定義を作らないとダメ。ビデオではその部分をPythonのスクリプトを書いて実行してるけど、なんせマイマシンにはPythonの実行環境もないので、そこは他のサンプル同様Global.asaxに書いてしまうことにします。

    protected void Application_Start(object sender, EventArgs e)
  {
    try
    {
      var account = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration();
      var tableStorage = TableStorage.Create(account);
      if (!tableStorage.DoesTableExist("MessageTable"))
        tableStorage.TryCreateTable("MessageTable");

    }
    catch{}
  }

一回作ってしまえば、クラス定義が変わるまで、存続するはずだからわざわざBeginRequestに書かなくてもいいんじゃないの?と常々思ってたので今回はApplication_Startに書いてます。コレでもちゃんと動くし。9割5分完成。

最後にdefault.aspxを書き換えて終わり。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CloudService2_WebRole._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
  <title>ShortMessage</title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
  何か一言<asp:TextBox ID="InputMessage" runat="server"></asp:TextBox>
  <asp:Button ID="SendMessage" runat="server" Text="送信" OnClick="SendMessage_Click" />
  </div>

  <div>
    <asp:Repeater ID="MessageList" runat="server">
      <HeaderTemplate>
      <ul>
      </HeaderTemplate>
      <ItemTemplate>
      <li>メッセージ:<%# Eval("Message") %></li>
      </ItemTemplate>
      <FooterTemplate>
      </ul>
      </FooterTemplate>
    </asp:Repeater>
  </div>
  </form>
</body>
</html>

コードビハインドはこっち。

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Microsoft.ServiceHosting.ServiceRuntime;

namespace CloudService2_WebRole
{
public partial class _Default : System.Web.UI.Page
{
  protected void Page_PreRender(object sender, EventArgs e)
  {
    MessageList.DataSource = (from m in new MessageContext().MessageTable
                              where m.PartitionKey == "FirstWeb"
                              select m);
    MessageList.DataBind();
  }

  protected void SendMessage_Click(object sender, EventArgs e)
  {
    var context = new MessageContext();
    context.AddObject("MessageTable",
      new ShortMessage(
        "FirstWeb",
        Guid.NewGuid().ToString(),
        InputMessage.Text));
    context.SaveChanges();

    InputMessage.Text = "";
  }
}
}

Page_LoadじゃなくてPreRenderにデータバインドを書くのを忘れずに。コレを動かす!

azurestorage2

見にくいけど、ローカルストレージは全部Stopped。WebRoleはローカルのFabricで動いてブラウザ内にはメッセージリストがちゃんと表示されてる。一応、Fiddlerで確認も。

azurestorage3

見にくいけど、2行目が最初の表示のバインドのためのリクエスト。3行目がメッセージのポスト時に実行されるPOST。4行目が、ポストバック後のバインドで取得するリクエスト。

単純なサンプルだけど、Azure Storage単体をAzureにホストしていないプログラムから利用するのが超簡単だというのが分かったので良しとしましょう!