XamarinでRealmを使用する方法


はじめに

Xamarin で Realm を使用する方法を調べていたのですが、ところどころに地雷がありますね。

さて、Realm (レルム) とは何かというと一言でいえばモバイルデータベースですね。

軽量データベースといえば、SQLite が有名ですが、モバイルアプリケーションから SQLite を使用するのは結構大変なようで、その問題を解決するために Realm が作成されたという背景があります。

その Realm ですが、主にモバイル環境の Android や iOS 端末で利用されます。サーバーサイドもあるようですが、ここではモバイルデータベースとして扱っていきます(実際、その方が普通と思います)。

Xamarin では PCL で Realm データベースにアクセスするロジックを記述し、Xamarin.iOS、Xamarin.Android から PCL を呼び出すことでロジックの共有が可能になります。

なお、開発環境は Visual Studio for Mac になります。

サンプルソースコード

この記事で取り上げているサンプルのソースコードは、GitHub に上げておいたので必要時に参考にしてください。

https://github.com/fnya/XamarinRealm

PCL で Realm を操作し、Xamarin.iOS から PCL を呼び出すようにしています。

Realm のインストール

Realm の Visual Studio へのインストールは、NuGet から可能です。

NuGet で「 realm」と検索すると、Realm パッケージが表示されるのでそれをインストールします。

注意事項があり、PCL のプロジェクトで Realm をインストールしたら、PCL を使用するプロジェクト(Xamarin.iOS、Xamarin.Android 等)でも Realm のインストールが必要になります。

Xamarin で Realm を使用する

それでは、Xamarin で Realm を使用する方法をご紹介します。

以下のサンプルコードは、PCL で作成されています。

名前空間の宣言

PCL のサンプルコードでは、以下の名前空間を宣言しています。なにげに重要です。

using System;
using Realms;
using System.Linq;
using System.Diagnostics;

Realmで使用するクラスの宣言

Realm で使用するクラスは RealmObject クラスを継承する必要があります。

using System;
using Realms;

namespace RealmSample
{
    public class Person : RealmObject
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }
}

Realmインスタンスの取得

Realm を使用するには、Realm のインスタンスを取得する必要があります。

コメントにも書いてありますが、データベースのコネクションはインスタンスのスコープが外れた際に自動的に閉じられるので開発者がなにかをする必要はありません。

public void ExcecuteSample01()
{
    //スコープ外になった際にデータベースは自動でクローズされる。。
    var realm = Realm.GetInstance();
}

Realm にデータを保存する

Realm にデータを保存するには、Realm のインスタンスにデータを追加します。

なお、データ更新時は自動でトランザクションが開始、終了されます。

public void ExcecuteSample02()
{
    var realm = Realm.GetInstance();
   
    //Realmデータベースのパスを出力
    Debug.WriteLine(realm.Config.DatabasePath);

    realm.Write(() =>
    {
        realm.Add(new Person { Name = "山田太郎", Age = 23 });
    });
    
}

サンプルコードで Realm データベースのパスを出力していますが、こうしないと Realm データベースのパスが深すぎて探し出すことができないためです。

パスが分かったら、 Mac であれば Realm Browser (無料) などのツールで登録したデータを確認することができます。

Realm を検索する

では、登録したデータを検索してみましょう。

Realm では LINQ を使用できるので、.NET に慣れている開発者は違和感なく使えると思います

public void ExcecuteSample03()
{
    var realm = Realm.GetInstance();

    //Realmデータベースのパスを出力
    Debug.WriteLine(realm.Config.DatabasePath);

    realm.Write(() =>
    {
        realm.Add(new Person { Name = "山田太郎", Age = 23 });
        realm.Add(new Person { Name = "佐藤花子", Age = 18 });
        realm.Add(new Person { Name = "田中哲朗", Age = 33 });
    });

    var people = realm.All<Person>().Where(p => p.Age > 20);

    foreach (var person in people)
    {
        Debug.WriteLine("Name=" + person.Name);
    }
}

検索したデータを更新する

検索したデータの更新(UPDATE)してみましょう。

サンプルコードでは、検索結果の上位1件を取得してデータを更新しています。

public void ExcecuteSample04()
{
    var realm = Realm.GetInstance();

    //Realmデータベースのパスを出力
    Debug.WriteLine(realm.Config.DatabasePath);

    realm.Write(() =>
    {
        realm.Add(new Person { Name = "山田太郎", Age = 23 });
        realm.Add(new Person { Name = "佐藤花子", Age = 18 });
        realm.Add(new Person { Name = "田中哲朗", Age = 33 });
    });

    //1件目の山田さんのみ抽出
    var person = realm.All<Person>().Where(p => p.Name.Contains("山田")).FirstOrDefault<Person>();

    //プロパティにセットするだけでデータ更新される
    realm.Write(() =>
    {
        person.Age = 30;
    });
}

