1

我有一個MVC應用拆分成幾個方面:多租戶應用

+ Root 
+ -- Areas 
    --+ US 
    + -- USCodeController : XXXCodeBaseController 
    --+ UK 
     -- UKCodeController : XXXCodeBaseController 
    --+ (Other countries pending) 
+ -- Controllers 
    --+ XXXCodeBaseController 

所以我在Controllers文件夾中定義了一些基本功能,然後繼承和延續區域文件夾中的功能。這樣,用戶界面可以根據每個國家,工作流程等特定的UI組件進行定製。

當談到使用Autofac時,我所要做的就是將IComponentContext傳遞給構造函數參數,這不是很好。這些問題包括:

  1. 當使用真正的IoC,我需要的IXXXServices注入,所以我得到的面積比類,而不是Autofac IComponentContext
  2. 每個區域與不同的數據庫相關聯的,而是一個共享模式 - 因此使用相同的EF庫,只能使用不同的連接字符串。

設計應該更直接地注入服務更優雅 - 這正是我所追求的。但是,解析器似乎沒有足夠的靈活性來解決控制器的問題,而且沒有大規模的黑客攻擊,對於每個使用的控制器而言,這種攻擊都變得非常大。這裏是如連接了手動控制器的一個例子:

builder.Register(s => new OrderService(r.ResolveNamed<IOrderRepository>("US")) 
     .Named("US") 
     .InstancePerHttpRequest(); 

// Lets say we add a DeclinedOrderController 

public class DeclinedOrderControllerBase 
{ 
    public DeclinedOrderControllerBase (IDeclinedOrderService service) 
    { } 
} 

爲了讓勾起來工作,你需要註冊加入Autofac,因爲我們需要在DeclinedOrderService

美國/英國特定的連接字符串
 builder.Register(s => new DeclinedOrderController(r.ResolvedName<IDeclinedOrderService>("US") 
     .Named("US") 
     .InstancePerHttpRequest(); 

我的問題是(最終):

  • 有沒有辦法來動態地選擇一組映射該區域的Autofac註冊會自動解析爲新的控制器?
+0

最終,您必須在某處指定服務的類型。一般來說,控制器是單獨負責的。 DI值得嗎?它解決了什麼問題? –

+0

因此,控制器正在使用共享服務類型。存儲庫將被注入到指向特定數據源的服務中。我現在將編寫一個示例應用程序並將其鏈接到該問題 –

回答

0

在MVC4中,您可以創建自定義控制器工廠以在創建控制器實例時捕獲控制器實例。您可以將其用作單個構圖點,而不必在每個控制器中執行此操作。

繼承自System.Web.Mvc.DefaultControllerFactory,創建自定義控制器工廠。覆蓋GetControllerInstance方法,並讓基函數爲您創建控制器。現在你可以讓你的依賴注入引擎組成控制器。

public class AreaControllerFactory : DefaultControllerFactory 
{ 
    // We use a controller factory to hook into the MVC pipeline in order to get a reference to controllers 
    // as they are being created. We inherit from the default controller factory because we do not want to 
    // have to locate the appropriate controllers ourself (we only want a reference to it). Once we have a 
    // reference, we simply hand the controller off for composition. 

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) 
    { 
     IController controller = base.GetControllerInstance(requestContext, controllerType); // create the controller 
     if (controller != null) 
     { 
      var container = ...; //construct your composition container 
      container.ComposeParts(controller); //compose your controller 
     } 
     return controller; 
    } 
} 

Global.asax.cs註冊控制器工廠在Application_Start方法。

var controllerFactory = new AreaControllerFactory(); 
ControllerBuilder.Current.SetControllerFactory(controllerFactory); 

爲了通過區篩選組合物的目的,我建立一個自定義,全局區組分辭典(例如區域名稱,適用類型的列表的字典)爲每個區域。

// NOTE:ComposablePartCatalog is MEF specific, change as needed 

internal static class AreaComponents 
{ 
    /// <summary> 
    /// A list of Area name, catalog pairs. 
    /// Each Area can provide a custom list of components to import from. 
    /// </summary> 
    internal static readonly Dictionary<string, ComposablePartCatalog> AreaCompositionCatalogs = 
     new Dictionary<string, ComposablePartCatalog>(); 
} 

填充字典中的每個區域的AreaRegistration實施RegisterArea方法裏面。

public class XAreaRegistration : AreaRegistration 
{ 
    public override void RegisterArea(AreaRegistrationContext context) 
    { 
     /* ... Standard route logic ... */ 

     // Set up an MEF catalog with Area components 
     var xCatalog = new AssemblyCatalog(
      typeof(MyPluginsNamespace.ArbitraryTypeToLookupAssembly).Assembly); 

     AreaComponents.AreaCompositionCatalogs["X"] = xCatalog; 
    } 
} 

使用該字典來構建組合容器給定控制器時,在我的自定義控制器工廠選擇組成的對象適當的子集。

// Capture current area name 
System.Web.HttpContextBase contextBase = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current); 
System.Web.Routing.RouteData routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(contextBase); 
object areaObject = routeData.DataTokens["area"]; 
string areaName = areaObject as string ?? string.Empty; 

// Create a composition container specific to this area 
ComposablePartCatalog areaCatalog = 
        AreaMefComponents.AreaCompositionCatalogs.ContainsKey(areaName) ? 
        AreaMefComponents.AreaCompositionCatalogs[areaName] : null; 
var container = new CompositionContainer(areaCatalog);