浏览知识库目录

C#

综合实战与进阶路线

整合领域、SQLite、HTTP、配置、异步、测试与发布,完成可维护的 StudyTasks 项目闭环。

综合实战与进阶路线

本篇把前面的语言和工程能力合并为可安装的 StudyTasks 命令行应用。重点不是继续堆功能,而是让领域、数据库、网络、配置、取消、测试和交付形成闭环;任何步骤失败时,系统仍处于可解释、可恢复的状态。


一、学习目标

  • 按依赖方向组装 Core、Infrastructure、Cli 和 Tests
  • add/list/done/delete/sync 共享一套领域规则
  • 用事务、取消和错误分类保护外部边界
  • 在干净 .NET 10 环境完成测试、打包、安装和冒烟

最终命令:

study-tasks add "阅读 C# 文档"
study-tasks list
study-tasks done 1
study-tasks delete 1
study-tasks sync

二、最终结构与依赖

StudyTasks/
  global.json
  StudyTasks.slnx
  Directory.Build.props
  src/
    StudyTasks.Core/
      Models/StudyTask.cs
      Ports/ITaskRepository.cs
      Ports/IRemoteTaskClient.cs
      Services/TaskService.cs
    StudyTasks.Infrastructure/
      Sqlite/SqliteTaskRepository.cs
      Http/RemoteTaskClient.cs
    StudyTasks.Cli/
      Program.cs
      CommandFactory.cs
      appsettings.json
  tests/
    StudyTasks.Tests/

依赖只能由外向内:

Cli -> Core
Cli -> Infrastructure -> Core
Tests -> public interfaces

Core 不引用 System.CommandLine、Microsoft.Data.Sqlite、配置、日志或 HTTP 实现。Cli 是组合根,Infrastructure 只实现 Core 定义的端口。

统一编译基线:

<Project>
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>14.0</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
</Project>

稳定依赖固定为 System.CommandLine 2.0.10、Microsoft.Extensions 10.0.10、Microsoft.Data.Sqlite 10.0.10 和 xUnit v3 3.2.2。


三、领域模型与端口

领域对象只允许合法状态,外部能力由端口表达:

public sealed record StudyTask(
    int Id,
    string Title,
    bool IsCompleted,
    DateTimeOffset CreatedAt,
    DateTimeOffset? CompletedAt)
{
    public static StudyTask Create(string title, DateTimeOffset now)
    {
        string clean = title.Trim();
        if (clean.Length is < 1 or > 120)
            throw new ArgumentException("标题长度必须为 1~120");
        return new(0, clean, false, now, null);
    }

    public StudyTask Complete(DateTimeOffset now) =>
        IsCompleted ? this : this with
        {
            IsCompleted = true,
            CompletedAt = now
        };
}

public interface ITaskRepository
{
    StudyTask Add(StudyTask task);
    IReadOnlyList<StudyTask> List();
    void Update(StudyTask task);
    bool Delete(int id);
}

远端同步另由 IRemoteTaskClient 暴露可取消的异步方法。CLI、JSON、HTTP 和数据库行都先验证,再进入领域;CLI 不拼 SQL,仓储不打印消息,HTTP 客户端不决定退出码。


四、数据与同步边界

SQLite 同时用领域校验和数据库约束保护数据:

CREATE TABLE task (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL
        CHECK (length(trim(title)) BETWEEN 1 AND 120),
    status TEXT NOT NULL
        CHECK (status IN ('todo', 'done')),
    created_at TEXT NOT NULL,
    completed_at TEXT NULL,
    CHECK (
        (status = 'todo' AND completed_at IS NULL)
        OR
        (status = 'done' AND completed_at IS NOT NULL)
    )
);

所有用户值参数化;状态更新和本地审计处于同一短事务。网络读取不能放进数据库事务。

sync 的默认策略是全部成功或全部失败:

读取并验证配置
  -> 发起带超时和取消的 HTTPS 请求
  -> 限制响应大小并反序列化 DTO
  -> 将全部 DTO 映射为合法领域对象
  -> 检查 ID 唯一和跨记录不变量
  -> 开启 SQLite 事务并替换数据
  -> 提交后输出数量

