从另一个控制器调用控制器时的依赖关系注入

本文关键字:控制器 依赖 关系 注入 另一个 调用 | 更新日期: 2023-09-27 18:37:04

我有一个 ASP.NET 5.0(vnext)项目,我正在实现Web Api和Mvc前端。我希望我的 Mvc 控制器调用 Web API 控制器,它工作正常。我根据 http://www.asp.net/vnext/overview/aspnet-vnext/create-a-web-api-with-mvc-6 上的示例构建了 api,它运行良好。Mvc 前端可以成功调用 WebApi 控制器,但是当我从 Mvc 控制器实例化它时,依赖注入框架不会提供 ITodoRepository。

public class Startup
{
    public void Configure(IApplicationBuilder app, ILoggerFactory logFactory)
    {
        ...
        app.UseServices(services =>
        {
            services.AddSingleton<ITodoRepository, TodoRepository>();
        });
        ...
[Route("api/[controller]")]
public class TodoController : Controller
{
    /* The ITodoRepository gets created and injected, but only when the class is activated by Mvc */
    TodoController(ITodoRepository repository)
    {
        _repository = repository;
    }
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        return _repository.AllItems;
    }
    ...
public class HomeController : Controller
{
    public IActionResult Index()
    {
        var tc = new TodoController(/* have to create my own ITodoRepository here */);
        return View(tc.Get());
    }
    ...

我能够使用 [Activate] 属性将 ITodoRepository 添加到 HomeController,然后将其传递给 TodoController 的构造函数,但这并没有通过气味测试。家庭控制器不必拥有甚至知道这些。

有没有另一种方法可以创建将调用 DI 逻辑并提供依赖项的 TodoController 实例?

从另一个控制器调用控制器时的依赖关系注入

如果您担心代码异味,主要关注的应该是让一个控制器调用另一个控制器。

控制器应在两种情况下调用:

  1. 按系统(即MVC)
  2. 通过您的单元测试

相反,我建议让两个控制器都调用一个业务逻辑组件,该组件本身可能使用依赖关系注入来获取其依赖关系,并且每个控制器可能也使用依赖关系注入来获取业务逻辑依赖关系。

public class HomeController : Controller {
    public HomeController(IMyAppBusinessLogic bll) { ... }
}
public class WebApiController : Controller {
    public WebApiController(IMyAppBusinessLogic bll) { ... }
}
public class MyAppBusinessLogic : IMyAppBusinessLogic {
    public MyAppBusinessLogic(ITodoRepository repository) { ... }
}

使用 app.UseServices 注册的任何中间件仅在 Web 请求范围内可用。尝试直接从 MVC 应用实例化 webapi 控制器时,没有 Web 请求上下文,因此不会解析依赖项。

出于

单元测试的目的手动创建执行上下文是正常的。不确定您使用的是哪个 DI 框架,但我在我的项目中做了类似的事情(OWIN 不是 vNext),它使用的是 SimpleInjector

public static void UseInjector(this IAppBuilder app, Container container)
{
    // Create an OWIN middleware to create an execution context scope
    app.Use(async (context, next) =>
    {
        using (var scope = container.BeginExecutionContextScope())
        {
            await next.Invoke();
        }
    });            
}