在ABP VNext项目中实现FreeSql与SqlSugar双ORM协同开发实战
当企业级应用需要同时支持不同团队的开发习惯或针对特定业务模块选择最优ORM方案时,单一数据访问层的局限性就会显现。本文将深入探讨如何在ABP VNext框架下构建同时兼容FreeSql和SqlSugar的混合架构,通过模块化设计实现技术栈的灵活组合。
1. 混合ORM架构设计原理
ABP框架的模块化系统为多ORM共存提供了天然支持。其依赖注入容器允许我们为不同仓储注册不同的ORM实现,关键在于正确划分领域边界并设计适当的抽象层。
核心设计原则:
- 领域隔离:不同业务模块使用独立的数据访问层
- 统一抽象:通过基础仓储类封装ORM特定操作
- 透明切换:业务代码无需感知底层ORM实现
典型的混合架构包含以下组件:
graph TD A[ABP应用模块] --> B[FreeSql模块] A --> C[SqlSugar模块] B --> D[FreeSql仓储基类] C --> E[SqlSugar仓储基类] D --> F[具体业务仓储] E --> F2. 环境配置与基础搭建
2.1 项目初始化
使用ABP CLI创建新解决方案:
abp new Acme.BookStore -t app -u mvc --mobile none --database-provider none2.2 ORM组件安装
通过NuGet添加必要包:
<!-- FreeSql 全套件 --> <PackageReference Include="FreeSql.Provider.MySql" Version="3.2.800" /> <PackageReference Include="FreeSql.DbContext" Version="3.2.800" /> <!-- SqlSugar 核心库 --> <PackageReference Include="SqlSugarCore" Version="5.1.4.63" />2.3 数据库连接配置
在appsettings.json中配置数据库连接:
{ "ConnectionStrings": { "Default": "Server=localhost;Port=3306;Database=BookStore;Uid=root;Pwd=123456;" } }3. ORM模块化集成实现
3.1 FreeSql模块封装
创建FreeSqlModule.cs实现ABP模块:
[DependsOn(typeof(AbpDddModule))] public class FreeSqlModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); var freeSql = new FreeSqlBuilder() .UseConnectionString(DataType.MySql, configuration.GetConnectionString("Default")) .UseAutoSyncStructure(false) // 禁用自动迁移 .Build(); context.Services.AddSingleton<IFreeSql>(freeSql); // 注册FreeSql的UnitOfWorkManager context.Services.AddUnitOfWork<FreeSqlUnitOfWorkManager>(); } }3.2 SqlSugar模块封装
创建SqlSugarModule.cs实现ABP模块:
[DependsOn(typeof(AbpDddModule))] public class SqlSugarModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); context.Services.AddSingleton<ISqlSugarClient>(provider => { var sqlSugar = new SqlSugarScope(new ConnectionConfig() { DbType = DbType.MySql, ConnectionString = configuration.GetConnectionString("Default"), IsAutoCloseConnection = true }, db => { // AOP配置 db.Aop.OnLogExecuting = (sql, pars) => { Logger<SqlSugarModule>.Debug($"Executing SQL: {sql}"); }; }); return sqlSugar; }); } }4. 仓储层设计与实现
4.1 FreeSql仓储基类
public abstract class FreeSqlRepository<TEntity> : DomainService where TEntity : class, IEntity { protected IFreeSql FreeSql => LazyServiceProvider.LazyGetRequiredService<IFreeSql>(); public virtual async Task<TEntity> GetAsync(Guid id) { return await FreeSql.Select<TEntity>() .Where(x => x.Id == id) .FirstAsync(); } public virtual async Task InsertAsync(TEntity entity) { await FreeSql.Insert(entity).ExecuteAffrowsAsync(); } }4.2 SqlSugar仓储基类
public abstract class SqlSugarRepository<TEntity> : DomainService where TEntity : class, IEntity { protected ISqlSugarClient Db => LazyServiceProvider.LazyGetRequiredService<ISqlSugarClient>(); public virtual async Task<TEntity> GetAsync(Guid id) { return await Db.Queryable<TEntity>() .Where(x => x.Id == id) .FirstAsync(); } public virtual async Task InsertAsync(TEntity entity) { await Db.Insertable(entity).ExecuteCommandAsync(); } }5. 业务层实现与使用
5.1 多ORM业务服务示例
public class BookAppService : ApplicationService { private readonly FreeSqlBookRepository _freeSqlRepo; private readonly SqlSugarAuthorRepository _sqlSugarRepo; public BookAppService( FreeSqlBookRepository freeSqlRepo, SqlSugarAuthorRepository sqlSugarRepo) { _freeSqlRepo = freeSqlRepo; _sqlSugarRepo = sqlSugarRepo; } public async Task<BookDto> CreateBookWithAuthorAsync(CreateBookDto input) { // 使用FreeSql处理书籍数据 var book = new Book { Name = input.BookName }; await _freeSqlRepo.InsertAsync(book); // 使用SqlSugar处理作者数据 var author = new Author { Name = input.AuthorName }; await _sqlSugarRepo.InsertAsync(author); return ObjectMapper.Map<Book, BookDto>(book); } }5.2 事务处理方案
public async Task TransactionalOperationAsync() { using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) { try { // FreeSql操作 await _freeSqlRepo.InsertAsync(entity1); // SqlSugar操作 await _sqlSugarRepo.InsertAsync(entity2); await uow.CompleteAsync(); } catch { await uow.RollbackAsync(); throw; } } }6. 性能优化与最佳实践
6.1 ORM选择策略
| 场景特征 | 推荐ORM | 原因 |
|---|---|---|
| 复杂查询 | FreeSql | Lambda表达式更丰富 |
| 简单CRUD | SqlSugar | 语法更简洁 |
| 批量操作 | SqlSugar | Bulk操作性能更优 |
| 多表关联 | FreeSql | 导航属性支持更好 |
6.2 常见问题解决方案
并发冲突:
- FreeSql:启用乐观锁
.UseGenerateCommandParameterWithLambda(true) .UseLazyLoading(false)- SqlSugar:使用事务隔离级别
db.Ado.BeginTran(IsolationLevel.ReadCommitted);日志记录:
// FreeSql日志 freeSql.Aop.CurdAfter += (s, e) => { Logger.LogDebug($"FreeSql: {e.Sql}"); }; // SqlSugar日志 db.Aop.OnLogExecuting = (sql, pars) => { Logger.LogDebug($"SqlSugar: {sql}"); };
7. 项目结构推荐
src/ ├── Acme.BookStore.Application ├── Acme.BookStore.Domain ├── Acme.BookStore.EntityFrameworkCore (可选) ├── Acme.BookStore.FreeSql │ ├── Repositories │ ├── Modules │ └── Entities ├── Acme.BookStore.SqlSugar │ ├── Repositories │ ├── Modules │ └── Entities └── Acme.BookStore.HttpApi.Host在实际项目中使用这种混合架构时,建议建立明确的团队规范,确定哪些模块使用哪种ORM,避免同一实体被不同ORM操作导致混乱。根据我们的实践,将核心领域模型交给更成熟的团队使用FreeSql处理,而将边缘业务交给偏好SqlSugar的团队开发,这种分工方式取得了不错的效果。