Realm Xamarin を使ってみる

2016/05/10 に [Realm Xamarin](https://realm.io/jp/news/introducing-realm-xamarin/) が公開されました。

Realm はモバイルアプリケーションのためのデータベースで、今まで Java / Objective‑C / Swift 等に対応していましたが、新たに Xamarin に対応したとの事なので Xamarin での基本的な使い方を試してみました。

なお、以降に出てくるコードは Xamarin.Forms の PCL に書いていますが、実行して確認したのは Android のみです（iOS は Mac がないため、UWP は現在未サポートなため確認していません。将来的に UWP のサポートは検討されているようです）。

# インストール
NuGet を使ってインストールします。
"Realm"で検索すれば見つかりますので、そのまま PCL および Android のプロジェクトにインストールすれば完了です。

# モデル作成
まず、データベースに格納するデータを保持するデータモデルクラスを作成します。
データモデルクラスは RealmObject の派生クラスとして作成し、保持するデータをプロパティとして定義します。

今回は、以下のようなデータモデルクラスを作成しました。

```csharp
public class Subscription : RealmObject
{
    [ObjectId]
    public string Id { get; set; }
    public DateTimeOffset Created { get; set; }
    public string Tag { get; set; }
    public string Description { get; set; }
    public string Url { get; set; }
}
```

モデルに含める事ができるのは、プリミティブ型（符号なしの物を除く、bool、 char、byte、short、int、long、float、double）と string、DateTimeOffset です。null 許容型（int? 等）も同様にサポートされています。

なお、Id に付加されている [ObjectId] 属性は、従来のデータベースでの primary key に相当する物です。

# データベースへのアクセス
Realm データベースへアクセスするためには、Realm クラスの GetInstance スタティックメソッドを呼び出してインスタンスを取得します。

アクセスが終わったら Close メソッドまたは Dispose メソッドを呼び出してクローズする必要があります。

Realm クラスは IDisposable インタフェースを実装していますので、一度アクセスしてすぐクローズする場合は、以下のように using ステートメントの内部で処理を行う事で確実にクローズできます。

```csharp
using (var realm = Realm.GetInstance())
{
        :
        :
}
```

# データベース変更
データベースの変更（挿入、更新、削除）は、書き込みトランザクション内で行う必要があります。

書き込みトランザクションは、以下のように BeginWrite メソッドでトランザクションを取得し、Commit メソッドで反映します。
Commit を呼ばないとロールバックされます。

```csharp
using (var transaction = realm.BeginWrite()) 
{
        :
        :
    transaction.Commit();
}
```

Commit まで含めて、以下のように Write メソッドにラムダ式を渡す書き方もできます。

```csharp
realm.Write(() => 
{
        :
        :
});
```

# データベース操作
実際にデータベース操作を行ってみます。

## 書き込み
まず、以下のようなデータを書き込みます。

|  ID  | Created | Tag | Description | URL |
|:----------|:-----------|:------------|:------------|:------------|
|s1|2016/05/15 14:45:00|Xamarin|Xamarin.Forms|https://github.com/xamarin/Xamarin.Forms|
|s2|2016/05/15 14:48:00|Xamarin|xamarin-android|https://github.com/xamarin/xamarin-android|
|s3|2016/05/15 14:51:00|Xamarin|Xamarin.Auth|https://github.com/xamarin/Xamarin.Auth|
|s4|2016/05/15 14:55:00|Realm|realm-java|https://github.com/realm/realm-java|
|s5|2016/05/15 14:56:00|Realm|realm-cocoa|https://github.com/realm/realm-cocoa|
|s6|2016/05/14 20:01:00|　|GitHub|https://github.com/|

書き込みは モデルの型を指定して CreateObject メソッドを呼び、生成したオブジェクトのプロパティを設定するだけです。


```csharp
using (var realm = Realm.GetInstance())
{
    realm.Write(() =>
    {
        var s1 = realm.CreateObject<Subscription>();
        s1.Id = "s1";
        s1.Created = new DateTimeOffset(2016, 5, 15, 14, 45, 0, 0, TimeSpan.Zero);
        s1.Tag = "Xamarin";
        s1.Description = "Xamarin.Forms";
        s1.Url = "https://github.com/xamarin/Xamarin.Forms";

        var s2 = realm.CreateObject<Subscription>();
        s2.Id = "s2";
        s2.Created = new DateTimeOffset(2016, 5, 15, 14, 48, 0, 0, TimeSpan.Zero);
        s2.Tag = "Xamarin";
        s2.Description = "xamarin-android";
        s2.Url = "https://github.com/xamarin/xamarin-android";

        var s3 = realm.CreateObject<Subscription>();
        s3.Id = "s3";
        s3.Created = new DateTimeOffset(2016, 5, 15, 14, 51, 0, 0, TimeSpan.Zero);
        s3.Tag = "Xamarin";
        s3.Description = "Xamarin.Auth";
        s3.Url = "https://github.com/xamarin/Xamarin.Auth";

        var s4 = realm.CreateObject<Subscription>();
        s4.Id = "s4";
        s4.Created = new DateTimeOffset(2016, 5, 15, 14, 55, 0, 0, TimeSpan.Zero);
        s4.Tag = "Realm";
        s4.Description = "realm-java";
        s4.Url = "https://github.com/realm/realm-java";

        var s5 = realm.CreateObject<Subscription>();
        s5.Id = "s5";
        s5.Created = new DateTimeOffset(2016, 5, 15, 14, 56, 0, 0, TimeSpan.Zero);
        s5.Tag = "Realm";
        s5.Description = "realm-cocoa";
        s5.Url = "https://github.com/realm/realm-cocoa";

        var s6 = realm.CreateObject<Subscription>();
        s6.Id = "s6";
        s6.Created = new DateTimeOffset(2016, 5, 14, 20, 01, 0, 0, TimeSpan.Zero);
        s6.Tag = "";
        s6.Description = "GitHub";
        s6.Url = "https://github.com/";
    });
}
```




## クエリ
クエリは通常の LINQ 構文で実行できます。

* 単純な検索

```csharp
using (var realm = Realm.GetInstance())
{
    var subscriptions =
        realm.All<Subscription>().Where(s => s.Tag == "Xamarin");
            :
            :
}
```

* ソート
 
```csharp
using (var realm = Realm.GetInstance())
{
	var subscriptions =
        realm.All<Subscription>().OrderByDescending(s => s.Created);
			:
			:
}
```

なお、null との比較（!= null 等）は、実行すると例外が発生してしまうため使用できません（Realm のソースを見ると明示的に判別して例外を発生させているので仕様のようです）。

## 更新
データの更新は、クエリで取得したオブジェクトのプロパティを書き換えるだけで行えます。

```csharp
using (var realm = Realm.GetInstance())
{
	var s6 = realm.All<Subscription>().Where(s => s.Id == "s6").First();

	realm.Write(() =>
	{
		s6.Created = new DateTimeOffset(2016, 5, 15, 16, 37, 0, 0, TimeSpan.Zero);
	});
}
```

## 削除
レコードの削除はクエリで取得したオブジェクトを Remove メソッドに渡して行います。

```csharp
using (var realm = Realm.GetInstance())
{
	var s2 = realm.All<Subscription>().Where(s => s.Id == "s2").First();

	realm.Write(() =>
	{
		realm.Remove(s2);
	});
}
```

# データベース削除
通常の用途では必要ないと思いますが、作成したデータベース自体を削除することもできます。

```csharp
var config = new RealmConfiguration();
Realm.DeleteRealm(config);
```

# まとめ
Realm Xamarin の基本的な操作を試してみました。
クエリを LINQ 構文で行える等、使いやすいライブラリだと思います。

個人的には早く UWP に対応してくれると嬉しいですね。

なお、今回の作確認に使用したプロジェクトは GitHub にあげてあります。
https://github.com/norimakiXLVI/RealmXamarinSample