iBoxDBをUnityで使う

# 初めに

SQLiteはよくあるし、
RealmはいまだにUnity対応してないっぽい。SEO?的な人がUnityなのに。
sequodb？というのが昔あったが見つからない。
なのでこのマイナーのを使ってみる。
検証してないのでSQLiteより良いとは言ってない。

iOSで動かないかもしれない。Androidは不明。
https://github.com/iboxdb/aspnet-cross-platform-db/issues/1

# iBoxDBとは

Fast ACID Table Style Document NoSQL Application Database
高速ACIDテーブルスタイルドキュメントNoSQLアプリケーションデータベース

[Wikipediより]

CIDとは、信頼性のあるトランザクションシステムの持つべき性質として1970年代後半にジム・グレイが定義した概念で、これ以上分解してはならないという意味の原子性（英: atomicity、不可分性）、一貫性（英: consistency）、独立性（英: isolation）、および永続性（英: durability）は、トランザクション処理の信頼性を保証するために求められる性質であるとする考え方である。

# ドキュメントが・・・

公式ドキュメントわかりにくかった。
日本語はおろか英語での記事もなかなか見つからない。
もしかしてこのQiitaの記事は日本語の全人類宇宙初のiBoxDB記事かもしれない。

中国語だけど動きがわかりやすいサンプルコードがあった。

http://www.cnblogs.com/VectorZhang/p/5448622.html

# 大雑把に理解

* こんな感じで保存の場所を決める。

```cs:
var dbPath = Path.Combine(Directory.GetCurrentDirectory(), "ibox");
```

* テーブル名、主キー的なのを決める

```cs:
config.EnsureTable<Account>("AccountTable", "Name");
```

Nameが後程UpdateやDeleteで一意に決定する主キー的なものだと思う。
`param object[]`になっていた。

# 使いやすくマネージャクラス作る

`DBManager.cs`という名前で作った。
名前が気に入らなければ、名前を変えても良いが、コンストラクタの名前の変更を忘れずに。
`Directory.GetCurrentDirectory()`はStreamingAssetとかが良いかも？

テーブル名は自分で指定するのは面倒と思うので型名で自動で決めるようにした。
しかし、単純に型を`ToString()`すると`+`が入ってしまい、エラーとなってしまったので、`Replace("+", "")`で逃げました。

```cs:DBManager.cs
/// <summary>
/// DB Manager.
/// </summary>
using System;
using System.IO;
using System.Collections.Generic;
using iBoxDB.LocalServer;

public class DBManager<T> where T : class, new()
{
    private DB.AutoBox box = null;
    private string tableName = "AccountTable";

    public DBManager(params string[] names)
    {
        var dbPath = Path.Combine(Application.persistentDataPath, "ibox");
        if (!Directory.Exists(dbPath))
        {
            Directory.CreateDirectory(dbPath);
        }

        this.server = new DB(dbPath);
        var config = this.server.GetConfig();

        this.tableName = typeof(T).ToString().Replace("+", "");
        config.EnsureTable<T>(this.tableName, names);

        if (this.server.IsClosed())
        {
            this.box = this.server.Open();
        }
        else
        {
            Debug.LogError("すでにOpenされています.");
        }
    }

    /// <summary>
    /// Close this instance.
    /// </summary>
    public void Close()
    {
        this.server.Close();
    }

    public bool Insert(T content)
    {
        bool res = this.box.Insert(this.tableName, content);
        return res;
    }

    public T SelectByKey(params object[] key)
    {
        var account = this.box.SelectKey<T>(this.tableName, key);
        return account;
    }

    public List<T> SelectByQuery(string query, params object[] arguments)
    {
        string stringFormat = string.Format("from {0} {1}", this.tableName, query);
        var account = this.box.Select<T>(stringFormat, arguments);
        return account;
    }

    public bool Update(Func<T, T> function, params object[] key)
    {
        var account = this.SelectByKey(key);
        bool res = this.box.Update(this.tableName, account);
        return res;
    }

    public bool DeleteByKey(params object[] key)
    {
        bool res = this.box.Delete(this.tableName, key);
        return res;
    }
}
```

