TinyDb 的集合接口提供了完整异步版本:InsertAsync / UpdateAsync / DeleteAsync / FindAsync / UpsertAsync


这篇文章给出在 Web API、后台任务中可直接复用的写法。

1. 为什么要用异步

  • 避免同步 I/O 阻塞线程池。

  • 提升高并发请求下的吞吐。

  • 更容易接入超时与取消控制。

2. 基础异步 CRUD 示例

using var db = new TinyDb.Core.TinyDbEngine("async.db");
var tasks = db.GetCollection<TodoTask>();

var task = new TodoTask
{
    Title = "完成接口联调",
    Priority = TaskPriority.High,
    Status = TodoStatus.Pending
};

// C
var id = await tasks.InsertAsync(task);

// R
var loaded = await tasks.FindByIdAsync(id);

// U
if (loaded != null)
{
    loaded.Status = TodoStatus.InProgress;
    await tasks.UpdateAsync(loaded);
}

// D
await tasks.DeleteAsync(id);

3. 批量异步写入

var batch = Enumerable.Range(1, 1000)
    .Select(i => new TodoTask
    {
        Title = $"任务-{i}",
        Priority = (TaskPriority)(i % 3),
        Status = TodoStatus.Pending
    })
    .ToList();

var inserted = await tasks.InsertAsync(batch);
Console.WriteLine($"批量异步插入: {inserted}");

4. 取消令牌(必须掌握)

4.1 外部请求驱动取消

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

try
{
    var result = await tasks.FindAsync(
        x => x.Status == TodoStatus.Pending,
        cancellationToken: cts.Token);

    Console.WriteLine($"查询完成: {result.Count}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("查询被取消(超时或外部取消)");
}

4.2 显式取消

using var cts = new CancellationTokenSource();
var running = tasks.InsertAsync(new TodoTask { Title = "长任务" }, cts.Token);

cts.Cancel();

try
{
    await running;
}
catch (OperationCanceledException)
{
    Console.WriteLine("写入被主动取消");
}

5. 并发执行建议

5.1 正确方式:有限并发

var semaphore = new SemaphoreSlim(8); // 并发上限
var source = Enumerable.Range(1, 2000);

var jobs = source.Select(async i =>
{
    await semaphore.WaitAsync();
    try
    {
        await tasks.InsertAsync(new TodoTask { Title = $"并发任务-{i}" });
    }
    finally
    {
        semaphore.Release();
    }
});

await Task.WhenAll(jobs);

5.2 不推荐:无上限 Task.WhenAll 扔 10w 个任务

会造成:

  • 内存膨胀。

  • 线程池调度压力增大。

  • 上下文切换过多导致抖动。

6. Web API 中的典型封装

public sealed class TodoService
{
    private readonly TinyDb.Collections.ITinyCollection<TodoTask> _tasks;

    public TodoService(TinyDb.Core.TinyDbEngine db)
    {
        _tasks = db.GetCollection<TodoTask>();
    }

    public async Task<ObjectId> CreateAsync(string title, CancellationToken ct)
    {
        var task = new TodoTask
        {
            Title = title,
            Priority = TaskPriority.Medium,
            Status = TodoStatus.Pending
        };

        var id = await _tasks.InsertAsync(task, ct);
        return id.As<ObjectId>();
    }

    public Task<List<TodoTask>> QueryPendingAsync(int page, int pageSize, CancellationToken ct)
    {
        return _tasks.FindAsync(
            x => x.Status == TodoStatus.Pending,
            (page - 1) * pageSize,
            pageSize,
            ct);
    }
}

7. 小结

  • 异步接口是服务端高并发的默认选项。

  • 永远把 CancellationToken 贯通到存储层。

  • 并发写入要有上限,不要无限制扔任务。

下一篇进入安全主题:密码保护和安全访问策略。


附:示例实体

using TinyDb.Attributes;
using TinyDb.Bson;

[Entity("todo_tasks")]
public partial class TodoTask
{
    [Id]
    public ObjectId Id { get; set; } = ObjectId.NewObjectId();
    public string Title { get; set; } = string.Empty;
    public TaskPriority Priority { get; set; } = TaskPriority.Medium;
    public TodoStatus Status { get; set; } = TodoStatus.Pending;
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

public enum TaskPriority { Low, Medium, High }
public enum TodoStatus { Pending, InProgress, Completed, Cancelled }