検索したデータを削除する

検索したデータを削除(DELETE)してみましょう。

サンプルコードでは、検索結果の上位1件を取得してデータを削除しています。

public void ExcecuteSample05()
{
    var realm = Realm.GetInstance();

    //Realmデータベースのパスを出力
    Debug.WriteLine(realm.Config.DatabasePath);

    realm.Write(() =>
    {
        realm.Add(new Person { Name = "山田太郎", Age = 23 });
        realm.Add(new Person { Name = "佐藤花子", Age = 18 });
        realm.Add(new Person { Name = "田中哲朗", Age = 33 });
    });

    //1件目の山田さんのみ抽出
    var person = realm.All<Person>().Where(p => p.Name.Contains("山田")).FirstOrDefault<Person>();
    
    realm.Write(() =>
    {
        realm.Remove(person);
    });

トランザクション制御

Realm ではトランザクション制御を正しく行うことができます。

なぜかトランザクション制御の情報がネット上になくて、最初できないのかと思いましたが、実際にコードを書いてみるとできることが分かりました。

public void ExcecuteSample06()
{
    var realm = Realm.GetInstance();

    //Realmデータベースのパスを出力
    Debug.WriteLine(realm.Config.DatabasePath);

    using (var trans = realm.BeginWrite())
    {
        try
        {
            realm.Add(new Person { Name = "山田太郎", Age = 23 });
            realm.Add(new Person { Name = "佐藤花子", Age = 18 });
            realm.Add(new Person { Name = "田中哲朗", Age = 33 });
            //throw new Exception("Fail!");
            trans.Commit();

        } catch (Exception ex){
            Debug.WriteLine("データ更新に失敗しました。" + ex.Message);
            trans.Rollback();
        }

    }
}

主キー設定

Realm でも主キーを使用することができます。但し、他のデータベースのように AutoIncremanet を実装していないので、主キーを一意にするためにサンプルコードでは UUID (GUID) を使用しています。どうも Realm ではこの方法が一般的なようですね。

主キーがあれば主キーを元にデータを更新することも可能です。但し、記述方法が一般的ではないのでサンプルコードには含めませんでした。

主キーはクラスで設定します。

using System;
using Realms;

namespace RealmSample
{
    public class Dog : RealmObject
    {
        [PrimaryKey]
        public string Id { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

    }
}

そして、登録時に UUID (GUID) を主キーに設定しています。

public void ExcecuteSample07()
{
    var realm = Realm.GetInstance();

    //Realmデータベースのパスを出力
    Debug.WriteLine(realm.Config.DatabasePath);

    realm.Write(() =>
    {
        //RealmにはAutoIncrementがないので UUID (GUID) を主キーとして利用。
        realm.Add(new Dog { Id = Guid.NewGuid().ToString(),Name = "ポチ", Age = 5 });
        realm.Add(new Dog { Id = Guid.NewGuid().ToString(),Name = "クロ", Age = 2 });
        realm.Add(new Dog { Id = Guid.NewGuid().ToString(),Name = "サチ", Age = 3 });
    });

}

インデックス設定

Realm でもインデックスを使用できます。

インデックスはクラスで設定します。

using System;
using Realms;

namespace RealmSample
{
    public class Cat : RealmObject
    {
        [PrimaryKey]
        public string Id { get; set; }

        [Indexed]
        public string Name { get; set; }

        public int Age { get; set; }
    }
}

あとは通常通り使用します。

public void ExcecuteSample08()
{
    var realm = Realm.GetInstance();

    //Realmデータベースのパスを出力
    Debug.WriteLine(realm.Config.DatabasePath);

    realm.Write(() =>
    {
        //RealmにはAutoIncrementがないので UUID を主キーとして利用。
        realm.Add(new Cat { Id = Guid.NewGuid().ToString(), Name = "タマ", Age = 5 });
        realm.Add(new Cat { Id = Guid.NewGuid().ToString(), Name = "ボチ", Age = 2 });
        realm.Add(new Cat { Id = Guid.NewGuid().ToString(), Name = "マル", Age = 3 });
    });
}

おわりに

Xamarin から Realm を使用する基本的な方法は、これで説明できたと思います。

暗号化などの高度なトピックスは改めて調査します。

しかし、罠が多いですね。

参考サイト