前回はBLOBについて書いてみた。今回はTABLEについて書いてみる。
前回同様書籍をベースにいろいろと挑戦してる。書籍を元に記載しているので、若干説明足らずである。その辺は、ご了承いただきたい。
TABLE
Azure Storage は次の3つがあるようである
- Table
- Blob
- Queue
今回は「table」について挑戦してみた。
TABLEとは?
表形式の一種で以下の3つで構成されている
- プロパティ
データベースでいうところのフィールドに相当 - エンティティ
プロパティの集合で、データベースでいうところのレコードに相当 - テーブル
エンティティの集合で、データベースでいうところのテーブルに相当
それぞれのクラス
- プロパティとエンティティ
エンティティは TableStorageEntity 継承でつくり、その中にプロパティが含まれる - コンテキスト
TableStorageDataServiceContextを継承して作り、主に「ストレージサービスとの接続(?)の管理」と「エンティティ集合の返却」を責務としてもつ。 - データソース
(絶対必要ではなさそうだが)コンテキストの保持や検索クエリの隠蔽を行い、Table利用者はこのクラスを用いてやり取りをする。
実装
- TableStorageEntity
public class CommentItem : TableServiceEntity
{
public string Message { get; set; }
public string Name { get; set; }
public string GuestName { get; set; }
public CommentItem()
{
RowKey = string.Format("{0:10}_{1}",
DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks,
Guid.NewGuid());
}
public CommentItem( string name )
{
PartitionKey = name;
RowKey = string.Format("{0:10}_{1}",
DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks,
Guid.NewGuid());
}
}
- TableStorageDataServiceContext
public class CommentItemContext : TableServiceContext
{
public CommentItemContext(string baseAddress, StorageCredentials credentials)
: base(baseAddress, credentials) { }
public IQueryable<CommentItem> CommentItems
{
get { return this.CreateQuery<CommentItem>("CommentItems"); }
}
}
- データソース
public class Comments
{
private CommentItemContext context;
static Comments()
{
//TableClient作成
var setting = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
var cloudTableClient = setting.CreateCloudTableClient();
cloudTableClient.CreateTableIfNotExist("CommentItems");
}
public Comments()
{
var setting = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
this.context = new CommentItemContext(
setting.TableEndpoint.AbsoluteUri,
setting.Credentials);
this.context.RetryPolicy = RetryPolicies.Retry(3,TimeSpan.FromSeconds(1));
}
public IEnumerable<CommentItem> Select(string name)
{
var query = from e in this.context.CommentItems
where e.PartitionKey == name
select e;
return query;
}
public void Add(CommentItem item)
{
this.context.AddObject("CommentItems", item);
this.context.SaveChanges();
}
}
はまった点
テーブルの作成
テーブルの作成として以下のような例があった。
CloudTableClient.CreateTablesFromModel(
typeof(CommentItem),
setting.TableEndpoint.AbsoluteUri,
setting.Credentials);
実際、ハンズオンのソース等も参考にすると上記でよさそうであるが、なぜかこの方法だと、Selectなどで失敗するケースが出てきたので以下のコードを使った
//TableClient作成
var setting = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
var cloudTableClient = setting.CreateCloudTableClient();
cloudTableClient.CreateTableIfNotExist("CommentItems");
引数なしのコンストラク(TableServiceEntity)
public CommentItem()
{
RowKey = string.Format("{0:10}_{1}",
DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks,
Guid.NewGuid());
}
public CommentItem( string name )
{
PartitionKey = name;
RowKey = string.Format("{0:10}_{1}",
DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks,
Guid.NewGuid());
}
最初、引数ありのコンストラクタしか用意していなかった。しかし、フレームワーク内で引数なしのコンストラクタも呼び出すようで、引数なしコンストラクタがない場合エラーが発生していた。
最初エラーの原因が分からなかったが、よく見ると引数なしコンストラクタが必要という事で、こちらを追加して対処した。
フィールドに日本語が追加できない
以下のコードでテーブルにエンティティを追加できるが、エンティティの中に日本語が含まれていると SaveChanges();で例外が発生した。
public void Add(CommentItem item)
{
this.context.AddObject("CommentItems", item);
this.context.SaveChanges();
}
仕方がないので、利用者サイドでUTF-8+BASE64で保存するようにした。
利用者サイド
登録部分
protected void SendButton_Click(object sender, EventArgs e)
{
var name = Request.QueryString["name"];
var commentItem = new CommentItem(name);
commentItem.Name = name;
commentItem.Message = Converter.ConvertToAsciiOnlyString( CommentTextBox.Text );
commentItem.GuestName = Converter.ConvertToAsciiOnlyString(AutherTextBox.Text);
var comments = new Comments();
comments.Add(commentItem);
Refresh();
}
登録部分では、CommentItem(TableServiceEntity)のインスタンスを生成し、そのインスタンスにプロパティを設定した。登録の為にデータソースであるCommentsのAddを用いて保存した。
なお、この時CommentItemに日本語を保存するときには、UTF-8+Base64で変換して保存している。
取得部分
また、取得部分で以下のようにした。
private void Refresh()
{
var name = Request.QueryString["name"];
var comments = new Comments();
CommentList.DataSource = comments.Select(name).ToList().Select(item=>{
item.GuestName = Converter.ConvertFromoAsciiOnlyString(item.GuestName);
item.Message = Converter.ConvertFromoAsciiOnlyString(item.Message);
return item;
});
CommentList.DataBind();
}
設計ポイント
TableServiceEntityのPartitionKeyとRowKeyが重要そうである。 DBMSの主キーであったりインデックスに相当する役割を持っているみたいである。
- 検索キー
検索キーとしてPartitionKeyを使う。この分散がしっかりできているとスケールアウトする - オーダーキー
検索した結果はRowKey順で取得できる。そのため、order by 相当がこのRowKeyに相当する
と思ってもよい。
この設計ポイントは、いろいろ考察が必要そうである。
さいごに
これで、何とかテーブルに各画像のコメントが追加できるようになった。
次回は、キューについて記載ができたらいいな...


コメントする