agilelabs-fx-docs main topics/data-access.md

数据访问

本页是 AgileLabs Framework 数据访问的规范主入口。当前文档策略已经收敛为两条线:Dapper / SQL 是默认推荐路径,EF Core 仅作为遗留兼容方案保留,不推荐新项目继续接入。

适用场景

  • 基于 SqlBaseRepository 的 SQL / Dapper 数据访问。
  • 需要显式控制 SQL、事务、分页和并发的项目。
  • 仍在维护既有 EF Core 存量项目的团队。

必须遵守

  • 新项目默认走 Dapper / SQL 路线,不再把 EF Core 作为默认数据访问方案。
  • Repository 生命周期为 Scoped
  • 数据访问必须通过业务 Repository 暴露,不把 SQL、连接和事务控制泄漏到 Controller。
  • 审计字段、租户身份、操作者身份等上下文相关逻辑必须运行在有效 WorkContext 内。
  • 若项目仍保留 EF Core,只能视为遗留兼容实现,并应有明确的迁移收敛路径。

推荐做法

  • 默认新建 SqlBaseRepository 派生类承接查询、分页、显式事务和写入。
  • 业务 Repository 表达业务语义,不把 Controller 直接暴露给底层访问细节。
  • 并发控制、审计字段回填、字段映射和返回 DTO 在 Repository 或其相邻的应用层内显式完成。
  • 存量 EF Core 模块优先按“先迁查询、再迁写入、最后移除 DbContext 主路径依赖”的方式逐步收敛。

源码入口

  • AgileLabs.Storage.PostgreSql/SqlBaseRepository.cs:复杂 SQL / Dapper 访问基类。
  • AgileLabs.EfCore.PostgreSQL/AgileLabDbContext.cs:遗留 EF Core 路线的审计字段回填。
  • AgileLabs.EfCore.PostgreSQL/Commiters/DbContextCommiter.cs:遗留 EF Core 路线的提交封装。
  • AgileLabs.WebApp.Extensions.EfCore/AspNet/WebApis/Filters/AutoCommiterFilterAttribute.cs:遗留 EF Core 路线的宿主提交链。

默认推荐:Dapper / SQL 路线

Dapper 路线的核心特点是行为显式、SQL 可见、AI 生成结果更容易被审查和修正。当前默认建议是:

  • 查询、分页、批量和事务直接放在 SqlBaseRepository 派生类中。
  • 写入逻辑显式表达更新条件、并发字段和影响行数检查。
  • 审计字段、租户信息、操作者信息通过 WorkContext 显式带入。
  • 不依赖 ORM 的隐式跟踪、隐式提交和导航属性级联行为。

为什么 EF Core 不再推荐

EF Core 在传统团队开发里能降低部分样板代码,但在 AI 编程场景里往往会把复杂性转移到更隐蔽的位置:

  • 真实行为分散在实体、配置类、DbContext、Filter、SaveChanges 扩展和导航属性中。
  • AI 生成代码容易只补齐表层调用,却遗漏跟踪状态、Include 边界、审计字段和提交链路。
  • 问题定位时经常要同时检查 LINQ、生成 SQL、实体状态、并发字段和宿主 Filter。
  • 存量项目里一旦混用 CrudRepository、自定义 DbContext 和 Dapper,维护成本会迅速放大。

所以当前文档策略是:EF Core 保留兼容说明,但不再作为默认路径推荐。

Dapper 主路径分层关系

flowchart TD
    Controller[Controller / AppService]
    Repo[业务 Repository]
    Sql[SqlBaseRepository]
    Pg[(PostgreSQL)]

    Controller --> Repo
    Repo --> Sql
    Sql --> Pg

什么时候选什么

场景 推荐方式 原因
新项目的标准读写 SqlBaseRepository 派生类 SQL 显式,可审查,可控
复杂列表、分页、多表查询 SqlBaseRepository SQL 表达更直接,分页能力更清晰
批量写入、显式事务 SqlBaseRepository 更适合控制事务和批量执行
需要 ts 并发控制 SqlBaseRepository + 显式 where 条件 冲突边界清晰
存量 EF Core 模块维护 仅兼容保留 需同时制定迁移计划

Dapper 写入链路

请求内的推荐写入链路应该保持显式:

sequenceDiagram
    participant Action as Controller / Service
    participant Repo as Repository
    participant Sql as SqlBaseRepository
    participant Db as PostgreSQL

    Action->>Repo: 调用增删改业务
    Repo->>Sql: ExecuteNoQueryAsync / TransScopeAsync
    Sql->>Db: 执行显式 SQL
    Db-->>Sql: 返回影响行数 / 查询结果
    Sql-->>Repo: 显式结果
    Repo-->>Action: DTO / 业务结果

这条链路的重点是:

  1. SQL、事务和并发检查都在 Repository 内显式表达。
  2. 影响行数、版本字段和失败分支不依赖隐式 SaveChanges 行为。
  3. 审计字段可以在参数组装时显式回填,或通过项目基类统一填充。

审计字段与 WorkContext

无论走 Dapper 还是遗留 EF Core,审计字段都仍依赖 WorkContext

这意味着:

  • 在 HTTP 请求里通常天然成立。
  • 在后台任务里必须先建 WorkContext,再做数据保存。
  • 如果你绕过 WorkContext 直接写库,最轻是审计字段缺失,最重会出现身份、租户或时间语义错误。

推荐的新项目注册示例:

services.AddScoped<OrderRepository>();
services.AddScoped<DapperBaseRepository>();

遗留 EF Core 兼容区

如果当前项目已经依赖 AgileLabDbContextCrudRepositoryDbContextCommiterAutoCommiterFilterAttribute,可以继续维护,但应视为迁移过渡态,而不是新的标准实现。

遗留 EF Core 路线里仍要遵守这些边界:

  • 一个系统只保留一个默认 DbContext
  • CrudRepository 只操作默认 DbContext
  • 指定 DbContext 不继续滥用默认仓储。
  • 所有新增模块优先迁往 Dapper,而不是继续扩 EF Core 面。

兼容链路示意:

flowchart TD
    Controller[Controller / AppService]
    Repo[业务 Repository]
    Crud[CrudRepository]
    Factory[IAgileLabDbContextFactory]
    Db[AgileLabDbContext]
    Committer[DbContextCommiter]
    Pg[(PostgreSQL)]

    Controller --> Repo --> Crud --> Factory --> Db --> Committer --> Pg

EF Core 迁移到 Dapper

存量项目的标准收敛方向是:

  1. 停止新增 EF Core 接入面。
  2. 先把查询和分页迁到 Dapper。
  3. 再把写入和事务迁到显式 SQL Repository。
  4. 最后收敛 CrudRepositoryDbContext 和宿主级自动提交链。

完整规范见 EF Core 迁移到 Dapper

常见坑

  • 新模块继续新增 DbContextCrudRepository
  • 在同一业务里同时混用 Dapper 写入和 EF Core 写入,却没有清晰边界。
  • 在 Controller 里直接拼连接和事务。
  • 遗留 EF Core 模块没有迁移计划,反而继续扩大使用面。

真实用例

  • niusys-webapi:更接近旧的默认 DbContext 模型,适合看遗留兼容路径。
  • gmandarin-backend:大量显式业务仓储。
  • woscm:适合重点看项目级 Dapper 基类、ts 并发控制和项目标准层封装。

相关页面