ASP.NET Core控制器多构造函数冲突问题的解决方法
问题解决:控制器多构造函数导致Swagger报错
问题根源
ASP.NET Core依赖注入容器解析控制器时,要求每个控制器只能有一个可被DI解析的公开构造函数。你新增的测试构造函数和原有构造函数都能被DI容器解析(IMediator已在DI中注册),容器无法确定使用哪个,因此抛出System.InvalidOperationException错误。[FromServices]特性用于标记参数来自DI,无法解决多构造函数的冲突。
解决方案
方案1:合并构造函数(推荐,符合最佳实践)
控制器只保留一个包含所有依赖的公开构造函数,单元测试时对未用到的依赖创建空Mock即可。
修改后的控制器代码
public class ProductController : ControllerBase { private readonly IMediator _mediator; private readonly IConfiguration _configuration; private readonly UserManager<User> _userManager; private readonly SignInManager<User> _signInManager; private readonly ProductDbContext _productDbContext; private readonly IUserService _userService; // 仅保留一个公开构造函数,注入所有依赖 public ProductController(IConfiguration configuration, UserManager<User> userManager, ProductDbContext productDbContext , SignInManager<User> signInManager, IUserService userService, IMediator mediator) { _configuration = configuration; _userManager = userManager; _productDbContext = productDbContext; _signInManager = signInManager; _userService = userService; _mediator = mediator; } [HttpGet] [AllowAnonymous] public async Task<IActionResult> GetAllProducts() { var products = await _mediator.Send(new GetAllProductsQuery()); return Ok(products); } }
修改后的单元测试代码
private readonly Mock<IMediator> _mediatorMock; private readonly ProductController _controller; public ProductControllerTest() { _mediatorMock = new Mock<IMediator>(); // 用Mock.Of创建未用到的依赖的空Mock,无需额外配置 var configMock = Mock.Of<IConfiguration>(); var userManagerMock = Mock.Of<UserManager<User>>(); var dbContextMock = Mock.Of<ProductDbContext>(); var signInManagerMock = Mock.Of<SignInManager<User>>(); var userServiceMock = Mock.Of<IUserService>(); _controller = new ProductController(configMock, userManagerMock, dbContextMock, signInManagerMock, userServiceMock, _mediatorMock.Object); } [Fact] public async Task ProductController_GetAllProducts_ShouldReturnStatusCode200() { //arrange var expected = new List<ProductModelDTO>(); _mediatorMock.Setup(m=> m.Send(It.IsAny<GetAllProductsQuery>(), default(CancellationToken))).ReturnsAsync(expected); //act var result = await _controller.GetAllProducts(); //assert var okResult = result as OkObjectResult; okResult?.StatusCode.Should().Be(200); okResult?.Value.Should().BeOfType<List<ProductModelDTO>>(); }
方案2:标记主构造函数(不推荐,维护性差)
若不想合并构造函数,可给生产环境用的构造函数添加[ActivatorUtilitiesConstructor]特性,指定DI容器优先使用该构造函数;同时将测试用构造函数设为internal,通过InternalsVisibleTo让测试项目可见。
// 标记生产用构造函数为主构造函数 [ActivatorUtilitiesConstructor] public ProductController(IConfiguration configuration, UserManager<User> userManager, ProductDbContext productDbContext , SignInManager<User> signInManager, IUserService userService) { _configuration = configuration; _userManager = userManager; _productDbContext = productDbContext; _signInManager = signInManager; _userService = userService; } // 测试用构造函数设为internal internal ProductController(IMediator mediator) { _mediator = mediator; }
在API项目的AssemblyInfo.cs中添加:
[assembly: InternalsVisibleTo("YourTestProjectName")]
此方法虽能解决问题,但会模糊控制器的依赖关系,长期维护成本高,不推荐使用。
内容的提问来源于stack exchange,提问作者Aboliii
相关产品推荐
相关产品推荐

