You need to enable JavaScript to run this app. C# .NET Core 如何在UnitOfWork中实现类似EF的Set<T>泛型方法按类型访问仓储
优惠活动
大模型
产品
解决方案
定价
更多

C# .NET Core 如何在UnitOfWork中实现类似EF的Set<T>泛型方法按类型访问仓储

UnitOfWork 泛型Set仓储获取方法实现

核心实现思路:内部维护「仓储接口类型 - 仓储实例」的映射缓存,通过泛型方法匹配返回对应仓储实例,和EF Core的Set<T>调用体验对齐,同时完全兼容原有硬编码仓储属性的旧代码,不需要改动现有控制器的业务逻辑。

1. 扩展IUnitOfWork接口

在原有接口基础上新增泛型仓储获取方法声明,保留原有具体仓储属性的定义,完全兼容历史代码的调用:

public interface IUnitOfWork : IDisposable
{
    // 原有具体仓储属性,适配历史调用
    IBooksRepository Books { get; }
    ICatalogueRepository Catalogues { get; }

    // 新增泛型仓储获取方法,可根据业务需要追加更严格的泛型约束
    TRepository Set<TRepository>() where TRepository : class;
}

2. UnitOfWork核心实现

类内部维护仓储实例缓存字典,结合DI容器完成实例解析,无硬编码的仓储依赖:

public class UnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _dbContext;
    private readonly IServiceProvider _serviceProvider;
    // 仓储实例缓存字典,键为仓储接口类型,值为对应实例
    private readonly Dictionary<Type, object> _repositoryCache = new();

    public UnitOfWork(AppDbContext dbContext, IServiceProvider serviceProvider)
    {
        _dbContext = dbContext;
        _serviceProvider = serviceProvider;
    }

    // 原有仓储属性直接复用Set方法返回实例,无需重复初始化
    public IBooksRepository Books => Set<IBooksRepository>();
    public ICatalogueRepository Catalogues => Set<ICatalogueRepository>();

    // 核心泛型Set方法实现
    public TRepository Set<TRepository>() where TRepository : class
    {
        var repositoryInterfaceType = typeof(TRepository);
        // 优先从缓存匹配当前请求周期内已初始化的仓储实例
        if (_repositoryCache.TryGetValue(repositoryInterfaceType, out var cachedInstance))
        {
            return (TRepository)cachedInstance;
        }

        // 缓存未命中时,从DI容器解析对应仓储实例
        var resolvedInstance = _serviceProvider.GetRequiredService<TRepository>();
        // 实例写入缓存,供后续同请求周期内复用
        _repositoryCache[repositoryInterfaceType] = resolvedInstance;
        
        return resolvedInstance;
    }

    // 原有工作单元逻辑完全保留
    public int Complete() => _dbContext.SaveChanges();
    public void Dispose() => _dbContext.Dispose();
}

3. DI配置适配

  • 所有仓储接口与实现类的原有DI注册逻辑完全不需要改动,Set方法会自动从容器解析对应实例。
  • UnitOfWork保持原有Scoped生命周期注册即可,与DbContext、仓储的生命周期保持一致,缓存的仓储实例为请求级别,无跨实例泄漏风险。

4. 调用兼容效果

  • 原有硬编码调用方式不受影响,可正常运行:
// 旧代码无侵入改造
var allBooks = _unitOfWork.Books.GetAll();
  • 支持泛型通用逻辑调用,可编写通用查询、权限校验逻辑,无需为每个仓储重复写相似代码:
// 新的泛型调用方式,对齐EF Core Set<T>体验
public async Task<List<TEntity>> GetAllAsync<TRepository, TEntity>() 
    where TRepository : IBaseRepository<TEntity>
{
    var repository = _unitOfWork.Set<TRepository>();
    return await repository.GetAllAsync();
}

可选优化点

  • 若所有仓储接口都继承统一的IRepository基接口,可将Set方法的泛型约束调整为where TRepository : IRepository,在编译期拦截非仓储类型的错误传入,提升类型安全性。
  • 若项目未使用DI托管仓储实例,可在类初始化阶段手动将接口与实例的映射写入缓存字典,无需改动Set方法的核心逻辑。

内容的提问来源于stack exchange,提问作者Rahul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.29 09:36:03