管理数据持久性

在本节中,您将学习 Lino 如何配置 Entity Framework Core,使领域层保持完全隔离,数据库迁移过程可预测且可控 — 无论是在传统的单体服务还是模块化服务中。


通过 lino new service 命令创建服务时,CLI 会请求两个关键决策:

  • 架构 — 传统服务或模块化服务。
  • 数据提供程序SqlServerPostgreSql(未来版本将添加更多提供程序)。

在传统(单体)服务或模块化(模块化单体)服务中,每个服务使用单一数据库。然而,在模块化服务中,每个模块都映射到同一数据库内的不同schema中。 这允许每个bounded context保持独立的逻辑命名空间,确保功能隔离独立版本控制更有序且安全的迁移

实体类型配置

Lino 遵循 Persistence‑Ignorant 原则:领域实体不涉及基础设施的细节。 为此,所有 ORM 映射都转移到了实现 IEntityTypeConfiguration<T> 的类中,位于 Configurations/<EntityName>Configuration.cs

  • 每个实体都有一个专门的配置文件。
  • 全局约定(例如:decimal(18,2)、排序规则、将 DateTime 设为 utc)可以集中管理在 ModelConfiguration 中。
  • 配置通过 DbContextOnModelCreating 方法中使用 modelBuilder.ApplyConfigurationsFromAssembly(...) 应用。

DbContexts

DbContext 表示应用程序的事务单元。Lino 会自动生成:

  • 传统服务 — 一个包含所有 DbSet<TEntity> 的单一 AppDbContext
  • 模块化服务 — 每个 bounded context 对应一个 <Module>DbContext。这样每个模块编译更快,迁移周期更短,减少了合并冲突的风险。

所有 DbContext 都注册在 <Module>/Infrastructure.Persistence,通过 IUnitOfWork 暴露,并通过 dependency injection 解析。

仓储

具体的仓储位于 <Module>/Infrastructure.Persistence.Repositories,并实现了在命名空间 <Module>/Domain.Repositories 中定义的接口。 每个仓储:

  • 封装复杂查询(LINQ,FromSql,DTO 投影)。
  • 仅公开领域所需的方法(聚合根为中心)。

工作单元(Unit of Work)

工作单元 作为一个事务边界,用于协调多个仓储(Repository),确保所有操作以 原子性 的方式执行。 在简单场景中,DbContext 本身通常已经足够。然而,Lino 会生成一个专用实现(UnitOfWork),提供更高的控制力和灵活性。

  • 在需要时,可通过 BeginTransactionCommitRollback 手动控制事务。
  • 在当前事务中发布 领域事件,确保它们在同一个 commit 循环内触发。
  • 集成事件 持久化到 Outbox 中,遵循 事务出站模式(Transaction Outbox Pattern),以确保在分布式架构下的可靠投递(如适用)。

管理迁移

Lino 使用 Entity Framework Core 完全简化并自动化了 数据库迁移 过程,消除了重复任务和常见的不一致风险。 要创建新的迁移,请运行以下命令:

lino database migrations add

运行命令后,交互式向导将提示您输入以下信息:

  • 服务 — 应用迁移的项目名称。
  • 模块 — (仅限模块化服务)所属更改的模块。
  • 迁移名称 — 输入更改描述(例如:AddCustomerIsActive)。 Lino 会自动管理当前版本和递增顺序,生成符合以下模式的 .sql 脚本: /Scripts/v1.2.3/001_AddCustomerIsActive.sql
注意: 除了 Entity Framework 生成的 .cs 文件外,Lino 还会自动创建包含 DDL 指令的相应 .sql 脚本。 这方便了手动执行更改或基础设施团队及DBA的审核。
发生了未处理的错误。 重新加载 🗙