[C#]Windows AzureでAzure Storageでテーブルを扱う話

Qiitaに記事を投稿したことはないけれど、最近はこういうのがトレンドなんだろってことで自分が最近やってみたことを書く。
はっきり言って上級者の人がいたらおそらくつまらない話だから聞き流したほうがよさそう。
#1,Azure Storageとは?#
Azure Storageについては以下のリンクに書いてある。
http://azure.microsoft.com/ja-jp/pricing/details/storage/
要するにKey-Value方式っぽいNoSQLなストレージサービス。
値段も、1GBあたり9円/月と相当な数のデータを格納しても安く済む。(とはいえ自分はこれ以外のサービスをほとんど知らないので高いという人もいるかもしれない)

Azure StorageはAccountNameとPrimaryKeyだけあれば簡単にAzure Storage本体にリクエストを投げられるので開発は非常に楽だ。AzureのSQLデータベースのほうは、IPアドレスによるファイアウォールがあるので、電車の中でテザリングしながら開発したりする自分には面倒な部分がある。(もちろんセキュリティ的な面で言えばIPアドレスによるファイアウォールは非常に有用なのかもしれないが。)


#2,つないでみる#
何かと便利なnugetを使えば簡単にストレージにアクセスする用のライブラリをインストールできる。今回使うAzure Storage用のライブラリは以下だ。
https://www.nuget.org/packages/WindowsAzure.Storage/

Azure Storageに接続するためのクラスを以下のように作る。

```csharp:TableConnection.cs
public class TableStorageConnection
{
   public CloudStorageAccount StorageAccount{get;private set;}

   public CloudTableClient TableClient{get;private set;}

   public TableStorageConnection()
   {
      StorageAccount=new CloudStorageAccount(new StorageCredentials("Azure Storageのアカウント名","Azure Storageのプライマリアクセスキー"),useHttps:false);//実際に運用するときにはuseHttpsはtrueにしたほうがいいのだろうか。
      TableClient=StorageAccount.CreateCloudTableClient();
   }
}
```

ASP.Netと一緒に使うのであればOwinContextから取得してもいいし、Singletonパターンで実装しておくのもありだろう。
とりあえず、このCloudStorageAccountとCloudTableClientの2つのインスタンスが作りたかったのだ。

#3,Entityを作ろう#
データを保存するのに利用するEntityを作成する。例えば、ユーザーのプロファイル等であれば以下のように作成するだろう。もちろん、インターフェースを作成しなくても問題なく動作する。

```csharp:IUserProfile.cs
public interface IUserProfile
{
   public string UserName{get;}

   public string NickName{get;}
}
```


```csharp:UserProfileEntity.cs
public class UserProfileEntity:IUserProfile,TableEntity
{
   public UserProfileEntity():base()
   {
   }

   public UserProfileEntity(Guid userId):base(userId.ToString(),Guid.NewGuid().ToString())
   {
   }
 
   public string UserName{get;set;}

   public string NickName{get;set;}
}
```

こんな感じだ。このクラスのベースクラスとなっているTableEntity型のコンストラクタは二つ存在している。引数をとらないコンストラクタと、string型の引数を2つ取るコンストラクタだ。
引数をとらないほうは別途PartitionKeyとRowKeyの2つを代入してやらねばならない。
UserName等をPartitionKeyに指定しておけば後で検索するのは非常に楽だ。

#4,便利にテーブルを扱う#
単にテーブルに接続する方法は容易だ。ググればすぐに出てくるだろう。.netの開発を長くやっている開発者であれば容易に思いつく方法ではあろうが、Attributeを使ってテーブルを簡単に扱えるような構造を考えてみる。
自分は仮にデータの保存先がAzureStorageから違う何かに代わってもいいようにデータの保存/取得などの処理を抽象化している。テーブル一つ当たりにそのテーブルへの抽象化された操作をするクラスを作る場合以下のような実装が便利だ。(最も、もっとレベルの高い人はよりいい実装を考え付くのかもしれない)

```csharp:AzureStorageTableAttribute.cs
[AttributeUsage(AttributeTargets.Class)]
public class AzureStorageTableAttribute:Attribute
{
   public AzureStorageTableAttribute(string tableName)
   {
      TableName=tableName;
   }
   public string TableName{get;private set;}
}
```

```csharp:AzureStorageManagerBase.cs
public abstract class AzureStroageManagerBase
{
   protected readonly TableStorageConnection _connection;

   protected CloudTable _table;

   protected AzureStorageManagerBase(TableSTorageConnection connection)
   {
      _connection=connection;
      var storageAttribute = GetType().GetCustomAttribute<AzureStorageTableAttribute>();
      if(storageAttribute!=null)
      {

         _table=connection.TableClient.GetTableReference(storageAttribute.TableName);
         _table.CreateIfNotExists();
      }else
            throw new InvalidArgumentException("このクラスにはテーブル名を指定するAttributeを設置する必要があります");
   }
}
```

こうしておけば、例えば以下のようなインターフェースを実装するデータを永続化したり取得したりするクラスがほしいとき

```csharp:IUserProfileManager.cs
public interface IUserProfileManager
{
   public void InsertUserProfile(Guid userId,string userName.string nickName);

   public IUserProfile ObtainUserProfile(Guid userId);
}
```

以下のように実装すればいいことは容易にわかるだろう。

```csharp:AzureStorageUserProfileManer.cs
[AzureStorageTable("UserProfiles")]
public class AzureStorageUserProfileManager:IUserProfileManager,AzureStorageManagerBase
{
   public AzureStorageUserProfileManager(TableStorageConnection connection):base(connection)
   {
   }

   public void InsertUserProfile(Guid userId,string userName.string nickName)
   {
      _table.Execute(TableOperation.Insert(new UserProfileEntity(userId){UserName=userName,NickName=nickName}));
   }

   public IUserProfile ObtainUserProfile(Guid userId)
   {
      var idStr=userId.ToString();
      return (from user in _table.CreateQuery<UserProfileEntity>() where user.PartitionKey == idStr select user).FirstOrDefault();
   }
}
```

と、まあこんな風にすれば以下のようにして容易にデータを永続化/取得することができる。

```csharp:Main.cs
//何かの中での処理
{
   IUserProfileManager manager=new AzureStorageUserProfileManager(new TableStorageConnection());
   manager.ObtainUserProfile(/*ユーザーのGuid*/);
}

```

とまあこんな感じだ。最初は初心者用記事を書こうと思ったのだがちょっとした僕の思い付き実装集になってしまった。とはいえ参考にしたい方は是非参考にしてほしい。