.NET Core 2 でLiteDBを試す

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

C#製ドキュメント指向NoSQLデータベースのLiteDBを試してみました。 以下、LiteDBの特徴です

  • サーバ不要
  • 単一ファイルにすべてのデータを格納する
  • マルチプロセス・マルチスレッド環境で安全に動作する
  • 暗号化対応 (AES/DES)

その他にも色々な特徴があるようです。詳細は公式サイトGitHubリポジトリを確認してください。

今回はLiteDBを使ってサンプルアプリケーションを作成してみます。

動作環境

  • macOS Sierra 10.12.6
  • .NET Core SDK 2.1.3
  • C# 7.0
  • LiteDB 4.1.0

プロジェクト作成

まずプロジェクトファイルを作成します

mkdir -p LiteDBExample
cd LiteDBExample
dotnet new

次に LiteDB のパッケージを追加します

dotnet add package LiteDB

ここまででLiteDBを使う準備ができました。 .NET Standardライブラリのみに依存した作りなので導入楽ちんですね。

LiteDBを使う

実際にLiteDBを使ってみましょう。今回は単純なサンプルアプリケーションなので、 Program.cs にコードを追加していきます。

準備

LiteDBでは1つのコレクションで複数のドキュメントを管理します。 今回はエントリを entries コレクションに、 タグを tags に保存するように準備します。

エンティティクラスを作成

ブログのエントリとタグを扱うクラスを用意してみます。

public class BlogTag
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class BlogEntry
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    public List<BlogTag> Tags { get; set; }

    // 確認しやすくするため、ToStringメソッドをオーバーライドする
    public override string ToString()
    {
        var tagNames = string.Join(", ", Tags.Select(x => x.Name));
        return $"BlogEntry(Id={Id}, Title={Title}, Tags=[{tagNames}])";
    }
}

BlogEntry.Tagsの扱いを決める

クラス内に非プリミティブ型のメンバを含む場合は、サブドキュメントとして埋め込む方法と、関連ドキュメントとして登録する方法があります。

  • サブドキュメントの場合
      { "_id": 1, "Tags": [{ "_id": 1, "Name": "C#" }], ... }
      

  • 関連ドキュメントの場合

      { "_id": 1, "Tags": [{ "$id": 1, "$ref": "tags" }], ... }
      
      { "_id": 1, "Name": "C#" }
      

今回は関連ドキュメントとして登録してみます。関連ドキュメントとして登録する場合は、 BsonMapper に関連を登録します。

var mapper = new BsonMapper();
mapper.Entity<BlogEntry>
    .DbRef(x => x.Tags, "tags")  // Tagsフィールド、tagsコレクションを参照するようにする
    ;

他にも BsonRef 属性を指定する方法もあるようです。詳細はLiteDBのWikiを確認してください。

データベースに接続

データベースに接続します。

var filename = "litedb-example.db";
using (var db = new LiteDatabase($"Filename={filename};", mapper))
{
    ...

READMEのサンプル同様の手順で接続したところ、以下のエラーが発生しました。 Unhandled Exception: System.PlatformNotSupportedException: Locking/unlocking file regions is not supported on this platform. Use FileShare on the entire file instead.

同様の問題が issue#787 で報告されていて、connectionStringに Mode=Exclusive の指定を追加すると動作するようになりました。

var filename = "litedb-example.db";
using (var db = new LiteDatabase($"Filename={filename}; Mode=Exclusive;", mapper))
{
    ...

コレクションを取得

エントリとタグを登録するためのコレクションを用意します。

var tags = db.GetCollection<BlogTag>("tags");
var entries = db.GetCollection<BlogEntry>("entries");

コレクションにはインデクスを定義することができます。インデクスを定義すると、検索の高速化やユニーク制約による一意性の保証が実現できるので用途に応じて定義しましょう。今回はタグの名前にユニークインデクスを定義します。

tags.EnsureIndex(x => x.Name, unique: true);

以上でコレクションの準備は完了です。

追加

1件追加する場合は Insert メソッドを、複数件追加する場合は InsertBulk メソッドを使用します。

// タグを一括追加
var entryTags = new List<BlogTag>
{
    new BlogTag { Name = "C#" },
    new BlogTag { Name = ".NET" },
};
tags.InsertBulk(entryTags);

// エントリを追加
var entry = new BlogEntry
{
    Title = ".NET CoreでLiteDBを試す",
    Body = "C#製ドキュメント指向NoSQLデータベースのLiteDBを試してみました。",
    Tags = entryTags,
};
entries.Insert(entry);

正しく反映されているか確認するため、コレクションの登録内容を検索してみます。 BlogEntry.Tags は関連ドキュメントの読み込みが必要なので、 Include メソッドを明示的に呼び出します。

Console.WriteLine("-- begin fetch");
foreach (var blogEntry in entries .Include(x => x.Tags).FindAll())
{
    Console.WriteLine(blogEntry);
}
Console.WriteLine("-- end fetch");

検索結果

-- begin fetch
BlogEntry(Id=1, Title=.NET CoreでLiteDBを試す, Tags=[C#, .NET])
-- end fetch

更新

更新の場合は Update メソッドを使用します。

entry.Title += " (追記あり)";
entries.Update(entry);

検索結果

-- begin fetch
BlogEntry(Id=1, Title=.NET CoreでLiteDBを試す (追記あり), Tags=[C#, .NET])
-- end fetch

削除

削除する場合は Delete メソッドを使用します。削除の場合は BlogEntry オブジェクトではなく、Id の値を指定します。

entries.Delete(entry.Id);

検索結果

-- begin fetch
-- end fetch

おわりに

今回は LiteDBの簡単な使い方を紹介しました。 他のライブラリへの依存がなく、単一ファイルで構成されていて手軽に試せるのが非常に魅力的でした。

今後.NETアプリを作るときに組み込んで、実際の使用感を確認してみたいと思います!