做C#开发的朋友,很多情况下都需要在本地保存一些结构化数据。用XML或JSON文件吧,数据量一大会导致读写性能很差,还没有查询能力;用SQL Server吧,光安装部署就够折腾的,做一个小工具完全没必要。这个时候,嵌入式数据库就是最合适的方案。LiteDB正是这样一个为.NET量身定制的单文件NoSQL数据库,它的设计思路借鉴了MongoDB,API风格非常相似,如果你接触过MongoDB,上手几乎零成本。本文将从安装、基本操作、进阶用法几个层面,完整介绍LiteDB在C#中的使用方法。

LiteDB是什么,为什么值得选择
LiteDB是一个开源的、纯C#实现的嵌入式NoSQL文档数据库,整个核心库编译后只有一个几百KB的DLL,通过NuGet直接引入项目即可,没有任何外部依赖。它的数据以BSON格式存储在单个.db文件中,这个文件可以随意拷贝、备份,不需要像传统数据库那样经历复杂的备份恢复流程。
和SQLite对比,两者都是嵌入式方案,但LiteDB是文档型数据库,数据以类JSON文档的方式组织,天生适合存储结构灵活的对象。比如你有一个用户类,里面嵌套了一个地址列表,用LiteDB直接整个对象存进去就行,不需要像关系型数据库那样拆成多张表再做关联映射。这种“存什么就是什么”的体验,在快速开发小型应用时非常爽快。
LiteDB的主要特性包括:支持类LINQ的查询语法、自动主键管理(默认识别Id属性)、支持索引加速查询、内置GridFS文件存储、支持事务操作,并且兼容.NET Framework 4.5以上和.NET Core、.NET 5/6/7/8全系列。需要注意的是,LiteDB适合单机单进程访问的场景,多进程并发写入并不是它的强项,这一点后面会详细说明。
安装与第一个增删改查示例
安装非常简单,在Visual Studio的NuGet包管理器中搜索LiteDB安装即可,或者使用命令行:
Install-Package LiteDB
安装完成后,先定义一个实体类。LiteDB约定属性名为Id的会自动作为主键(BSON文档中的_id),如果是int类型会自增,Guid类型会自动生成。接下来看一个完整的基础操作示例:
using LiteDB;
using System;
using System.Linq;
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string[] Phones { get; set; }
public bool IsActive { get; set; }
}
class Program
{
static void Main()
{
// 打开或创建数据库文件,using保证连接正确释放
using (var db = new LiteDatabase(@"C:\data\mydata.db"))
{
// 获取集合,相当于关系型数据库中的表
var col = db.GetCollection<Customer>("customers");
// 插入数据
var customer = new Customer
{
Name = "张三",
Age = 28,
Phones = new[] { "13800000000", "010-8888888" },
IsActive = true
};
col.Insert(customer);
Console.WriteLine("插入成功,自增Id为: " + customer.Id);
// 查询:LINQ风格
var result = col.Find(x => x.Age > 25 && x.IsActive).ToList();
Console.WriteLine("查询到 {0} 条记录", result.Count);
// 更新
customer.Age = 29;
col.Update(customer);
// 删除
col.Delete(customer.Id);
}
}
}这段代码涵盖了最核心的四个操作。几个细节值得注意:LiteDatabase实现了IDisposable接口,务必用using包裹,否则可能出现数据没有完整刷盘的问题。连接字符串除了文件路径,还可以带参数,比如"Filename=mydata.db;Password=123456"可以启用加密,Connection=shared允许多个线程共享同一个连接实例。
关于连接的生命周期,官方推荐在应用程序整个运行期间只维护一个LiteDatabase实例,把它做成单例,而不是每次操作都开关一次。频繁创建连接虽然不会像网络数据库那样开销巨大,但重复打开文件和初始化元数据也是不必要的浪费。
索引、查询与BsonDocument灵活操作
数据量小的时候,全表扫描没什么感觉,但一旦集合里有了几万条文档,没有索引的查询会明显变慢。LiteDB提供了和MongoDB类似的索引创建API:
var col = db.GetCollection<Customer>("customers");
// 创建普通索引,返回索引名
col.EnsureIndex(x => x.Name);
// 创建唯一索引,重复值插入会抛异常
col.EnsureIndex(x => x.Name, unique: true);
// 复合索引在LiteDB 5.x中通过字符串表达式创建
col.EnsureIndex("idx_age_active", "$.Age:INT, $.IsActive:BOOL");EnsureIndex这个方法名很讲究,它的语义是“确保索引存在”,如果索引已经建好了就不再重复创建,所以可以放心地在程序初始化时调用。建好索引后,查询会自动命中,不需要在查询语句里显式指定。
除了强类型的GetCollection<T>,LiteDB还支持无schema的BsonDocument方式,适合处理结构不确定的数据,比如读取外部导入的JSON:
var col = db.GetCollection("logs");
var doc = new BsonDocument();
doc["level"] = "INFO";
doc["message"] = "用户登录成功";
doc["time"] = DateTime.Now;
doc["extra"] = new BsonDocument { ["ip"] = "192.168.0.1" };
col.Insert(doc);
// 使用SQL风格语句查询
var results = col.Query()
.Where("level = 'INFO' AND time >= @from")
.OrderByDescending("time")
.Limit(100)
.Bind("from", DateTime.Today)
.ToDocuments();
foreach (var r in results)
{
Console.WriteLine(r["message"].AsString);
}这种弱类型模式和强类型模式可以混用,同一个集合既可以用BsonDocument插入,也可以用实体类读取,LiteDB会自动做字段映射。映射规则可以通过BsonMapper全局定制,比如把属性名映射成别的字段名、忽略某些属性、指定枚举的存储方式等。
文件存储GridFS与事务处理
LiteDB内置了GridFS风格的文件存储,叫LiteStorage,可以把图片、附件等大文件分块存进数据库文件里,避免文件系统和数据库记录不一致的麻烦:
// 上传文件
var storage = db.GetStorage("attachments");
var fileId = storage.Upload("report-001", @"C:\files\report.pdf");
// 下载文件
storage.Download(fileId, @"C:\files\report_copy.pdf");
// 查询文件信息
var file = storage.FindById(fileId);
Console.WriteLine("文件大小: " + file.Length);
// 删除文件
storage.Delete(fileId);上传时第一个参数是自定义的文件ID字符串,第二个可以是路径或流。文件会被切成固定大小的块存储,读取时自动拼接,对上层完全透明。做桌面软件时,把用户头像、导出的报表统一塞进数据库文件里,备份时只需要拷贝一个文件,管理起来相当省心。
事务方面,LiteDB的写操作默认就有事务保证,单条插入或更新不会出现写一半的情况。批量操作时可以手动控制事务:
db.BeginTrans();
try
{
foreach (var item in bigList)
{
col.Insert(item);
}
db.Commit();
}
catch
{
// 出错回滚,已插入的数据全部撤销
db.Rollback();
}手动事务的好处是原子性可控,要么全部成功要么全部回滚,这在做资金流水、库存扣减这类要求严格一致性的业务时非常关键。
适用场景与注意事项
LiteDB最适合的场景包括:桌面工具软件的本地数据存储、单机版小型管理系统的数据层、移动端Xamarin或MAUI应用的本地缓存、插件系统的配置持久化、游戏存档等。这些场景的共同点是单进程访问、数据量在几十万文档以内、对部署便捷性要求高。
使用时有几点务必留意。第一,LiteDB官方建议一个数据库文件同时只被一个进程打开,多进程并发写同一个文件可能触发文件锁冲突甚至数据损坏,如果确实需要多进程访问,应该通过一个服务进程统一读写。第二,并发读写要开启共享连接模式,并在多线程环境下使用Connection=shared参数,集合对象本身不是线程安全的,跨线程使用时要做好同步。第三,定期调用db.Rebuild()可以压缩数据库文件、重建索引,长期增删数据后文件会存在空洞,重建能回收空间。
最后给一个选型建议:如果你的应用是单机运行、数据结构偏对象化、追求零部署成本,LiteDB几乎是最优解;如果将来有向多用户网络服务演进的可能,那么一开始就考虑SQLite或者直接上MongoDB会更稳妥。技术选型没有绝对的好坏,关键是匹配业务的生命周期。