Bootstrap

探索 .NET 9 控制台应用中的 LiteDB 异步 CRUD 操作

本文主要是使用异步方式,体验 litedb 基本的 crud 操作。

LiteDB 是一款轻量级、快速且免费的 .NET NoSQL 嵌入式数据库,专为小型本地应用程序设计。它以单一数据文件的形式提供服务,支持文档存储和查询功能,适用于桌面应用、移动应用和小型 Web 应用等场景。

LiteDB 的主要特点包括:

  • 无服务器的 NoSQL 文档存储LiteDB 是一个嵌入式数据库,无需独立服务器,数据存储在一个单一文件中,类似于 SQLite
  • 简单易用的 APILiteDB 提供类似 MongoDB 的简单 API,支持 LINQ 查询和 SQL-like 命令,使得开发者可以轻松上手。
  • 线程安全和 ACID 事务支持:LiteDB 支持线程安全操作和完整的 ACID 事务处理,确保数据的一致性和完整性。
  • 数据恢复功能LiteDB 使用写前日志(WAL)机制来保证数据恢复能力,即使在写入失败的情况下也能恢复数据。
  • 跨平台支持LiteDB 可以在 Windows、Linux 和 macOS 等多个平台上运行,具有良好的跨平台兼容性。
  • 加密存储LiteDB 支持使用 DESAES 加密算法对数据文件进行加密,保障数据的安全性。
  • 文件和流数据存储LiteDB 支持存储文件和流数据,类似于 MongoDBGridFS 功能。
  • 开源免费LiteDB 是一个开源(MIT)项目,任何人都可以使用和修改其代码,并且完全免费,包括商业用途。

LiteDB 的使用非常简单,可以通过 NuGet 包管理器轻松安装,并且提供了丰富的文档和示例代码帮助开发者快速上手。此外,LiteDB 还提供了一个名为 LiteDB Studio 的图形用户界面工具,方便用户管理和可视化数据库内容。

总之,LiteDB 是一款功能强大、易于使用的 NoSQL 数据库解决方案,适用于多种场景,值得开发者关注和尝试。

使用 .NET CLI 创建一个控制台应用程序,可以按照以下步骤进行:

  1. 安装 .NET 9 SDK:首先,确保你已经安装了 .NET 9 SDK。你可以从官方网站下载并安装最新版本的 .NET SDK

  2. 创建新的控制台应用程序:使用 dotnet new console 命令创建一个新的控制台应用程序。这个命令会生成一个包含基本结构的项目文件夹。

dotnet new console -n LiteDBExample
  1. 导航到项目目录:进入刚刚创建的项目目录。
 cd LiteDBExample
  1. 运行应用程序:使用 dotnet run 命令运行应用程序。
dotnet run

基础控制台项目就创建好了,丝滑般体验;

接下来我们使用 litedb 提供的 nuget 包,进行简单的封装,构建出异步操作的 crud 方法 。

  • 安装相关 nuget 包:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="LiteDB" Version="5.0.21" />
    <PackageReference Include="LiteDB.Async" Version="0.1.8" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
  </ItemGroup>

</Project>
  • 创建实体类型 :Person
public class Person
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public string Name { get; set; } = default!;
    public uint Age { get; set; }
}
  • 定义仓储类规范:IRepository
using LiteDB;

namespace LiteDBExample.Core.Interfaces;

public interface IRepository<TEntity> where TEntity : class
{
    Task<BsonValue> AddAsync(TEntity entity);
    Task<int> AddRangeAsync(IEnumerable<TEntity> entities);
    Task<IEnumerable<TEntity>> GetAllAsync();
    Task<TEntity?> GetByIdAsync(object id);
    Task<bool> UpdateAsync(TEntity entity);
    Task<int> UpdateRangeAsync(IEnumerable<TEntity> entities);
    Task<bool> DeleteAsync(object id);
    Task<int> DeleteRangeAsync(IEnumerable<object> ids);
}
  • 实现 PersonDbContext
using LiteDB;
using LiteDB.Async;
using LiteDBExample.Core.Entities;

namespace LiteDBExample.Data;

public class PersonDbContext(string connectionString) : IDisposable
{
    private readonly LiteDatabaseAsync _database = new(connectionString);

    public Task<BsonValue> AddAsync(Person person)
    {
        var collection = _database.GetCollection<Person>("people");
        return collection.InsertAsync(person);
    }

    public Task<int> AddRangeAsync(IEnumerable<Person> persons)
    {
        var collection = _database.GetCollection<Person>("people");
        return collection.InsertAsync(persons);
    }

    public Task<IEnumerable<Person>> GetAllAsync()
    {
        var collection = _database.GetCollection<Person>("people");
        return collection.FindAllAsync();
    }

    public Task<Person?> GetByIdAsync(object id)
    {
        BsonValue bsonId = new(id);
        var collection = _database.GetCollection<Person>("people");
        return collection.FindByIdAsync(bsonId);
    }

    public Task<bool> UpdateAsync(Person person)
    {
        var collection = _database.GetCollection<Person>("people");
        return collection.UpdateAsync(person);
    }
 
    public Task<int> UpdateRangeAsync(IEnumerable<Person> persons)
    {
        var collection = _database.GetCollection<Person>("people");
        return collection.UpdateAsync(persons);
    }

