ASP.Net Core从另一个控制器调用控制器

时间:2016-01-23 12:41:52

标签: dependency-injection asp.net-core

在我的ASP.Net Core MVC 6解决方案中,我有两套控制器。一组包含具有常规视图的网页。另一组包含API控制器。

为避免重复数据库逻辑,Web控制器正在使用API​​控制器。目前,我通过将DbContext作为构造函数参数手动创建所需控制器的实例。这是通过依赖注入为Web控制器提供的DbContext。

但每当我向API控制器添加另一个构造函数参数时,我需要修改所有使用此API控制器的Web控制器。

如何使用内置于ASP.Net 5的依赖注入系统为我创建所需API控制器的实例?然后它会自动填充所需的构造函数参数。

一种解决方案可能是将db逻辑从API控制器移动到单独的层,并从API和Web控制器调用它。这不能解决我的问题,因为新图层仍然需要相同的参数,而且我不喜欢不必要的布线。

另一种解决方案是让网络控制器通过网络电话访问API,但这只会增加应用程序的复杂性。

今天我这样做:

public IActionResult Index()
{
    using (var foobarController = new Areas.Api.Controllers.FoobarController(
        // All of these has to be in the constructor of this controller so they can be passed on to the ctor of api controller
        _dbContext, _appEnvironment, 
        _userManager, _roleManager, 
        _emailSender, _smsSender))
    {
        var model = new IndexViewModel();
        model.Foo = foobarController.List(new FoobarRequest() { Foo = true, Bar = false });
        model.Bar = foobarController.List(new FoobarRequest() { Foo = false, Bar = true });
        return View(model);
    }
}

我希望这样的事情: (这个例子不起作用。)

using (var foobarController = CallContextServiceLocator.Locator.ServiceProvider.GetService<Areas.Api.Controllers.FoobarController>())
{
    var model = new IndexViewModel();
    model.Foo = foobarController.List(new FoobarRequest() { Foo = true, Bar = false });
    model.Bar = foobarController.List(new FoobarRequest() { Foo = false, Bar = true });
    return View(model);
}

5 个答案:

答案 0 :(得分:18)

  

如何使用内置于ASP.Net 5的依赖注入系统为我创建所需API控制器的实例?

Startup.cs中,您可以告诉MVC注册所有controllers as services

services.AddMvc().AddControllersAsServices();

然后,您只需通过DI机制将所需的控制器注入其他控制器并调用其操作方法。

答案 1 :(得分:9)

为了能够使用来自另一个控制器的控制器,您需要:

  1. 在Startup.cs ConfigureServices中注册控制器:services.AddTransient <Areas.Api.Controllers.FoobarController, Areas.Api.Controllers.FoobarController>();
  2. 您必须将要作为ctor参数访问的控制器传递给主控制器。
  3. 如果您需要访问控制器中的本地属性,例如UserUrl,则有两种方法可以执行此操作。

    第一种方法是使用DI获取IHttpContextAccessor的实例来访问UserIUrlHelper来访问Url个对象:

    public class FoobarController : Controller
    {
        private readonly ApplicationDbContext _dbContext;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IUrlHelper _urlHelper;
        public FoobarController(ApplicationDbContext dbContext, IHttpContextAccessor httpContextAccessor, IUrlHelper _urlHelper, [...])
        {
             _dbContext = dbContext;
             _httpContextAccessor = httpContextAccessor;
             _urlHelper = urlHelper;
        }
    
        public FoobarResponse List(FoobarRequest request)
        {
            var userId = _httpContextAccessor.HttpContext.User.GetUserId();
            var response = new FoobarResponse();
            response.List = _dbContext.Foobars.Where(f => f.UserId == userId).ToList();
            response.Thumb = 
            return response;
        }
    }

    第二种方法是在呼叫控制器中设置它:

    public class HomeController : Controller
    {
        private Areas.Api.Controllers.FoobarController _foobarController;
        public HomeController(Areas.Api.Controllers.FoobarController foobarController)
        {
            _foobarController = foobarController;
        }
    
        private void InitControllers()
        {
            // We can't set this at Ctor because we don't have our local copy yet
            // Access to Url 
            _foobarController.Url = Url;
            // Access to User
            _foobarController.ActionContext = ActionContext;
            // For more references see https://github.com/aspnet/Mvc/blob/6.0.0-rc1/src/Microsoft.AspNet.Mvc.ViewFeatures/Controller.cs
            // Note: This will change in RC2
        }
    
        public IActionResult Index()
        {
            InitControllers();
    
            var model = new IndexViewModel();
            model.Foo = _foobarController.List(new FoobarRequest() { Foo = true, Bar = false });
            model.Bar = _foobarController.List(new FoobarRequest() { Foo = false, Bar = true });
            return View(model);
        }
    }

    可以找到ASP.Net Core MVC6 RC1 Controller的源代码here。然而,对RC2进行了大量重写,并且必须复制以便访问User和Url的属性才会发生变化。

