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

时间:2015-01-12 00:09:27

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

我有一个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实例?

2 个答案:

答案 0 :(得分:5)

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

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

  1. 由系统(即MVC)
  2. 通过单元测试
  3. 相反,我建议让两个控制器调用业务逻辑组件,该组件本身可能使用依赖注入来获取其依赖关系,并且每个控制器也可能使用依赖注入来获取业务逻辑依赖关系。

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

答案 1 :(得分:0)

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