C#/.NET中结合Dependency injection与Unit of Work模式的实现问题
解决方案
下面提供3种可直接落地的方案,均无需为每个仓储单独实现工厂类:
方案1:通用泛型仓储工厂
仅需实现1个泛型工厂,即可支持所有仓储的创建,兼容带UoW和无UoW两种场景。
实现代码
// 工厂接口 public interface IRepositoryFactory { // 支持传入可选的UoW参数 TRepository Create<TRepository>(IUnitOfWork? uow = null) where TRepository : IRepository; } // 工厂实现 public class RepositoryFactory : IRepositoryFactory { private readonly IConnectionFactory _connectionFactory; // ConnectionFactory从DI注入 public RepositoryFactory(IConnectionFactory connectionFactory) { _connectionFactory = connectionFactory; } public TRepository Create<TRepository>(IUnitOfWork? uow = null) where TRepository : IRepository { // 用.NET内置依赖注入工具类自动构造仓储实例,自动匹配构造参数 return uow == null ? ActivatorUtilities.CreateInstance<TRepository>(_connectionFactory) : ActivatorUtilities.CreateInstance<TRepository>(uow, _connectionFactory); } }
注册方式
在DI容器中仅需注册一次工厂即可:
services.AddSingleton<IRepositoryFactory, RepositoryFactory>();
使用示例
// 带UoW场景 using (var uow = new UnitOfWork(_connectionFactory)) // 或者UoW也从工厂/DI获取 { var itemRepository = _repositoryFactory.Create<ItemRepository>(uow); itemRepository.Add(new Item()); uow.Commit(); } // 无UoW场景 var itemRepository = _repositoryFactory.Create<ItemRepository>(); var item = itemRepository.Get(itemId);
优缺点
- 优点:实现简单,职责单一,不需要修改现有UoW和仓储的实现逻辑
- 缺点:需要在使用仓储的类中注入IRepositoryFactory实例
方案2:UoW内置仓储提供者
将仓储创建能力内置到UoW实现中,一次获取UoW即可直接拿到所有关联的仓储,代码更简洁。
实现代码
public interface IUnitOfWork : IDisposable { IDbConnection Connection { get; } IDbTransaction? Transaction { get; } void Commit(); void Rollback(); // 新增泛型方法获取仓储 TRepository GetRepository<TRepository>() where TRepository : IRepository; } public class UnitOfWork : IUnitOfWork { private readonly IConnectionFactory _connectionFactory; private IDbConnection? _connection; private IDbTransaction? _transaction; // 缓存已创建的仓储,避免重复实例化 private readonly Dictionary<Type, object> _repoCache = new(); // ConnectionFactory从DI注入 public UnitOfWork(IConnectionFactory connectionFactory) { _connectionFactory = connectionFactory; _connection = _connectionFactory.CreateConnection(); _connection.Open(); _transaction = _connection.BeginTransaction(); } public TRepository GetRepository<TRepository>() where TRepository : IRepository { var repoType = typeof(TRepository); if (_repoCache.TryGetValue(repoType, out var existRepo)) { return (TRepository)existRepo; } // 构造仓储时自动传入当前UoW实例 var newRepo = ActivatorUtilities.CreateInstance<TRepository>(this, _connectionFactory); _repoCache.Add(repoType, newRepo); return newRepo; } // 省略Commit、Rollback、Dispose的常规实现 }
使用示例
// UoW从DI注入获得 using (var uow = _unitOfWork) { var itemRepository = uow.GetRepository<ItemRepository>(); itemRepository.Add(new Item()); uow.Commit(); }
优缺点
- 优点:使用方便,代码更简洁,同一个UoW下的仓储实例自动复用,保证事务一致性
- 缺点:UoW的职责略有扩展,不符合严格的单一职责原则,绝大多数业务场景下可忽略该问题
方案3:直接利用DI容器创建仓储(无需额外工厂类)
如果你的项目中已经将所有仓储注册到DI容器,可以直接用ActivatorUtilities手动构造,连工厂类都不需要写:
// 在需要创建仓储的类中注入IServiceProvider private readonly IServiceProvider _serviceProvider; // 带UoW场景 using (var uow = new UnitOfWork(_connectionFactory)) { // 手动传入构造参数uow,剩余依赖从DI容器自动填充 var itemRepository = ActivatorUtilities.CreateInstance<ItemRepository>(_serviceProvider, uow); itemRepository.Add(new Item()); uow.Commit(); } // 无UoW场景 var itemRepository = ActivatorUtilities.CreateInstance<ItemRepository>(_serviceProvider); var item = itemRepository.Get(itemId);
优缺点
- 优点:不需要编写任何工厂类,零维护成本
- 缺点:直接依赖IServiceProvider属于服务定位器模式,不符合显式依赖原则,对代码规范要求较高的项目慎用
关键注意点
所有方案均依赖.NET内置的ActivatorUtilities类(位于Microsoft.Extensions.DependencyInjection包,ASP.NET Core项目默认自带),会自动根据仓储的构造函数参数匹配传入的实例和DI容器中的服务,你只需要保证仓储的构造函数符合以下规则即可:
public class ItemRepository : IRepository<Item> { private readonly IDbConnection _connection; private readonly IDbTransaction? _transaction; // 带UoW的构造函数 public ItemRepository(IUnitOfWork uow, IConnectionFactory connectionFactory) { _connection = uow.Connection; _transaction = uow.Transaction; } // 无UoW的构造函数 public ItemRepository(IConnectionFactory connectionFactory) { _connection = connectionFactory.CreateConnection(); } // 省略仓储方法实现 }
内容的提问来源于stack exchange,提问作者wekso
相关产品推荐
相关产品推荐

