画面から設定された情報やアプリ内部で管理しているデータなどをファイルに保存しておきたいとき、あなたならどうしますか?
SQLiteなどのローカルデータベースに保存させるにはスキーマ作成が大変だし、かといってJSONやXMLなどで持つのも管理が面倒・・・
そんな時に利用したいのが LiteDBです。 SQLiteのようにCreate文でテーブル作成する必要もなく、JSONやXMLのような処理の煩雑さもなく、軽量で超手軽なローカルデータベースです。
今回は、このLiteDBについてのインストール方法と使い方について紹介します。NoSQLを体験してみたいかたも、是非参考にしてください。
LiteDBの概要
初めに、LiteDBに関する情報を整理しておきます。
LiteDBとは
LiteDBは、.NETフレームワーク用のオープンソースのNoSQLデータベースです。サーバーレスのデータベースであり、ローカルストレージにデータを保存するために使用されます
C#で完全に書かれており、単一のDLLファイルとして提供されています。
.NET 4.5以上、または.NET Standard 2.0以上で動作し、Windows、Mac、Linux、Xamarinなど、すべての.NETプラットフォームで利用可能です
1 2 3 4 5 6 7 8 9 10 11 12 |
// LiteDBの基本的な使用例 using(var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); var customer = new Customer { Name = "John Doe", Phones = new string[] { "8000-0000", "9000-0000" }, IsActive = true }; col.Insert(customer); } |
LiteDBの特徴とメリット
LiteDBの主な特徴は以下の通りです
- NoSQL: スキーマの定義が不要で、動的なデータ構造をサポート
- 100% C#で構築: .NET開発者にとって親和性が高く、簡単に統合が可能
- クロスプラットフォーム: すべての.NETプラットフォームで動作
- 単一ファイルデータベース: 複数のファイルではなく、一つのデータファイルで管理
- 軽量: DLLファイルサイズは500KB程度と非常に小さい
- サーバー不要: ローカル環境で完結(DBサーバーが不要)
- Unity対応: ゲーム開発環境であるUnityでも使用可能
- スレッドセーフ: マルチスレッド環境でも安全に動作
- 暗号化対応: データベースファイルの暗号化が可能
SQLiteと LiteDBの違い
ローカルにデータを保存する手段としてSQLiteがありますので、両者の違いを表にまとめました。LiteDBの方がサイズが小さくスキーマが不要なことから、LiteDBはアプリ内で管理している小規模のデータを管理したい場合に有効です。
特徴 | SQLite | LiteDB |
---|---|---|
言語 | C言語 | C# |
サイズ | 約3MB | 500KB以下 |
クロスプラットフォーム | 対応 | 対応 |
データベースタイプ | リレーショナルDB | NoSQL |
スキーマ | 固定スキーマ | スキーマレス |
パフォーマンス | 大量のレコードで高速 | 少ないレコード数で高速 |
使用ケース | 数100万レコード単位のデータ処理 | ローカルデータの保存 |
暗号化 | 対応 | 対応 |
スレッドセーフ | 対応 | 対応 |
LiteDBで最初に理解しておくこと
LiteDBはNoSQL データベースであるため、SQLiteと考え方が全く異なります。ここで説明する内容を最初に理解しておけば、後で紹介するサンプルプログラムが理解しやすくなるので、初めてLiteDBを使う方は目を通しておいてください。
LiteDBはデータをコレクションとして管理する
コレクションとは、配列やリスト、辞書(Dictionary)など複数の要素が集まったデータ群の一般的な呼称です。
SQLiteでは、このようなデータ群をテーブル形式で管理しますが、LiteDBは BSON と呼ばれるJSONから派生したフォーマットで管理します。
コレクションはデータ登録時に任意の名前を付けることが可能で、この名前を使ってコレクションへのデータの追加や削除を行います。
BSONはJSONの拡張版であり、バイナリ形式でデータをエンコードすることにより効率性や拡張性を向上させたフォーマットです。BSONは主にMongoDBなどのデータベースで使用され、JSONと同様にデータのシリアライズやストレージに適しています。
クラスをそのまま保存することが可能
LiteDBはクラス(実際はクラスのインスタンス)を丸ごと(メンバごと)保存することが可能です。ただし、そのクラスに実装されているメソッドは無視(保存対象外)され、複雑な構造を持つクラス(DataTableのようなもの)はそのままでは保存できません。また、UIコントロールも保存の対象外となります。
1つのコレクションには同じ構造を持つクラスをいくつも登録できる
1つのコレクションには、同じ構造を持つクラスをいくつも登録可能です。この時、1つづつ登録することも、まとめて複数個を登録することも可能です。
1つのコレクションに異なったクラスの混在が可能
1つのコレクションに対して、メンバ名やデータ型が異なるクラスを、いくつでも格納することが可能です。
とはいうものの、検索やクエリなどの操作が複雑になるため推奨されていません。あくまでも1つのコレクションには1つのクラスを保存するようにしましょう。
一意制約とID プロパティの扱い
1つのテーブルに複数のレコードを登録した場合、個々のレコードを識別する必要があります。この時、IDという名前でプロパティを作成しておくことで、データの2重登録が防げます(一意制約エラーが発生する)。
IDというプロパティが見つかると、LiteDBの内部で _id というカラム名が作成され、これと紐づけされます。
IDの型は任意ですが、ObjectID型にしておくことで自動採番と同じ扱いにできます(ObjectIDはインスタンスごとの固有IDであるため)。
ObjectID 以外を使う場合は、一意になるような値を自分で振る必要があります。
もし複数プロパティの組み合わせで一意になる場合は、その組み合わせをタプルにしてIDとして使うなどの方法を使います。
IDは必須ではないため省略しても構いませんが、この場合は重複チェックは行われません(同じ値が複数レコード登録される)。
全く別のプロパティをIDとして使いたい場合は、そのプロパティの上に [BsonID]を記述します。
下記は、 Email をIDとして使うように指定した例です。
1 2 3 4 5 6 7 8 |
public class Customer { public string Name { get; set; } public int Age { get; set; } [BsonId] public string Email { get; set; } public bool IsActive { get; set; } } |
テーブルの中を確認すると、Email が空白になり、_id にメールアドレスが格納されていることが分かります。
データの特定はラムダ式が使える
LiteDBのデータはBSONで保存されており、一般的なデータベースでいういカラムが存在しないため、SQLでいうWhereのように条件式を使ったデータの特定ができません。
LiteDBでは、BSONフォーマットのデータに含まれるタグ名を使ってデータを特定します。このため、データの読込みや削除で用意されているメソッドには、ラムダ式を指定することができます。
LiteDBのインストール
Nugetから次のキーワードで検索すると LiteDB が表示されるので、これをインストールします。
LiteDB
LiteDBの使い方
LiteDBを使う場合は、プログラムの先頭に下記を1行記述しておきます。
1 |
using LiteDB; |
データの読込/更新/削除は次の手順で行います。
データベースファイルの作成
引数にデータベース名を指定し、LiteDatabase() メソッドを呼ぶだけで 空のデータベースファイルが作成されます。
1 |
new LiteDatabase(@"MyData.db"); |
データの読込(Select)
データの読込ではFindメソッドを使います。引数で指定したラムダ式が true になる要素をすべて取得します。
Find(データを特定するためのラムダ式)
1 2 3 4 5 6 7 8 9 |
using(var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); var customers = col.Find(x => x.Age <= 30); foreach (var customer in customers) { Console.WriteLine($"Name: {customer.Name}, Age: {customer.Age}"); } } |
1件だけを取得したい場合は FindOne メソッドを使います。ラムダ式で複数レコードがヒットした場合は、最初に見つかった要素が返されます。
FindOne(データを特定するためのラムダ式)
1 2 3 4 5 6 |
using(var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); var customer = col.FindOne(x => x.Name == "John Doe"); Console.WriteLine($"Name: {customer.Name}, Age: {customer.Age}"); } |
全件をまとめて読み込むには、FindAll() を使います。
FindAll()
1 2 3 4 5 6 7 8 9 10 |
using (var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); // コレクション内のすべてのドキュメントを取得する var allCustomers = col.FindAll(); foreach (var customer in allCustomers) { Console.WriteLine($"Name: {customer.Name}, Age: {customer.Age}"); } } |
クラスにIDを定義している場合、そのIDでデータを取得することが可能です。
FindById(取得したい要素のID)
データの登録(Insert)
データの登録には Insert メソッドを使います。引数には保存したいクラスを指定します。ListやArrayなどの配列(IEnumerable型)を渡すことで、一括でのInsertも可能です。
Insert(保存したいクラス)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using(var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); var customer = new Customer { Name = "John Doe", Age = 30 }; //1件だけ挿入 col.Insert(customer); var all_customers = new List<Customer> { new Customer { Name = "John Doe", Age=24, Email = "john@example.com", IsActive = true }, new Customer { Name = "Jane Smith",Age=31, Email = "jane@example.com", IsActive = true }, new Customer { Name = "Mike Turner", Age = 47, Email = "mike@example.com", IsActive = false } }; //リストで挿入 col.Insert(all_customers); } |
大量のデータを高速に挿入するために InsertBlukも用意されています。第一引数には配列データ(IEnumerable型)を、第二引数にはバッチサイズ(省略可能で初期値は5000)が指定可能です。
InsertBulk(配列データ,[バッチサイズ])
データの更新(UpdateとUpsert)
データの更新は Updateメソッドを使います。あらかじめ FindOne でデータを読み込み、それに対して Updateで更新を行います。
Update(更新したいクラス)
1 2 3 4 5 6 7 |
using(var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); var customer = col.FindOne(x => x.Name == "John Doe"); customer.Age = 31; col.Update(customer); } |
コレクションに存在しなければ挿入(Insert)存在すれば更新(Update)してくれる Upsertメソッドも用意されています。
Upsert(更新したいクラス)
1 2 3 4 5 6 7 |
using(var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); var customer = col.FindOne(x => x.Name == "John Doe"); customer.Age = 31; col.Upsert(customer); } |
データの削除(Delete)
データの削除は Delete メソッドを使います。前述したIDプロパティをクラス内に定義している場合、その値を使って削除も可能です。
Delete(削除したい要素のID)
1 2 3 4 5 6 |
using (var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customer"); var items = col.FindAll().ToList(); col.Delete(items[0].Id); } |
引数に削除したいデータを特定するためのラムダ式を記述することでも削除が可能です。
DeleteMny(削除対象のデータを特定するラムダ式)
1 2 3 4 5 |
using(var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<Customer>("customers"); col.Delete(x => x.Name == "John Doe"); } |
コレクション内のすべての要素を削除する場合は DeleteAll メソッドを使います。
DeleteAll I()
DataTableを保存する場合
DataTable を LiteDBに直接保存する方法は無いので、LiteDBがサポートしている形式に変換する必要があります。
そこでJsonに変換してからLiteDBに書き込むようにしました。
Jsonに変換する方法で最も簡単なのが Newtonsoft.json を使う方法なので今回はこれを使いました。このソースを利用する場合は、あらかじめNugetでインストールしておいてください。
1 |
using Newtonsoft.Json; |
下記はDataTableを指定したコレクション名で保存するサンプルソースです。これを何度も実行すると同じデータがいくつも保存されるので、実際に使う場合は存在チェックをするか、一旦 FindOne()メソッドで読み込んで上書きするか、一旦削除してInsertするかの対策を講じて下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private void StoreData(string name,DataTable dt) { // DataTableをXMLに変換 string jsonText = JsonConvert.SerializeObject(dt); using (var db = new LiteDatabase(@"MyData.db")) { // コレクションを取得または作成 var col = db.GetCollection<BsonDocument>(name); // XMLデータをBsonDocumentに変換して保存 var doc = new BsonDocument { { "xmlContent", new BsonValue(jsonText) } }; col.Insert(doc); } } |
以下は指定したコレクション名からDataTableを取得するサンプルソースです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
private DataTable RetrieveData(string name) { DataTable dt = new DataTable(); using (var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<BsonDocument>(name); var doc = col.FindAll().FirstOrDefault(); if (doc != null && doc.ContainsKey("xmlContent")) { string jsonText = doc["xmlContent"].AsString; dt = JsonConvert.DeserializeObject<DataTable>(jsonText); } } return dt; } |
Array、List、Dictionary などを保存する場合
Array、List、Dictionary などの配列データもそのままLiteDBに登録できません。ただしクラスのプロパティとして実装すれば登録が可能になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using LiteDB; using System.Collections.Generic; // 配列データを格納するクラスを作成 public class MyData { public int Id { get; set; } public int[] IntArray { get; set; } public List<string> StringList { get; set; } public Dictionary<string, int> StringIntDictionary { get; set; } } // 格納したい配列データを作成 var data = new MyData { IntArray = new int[] { 1, 2, 3 }, StringList = new List<string> { "a", "b", "c" }, StringIntDictionary = new Dictionary<string, int> { { "key1", 1 }, { "key2", 2 } } }; // 配列データの保存 using (var db = new LiteDatabase(@"MyData.db")) { var col = db.GetCollection<MyData>("myDataCollection"); col.Insert(data); } |
データベースの中身を参照できるLiteDB.Studio
LiteDB で作成したデータベースファイルは、LiteDB.Studio というツールで中身を確認できます。下記のURLから
LiteDB.Studio.exe をダウンロードしてください。
ダウンロードが完了したら実行します。単体のEXEファイルなのでインストール不要です。
実行すると下記の画面が表示されます。「Connect」をクリックし、表示されたダイアログにLiteDBで作成したデータベースファイルをドラッグ&ドロップします。
次の画面が表示されるので、参照したいテーブルを選んで、Runボタンをクリックすると、データが表示されます。
まとめ
今回は SQLiteより軽く手軽なローカルデータベース「LiteDB」について、概要、最初に理解しておくべきこと、インストール方法、使い方について解説しました。
ローカルデータベースとしてはSQLiteが有名ですが、LiteDBはNoSQLであるため考え方や使い方が全くことなります。
SQLiteと同じだと考えていると理解しずらくなりますので、気持ちを切り替えて取り組みましょう。
アプリ内の変数を単純に保存する場合は圧倒的にLiteDBの方が楽なので、これを機に皆さんも使ってみてはいかがでしょう。
コメント