谈谈 .NET8 平台中对 LiteDB 的 CRUD 操作
哪个啥!纯 C# 编写的 LiteDB 你还不会操作?
- LiteDB 简介
- LiteDB 安装
- 同步版 LiteDB
- 异步版 LiteDB.Async
- LiteDB Studio
- LiteDB CRUD 操作举例
- LiteDB vs SQLite 对比
- 1、谈谈 sqlite 和 litedb 的 ACID 事务支持?
- 2、谈谈 sqlite 和 litedb 的稳定性和可靠性?
- 3、谈谈 sqlite 和 litedb 的应用场景?
- LiteDB vs SQLite 性能测试
LiteDB 简介
LiteDB - 一个单数据文件 .NET NoSQL 文档存储。
LiteDB
是一个小巧、轻量级、快速、简单易用的 NoSQL
嵌入式数据库,它支持文档存储和查询,具有高性能、可嵌入性、跨平台等特点。
LiteDB
是一个 .NET
平台下的开源项目,它可以在 Windows、Linux、MacOS、Android、iOS
等多个平台上运行。LiteDB
的 API
简单易用,支持LINQ
查询,同时还提供了一个可视化的管理工具 LiteDB Studio
,方便用户进行数据库的管理和操作。
以下是 LiteDB
的一些特点和用法介绍:
- 支持
Serverless NoSQL
文档存储和查询,类似于MongoDB
的简单API
。 100% C#
代码,支持.NET 4.5/.NETStandard 1.3/2.0
,单DLL
(小于450 kb
)。- 支持
线程和进程安全
,多线程访问,提高数据的并发性能
。 - 支持文档/操作级别的
ACID/事务
和索引
,保证数据的一致性和查询效率。 - 支持写失败后的
数据还原 (日志模式)
。 - 支持
多种数据类型
,包括字符串、整型、浮点型、日期时间、二进制数据等。 - 支持
嵌套文档和数组
,方便存储复杂数据结构。 - 支持
LINQ
查询,方便进行数据筛选和排序。 - 支持
数据加密和压缩
,保证数据的安全性和存储效率。 - 可使用
DES (AES)
加密算法进行数据文件加密。 - 可使用特性或
fluent
映射API
将你的POCO
类映射为BsonDocument
。 - 可存储文件与流数据 (类似
MongoDB
的GridFS
)。 - 单数据文件存储 (类似
SQLite
)。 - 支持基于文档字段索引的快速搜索 (每个集合支持多达
16
个索引)。 Shell
命令行,在线版本尝试。开源
,对所有人免费
(包括商业应用)。
相关文档:
LiteDB
项目地址,https://github.com/mbdavid/LiteDBLiteDB
在线文档,https://dev.listera.top/docs/litedb/
LiteDB 安装
LiteDB
的安装和使用非常简单,只需要在项目中添加 LiteDB
的引用,就可以使用 LiteDB
的 API
进行数据库的操作。
nuget
地址:
- https://www.nuget.org/packages/LiteDB
- https://www.nuget.org/packages/LiteDB.Async
同步版 LiteDB
异步版 LiteDB.Async
此处我们选择 LiteDB.Async
异步版本安装。
1、命令方式安装:
.NET CLI
dotnet add package LiteDB.Async --version 0.1.7
Package Manager
NuGet\Install-Package LiteDB.Async --version 0.1.7
2、项目 Package
包依赖引用:
dotnet add package LiteDB.Async
- 或者添加
Package
包引用
<PackageReference Include="LiteDB.Async" Version="0.1.7" />
3、还可以从 NuGet
安装:
Install-Package LiteDB
LiteDB Studio
同时,LiteDB
还提供了一个可视化的管理工具 LiteDB Studio
,方便用户进行数据库的管理和操作。
- 原文:A GUI tool for viewing and editing documents for LiteDB v5.
- 译文:用于查看和编辑
LiteDB v5
文档的GUI
工具。
这里我们选择 Shared
模式连接:
输入 sql
语句执行查询操作,显示如下:
Grid
显示
Text
显示
执行代码初始化生产的 .db
文件非常的轻量:
LiteDB Studio
项目地址:
- https://github.com/mbdavid/LiteDB.Studio
LiteDB CRUD 操作举例
这里我们模拟 WeatherForecast(天气预报)
的数据为例,使用 .net cli
创建项目如下:
dotnet new webapi -o WebAppLiteDb --no-https -f net8.0
提示:执行命令前,记得安装
.net8 sdk
。
- 查看
.net
信息
dotnet --info
创建好模板 “ASP.NET Core Web API”
项目后,使用 Microsoft Visual Studio Community 2022 (64 位) IDE
打开项目,添加如下 nuget
包依赖:
项目 WebAppLiteDb
依赖的 nuget
包文件如下:
LiteDB.Async v0.1.7
(单纯LiteDB
操作只需要该包即可)Serilog.AspNetCore v8.0.0
Serilog.Sinks.LiteDB v1.0.29
Swashbuckle.AspNetCore v6.5.0
项目 WebAppLiteDb
的 Project
文件如下:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LiteDB.Async" Version="0.1.7" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.LiteDB" Version="1.0.29" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
<Folder Include="AppData\DataBase\" />
</ItemGroup>
</Project>
查看项目 WebAppLiteDb
结构如下:
文件 Program.cs
改造如下:
using Serilog;
const string OUTPUT_TEMPLATE = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}";
// creates custom collection `applog`
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: OUTPUT_TEMPLATE)
.WriteTo.LiteDB(@"WebAppLiteDb\AppData\DataBase\logs.db", logCollectionName: "applog")
.CreateLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Host.ConfigureAppConfiguration((hostingContext, config) => {
char b = Path.DirectorySeparatorChar; // 符号
string rootPath = hostingContext.HostingEnvironment.ContentRootPath;
string configPath = $"{rootPath}{b}AppData{b}Configuration";
config.SetBasePath(configPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
}).UseSerilog(Log.Logger, dispose: true);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
调整模型类 WeatherForecast.cs
文件到 Models
文件夹:
namespace WebAppLiteDb.Models;
public class WeatherForecast
{
public Guid Id { get; set; }
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
修改 appsettings.json
和 appsettings.Development.json
配置文件:
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Serilog 日志级别由低到高 (等级越低输出的日志信息越详细):
- Verbose -> Debug -> Information -> Warning -> Error -> Fatal。
ILogger
对象用于记录日志,和其他日志框架差不多。
接下来我们改造控制器类文件,WeatherForecastController.cs
信息修改如下:
- 构造函数
DI
注入日志记录器。
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
- 新建一个字符串集合,模拟天气情况摘要。
/// <summary>
/// 模拟天气情况摘要
/// </summary>
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
连接 LiteDB
的操作,此处使用的 nuget
包是 LiteDB.Async
:
//打开数据库,如果不存在会自动创建。
var db = new LiteDatabaseAsync(_connectionString);
//打开一个表和 MongoDB 一样的
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
关于 litedb-async
该库允许将异步编程技术与
LiteDb
库一起使用。它适用于需要轻量级NoSQL
数据库或将受益于轻量级NoSQL
数据库的Xamarin
和WPF
应用程序,但也不想在数据库操作发生时打开和管理大量线程或阻止 UI。
我们为您管理线程。
在nuget
上可用,https://www.nuget.org/packages/LiteDB.Async/
- 添加关于
LiteDB
的CRUD
操作,代码如下:
#region About LiteDB CRUD
private static readonly string _connectionString = "Filename=AppData/DataBase/LiteDBtest.db;Connection=shared;Password=123456";
private static readonly Stopwatch _sw = new();
/// <summary>
/// 单条数据添加
/// </summary>
/// <returns></returns>
[HttpPost(Name = "AddSingle")]
public async Task<Guid> AddSingleAsync()
{
// 模拟数据
var wf = new WeatherForecast
{
Id = Guid.NewGuid(),
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(1)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
};
_sw.Start();
//打开数据库,如果不存在会自动创建。
using var db = new LiteDatabaseAsync(_connectionString);
//打开一个集合和 MongoDB 一样的,类似关系数据库的表。
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
var item = await collection.InsertAsync(wf);
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[AddSingle] ==> 插入一条数据:{item.AsGuid},耗时:{ts.TotalMilliseconds}ms.");
return item.AsGuid;
}
/// <summary>
/// 批量数据添加
/// </summary>
/// <returns></returns>
[HttpPost(Name = "AddBulk")]
public async Task<int> AddBulkAsync(int conut)
{
if (conut <= 0) conut = 1000;
// 模拟数据
var wfs = Enumerable.Range(1, conut).Select(index => new WeatherForecast
{
Id = Guid.NewGuid(),
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray();
_sw.Start();
using var db = new LiteDatabaseAsync(_connectionString);
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
int rcount = await collection.InsertBulkAsync(wfs);
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[AddBulk] ==> 批量插入数据:{ rcount },耗时:{ ts.TotalMilliseconds }ms.");
return rcount;
}
/// <summary>
/// Id 查询单条数据
/// </summary>
/// <param name="id">Guid</param>
/// <returns></returns>
[HttpGet(Name = "GetSingle")]
public async Task<WeatherForecast> GetSingleAsync(Guid id)
{
_sw.Start();
using var db = new LiteDatabaseAsync(_connectionString);
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
var item = await collection.FindOneAsync(x => x.Id == id);
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[GetSingle] ==> 查询单条数据:{item.Id},耗时:{ts.TotalMilliseconds}ms.");
return item;
}
/// <summary>
/// 查询所有数据
/// </summary>
/// <returns></returns>
[HttpGet(Name = "GetAll")]
public async Task<IEnumerable<WeatherForecast>> GetAllAsync()
{
_sw.Start();
using var db = new LiteDatabaseAsync(_connectionString);
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
var list = await collection.FindAllAsync();
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[GetAll] ==> 查询所有数据:{list.Count()},耗时:{ts.TotalMilliseconds}ms.");
return list;
}
/// <summary>
/// 更新数据
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
[HttpPut(Name = "UpdateSingle")]
public async Task<bool> UpdateSingleAsync([FromBody] WeatherForecast item)
{
_sw.Start();
using var db = new LiteDatabaseAsync(_connectionString);
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
bool isOk = await collection.UpdateAsync(item);
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[UpdateSingle] ==> 更新单条数据:{item.Id},耗时:{ts.TotalMilliseconds}ms.");
return isOk;
}
/// <summary>
/// 批量更新
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
[HttpPut(Name = "UpdateBulk")]
public async Task<int> UpdateBulkAsync([FromBody] List<WeatherForecast> list)
{
_sw.Start();
using var db = new LiteDatabaseAsync(_connectionString);
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
int rcount = await collection.UpdateAsync(list);
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[UpdateSingle] ==> 批量更新数据:{rcount},耗时:{ts.TotalMilliseconds}ms.");
return rcount;
}
/// <summary>
/// Id 删除单条数据
/// </summary>
/// <param name="id">Guid</param>
/// <returns></returns>
[HttpDelete(Name = "DeleteSingle")]
public async Task<bool> DeleteSingleAsync(Guid id)
{
_sw.Start();
using var db = new LiteDatabaseAsync(_connectionString);
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
bool isOk = await collection.DeleteAsync(id);
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[DeleteSingle] ==> 删除单条数据:{id},耗时:{ts.TotalMilliseconds}ms.");
return isOk;
}
/// <summary>
/// 删除所有
/// </summary>
/// <returns></returns>
[HttpDelete(Name = "DeleteAll")]
public async Task<int> DeleteAllAsync()
{
_sw.Start();
using var db = new LiteDatabaseAsync(_connectionString);
var collection = db.GetCollection<WeatherForecast>(nameof(WeatherForecast));
int rcount = await collection.DeleteAllAsync();
_sw.Stop();
TimeSpan ts = _sw.Elapsed;
_logger.LogInformation($"[DeleteAll] ==> 删除多条数据:{rcount},耗时:{ts.TotalMilliseconds}ms.");
return rcount;
}
#endregion
到这里项目结构就改造好了,此时我来启动项目运行看看,显示如下页面:
控制台输出日志信息如下:
执行上面的 CRUD
方法,日志信息(这里截取部分)记录执行时间如下:
2023-12-04 15:44:18.898 <> [INF] [AddSingle] ==> 插入一条数据:052816a8-607a-4682-b673-7ad21a588528,耗时:139.1579ms.
2023-12-04 15:44:32.010 <> [INF] [AddBulk] ==> 批量插入数据:1000,耗时:284.2237ms.
2023-12-04 15:45:05.059 <> [INF] [GetSingle] ==> 查询单条数据:000660df-7e70-4c32-9734-4e657e157509,耗时:306.4881ms.
2023-12-04 15:45:13.158 <> [INF] [GetAll] ==> 查询所有数据:2001,耗时:307.4817ms.
2023-12-04 15:45:51.955 <> [INF] [DeleteSingle] ==> 删除单条数据:000660df-7e70-4c32-9734-4e657e157509,耗时:426.993ms.
2023-12-04 15:46:05.279 <> [INF] [DeleteAll] ==> 删除多条数据:2000,耗时:670.6834ms.
注意:
LiteDB
单次批量数据写入是1000
条。
说明:更新方法参数需要拼接实体模型,此处就不在拼接测试,上面的方法执行有效,感兴趣的可以自行拼接实体模型参数测试。
LiteDB vs SQLite 对比
说道 LiteDB
数据库,这里不得不提一个类似的数据库 —— SQLite
,它俩数据库很类似,但是定位又各不相同:
Item | LiteDB | SQLite |
---|---|---|
文件类型 | Single data file | Single data file |
数据库类型 | Serverless NoSQL | Serverless relational database |
事务支持/ACID | 通过 Transaction 类来控制 | 通过 BEGIN、COMMIT 和 ROLLBACK 语句来控制 |
稳定性 & 性能 | 较高 | 高 |
编写语言 | C#/CSharp | C |
跨平台支持 | 是 | 是 |
开源协议 | MIT ,https://github.com/mbdavid/LiteDB/blob/master/LICENSE | https://github.com/sqlite/sqlite/blob/master/LICENSE.md,https://www.sqlite.org/copyright.html |
官方文档:
SQLite
,https://www.sqlite.org/LiteDB
,https://www.litedb.org/
说明:下面几个对比问题均由
CSDN
平台提供的C知道
回答,仅供参考。
1、谈谈 sqlite 和 litedb 的 ACID 事务支持?
SQLite
和 LiteDB
都支持 ACID
事务,其中 ACID
是指原子性(Atomicity
)、一致性(Consistency
)、隔离性(Isolation
)和持久性(Durability
)。
-
在
SQLite
中,事务是通过BEGIN、COMMIT
和ROLLBACK
语句来控制的。SQLite
默认使用自动提交模式,也就是说,每个SQL
语句都会被立即提交。如果需要使用事务,可以使用BEGIN
语句来开始一个事务,使用COMMIT
语句来提交事务,或者使用ROLLBACK
语句来回滚事务。SQLite
支持四种隔离级别:SERIALIZABLE、REPEATABLE READ、READ COMMITTED和READ UNCOMMITTED
。 -
在
LiteDB
中,事务是通过Transaction
类来控制的。可以使用Transaction
类的Begin
方法来开始一个事务,使用Commit
方法来提交事务,或者使用Rollback
方法来回滚事务。LiteDB
支持两种隔离级别:READ UNCOMMITTED和SNAPSHOT
。
总的来说,SQLite
和 LiteDB
都支持 ACID
事务,但是 SQLite
在大型项目中可能会存在性能问题,而 LiteDB
则更适合单机小游戏等小型项目。
2、谈谈 sqlite 和 litedb 的稳定性和可靠性?
SQLite
和 LiteDB
都是轻量级数据库,它们的稳定性和可靠性都非常高。SQLite
是一个嵌入式数据库,它的数据存储在单个文件中,因此非常适合嵌入式设备和移动应用程序。SQLite
的稳定性和可靠性得到了广泛的认可,它已经被广泛地应用于各种应用程序中,包括谷歌的 Android
操作系统。LiteDB
是一个 .NET
平台上的嵌入式 NoSQL
文档数据库,它的数据存储在单个文件中,非常适合 .NET
应用程序。LiteDB
的稳定性和可靠性也非常高,它已经被广泛地应用于各种 .NET
应用程序中。
3、谈谈 sqlite 和 litedb 的应用场景?
SQLite
和 LiteDB
都是轻量级的嵌入式数据库,适用于一些小型应用场景。其中,SQLite
是一种关系型数据库,支持 SQL
语句,适用于需要高度可靠性和稳定性的应用场景,例如浏览器、操作系统等。而 LiteDB
则是一种文档型数据库,支持类似 MongoDB
的文档查询语法,适用于需要快速开发和部署的小型应用场景,例如单机小游戏、桌面应用等。
LiteDB vs SQLite 性能测试
这里有一个 LiteDB v3
版本和 SQLite
的性能测试对比。
LiteDB-Perf
项目地址,https://github.com/mbdavid/LiteDB-Perf
A simple actions to compare SQLite and LiteDB v3.
Results
(测试结果):
Low is better
(测试结果,时间越短越好)
参数项对比说明:
Item | LiteDB | SQLite |
---|---|---|
#1 | Default | Default |
#2 | Encrypted | Encrypted |
#3 | Exclusive mode and no journal | No journal |
测试主机说明:
- 原文:
Tested on MacBook Pro 2012 i5, Win10, 8Gb RAM, SSD.
- 译文:在
MacBook Pro 2012 i5、Win10、8Gb RAM、SSD
上测试。
此处我们使用的是 LiteDB v5.x
版本,对于上面的 sqlite
和 litedb
性能测试,感兴趣的小伙伴可以参照上面测试例子自行测试。