任何网络、协议或数据错误都不修改本地数据库。若改为增量同步,必须另外定义游标、幂等键、冲突规则和删除语义,不能只比较本地时间。


五、配置、CLI 与错误契约

appsettings.json 提供非秘密默认值:

{
  "StudyTasks": {
    "Database": "tasks.db",
    "ApiUrl": "https://api.example.com/tasks",
    "TimeoutSeconds": 15,
    "MaxConcurrency": 4
  }
}

Token 通过 StudyTasks__ApiToken 提供。命令行、环境变量、环境专用 JSON、基础 JSON 依次覆盖。仅在执行 sync 时要求 Token;--help--version 和本地 CRUD 不连接网络。

退出码保持稳定:

退出码 含义
0 成功
2 参数或配置错误
3 任务不存在
4 暂时性外部故障
5 数据或协议不兼容
130 用户取消

stdout 只放稳定结果,stderr 放用户可操作的错误,结构化日志记录诊断字段。秘密不得进入三者中的任何一个。


六、测试矩阵

场景 预期
空白或超长标题 拒绝且不写数据库
重复完成 幂等,不改变首次完成时间
未知 ID stderr 提示,退出码 3
数据库约束失败 当前事务整体回滚
环境变量存在 覆盖 JSON 配置
HTTP 401/429/500 正确分类,只有限重试幂等请求
非法或超大 JSON 拒绝且本地数据不变
用户取消 传播取消,退出码 130
并发同步 同时请求数不超过配置
NuGet Tool 安装 在源码目录外可运行帮助和 CRUD

领域测试不接 I/O;SQLite 使用临时数据库;HTTP 使用自定义 HttpMessageHandler 或本地测试服务。测试必须可重复,不依赖公网、真实时间和执行顺序。


七、安全、性能与发布检查

  • SQL 全部参数化,动态结构来自白名单
  • API 只接受 HTTPS,Token 不写入文件或日志
  • HTTP 有超时、外部取消、响应大小和内容类型检查
  • 文件路径不能突破允许的数据目录
  • 远端数据完整验证后才进入一个短事务
  • 重试只用于暂时性且幂等的操作
  • 并发有上限,创建的任务都被等待
  • 迁移前备份,并实际演练恢复

性能先测量冷启动、查询计划、事务时长、HTTP 延迟、峰值内存和日志量,再决定是否使用索引、缓存或 ValueTask。不要用无限并发掩盖慢服务。

发布检查:

dotnet --info
dotnet restore --locked-mode
dotnet format --verify-no-changes
dotnet build StudyTasks.slnx -c Release \
  --no-restore --warnaserror
dotnet test StudyTasks.slnx -c Release --no-build
dotnet pack src/StudyTasks.Cli -c Release --no-build
dotnet publish src/StudyTasks.Cli -c Release

随后在干净 .NET 10 环境安装工具,实际执行 add/list/done/delete/sync。发布记录应包含版本、源提交、SDK 与包版本、产物 SHA-256、已知限制和回滚步骤。


八、常见错误

  • 让 CLI、仓储和 HTTP 客户端各自重复业务规则
  • 在数据库事务中等待网络,扩大锁范围和失败影响
  • 只验证成功路径,没有覆盖取消、超时和无效数据
  • 记录完整配置对象,意外泄露 Token
  • 在源码目录中通过烟测,却没有测试安装后的产物

九、练习与自测

  1. 为什么 Core 定义端口,而 Infrastructure 实现适配器?
  2. 为什么远端数据要先全部验证,再开启 SQLite 事务?
  3. 用户取消、超时、HTTP 失败和协议错误怎样分类?
  4. 如何证明并发没有超过资源上限?
  5. 如何证明 NuGet Tool 在干净环境中可安装运行?
  6. 二进制回滚与数据回滚为什么是两件事?

如果这些问题都能通过代码、测试和发布记录回答,你已经完成了从“会写 C# 语法”到“能交付 .NET 项目”的闭环。


十、官方资料

上一篇:NuGet、发布与部署 | 返回:C# 完整学习路线