答案 2 :(得分:5)

不要这样做。将该逻辑移动到在2个控制器之间共享的另一个组件。作为HTTP调用的结果,框架将调度控制器,而不是您的公共API表面。通常,您的控制器应该用作将HTTP请求转换为业务对象的位置。这些对象的操作应该委托给另一个层(特别是如果需要在应用程序中的多个位置使用它)。

答案 3 :(得分:0)

为什么你的新图层需要接线?为什么不将对象引入两个控制器并在该对象上调用方法。 DI容器可以解决这个新对象的依赖关系而不需要重复连接吗?

即你可以拥有:

public class MvcController
{
    SharedComponent sharedComponent;
    public MvcController(SharedComponent sharedComponent)
    {
        this.sharedComponent = sharedComponent;
    }
    public IActionResult Index()
    {
        var model = new IndexViewModel();
        model.Foo = shredComponent.List(new FoobarRequest() { Foo = true, Bar = false });
        model.Bar = shredComponent.List(new FoobarRequest() { Foo = false, Bar = true });
        return View(model);   
    }
}

//对API控制器重复此操作

public class SharedComponent
{
    public SharedComponent(DBContext dbContext, AppEnvironment appEnvironment, UserManager userManager, RoleManager roleManager, 
        EmailSender emailSender, SmsSender smsSender)
    {
       ...Store in fields for later usage
    }
}

答案 4 :(得分:0)

我必须与其他人达成共识,注入控制器可能不是最佳途径。主要是因为它将业务逻辑与ASP.Net结合在一起,而不是像我认为的那样将其像IO设备一样对待。

假设我们有一个如下所示的界面:

public interface ICalculator {
    int Add(int left, int right);
}

并且我们有一个存储业务逻辑的实现:

public class MyCalculator : ICalculator {
    public int Add(int left, int right) => left + right;
}

此实现可以用作后台服务,在与WPF应用程序相同的过程中,也可以用作ASP.NET WebAPI控制器。看起来像这样:

[ApiController]
[Route("api/{controller}")]
public void CalculatorController : Controller, ICalculator {
    private readonly ICalculator _calculator;

    public CalculatorController(ICalculator calc) => _calculator = calc;

    [Route("Add")]
    public int Add(int left, int right) => _calculator.Add(left, right);
}

如果该控制器对存储库具有依赖性,那么您也可以注入该接口。我个人喜欢定义存储库的集合(例如IUserRepository),并且只注入所需的内容,而不是整个DbContext。

public CalculatorController(ICalculator calculator, IDbContext db) { }

控制器没有什么不对的,这取决于它所装饰的东西。只要确保您拥有一组可以断言各种事情的测试即可。例如,您可以断言,当调用特定的控制器方法时,另一个接口上的特定方法也被调用。

我个人认为这种方法更合适。可以使用某些技术,但应与业务规则保持一定距离。开发人员应该能够采用管理代码特定部分的业务规则,并轻松地从WCF服务切换到ASP.NET WebAPI。

我个人曾经是几个项目的一部分,在这个项目中,我们不得不从一种数据库技术切换到另一种数据库技术(从SQL Server到CouchDB),并且其中的微服务需要作为静态Web API服务而不是Windows运行。服务。如果以这种方式设计事物,那么与通常的事物构成相比,这些类型的项目就变得相对琐碎了。