数据持久化
本文档引用的文件
- EfCoreMyEntityNameRepository.cs
- IMyEntityNameRepository.cs
- MyEntityName.cs
- CMSPluginEfCoreExtensions.MyEntityName.cs
- CMSPluginDbContext.cs
- CMSPluginEfCoreEntityExtensionMappings.cs
- CMSPluginDapperRepository.cs
- ICMSPluginDapperRepository.cs
- CMSPluginDbProperties.cs
- CMSPluginEntityFrameworkCoreModule.cs
- CMSPluginDbSchemaMigrator.cs
- MyEntityNameSpecification.cs
目录
引言
本项目采用领域驱动设计(DDD)架构,数据持久化机制以 Entity Framework Core(EF Core)为核心,结合 Dapper 实现高性能查询,支持 MySQL、PostgreSQL 和 SQL Server 多数据库。通过仓储模式(Repository Pattern)封装数据访问逻辑,确保领域层与基础设施层解耦。本文档全面分析该机制的实现细节。
仓储模式与接口契约
项目采用标准仓储模式,通过 IMyEntityNameRepository 接口定义数据访问契约,EfCoreMyEntityNameRepository 提供 EF Core 实现。接口定义了实体查询、计数、排序获取、名称唯一性校验等业务方法,确保领域服务通过抽象接口操作数据,不依赖具体实现。
IMyEntityNameRepository 继承自 IBasicRepository<MyEntityName, Guid>,扩展了 FindByNameAsync、NameExistAsync、GetMaxSortAsync 等业务专用方法,体现了领域驱动设计中“富仓储”的理念。
代码路径
IMyEntityNameRepository接口定义了所有查询契约EfCoreMyEntityNameRepository实现了这些契约
Section sources
EF Core 配置与实体映射
EF Core 的实体映射通过扩展方法 CMSPluginEfCoreExtensions.MyEntityName.cs 集中配置。ConfigureMyEntityName 方法在 ModelBuilder 上为 MyEntityName 实体定义了完整的数据库映射规则。
Diagram sources
表名与架构映射
实体映射到数据库表时,使用 CMSPluginDbProperties 中定义的前缀和架构:
b.ToTable((CMSPluginDbProperties.DbTablePrefix + "_MyEntityNames").ToLower(), CMSPluginDbProperties.DbSchema)
默认表名为 scms_myentitynames,位于默认架构下,支持通过配置修改。
属性配置
关键属性配置如下:
Code:最大长度由MyEntityNameConsts.MaxCodeLength定义,必填Name:最大长度由MyEntityNameConsts.MaxNameLength定义,必填Sort:整数类型,用于排序Remark:可选,最大长度由MyEntityNameConsts.MaxRemarkLength定义IsDisabled:可空布尔值,表示是否禁用
索引配置
为 Name 字段创建了唯一索引,确保名称的唯一性约束:
b.HasIndex(u => u.Name);
关系定义
当前 MyEntityName 实体未定义导航属性,为聚合根(Aggregate Root),其内部关系由领域逻辑维护,符合 DDD 原则。
Section sources
数据库上下文管理
项目定义了 ICMSPluginDbContext 接口作为数据库上下文契约,继承自 IEfCoreDbContext。具体的上下文实现(如 CMSPluginDbContext)在各数据库提供程序项目中(MySQL、PostgreSQL、SqlServer)提供。
Diagram sources
OnModelCreating 方法中调用 builder.ConfigureMyEntityName() 注册实体配置,并通过 builder.TryConfigureObjectExtensions<CMSPluginDbContext>() 应用对象扩展,支持模块化配置。
数据库连接字符串名称通过 [ConnectionStringName] 特性指定为 MyPluginName,定义在 CMSPluginDbProperties.ConnectionStringName。
Section sources
多数据库支持实现原理
项目通过为不同数据库提供程序创建独立的项目(CMS.Plugin.MyPluginName.MySQL、CMS.Plugin.MyPluginName.PostgreSql、CMS.Plugin.MyPluginName.SqlServer)实现多数据库支持。每个项目包含一个继承自 AbpDbContext 的 CMSPluginDbContext 实现。
运行时,根据依赖注入容器中注册的数据库提供程序(如 UseMySql、UseNpgsql、UseSqlServer),DbContextOptions<CMSPluginDbContext> 将使用相应的数据库驱动。由于所有实现共享相同的 ICMSPluginDbContext 接口和 EF Core 配置,上层代码完全透明,实现了数据库无关性。
Section sources
实体扩展性设计
项目通过 CMSPluginEfCoreEntityExtensionMappings 类实现实体的可扩展性。该静态类在 CMSPluginEntityFrameworkCoreModule 的 PreConfigureServices 阶段调用 Configure() 方法进行初始化。
Diagram sources
OneTimeRunner 确保配置仅执行一次。通过 ObjectExtensionManager,其他模块可以为 MyEntityName 等实体添加额外的 EF Core 映射属性(如新字段),而无需修改核心代码,实现了良好的模块化和可扩展性。
Section sources
Dapper 使用场景与性能优化
项目同时集成了 Dapper,通过 ICMSPluginDapperRepository 接口和 CMSPluginDapperRepository 实现。CMSPluginDapperRepository 继承自 DapperRepository<ICMSPluginDbContext>,共享 EF Core 的数据库连接和事务。
Diagram sources
使用场景
Dapper 适用于以下场景:
- 复杂查询:涉及多表连接、子查询、窗口函数等 EF Core 生成 SQL 效率较低的场景
- 高性能读取:对性能要求极高的只读查询,Dapper 的映射开销远低于 EF Core
- 存储过程调用:直接调用数据库存储过程
- 批量操作:使用
SqlBulkCopy或 Dapper 扩展进行高效批量插入/更新
与 EF Core 的互补关系
- EF Core:用于常规的 CRUD 操作、变更跟踪、事务管理,适合写操作和简单查询
- Dapper:用于复杂、高性能的读操作,作为 EF Core 的补充
性能优化建议
- 读写分离:写操作使用 EF Core 仓储,读操作优先考虑 Dapper
- **避免 SELECT ***:Dapper 查询应明确指定所需字段
- 参数化查询:始终使用参数化查询防止 SQL 注入
- 连接复用:通过
GetDbConnectionAsync获取连接,确保在 EF Core 事务中的一致性 - 异步编程:使用
QueryAsync等异步方法避免阻塞 - 缓存策略:对不常变的数据,结合 Redis 等缓存 Dapper 查询结果
Section sources
总结
本项目构建了一套完整、灵活且高性能的数据持久化机制。通过仓储模式实现领域层与数据访问的解耦,利用 EF Core 提供强大的 ORM 功能和实体映射能力,支持多数据库。通过独立的 Dapper 集成,解决了复杂查询和性能瓶颈问题。实体扩展机制确保了系统的可扩展性。整体设计遵循 DDD 原则,为业务发展提供了坚实的数据基础。