2012-08-16 70 views
0

_ __ _ __ _ ____ UPDATE __ _ __ _ __ _ ___MVC4基於域指定路由命名空間

從不同的角度走近用自定義的ControllerFactory解決問題,請參閱下面的答案。

_ __ _ ____ 原來的問題 __ _ __ _ __

我已經使用了多個品牌的部署一個MVC的代碼庫。爲了滿足每個客戶的需求,我們設置了可覆蓋的控制器和視圖。

路線註冊記憶是這樣的

protected void Application_Start() 
    { 
     string deploymentName = GetDeploymentName(); 
     RouteConfig.RegisterRoutes(RouteTable.Routes, deploymentName); 
    } 

內,我們首先尋找在部署命名空間匹配控制器的RegisterRoutes,如果沒有發現,在基地命名空間中的樣子。

這工作得很好,如果我們有一個單獨的IIS應用程序中設置每個部署,因爲那時每個部署執行其自己的Application_Start()

但是,我們正在嘗試運行在一個IIS站點intsance所有部署。這意味着Application_Start()僅被調用一次,僅爲第一次部署註冊路由才能被擊中。

我想移動的RegisterRoutes出的Application_Start()的喜歡的東西在session_start()或Application_PreRequestHandlerExecute()

protected void Application_PreRequestHandlerExecute() 
    { 
     string deploymentName = GetDeploymentName(); 

     using (RouteTable.Routes.GetWriteLock()) 
     { 
      RouteTable.Routes.Clear(); 
      RouteConfig.RegisterRoutes(RouteTable.Routes, deploymentName); 
     } 
    } 

有了這個代碼,每個請求獲取當前部署的命名空間,清除routetable,和使用新的名稱空間重新註冊路由。

2問題:

1:基於無論先前路線是請求還是執行。所以我處於生命週期的錯誤部分,因爲此時的請求已經註定要用於特定的控制器和操作。

2:下一個請求將拋出一個異常:

異常詳細信息:System.InvalidOperationException:多種類型 發現匹配命名爲「家」的控制器。這可能發生,如果 該服務申請(「{控制器}/{行動}/{ID}」) 沒有指定名稱空間搜索控制器匹配 請求的路線。如果是這種情況,請通過調用帶有'namespaces'參數的'MapRoute'方法的 重載來註冊此路由。

即使我沒有註冊以前的路由,應用程序似乎仍然掛在先前註冊的控制器上,現在我已經註冊了另一個路由到另一個同名控制器,但存在不明確性兩個控制器之間。

我的兩個主要問題是:

1.是否有任何其他生命週期事件比的Application_Start()等,我可以使用以前的請求已經解析爲前面現有路由註冊新路線?

2.我該如何徹底清除路線和控制器,以避免在之前註冊的控制器中出現歧義異常?

回答

2

我要對此都錯了......我不應該擔心的路線,我應該擔心控制器實例化。我發現,您可以在的Application_Start()自己的定製工廠更換的ControllerFactory:

ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory(domainNamespaces)); 

所以在這裏我傳遞一個字典域密鑰,名稱空間值的構造器在我的自定義控制器工廠

然後在工廠我重寫,看起來對控制器的類型

public class CustomControllerFactory : DefaultControllerFactory 
{ 
    public Dictionary<string, string> DomainNamespaces { get; set; } 

    public CustomControllerFactory(Dictionary<string, string> domainNamespaces) 
     : base() 
    { 
     DomainNamespaces = domainNamespaces; 
    } 

    protected override Type GetControllerType(RequestContext requestContext, string controllerName) 
    { 

     controllerName += controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ? String.Empty : "Controller"; 

     string requestDomain = requestContext.HttpContext.Request.Url.Host; 
     string defaultNamespace = "API.Controllers.Base"; 
     string deploymentNamespace = "API.Controllers.Deployments." + DomainNamespaces[requestDomain]; 

     Assembly asm = Assembly.GetExecutingAssembly(); 
     Type deploymentMatch = null; 
     Type defaultMatch = null; 

     foreach (Type type in asm.GetTypes()) 
     { 
      if (type.Namespace == deploymentNamespace && type.Name.Equals(controllerName, StringComparison.OrdinalIgnoreCase)) 
      { 
       deploymentMatch = type; 
      } 
      else if (type.Namespace == defaultNamespace && type.Name.Equals(controllerName, StringComparison.OrdinalIgnoreCase)) 
      { 
       defaultMatch = type; 
      } 
     } 


     return deploymentMatch ?? defaultMatch; 
    } 
} 
+0

去ControllerFactory *路線* +1。 (phun) – eduncan911 2012-09-19 19:39:29

+0

哈哈,謝謝:) – Michael 2012-09-20 20:39:31

+0

如果你保留字典中的類型,那麼你可以放棄foreach搜索...... – 2015-11-03 20:02:03

1

如果您在一個實例中運行所有操作,並且兩個用戶正在打開會話,那麼哪條路由應該保持註冊狀態,因爲它們都在瀏覽您的網站?

相反,當環境的變化,您可以編寫處理當前上下文定製路由註冊新航線:MVC 3 use different controllers based on request parameters

+0

好點的方法,它需要每個請求,而不是每個會話,因爲路線是應用程序作用域。 – Michael 2012-08-16 16:06:06

+0

該實現的問題是它不包含設置命名空間以查找控制器的方法。我嘗試設置route.DataTokens [「命名空間」] =新的字符串[] {命名空間};但它似乎忽略了它 – Michael 2012-08-16 16:49:36

+0

您是否嘗試過使用區域? – 2012-10-23 14:53:21