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

Dependency Injection when controller called from another controller

我有一个 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 controller,但是从Mvc controller实例化ITodoRepository时依赖注入框架没有提供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 实例?

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

出于单元测试的目的手动创建执行上下文是正常的。不确定您使用的是哪个 DI 框架,但我在使用 SimpleInjector

的项目(OWIN 而不是 vNext)中做了类似以下的事情
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();
        }
    });            
}

如果您担心代码味道,主要问题应该是让一个控制器调用另一个控制器。

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

  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) { ... }
}