2016-01-23 666 views
4

在我的ASP.Net Core MVC 6解決方案中,我有兩套控制器。一套包含其常規視圖的網頁。另一組包含API控制器。ASP.Net Core從另一個控制器調用控制器

爲避免重複使用數據庫邏輯,Web控制器正在使用API​​控制器。目前,我正在通過將DbContext作爲構造函數參數手動創建所需控制器的實例。這是通過依賴注入給予Web控制器的DbContext。

但是,無論何時向API控制器添加其他構造函數參數,我都需要修改所有使用此API控制器的Web控制器。

如何使用內建的依賴注入系統內建到ASP.Net 5來爲我創建所需API控制器的實例?然後它會自動填寫所需的構造函數參數。

一種解決方案可能是將數據庫邏輯從API控制器移動到單獨的層,並從API和Web控制器中調用該邏輯。這不會解決我的問題,因爲新層仍然需要相同的參數,我不喜歡不必要的佈線。

另一種解決方案是讓Web控制器通過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); 
} 

回答

2

不要這樣做。將該邏輯移至另一個在兩個控制器之間共享的組件。

+0

這並沒有解決任何問題。引用自己的看法:「一種解決方案可能是將數據庫邏輯從API控制器移到單獨的層,並從API和Web控制器中調用它,但這並不能解決我的問題,因爲新層仍然需要相同的參數,而我不是不必要的接線迷。「 –

+0

使用依賴注入系統來獲取該事物的一個實例。我不明白爲什麼這是一個問題。你可以用一些間接的方法來解決任何問題:D – davidfowl

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 
    } 
} 
+1

對於SharedComponent中的每個新方法,我都必須連接來自ApiController的鏡像調用。在實踐中複製所有簽名並返回值並有兩組維護。這是額外的佈線,當班級已經在那裏時,這似乎是不必要的。這是一種偏好,有些人對此很好。 –

3

爲了能夠從另一個控制器使用控制器,你需要:

  1. 在Startup.cs ConfigureServices中註冊控制器:services.AddTransient <Areas.Api.Controllers.FoobarController, Areas.Api.Controllers.FoobarController>();
  2. 您必須將想要訪問的控制器作爲ctor參數傳入主控制器。

如果需要訪問本地特性的控制器,如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控制器的源代碼可以在here找到。然而,它正在對RC2進行大量重寫,並且爲了訪問用戶和Url,必須複製的屬性纔會發生變化。

+0

我想指出,在RC2中,這似乎是以不同的方式解決的。我沒有仔細研究代碼,但看起來未來可能會有更「官方」的方式。請參閱https://github.com/aspnet/Mvc/blob/2b0bea675ec55dc752aacd47223376045a9be1ab/src/Microsoft.AspNetCore.Mvc.Core/Controllers/DefaultControllerFactory.cs上的'CreateController' –

相關問題