如何在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
相关产品推荐
相关产品推荐

