如何实现Conditional Cascade Dependency Injection?UoW与Repo共享DB连接求助
解决方案
核心思路是利用依赖注入的Scoped生命周期保证同一请求(作用域)内共享同一个IDbConnection,同时让Uow负责连接初始化与事务管理,Repo直接复用已就绪的连接,无需嵌套结构。
步骤1:调整依赖注入注册
修正Program.cs中的服务注册逻辑,确保生命周期与依赖关系正确:
DBContext注册为单例(仅持有连接字符串,无状态,适合全局复用)IDbConnection注册为Scoped,由DBContext动态创建(每个作用域对应一个独立连接)Uow和Repo注册为Scoped,自动注入同一作用域的IDbConnection
// Program.cs var builder = WebApplication.CreateBuilder(args); // 1. 注册单例DBContext builder.Services.AddSingleton<DBContext>(sp => new DBContext(@"Data Source=...")); // 2. 注册Scoped的IDbConnection,每个请求创建一次 builder.Services.AddScoped<IDbConnection>(sp => { var dbContext = sp.GetRequiredService<DBContext>(); return dbContext.GetConnection(Models.ConnectionType.SqlLite); }); // 3. 注册Uow和Repo,自动注入同一作用域的连接 builder.Services.AddScoped<IUow, Uow>(); builder.Services.AddScoped<IRepoAaa, RepoAaa>(); var app = builder.Build(); // ... 后续中间件配置
步骤2:保持原有Uow和Repo实现不变
你的Uow构造函数已经负责打开连接并启动事务,Repo直接复用注入的连接,无需修改核心逻辑:
// Uow实现 public class Uow : IUow, IDisposable { private readonly IDbConnection _dbConnection; private readonly IDbTransaction _dbTransaction; private bool _disposedValue; public Uow(IDbConnection dbConnection) { _dbConnection = dbConnection; _dbConnection.Open(); _dbTransaction = _dbConnection.BeginTransaction(); } // 补充事务操作方法 public void Commit() => _dbTransaction.Commit(); public void Rollback() => _dbTransaction.Rollback(); protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { _dbTransaction?.Dispose(); _dbConnection?.Dispose(); } _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } // RepoAaa实现 public class RepoAaa : IRepoAaa, IDisposable { private readonly IDbConnection _dbConnection; private bool _disposedValue; public RepoAaa(IDbConnection dbConnection) { _dbConnection = dbConnection; } // 示例SQL执行方法(后续可直接替换为Dapper) public IEnumerable<SomeModel> GetAll() { using var cmd = _dbConnection.CreateCommand(); cmd.CommandText = "SELECT * FROM SomeTable"; using var reader = cmd.ExecuteReader(); // ... 数据映射逻辑 return new List<SomeModel>(); } protected virtual void Dispose(bool disposing) { if (!_disposedValue) { _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } }
方案合理性说明
- 单例DBContext:仅持有连接字符串,无状态且线程安全,适合全局复用。
- Scoped连接:每个HTTP请求对应一个独立连接,既避免连接池浪费,又保证同一请求内Uow和Repo共享连接。
- 初始化顺序保障:DI容器会先创建
IDbConnection,再注入到Uow,Uow构造时自动打开连接并启动事务,后续Repo直接使用已就绪的连接。 - 无嵌套结构:Repo直接依赖
IDbConnection,无需从Uow中获取,符合你的设计要求。
额外优化建议
- 将连接配置移至
appsettings.json,避免硬编码:
注册时从配置读取:// appsettings.json { "Database": { "ConnectionString": "Data Source=...", "ConnectionType": "SqlLite" } }var dbConfig = builder.Configuration.GetSection("Database"); builder.Services.AddSingleton<DBContext>(sp => new DBContext(dbConfig["ConnectionString"])); builder.Services.AddScoped<IDbConnection>(sp => { var dbContext = sp.GetRequiredService<DBContext>(); var connectionType = Enum.Parse<ConnectionType>(dbConfig["ConnectionType"]); return dbContext.GetConnection(connectionType); }); - 后续引入Dapper时,直接在Repo中使用
_dbConnection.Query<T>(...)即可,无需修改依赖结构。
内容的提问来源于stack exchange,提问作者MckPT
相关产品推荐
相关产品推荐

