65.9K
CodeProject 正在变化。 阅读更多。
Home

LiteDB - 一个文件式.NET NoSQL 文档数据库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (118投票s)

2015年1月25日

CPOL

7分钟阅读

viewsIcon

556469

downloadIcon

92

简单、快速、免费的嵌入式.NET NoSQL 单文件文档数据库

引言

本文档是对我的数据库项目 LiteDB 的概述——一个小型、快速、免费的嵌入式 .NET NoSQL 单文件文档数据库——现在是新版本 **4.0**。

特点

  • 无服务器 NoSQL 文档数据库
  • API 极其简洁,非常类似于 **MongoDB 官方驱动**
  • 100% C# 代码,支持 .NET 3.5/4.0,单个 DLL(小于 300KB)- 无外部依赖
  • 支持跨平台:**完整 .NET 和 NET Standard 1.3+2.0(适用于 UWP 10、Xamarin iOS 和 Android)**
  • 线程安全 / 多进程
  • 写入失败时可恢复(日志模式)
  • 支持 POCO 类或 BsonDocument
  • 使用 DES 加密数据文件
  • FileStorage 用于存储文件和流数据(类似于 MongoDB 中的 GridFS
  • 单文件存储(类似于 SQLite)
  • 为文档字段建立索引以实现快速搜索(每个集合最多 16 个索引)
  • 支持 LINQ 查询,带有 Include 支持
  • 命令行 Shell(尝试在线版本
  • 开源免费,适用于所有人,包括商业用途
  • 通过 NuGet 安装:Install-Package LiteDB

有什么新内容

  • 新增 JSON Path 支持
  • 索引定义支持表达式。您可以索引文档中的任何字段
  • 使用 AttributesFluent API 映射实体类
  • LiteRepository 类,提供简单易用的访问方式
  • DbRef 用于跨文档集合引用

LiteDB 的主要灵感来自于 MongoDB。我试图创建一个功能类似于 MongoDB 的数据库,但规模非常小,只包含小型应用程序最重要的一些功能。如果您熟悉 MongoDB,那么您就已经了解 LiteDB 了。

如何使用

存储和搜索文档的快速示例

// Open data file (or create if not exits)
using(var db = new LiteDatabase(@"C:\Temp\MyData.db"))
{
    // Get customer collection
    var col = db.GetCollection<Customer>("customers");
    
    var customer = new Customer { Id = 1, Name = "John Doe" };

    // Insert new customer document
    col.Insert(customer);
    
    // Update a document inside a collection
    customer.Name = "Joana Doe";
    
    col.Update(customer);
    
    // Index document using a document property
    col.EnsureIndex(x => x.Name);

    // Simple Linq support
    var result = col.Find(x => x.Name.StartsWith("Jo"));
}

适用场景?

  • 小型 Web 应用程序、网站博客或论坛
  • 每个**账户/用户**使用一个 datafile 进行数据存储
  • 桌面/本地小型应用程序
  • 应用程序文件格式
  • 少量并发写入用户操作

快速入门

让我们来看一些关于 LiteDB 数据库和数据结构的示例。如果您需要更多文档,可以阅读 Github Wiki 文档

Documents

LiteDB 通过文档来存储和检索数据文件中的数据。您的文档定义可以是 POCO 类或 BsonDocument 类。无论哪种情况,LiteDB 都会将您的文档转换为 BSON 格式存储在磁盘上。

BSON 是 Binary JSON,一种将数据对象序列化为二进制数组的格式。BSON 拥有比 JSON 更多的数据类型,例如 DateTimeGuidObjectId

使用 POCO 类存储文档

POCO 类是简单的 C# 类,只包含 get/set 属性。这是创建强类型文档的最佳方式。您的类必须有一个标识符属性。您可以使用名为 Id 的属性、<ClassName>Id 或使用 [BsonId] 属性装饰任何属性。

// A poco entity, must have Id
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Phone> Phones { get; set; }
}

// It's not a entity, don't need Id
public class Phone
{
    public int Code { get; set; }
    public string Number { get; set; }
    public PhoneType Type { get; set; }
}

public enum PhoneType { Mobile, Landline } 
  • 在内部,文档 ID 表示为 _id 属性
  • 不要使用复杂的数据类型(例如 DataSetDataTable
  • 不要使用可处置对象(例如 StreamGraphics
  • Enum 在序列化时将被转换为 string
  • 您的 Id 值必须是唯一的且不能为 null
  • 如果您将 Id 留空,LiteDB 将在 insert 时自动生成。

映射约定

BsonMapper.ToDocument() 会自动将每个类属性转换为文档字段,遵循以下约定:

  • 类必须是 public,并有一个 public 的无参构造函数
  • 属性必须是 public
  • 属性可以是只读或读写的
  • 类必须有一个 Id 属性、<ClassName>Id 属性或任何带有 [BsonId] 属性的属性来存储 Id 信息
  • 属性可以用 [BsonIgnore] 装饰,以使其不被映射到文档
  • 属性可以用 [BsonField] 装饰,以获得自定义字段名称
  • 不允许循环引用
  • 最多允许 20 层嵌套类
  • 类的字段不会被转换为文档
  • BsonMapper 使用一个全局实例来缓存映射信息,以提高性能。该实例也可以通过 LiteDatabase.Mapper 属性访问。

用于文档映射的 Fluent API

如果您想更改默认的约定映射,可以使用 EntityBuilder 将您的实体类映射到 BsonDocument

var mapper = BsonMapper.Global;

mapper.Entity<Customer>()
    .Id(x => x.CustomerKey)
    .Field(x => x.Name, "customer_name")
    .Ignore(x => x.Age)
    .Index(x => x.Name, unique);

DbRef 用于跨文档集合引用

LiteDB 可以使用跨文档引用,仅通过 _id 值映射内部实体(而不是嵌入整个子文档)。

// Your entity order class        
public class Order
{
    public int OrderId { get; set; }
    public Customer Customer { get; set; }
}
​​​
// Map Customer property to "customers" collections 
BsonMapper.Global.Entity<Order>()
    .DbRef(x => x.Customer, "customers"); 

// When insert an order, only customer _id will be included as reference
    
// Get orders collection
var orders = db.GetCollection<Order>("orders");

// Find an order including Customer reference
var order = orders
    .Include(x => x.Customer)
    .FindById(123);

LiteDBOrder 文档序列化为 `{ _id: 123, Customer: { $id:1, $ref: "customers" } }`。当您需要查询 Order 并包含 Customer 实例时,只需在运行 Find 方法之前添加即可。

使用 BsonDocument 存储文档

BsonDocument 是一个特殊的类,它使用内部的 Dictionary<string, object> 来映射任何文档。这对于读取未知文档类型或将其用作通用文档非常有用。

// Create a BsonDocument for Customer with phones
var doc = new BsonDocument();
doc["_id"] = ObjectId.NewObjectId();
doc["Name"] = "John Doe";
doc["Phones"] = new BsonArray();
doc["Phones"].Add(new BsonObject());
doc["Phones"][0]["Code"] = 55;
doc["Phones"][0]["Number"] = "(51) 8000-1234";
doc["Phones"][0]["Type"] = "Mobile";

使用 BsonDocument,您可以创建任何复杂的文档架构。

集合(Collections)- 存储

LiteDB 将文档组织在存储中(在 LiteDB 中称为集合)。每个集合都有一个唯一的名称,并包含具有相同架构/类型的文档。您可以通过 LiteDatabase 实例的 GetCollection 方法获取强类型集合或通用的 BsonDocument 集合。

var db = new LiteDatabase(stringConnection);

// Get a strong typed collection
var customers = db.GetCollection<Customer>("Customers");

// Get a BsonDocument collection 
var customers = db.GetCollection("Customers");

Collection 包含所有用于操作文档的方法

  • Insert - 插入新文档
  • FindByIdFindOneFind - 使用 Query 对象或 LINQ 表达式查找文档。此时,只支持简单的 LINQ - 左侧是属性,右侧是值。
  • Update - 更新文档
  • Delete - 删除指定 ID 的文档或使用查询删除文档
  • Include - 使用 include 填充基于其他集合的属性
  • EnsureIndex - 如果索引不存在则创建。所有查询都必须有索引。

查询

LiteDB 中,查询使用索引来查找文档。您可以使用 Query 辅助器或 Linq 表达式。

var customers = db.GetCollection<Customer>("customers");

// Create a new index (if not exists)
customers.EnsureIndex("Name");

// Query documents using 'Name' index
var results = customers.Find(Query.StartsWith("Name", "John"));

// Or using Linq
var results = customers.Find(x => x.Name.StartsWith("John"));

// Return document by ID (PK index)
var customer = customers.FindById(1);

// Count documents using Query helper
var count = customers.Count(Query.GTE("Age", 22));

// All query results returns an IEnumerable<T>, so you can use Linq in results
var results = customers
    .Find(x => x.Name.StartsWith("John") && x.Salary > 500) // two indexed queries
    .Where(x => x.LastName.Length > 5) // in memory query
    .Select(x => new { x.Name, x.Salary })
    .OrderBy(x => x.Name);

Query 类支持 **All**、**Equals**、**Not**、**GreaterThan**、**LessThan**、**Between**、**In**、**StartsWith**、**Contains**、**AND** 和 **OR**。

故障容错 - 日志记录/恢复

LiteDB 使用磁盘上的日志文件来保证写操作的持久性。这意味着在执行写操作之前,所有脏页都会先写入同一个文件,然后再写入主数据文件(使用文件末尾来存储干净的页面)。

此功能可以在连接字符串中禁用 - 运行速度更快,但如果在写操作时发生崩溃,您的数据文件可能会不一致。

处理文件 - FileStorage

同时,我们也需要在数据库中存储文件。为此,LiteDB 有一个特殊的 FileStorage 集合来存储文件,没有文档大小限制(单个文件最大限制为 2GB)。它类似于 MongoDB 的 GridFS

// Storing a file stream inside database
db.FileStorage.Upload("my_key.png", stream);

// Get file reference using file id
var file = db.FileStorage.FindById("my_key.png");

// Find all files using StartsWith
var files = db.Files.Find("my_");

// Get file stream
var stream = file.OpenRead();

// Write file stream in a external stream
db.FileStorage.Download("my_key.png", stream);

LiteDB 创建两个集合来处理文件:_files_chunks_files 集合包含文件信息(文件 ID、文件名、上传日期和元数据)。文件数据内容被分割存储在 _chunks 集合中。

LiteDB.Shell

LiteDB 包含一个 Shell 命令行应用程序。该 Shell 可用于在数据文件中运行命令,而无需其他应用程序。以下是一些最有用的命令。要查看所有命令,请使用 "help" 命令。

Shell commands
==============

> open <filename|connectionString> : Open a new database
> pretty on|off : Turns on/off pretty json format
> timer : Show timer before prompt
> dump > <dumpfile.dmp> : Export database as insert commands
> dump < <dumpfile.dmp> : Import database

Collections commands
====================

> db.<collection>.insert <jsonDoc> : Insert a new document into collection
> db.<collection>.update <jsonDoc> : Update a document inside collection
> db.<collection>.delete <filter> : Delete documents using a filter clausule (see find)
> db.<collection>.find [top N] <filter> : Show filtered documents based on index search
> db.<collection>.count <filter> : Show count rows according query filter
> db.<collection>.ensureIndex <field> [unique] : Create a new index document field
> db.<collection>.drop : Drop collection and destroy all documents inside
<filter> = <field> [=|>|>=|<|<=|!=|like|contains|between] <jsonValue> : 
            Filter query syntax
<filter> = (<filter> [and|or] <filter> [and|or] ...) : Multi queries syntax
<jsonDoc> = {_id: ... , key: value, key1: value1 } : 
             Represent an extended json for a BsonDocument.

File storage commands
=====================

> fs.find : List all files on datafile
> fs.find <fileId> : List file info from a key. Supports * for starts with key
> fs.upload <fileId> <filename> : Insert a new file inside database
> fs.download <fileId> <filename> : Save a file to disk passing a file key and filename
> fs.update <fileId> {key:value} : Update metadata file
> fs.delete <fileId> : Remove a file inside database

历史

  • 更新 v4.0.0 - 2017 年 10 月 18 日
    • 仅支持 .NET4
    • 新增表达式支持
    • 修复了 v3 中的并发问题
    • 通过 NETStandard 1.3 + 2.0 支持跨平台
  • 更新 v2.0.0 - 2016 年 8 月 1 日
    • 支持跨平台 - UWP、Xamarin - iOS 和 Android
    • .NET 3.5
    • 新的磁盘访问方式,避免使用锁定函数:仅使用读/写独占模式
    • 恢复了一些 v1 的功能:用户事务控制、注册 autoId 和全局映射器实例
  • 更新 v.2.0.0-rc - 2015 年 12 月 24 日
    • 加密数据文件
    • 数据文件从 v0.9.0 / v1.0.x 迁移
    • 将数据文件转储为 insert 语句
    • 改进了缓存/并发支持
    • 圣诞快乐 :)
  • 更新 v.2.0.0-beta - 2015 年 11 月 27 日
    • 抽象持久化层
    • Fluent 映射 API
    • 新的 DbRef
    • 虚拟索引字段
    • 新的缓存清理系统
    • 支持初始数据库大小和最大数据库大小
    • 延迟加载
    • ThreadSafeProcessSafe
    • 收缩数据文件
    • 数据库日志信息
  • 更新 v1.0.2 - 2015 年 5 月 17 日
    • 改进了 BsonMapper 对接口和基类的序列化/反序列化,通过文档中的 _type 字段(类似于 MongoDB 中的用法)
    • BsonMapper 支持 NameValueCollection
    • 添加了对布尔 Linq 操作的支持,例如 x => x.IsActive
    • 修复了 string 索引 update/delete 操作中的 bug
    • 移除了全文档扫描查找操作 - 现在仅通过索引查找
    • 自动创建不存在的索引
  • 更新 v1.0 - 2015 年 3 月 28 日
    • 新的 BsonSerializer,移除了 fastBinaryJson 并实现了真正的 BSON 规范
    • 新的 BsonMapper,用于更灵活地配置 POCO 与 BsonDocument 之间的转换
    • 新的 JsonReader 实现:未使用正则表达式,速度提升 4 倍
    • 新的 ObjectId,用于 Id 文档
    • 索引创建选项 - 去除空格、去除重音符、忽略大小写
    • [BsonIndex] 属性,用于标记实体属性,以便在查询时自动创建索引
    • 为实体 Id 属性自动生成 Id
    • Find() 可以在没有索引的情况下执行(将执行全文档扫描)
    • FindAll() 支持升序/降序结果
    • 新的 Query.Contains
    • 获取索引的 Min()/Max()
    • DbRef<> - 一个简单的引用文档的类
    • 改进了 Drop collection 和 drop index
    • 移除了 _master 集合 - 避免 1 页读取
  • 更新 v0.9 - 2015 年 2 月 7 日
    • 添加 MongoDB 性能测试
    • 添加 Shell 快速上手指南
  • 初始版本 - 2015 年 1 月 25 日
© . All rights reserved.