    public Task<bool> DeleteAsync(object id)
    {
        BsonValue bsonId = new(id);
        var collection = _database.GetCollection<Person>("people");
        return collection.DeleteAsync(bsonId);
    }

    public Task<int> DeleteRangeAsync(IEnumerable<object> ids)
    {
        var collection = _database.GetCollection<Person>("people");
        return collection.DeleteManyAsync(a => ids.Contains(a.Id));
    }

    public void Dispose()
    {
        _database.Dispose();
    }
}
  • 定义 Person 类仓储:IPersonRepository
using LiteDBExample.Core.Entities;
using LiteDBExample.Core.Interfaces;

namespace LiteDBExample.Repositories;

public interface IPersonRepository : IRepository<Person>
{
}
  • 实现 Person 类仓储:PersonRepository
using LiteDB;
using LiteDBExample.Core.Entities;
using LiteDBExample.Data;

namespace LiteDBExample.Repositories;

public class PersonRepository(PersonDbContext context) : IPersonRepository
{
    private readonly PersonDbContext _context = context ?? throw new ArgumentNullException(nameof(context));

    public Task<BsonValue> AddAsync(Person entity)
    {
        return _context.AddAsync(entity);
    }

    public Task<int> AddRangeAsync(IEnumerable<Person> entities)
    {
        return _context.AddRangeAsync(entities);
    }

    public Task<IEnumerable<Person>> GetAllAsync()
    {
        return _context.GetAllAsync();
    }

    public Task<Person?> GetByIdAsync(object id)
    {
        return _context.GetByIdAsync(id);
    }

    public Task<bool> UpdateAsync(Person entity)
    {
        return _context.UpdateAsync(entity);
    }

    public Task<int> UpdateRangeAsync(IEnumerable<Person> entities)
    {
        return _context.UpdateRangeAsync(entities);
    }

    public Task<bool> DeleteAsync(object id)
    {
        return _context.DeleteAsync(id);
    }

    public Task<int> DeleteRangeAsync(IEnumerable<object> ids)
    {
        return _context.DeleteRangeAsync(ids);
    }
}
  • Program.cs 中应用 IPersonRepository
using LiteDBExample.Core.Entities;
using LiteDBExample.Repositories;
using Microsoft.Extensions.DependencyInjection;
using LiteDBExample.Data;

namespace LiteDBExample;

internal class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Hello, LiteDB!");

        var services = new ServiceCollection();
        ConfigureServices(services);

        using var serviceProvider = services.BuildServiceProvider();
        var personRepository = serviceProvider.GetRequiredService<IPersonRepository>();

        // 多个 Person
        var newPeople = new List<Person>
        {
            new Person { Name = "John Kiny", Age = 16 },
            new Person { Name = "John Doe", Age = 30 },
            new Person { Name = "Jane Smith", Age = 28 },
            new Person { Name = "Alice Johnson", Age = 35 }
        };

        try
        {
            Console.WriteLine("===> litedb Async Method Test");

            {
                // 添加单个 Person
                var person = new Person { Name = "Alice Array", Age = 32 };
                var bsonId = await personRepository.AddAsync(person);
                Console.WriteLine($"bsonId={bsonId}");

                // 查询单个 Person
                var queryPerson = await personRepository.GetByIdAsync(bsonId);
                Console.WriteLine($"guid={queryPerson.Id}, {queryPerson.Name} is {queryPerson.Age} years old.");

                // 更新单个 Person
                person.Age += 1;
                person.Name = "Array";
                var updateCount = await personRepository.UpdateAsync(person);
                Console.WriteLine($"updateCount={updateCount}");

                // 删除单个 Person
                var deleteCount = await personRepository.DeleteAsync(bsonId);
                Console.WriteLine($"deleteCount={deleteCount}");

                // 批量添加多个 Person
                int addRange = await personRepository.AddRangeAsync(newPeople);
                Console.WriteLine($"addRange={addRange}");

                // 查询所有 Person
                var people = await personRepository.GetAllAsync();
                foreach (var p in people)
                {
                    Console.WriteLine($"guid={p.Id}, {p.Name} is {p.Age} years old.");
                }

                // 批量更新多个 Person
                var updatedPeople = people.Select(p => new Person { Id = p.Id, Name = p.Name, Age = p.Age + 1 });
                int updateRows = await personRepository.UpdateRangeAsync(updatedPeople);
                Console.WriteLine($"updateRows={updateRows}");

                // 批量删除多个 Person
                var idsToDelete = new List<object>();
                foreach (var item in people.Select(p => p.Id))
                {
                    idsToDelete.Add(item);
                };

                var ids = idsToDelete.AsEnumerable();
                int deleteRows = await personRepository.DeleteRangeAsync(ids);
                Console.WriteLine($"deleteRows={deleteRows}");
            }

            Console.ReadLine();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
        }
    }

    private static void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IPersonRepository, PersonRepository>();
        services.AddScoped<PersonDbContext>(provider => new PersonDbContext("filename=litedb_example.db"));
    }
}
  • 运行应用:dotnet run

dotnet run

  • 使用 LiteDB.Studio 工具查看 litedb 数据;

当批量添加数据的时候,查看数据信息如下:

  • Grid 查看

litedb-select-grid

  • Text 查看

litedb-select-text

从上图中可以看到 litedb_example.db 文件非常轻量,大概 30kb 左右;

;