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

如何在Test与Controller类中实例化Repository且不通过DI传入Context?

现有代码

Test.cs

public class Test
{
    private readonly Mock<Repository> _repository;

    public Test()
    {
        this._repository = new Mock<Repository>();
    }

    [Fact]
    public async void Test1()
    {
        this._repository.Setup(x => x.Rep.GetAllAsync()) 
            .ReturnsAsync(Fix.GetMock());

        controller sut = new controller();
        ActionResult result = await sut.GetAll();
        OkObjectResult objectResults = (OkObjectResult) result;
        objectResults?.Value.Should().BeOfType<Entity[]>();
    }
}

Controller.cs

public class Controller : BaseApiController
{
    private readonly IRepository _repository;

    [HttpGet]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<ActionResult> GetAll()
    {
        IEnumerable<Rep> rep = await _repository.Rep.GetAllAsync();
        return Ok(rep);
    }
}

Repository.cs

public class Repository : IRepository
{
    private readonly Context _context;

    public Repository(Context context)
    {
        this._context = context;
    }

    public ValueTask<IEnumerable<Rep>> GetAllAsync(Rep rep)
    {
        return this._context.Set<Rep>().ToArrayAsync();
    }
}

技术疑问

如何在Test类与Controller类中使用并实例化Repository,且无需在Test/Controller的构造函数中通过依赖注入(DI)传入Context?


解决方案

1. 调整Controller的依赖逻辑

Controller只需要依赖IRepository抽象,无需直接处理Context。补充构造函数注入逻辑,让DI容器负责Repository和Context的实例关联:

public class Controller : BaseApiController
{
    private readonly IRepository _repository;

    // 注入IRepository,完全不接触Context
    public Controller(IRepository repository)
    {
        _repository = repository;
    }

    [HttpGet]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public async Task<ActionResult> GetAll()
    {
        // 匹配Repository中GetAllAsync的参数定义
        IEnumerable<Rep> rep = await _repository.GetAllAsync(null);
        return Ok(rep);
    }
}

在项目的DI配置(如Program.cs)中注册依赖关系,由容器自动处理Context到Repository的注入:

builder.Services.AddDbContext<Context>(options => 
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

2. 测试类Mock抽象而非具体类

测试时完全隔离数据库依赖,直接MockIRepository接口,不需要创建Context或Repository实例:

public class Test
{
    private readonly Mock<IRepository> _mockRepository;
    private readonly Controller _sut;

    public Test()
    {
        _mockRepository = new Mock<IRepository>();
        // 传入Mock的IRepository实例给Controller,无需Context
        _sut = new Controller(_mockRepository.Object);
    }

    [Fact]
    public async Task Test1() // 用Task替代void,避免异步测试遗漏问题
    {
        // 匹配GetAllAsync的参数规则
        _mockRepository.Setup(x => x.GetAllAsync(It.IsAny<Rep>()))
            .ReturnsAsync(Fix.GetMock());

        ActionResult result = await _sut.GetAll();
        OkObjectResult objectResults = result as OkObjectResult;
        objectResults?.Value.Should().BeOfType<Entity[]>();
    }
}

3. 可选:给Repository添加静态工厂方法(手动实例化场景)

如果需要在非DI场景手动创建Repository,可以添加静态工厂方法,内部封装Context的创建逻辑:

public class Repository : IRepository
{
    private readonly Context _context;

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

    // 静态工厂方法,内部创建Context并返回Repository实例
    public static IRepository Create()
    {
        var options = new DbContextOptionsBuilder<Context>()
            .UseInMemoryDatabase(databaseName: "TempTestDb") // 示例用内存数据库
            .Options;
        var context = new Context(options);
        return new Repository(context);
    }

    public ValueTask<IEnumerable<Rep>> GetAllAsync(Rep rep)
    {
        return _context.Set<Rep>().ToArrayAsync();
    }
}

使用时直接调用工厂方法即可,无需手动传入Context:

var repo = Repository.Create();

核心要点

  • 依赖倒置:始终依赖IRepository抽象而非具体实现,彻底解耦Context。
  • 测试隔离:通过Mock接口避免数据库依赖,提升测试效率和稳定性。
  • 容器托管:生产环境中由DI容器统一管理Repository和Context的生命周期,上层代码无需关心底层依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:20:35