You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core仓储模式下解析两个DbContext及依赖注入错误解决

问题描述

拥有两个DbContext类型:

private static readonly AutomationContext _aContext;
private static readonly RajaDb2Context _bContext;

同时定义了两个结构完全一致的仓储接口:

public interface IRepository<T>
{
    T? GetById(object id);
    T? GetOne(Expression<Func<T, bool>> predicate);
    IEnumerable<T> Get(Expression<Func<T, bool>> predicate);
    IEnumerable<T> GetAll();
    void Add(T entity);
    void AddRange(IEnumerable<T> entities);
    void Remove(T entity);
    void RemoveRange(IEnumerable<T> entities);
    void Update(T entity);
    void UpdateRange(IEnumerable<T> entities);
}

public interface IRepository2<T>
{
    T? GetById(object id);
    T? GetOne(Expression<Func<T, bool>> predicate);
    IEnumerable<T> Get(Expression<Func<T, bool>> predicate);
    IEnumerable<T> GetAll();
    void Add(T entity);
    void AddRange(IEnumerable<T> entities);
    void Remove(T entity);
    void RemoveRange(IEnumerable<T> entities);
    void Update(T entity);
    void UpdateRange(IEnumerable<T> entities);
}

需求是将AutomationContext注入到IRepository<T>的实现,RajaDb2Context注入到IRepository2<T>的实现。编写了泛型解析器类:

public static class DependencyResolver<T> where T : class
{
    private static readonly AutomationContext _aContext;
    private static readonly RajaDb2Context _bContext;

    public static IRepository<T> ResolveAutomation
    {
        get
        {
            return new Repository<T>(_aContext);
        }
    }

    public static IRepository2<T> Raja2
    {
        get
        {
            return new RajaDb2Repository<T>(_bContext);
        }
    }
}

但在DI容器注册时:

services.AddScoped(typeof(IRepository<>), DependencyResolver<>.ResolveAutomation);
services.AddScoped(typeof(IRepository2<>), DependencyResolver<>.Raja2);

出现错误:

Error CS0305
Using the generic type 'DependencyResolver' requires 1 type arguments

仓储实现类Repository<T>如下:

public class Repository<T> : IRepository<T> where T : class
{
    private readonly DbContext _context;

    public Repository(DbContext context)
    {
        _context = context;
    }

    public T? GetById(object id)
    {
        return _context.Set<T>().Find(id);
    }

    public T? GetOne(Expression<Func<T, bool>> predicate)
    {
        return _context.Set<T>().FirstOrDefault(predicate);
    }

    public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
    {
        return _context.Set<T>().Where(predicate).AsEnumerable();
    }

    public IEnumerable<T> GetAll()
    {
        return _context.Set<T>().AsEnumerable();
    }

    public void Add(T entity)
    {
        _context.Set<T>().Add(entity);
        _context.SaveChanges();
    }

    public void AddRange(IEnumerable<T> entities)
    {
        _context.Set<T>().AddRange(entities);
        _context.SaveChanges();
    }

    public void Remove(T entity)
    {
        _context.Set<T>().Remove(entity);
        _context.SaveChanges();
    }

    public void RemoveRange(IEnumerable<T> entities)
    {
        _context.Set<T>().RemoveRange(entities);
        _context.SaveChanges();
    }

    public void Update(T entity)
    {
        _context.Set<T>().Update(entity);
        _context.SaveChanges();
    }

    public void UpdateRange(IEnumerable<T> entities)
    {
        _context.Set<T>().UpdateRange(entities);
        _context.SaveChanges();
    }
}
错误原因
  1. 泛型类访问错误:DependencyResolver<T>是带类型参数的泛型类,无法通过DependencyResolver<>.ResolveAutomation这种开放式泛型语法访问其静态属性,必须指定具体的类型参数(如DependencyResolver<Entity>.ResolveAutomation),但DI注册泛型服务需要的是开放式泛型的映射,这种方式无法满足。
  2. 静态DbContext未初始化:DependencyResolver<T>中的_aContext和_bContext是静态只读字段,但没有赋值,即使解决泛型问题,运行时也会抛出空引用异常,且静态字段无法利用DI容器的生命周期管理。
