【问题标题】:Dependency Injection when controller called from another controller从另一个控制器调用控制器时的依赖注入
【发布时间】:2015-03-09 17:22:43
【问题描述】:

我有一个 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 的构造函数,但这并没有通过气味测试。家庭控制器不应该拥有甚至知道这些。

是否有另一种方法来创建 TodoController 实例,该实例将调用 DI 逻辑并提供依赖项?

【问题讨论】:

  • 我相信你需要注入 TodoController 作为对 homecontroller 的依赖。它应该触发依赖链的创建。
  • 添加 TodoController 作为依赖并在 Startup.cs 中注册的正确方法是什么?我是否必须创建一个接口 ITodoController 然后执行 services.AddTransient 之类的操作?
  • @lordjeb 你是如何从 MVC 控制器调用 webapi 方法的? webapi 上下文必须存在才能使管道中的中间件正常工作。
  • @su8898 我只是想直接实例化 WebApi 控制器,以避免整个中间件。我的 WebApi 控制器返回我需要的对象,所以我想我可以直接调用它。
  • 这个问题让我脑洞大开。停止滥用控制器。

标签: c# asp.net-mvc asp.net-web-api asp.net-core


【解决方案1】:

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

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

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

【讨论】:

  • 好主意,但你如何做到这一点?你如何初始化MyAppBusinessLogic
【解决方案2】:

使用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();
        }
    });            
}

【讨论】:

  • 我正在 HomeController.Index() 中处理一个 Web 请求。有什么方法可以在该上下文中实例化 TodoController 吗?
  • 对于 DI,我只是使用 vnext 的内置功能。
猜你喜欢
  • 2011-11-18
  • 2016-02-01
  • 1970-01-01
  • 2015-09-07
  • 1970-01-01
  • 1970-01-01
  • 2021-12-26
  • 2018-10-21
相关资源
最近更新 更多