数据访问
本页是 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 / 业务结果
这条链路的重点是:
- SQL、事务和并发检查都在 Repository 内显式表达。
- 影响行数、版本字段和失败分支不依赖隐式 SaveChanges 行为。
- 审计字段可以在参数组装时显式回填,或通过项目基类统一填充。
审计字段与 WorkContext
无论走 Dapper 还是遗留 EF Core,审计字段都仍依赖 WorkContext:
这意味着:
- 在 HTTP 请求里通常天然成立。
- 在后台任务里必须先建 WorkContext,再做数据保存。
- 如果你绕过 WorkContext 直接写库,最轻是审计字段缺失,最重会出现身份、租户或时间语义错误。
推荐的新项目注册示例:
services.AddScoped<OrderRepository>();
services.AddScoped<DapperBaseRepository>();
遗留 EF Core 兼容区
如果当前项目已经依赖 AgileLabDbContext、CrudRepository、DbContextCommiter 或 AutoCommiterFilterAttribute,可以继续维护,但应视为迁移过渡态,而不是新的标准实现。
遗留 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
存量项目的标准收敛方向是:
- 停止新增 EF Core 接入面。
- 先把查询和分页迁到 Dapper。
- 再把写入和事务迁到显式 SQL Repository。
- 最后收敛
CrudRepository、DbContext和宿主级自动提交链。
完整规范见 EF Core 迁移到 Dapper。
常见坑
- 新模块继续新增
DbContext和CrudRepository。 - 在同一业务里同时混用 Dapper 写入和 EF Core 写入,却没有清晰边界。
- 在 Controller 里直接拼连接和事务。
- 遗留 EF Core 模块没有迁移计划,反而继续扩大使用面。
真实用例
- niusys-webapi:更接近旧的默认
DbContext模型,适合看遗留兼容路径。 - gmandarin-backend:大量显式业务仓储。
- woscm:适合重点看项目级 Dapper 基类、
ts并发控制和项目标准层封装。