Asp.net模塊化開發之Mvc分區擴展框架

来源:http://www.cnblogs.com/xiangji/archive/2016/03/19/5294342.html
-Advertisement-
Play Games

對於一個企業級項目開發,模塊化是非常重要的。 預設Mvc框架的AreaRegistration對模塊化開發已經支持比較好了,還有必要擴展嗎?還能擴展嗎? 本文中的慄子是使用.net4.0、Mvc4.0及Unity2.0(企業庫4.0)的,提供完整源碼。 本分區擴展集成了IoC和分區DI(依賴註入)及


對於一個企業級項目開發,模塊化是非常重要的。

預設Mvc框架的AreaRegistration對模塊化開發已經支持比較好了,還有必要擴展嗎?還能擴展嗎?

本文中的慄子是使用.net4.0、Mvc4.0及Unity2.0(企業庫4.0)的,提供完整源碼。

本分區擴展集成了IoC和分區DI(依賴註入)及分區過濾器的支持。

本分區擴展框架(Fang.Mvc)在演示慄子源碼中包含完整源碼,拿到自己的項目直接引用即可使用了。

感興趣的同學請繼續,用AreaRegistration有不爽的看官請拭目以待...


一、先說一下我用Mvc不爽的地方,看大家是否有同感

1、分區類(繼承AreaRegistration)是個"特殊"類型,只能創建一個對象,而且只能簡單的"New",不能使用構造函數、屬性和方法來初始化對象

   好在Mvc是開源的,我們從AreaRegistration的源碼中發現是,Mvc是通過AreaRegistration.RegisterAllAreas()初始化所有分區

   從上圖可以看到Mvc先通過把每個繼承AreaRegistration的類型找出來,再通過Activator.CreateInstance創建對象。在我看來類型和對象應該是一對多的關係,分區類也一樣。我希望對分區的欄位和屬性進行不同的初始化以便可以使用同一個類型創建不同的分區。

2、分區的完整路徑提前寫死了,哪怕是換一下前面的路徑都休想

   比如有個評論的功能,我希望做成一個分區。新聞的評論地址規則為/News/Comment/,博客的評論地址規則為/Blog/Comment/,使用Mvc預設分區就只能定義為/Comment/了

3、路由初始化需要使用Global.asax

   其一、但是多個分區部署同一站點用誰的Global.asax呢?有人說每個Global都定義RegisterAllAreas,用誰的都行。但是對於程式員這個大多都是偏執狂的人群是很難接受的。如果哪個項目下線,正好整個站點都使用的是這個項目的Global,那就都掛了!有風險啊。

   其二、那就建一個項目為主項目,其他項目都以分區的形式定義?What?誰是主項目?偏執狂糾結中...

   其三、建一個項目為空項目為主項目,只有Global和RegisterAllAreas,還是覺得挺彆扭的。

   其四、定義一個HttpModule在Init中執行RegisterAllAreas。這個還算靠譜,建議大家使用。本擴展框架也是定義一個HttpModule來初始化一些Mvc的配置達到擴展目的的。

4、Mvc很多種過濾器,但預設配置過濾器的方法只有兩種,一種是Attribute,一種是GlobalFilters(全局過濾器)

   其一、Attribute定義過濾器太細了,要每個Controller或者Action去加,不能漏哦,特別是許可權判斷

   其二、GlobalFilters攔截所有請求,如果使用分區開發的項目,每個分區有自己的過濾器需求,該需求可能與其他分區衝突(影響其他分區的Controller正常執行)。

   其三、分區我希望復用,其中的Controller當然也要服用,在不同的部署中有使用不同過濾器或者過濾器參數的需求,Attribute定義的過濾器很難滿足要求。

   總之我希望有一種過濾器隻影響當前部署的分區,不需要提前和分區及Controller綁定(高耦合),部署(運行時)的時候再和指定分區關聯(當然要藉助IoC容器,這裡使用的是微軟的Unity框架)。

5、Mvc4.0支持DI,如果沒有IoC容器的支持,很雞肋

   其一、Mvc4.0預設的DI基本上沒有什麼作用,必須擴展。

   其二、每個分區有不同的DI(依賴註入)需求,我希望每個分區可以配置成不同的。

二、Mvc分區擴展方法

1、我們繼續從源碼查找分區初始化的過程

   RegisterAllAreas調用CreateContextAndRegister

   但是CreateContextAndRegister是一個internal保護的方法,不讓我們用啊!好在這個方法沒有幾行代碼,我們直接再寫一個類似方法就可以了。

2、Mvc最重要的就是路由規則,這個也要搞清楚

   這個簡單,隨便建一個分區就可以看得很清楚

   原來Mvc是通過RegisterArea調用AreaRegistrationContext的MapRoute方法來設置路由規則的;而AreaRegistrationContext對象創建就更簡單,直接New出來的(參考前面CreateContextAndRegister的源碼截圖)!

   現在基本上都搞清楚了,可以動手進行擴展了。