# 使ってみる

さっきのマネージャクラスを利用してみましょう。

使いたいテーブルを決める

```cs:
public class Account
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    public string Desc { get; set; }
}
```

* 初期化

ジェネリック型引数に汎用的な型で使うようにした。
`Name`は主キー的なものを指定。
これがすでにあればInsertをした時に`false`が返ってくる。

```cs:
DBManager<Account> dbManager = new DBManager<Account>("Name");
```

* Close

一度に2回`Open`できません。このマネージャクラスでいう`new`で別のインスタンス作る前に、いったん`Close()`しましょう。

```cs:
dbManager.Close();
```

* 取り出し

`null`チェックしないとエラーになるので入れてね。

```cs:
var ac = dbManager.SelectByKey("002");
if (ac != null)
{
    Debug.LogFormat("{0} {1} {2} {3}", ac.Name, ac.Age, ac.Desc, ac.Email);
}
```

* 挿入

Nameは主キー的なものにしている。
これがすでにあればInsertをした時に`false`が返ってくる。

```cs:
var ac = new Account() { Name = "001", Age = 14, Desc = "this is test", Email = "fake@qq.com" };
dbManager.Insert(ac);
var ac2 = new Account() { Name = "002", Age = 88, Desc = "this is test 33", Email = "fake2@qq.com" };
dbManager.Insert(ac2);
```

* 削除

キーを引数

```cs:
dbManager.DeleteByKey("002");
```

`DB.AutoBox`の`Delete("テーブル名")`メソッドに２番目の引数を無しにしたら、以下のエラーが返ってくるので、全件削除はどうするのか？

```
       InvalidCastException: Cannot cast from source type to destination type.
        iBoxDB.ByteCodes.C219.m415 (System.Object p1, System.Object p2)
        iBoxDB.ByteCodes.C75+C76.Compare (System.Object x, System.Object y)
        iBoxDB.ByteCodes.C75.m196 (System.Object p1, System.Object p2)
        iBoxDB.ByteCodes.C81.Compare (iBoxDB.ByteCodes.C135 x, iBoxDB.ByteCodes.C135 y)
        iBoxDB.ByteCodes.C141+C143[iBoxDB.ByteCodes.C135].m264 (iBoxDB.ByteCodes.C135 p1, System.Comparison`1 p2)
        iBoxDB.ByteCodes.C149[iBoxDB.ByteCodes.C135].m285 (iBoxDB.ByteCodes.C144 p1, iBoxDB.ByteCodes.C141 p2, System.Comparison`1 p3, iBoxDB.ByteCodes.C135 p4)
        iBoxDB.ByteCodes.C149[iBoxDB.ByteCodes.C135].m284 (System.Comparison`1 p1, iBoxDB.ByteCodes.C135 p2)
        iBoxDB.ByteCodes.C140[iBoxDB.ByteCodes.C135].m253 (System.Comparison`1 p1, iBoxDB.ByteCodes.C135 p2, iBoxDB.ByteCodes.C135& p3)
        iBoxDB.ByteCodes.C197.m389[Object] (System.Object p1)
        iBoxDB.ByteCodes.C27.m115[Object] (System.String p1, System.Object p2)
```

* 更新

第1引数で、ラムダ式かメソッドを渡して、変更したい処理を渡す。
`null`チェック入れないともともとDB内に格納されてなかった時にエラーになるので注意

```cs:
dbManager.Update((account) =>
{
    if (account != null)
    {
        account.Age = 15;
        account.Desc = "fake";
        account.Email = "email";
    }
    return account;
}, "002");
```

* 最後に

これでだいたい使い方わかると思うけど、全件削除のやり方わかったら載せます。
あと、全選択の例も。
何かわかったら教えてね。