正确解决方案

步骤1:注册DbContext到DI容器

首先确保两个DbContext已正确注册到DI,示例如下(根据实际数据库类型调整):

// 注册AutomationContext
services.AddDbContext<AutomationContext>(options =>
    options.UseSqlServer("Your_Automation_Connection_String"));

// 注册RajaDb2Context
services.AddDbContext<RajaDb2Context>(options =>
    options.UseSqlServer("Your_RajaDb2_Connection_String"));

步骤2:创建对应IRepository2的实现类

为了清晰绑定不同的DbContext,为IRepository2<T>创建专属实现类(复用Repository<T>的逻辑,仅依赖RajaDb2Context):

public class RajaDb2Repository<T> : IRepository2<T> where T : class
{
    private readonly RajaDb2Context _context;

    public RajaDb2Repository(RajaDb2Context context)
    {
        _context = context;
    }

    public T? GetById(object id)
    {
        return _context.Set<T>().Find(id);
    }

    public T? GetOne(Expression<Func<T, bool>> predicate)
    {
        return _context.Set<T>().FirstOrDefault(predicate);
    }

    public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
    {
        return _context.Set<T>().Where(predicate).AsEnumerable();
    }

    public IEnumerable<T> GetAll()
    {
        return _context.Set<T>().AsEnumerable();
    }

    public void Add(T entity)
    {
        _context.Set<T>().Add(entity);
        _context.SaveChanges();
    }

    public void AddRange(IEnumerable<T> entities)
    {
        _context.Set<T>().AddRange(entities);
        _context.SaveChanges();
    }

    public void Remove(T entity)
    {
        _context.Set<T>().Remove(entity);
        _context.SaveChanges();
    }

    public void RemoveRange(IEnumerable<T> entities)
    {
        _context.Set<T>().RemoveRange(entities);
        _context.SaveChanges();
    }

    public void Update(T entity)
    {
        _context.Set<T>().Update(entity);
        _context.SaveChanges();
    }

    public void UpdateRange(IEnumerable<T> entities)
    {
        _context.Set<T>().UpdateRange(entities);
        _context.SaveChanges();
    }
}

步骤3:注册泛型仓储接口与实现

直接通过DI的泛型注册能力,将接口与对应实现绑定,DI会自动注入匹配的DbContext:

// 绑定IRepository<T>到Repository<T>,自动注入AutomationContext
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

// 绑定IRepository2<T>到RajaDb2Repository<T>,自动注入RajaDb2Context
services.AddScoped(typeof(IRepository2<>), typeof(RajaDb2Repository<>));

替代方案(不新增仓储实现类)

如果不想新增RajaDb2Repository<T>,可以通过工厂方法注册,手动指定注入的DbContext:

// 注册IRepository<T>,注入AutomationContext
services.AddScoped(typeof(IRepository<>), sp =>
{
    var entityType = typeof(IRepository<>).GetGenericArguments()[0];
    var repositoryType = typeof(Repository<>).MakeGenericType(entityType);
    var context = sp.GetRequiredService<AutomationContext>();
    return (IRepository<object>)Activator.CreateInstance(repositoryType, context);
});

// 注册IRepository2<T>,注入RajaDb2Context
services.AddScoped(typeof(IRepository2<>), sp =>
{
    var entityType = typeof(IRepository2<>).GetGenericArguments()[0];
    var repositoryType = typeof(Repository<>).MakeGenericType(entityType);
    var context = sp.GetRequiredService<RajaDb2Context>();
    return (IRepository2<object>)Activator.CreateInstance(repositoryType, context);
});

额外优化建议

  • 两个仓储接口结构完全一致,可以考虑提取一个基础接口IReadOnlyRepository<T>和IWriteRepository<T>,减少代码重复。
  • 仓储实现中的SaveChanges()建议放到业务层统一调用,避免每个操作都提交,提升事务控制灵活性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:18:10