3、首先對分區擴展類(Area),我們來增加一些功能

   我這裡定義了一個類型Fang.Mvc.Area繼承AreaRegistration。這個是可以按個人愛好加一些功能的,最好是一些常用功能,每個分區項目的個性化功能可以通過再次繼承這個類型來擴展

   3.1 擴展分區基本屬性

   

   通過Name屬性實現分區名的配置(註意:同一個站點下分區名不能重覆)。

   通過Path屬性分區路徑首碼,就是分區的路由規則前都加一個這個路徑。其一、大家知道,如果路由規則衝突化,Mvc無法找到正常的路由規則,這個很要命,增加一個部署是配置的路徑就很有必要了。其二、我的需求比較奇特,我希望同一個站點下可以部署多個相同的分區,就像前面說的我們要給/News/和/Blog/都部署Comment分區,訪問路徑不一樣,訪問的資料庫或者數據表也可以不一樣,對已數據源不一樣就靠DI了,後面再繼續說。

   通過NameSpaces屬性配置當前分區查找Controller的範圍。如果沒有配置就按Mvc預設處理,按當前分區類的命名空間。

4、定義一個路由規則註冊類(AreaRoute)來達到把Area的配置應用到路由註冊

   AreaRoute通過調用AreaRegistrationContext的對應方法註冊

   AreaRoute調用Area的CheckName、CheckUrl和CheckNameSpaces來實現Area的配置

   

   把分區的路由規則保存到Area的_routes列表中備用(這裡不詳述作用,不在本文探討範圍內)

5、現在開始搞定分區過濾器

   5.1 在Area類增加屬性Filters配置分區過濾器

   5.2 定義AreaFilterProvider類型來調用Area的Filters

   AreaFilterProvider繼承Mvc框架的IFilterProvider介面

   在分區初始化時把AreaFilterProvider添加到Mvc的FilterProviders.Providers中,任務完成。

6、該輪到期盼已久DI容器了

   6.1 首先定義Fang.Mvc.Container類來封裝一下Unity容器

   這個使用其他容器工具也是可以的,甚至封裝一個工廠,支持多種容器也未不可。

   至於有人說Unity有"bug",不適合做DI。我使用Unity容器有幾年了,還沒完全搞明白Unity,我認為Unity容器還是挺博大精深的,足夠我用了。至於"bug"也算不上bug,最多是坑,多踩踩就平了。至於Unity(及企業庫)有多挺博大精深,不在本文探討範圍,敢興趣的可以自己研究,找高手探討。

   找我交流也行(但是我還在學習中,認識可能還膚淺或者有錯誤,最好不要誤導了大家)。

   6.2 增加屬性DependencyContainer配置DI(依賴註入)容器的名字

   6.3 定義AreaDependencyResolver類來實現分區配置和DI配置的結合

   AreaDependencyResolver繼承Mvc框架的IDependencyResolver介面

   接下來就簡單了,按Http上線文信息獲取分區配置,調用容器工廠,調用容器實現GetService方法即可。

7、萬事俱備了,借HttpModule的東風吹起來

   定義類AreaMergeModule繼承asp.net的IHttpModule,在Init方法中一通調用初始化以上擴展的功能的初始化。

   調用容器工廠獲取Mvc容器,在容器中獲取所有的Area對象並逐個初始化,再初始化AreaDependencyResolver。

三、重頭戲開場,炒"慄子"

1、一個站點配置兩個分區,分別調用不同的數據源

   源碼路徑:\Test\MvcApplication1

   1.1 先看分區及容器配置

   配置文件:\Test\MvcApplication1\ConfigFiles\unity.config

   

   以上使用分區類MvcApplication1.RouteConfig定義兩個分區(A和B),每個分區的名字和路徑等都配置不一樣。

   另外還額外定義了兩個依賴註入的容器。名字分別為兩個分區配置的依賴註入的容器名。

   以上還可以看出分區類和分區的Controller沒有依賴關係,事實上只要路由規則沒有特殊路由及其他要求,可以使用定義好的預設分區類Area。而且分區定義是按命名空間,事實上原有複雜系統可以在不修改原項目的情況下按邏輯(命名空間)拆分為多個分區,以後看情況再逐個分區拆分為獨立項目,優化項目結構。

   如果有同學說你上面的配置看不懂,不好意思要補課了,找度娘,搜索"Unity2.0容器配置"(小心有一個叫Unity3d的東西是做游戲的,和這個沒關係)。

   1.2 再看HttpModule配置

   配置文件:\Test\MvcApplication1\Web.config

   

   system.web的httpModules在經典模式下起作用,system.webServer的modules在集成模式下起作用,如果不確定兩個都配上就Ok了。

   1.3 分區路由配置(與Mvc預設路由配置及分區配置對比)

   配置文件:\Normaltest\MvcApplication4\App_Start\RouteConfig.cs

   

   配置文件:\Test\MvcApplication1\App_Start\RouteConfig.cs

   

   以上三種路由配置中,可以看出新改良的分區擴展居然比Mvc預設分區配置和靜態路由配置更相似

   首先使用該擴展配置路由沒有新的學習成本

   其次原非分區項目要改成分區項目建一個分區類(擴展後的),把路由規則直接複製過來,意味遷移成本非常低。

   1.4 再看一下Controller怎麼用DI

   代碼文件:\Test\MvcApplication1\Controllers\HomeController.cs

   HomeController執行依賴一個數據源(Config),但是HomeController並沒有定義和調用獲取數據源的方法,只是把數據源(Config屬性)聲明為Dependency的。

   1.5 最後看測試效果

   

   

   例子有點簡單,完全說明問題,兩個不同路徑(分區路徑)的Url都訪問到HomeController而且數據源(Config屬性)自動初始化成功,而且這兩個地址調用了不同的數據源。

2、分區過濾器的慄子

   源碼路徑:\Test\Site

   2.1 先看分區及容器配置

   配置文件:\Test\Site\ConfigFiles\unity.config

   

   這次配置更複雜了一點,定了三個分區,使用兩個分區類。配置了一個過濾器,而且有兩個分區(不同分區類)引用了該過濾。(依賴註入配置同前一個例子,沒必要說了)

   2.2 其他配置和上一個例子相似,直接看三個分區效果

   

   

   

   對此不想多說了,一切都在以上的熱乎乎的慄子里,我的目的已經達到了。你的目的達到了嗎?

 

   模塊開發是個系統性問題,後續我還將發表使用Mvc分區擴展框架更好的解決實際開發問題文章,敬請期待...

四、高潮來了,奉送源碼

下載慄子(分區擴展框架)源碼

   瞬間高潮就結束了,是不是有些遺憾啊。本框架是從搜房內部框架中拆分出來的一部分。隨著.net開源社區的快速發展,搜房作為廣泛使用.net的大型互聯網公司之一,正在逐步以新的姿態擁抱開源。

   我們鼓勵(甚者獎勵)搜房的程式員參與開源項目或者把內部通用系統脫敏整理後在網上開源。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、讓linux支持中文 1、將Linux的env設置了LANG=en_US.UTF-8; 2、本地的Shell客戶端編碼也設置成UTF-8,這樣讓在windows上傳到linux的文件或者目錄不會出現亂碼; 3、重要:如果用SecureFXPortable上傳時需要需要手工編輯SecrueFX的這
  • 以前在Windows下使用百度或者搜狗輸入法的截圖工具很方便。❶快捷鍵( ,我設置的是這個),❷選擇區域,❸編輯所選區域,包括添加文字,線條框框,調色,❹點擊『✔️』選擇保存位置,修改文件名保存。這個是一個完整的流程,但使用Mac OS X之後,這些都要是單獨的過程: 1. ,選擇截取屏幕,保存到桌
  • 防火牆配置後執行service iptables save 出現"Failed to restart iptables.service: Unit iptables.service failed to load: No such file or directory."錯誤,在CentOS 7或RHE
  • 一、基本命令 1、立即關機並重啟動,執行如下命令: shutdown -r now 或者reboot 2、立即關機,執行如下命令: shutdown -h now 或者poweroff 3、等待2分鐘關機並重啟動,執行如下命令: shutdown -r 2 4、等待2分鐘關機,執行如下命令: shu
  • 因為總要切換ip,所以百度了一下腳本 如下http://jingyan.baidu.com/article/d2b1d1029d21b95c7e37d4fa.html 動態ip netsh interface ip set address name="本地連接" source=dhcpnetsh i
  • VS2005代碼編輯器的展開和摺疊代碼確實很方便和實用。以下是展開代碼和摺疊代碼所用到的快捷鍵,很常用: Ctrl + M + O: 摺疊所有方法 Ctrl + M + M: 摺疊或者展開當前方法 Ctrl + M + L: 展開所有方法 解決VS2010中工具箱的的不見的問題: 按快捷鍵Ctrl+
  • 先上代碼:
  • 1、MVC的前臺頁面編譯完之後,也會生成一個前臺頁面類。在前天頁面中加入這段代碼this.GetType().Assembly.GetLocation()得到當前類所在的程式集,可以查看其所在的程式,會發現,如果是若類型視圖其繼承的是WebViewPage<object>,而強類型則是機車WebVi
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...