Asp.net模塊化開發之Mvc分區擴展框架(送源碼)

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

對於一個企業級項目開發,模塊化是非常重要的。 預設Mvc框架的AreaRegistration對模塊化開發真的支持很好嗎?真的有很多複雜系統在使用預設的分區開發的嗎?我相信大部分asp.net的技術團隊最開始都研究過分區,甚至在實際項目裡面有嘗試運用,但是碰到了種種問題"各種坑",最後回頭是岸放棄了


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

預設Mvc框架的AreaRegistration對模塊化開發真的支持很好嗎?真的有很多複雜系統在使用預設的分區開發的嗎?我相信大部分asp.net的技術團隊最開始都研究過分區,甚至在實際項目裡面有嘗試運用,但是碰到了種種問題"各種坑",最後回頭是岸放棄了(我們的團隊也碰到了類似問題,也有人評論中說起,直接搜索asp.net mvc分區也有不少類似信息)。

有人說asp.net Mvc框架就不適合做模塊化開發,我們可以弄一個其他框架來做企業級的分模塊開發,確實現在好像已經有類似的開源項目。

但是,這個確實沒有必要,這裡對Mvc框架做了一個簡單的擴展,基本能做到分模塊開發和單個簡單Mvc項目開發沒有太大區別,並分享給大家。

本文中的慄子是使用.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(依賴註入)需求,我希望每個分區可以配置成不同的。

6、使用預設的分區開發出現重名的控制器錯誤或者視圖渲染出錯

   6.1 在主項目中增加分區,如果新加分區的控制器(Controller)類名與主項目中控制器同名,導致主項目的控制器出錯,但是分區的控制器正常

     這是個挺要命的問題,新加分區不出錯,主項目的控制器出錯,這個問題很難定位,我們一般是哪裡出錯找哪裡。但是我們在主項目的控制器里找不到錯誤,而且這個控制器原來是正常工作的。新加了分區功能,但他"幹掉"原來正常的功能!!!(很恐怖不是嗎?如果知道  原因當然好處理,關鍵是很多人不會去懷疑新加並正常運行的分區,新分區的開發人員也可能會"理直氣壯"的否認是自己的問題。)

   6.2 新加分區的視圖出錯

        一種原因是預設分區新加視圖的位置和普通項目不一樣,搞不好弄錯位置

       還有一種原因,主項目的視圖與新分區的視圖衝突,這個就有點噁心了

      

      總之預設的分區開發總是和普通的項目(Mvc)開發有很多的不同,導致分模塊開發的困難和故障,甚至使人敬而遠之,徹底不分區。

二、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預設處理,按當前分區類的命名空間。

   這樣我們的每個分區是獨立的(通過NameSpaces隔離),每個分區既可以獨立運行調試和可以和其他分區部署到一起也不會相互衝突。和普通簡單的Mvc項目幾乎沒有區別,大大減少開發和學習成本。當然有人會說,用這個分區擴展框架不是還要現學容器框架(Unity),這個確是事實。但是容器框架(Unity)的作用不僅僅是管理好幾個分區,容器的Ioc、Aop等作用對項目開發那是如虎添翼,容器技術幾乎是大型系統開發必備工具,小小的分區框架集成了容器本身也是該框架的一大亮點。

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
更多相關文章
  • 推薦一本不錯的書籍,《電子設計從零開始》(楊欣)。通讀此書,通俗易懂,還結合multisim進行模擬驗證。對本科階段的模電書籍是一種顛覆。 以下截取自裡面部分章節,如何計算共射極放大電路的各個參數。很實用。 1.Vcq為集電極的靜態工作電壓,Vcq的選取為了避免出現飽和和截止失真,使Vcq ≈ 1/
  • 系統來自:系統媽:http://www.xitongma.com 新蘿蔔家園GHOST win7系統32位極速裝機特別版 V2016年3月 系統概述 新蘿蔔家園ghost win7系統32位極速裝機特別版加快“網上鄰居”共用速度;取消不需要的網路服務組件,系統支持Windows安裝,可以在Windo
  • 文件和目錄 命令 選項 註解 示例 文件的基本操作 ls [選項][文件] 顯示所有文件和目錄 ls -al -a(A) 顯示所有文件和目錄,包括隱藏文件和目錄(顯示所有文件和目錄,包括隱藏文件和目錄,但不現實“.”和“..”目錄) 查看當前目錄下的所有文件的詳細信息=ll -l 顯示文件和目錄的完
  • 學習linux時候的基礎,第一部分,常用命令
  • OWIN源碼,Katana project
  • 來源:微信公眾號CodeL 以下是個人學習之路的簡單分享,不足之處歡迎大神們批評指正! 在網站開發的初期,我們沒有考慮更多的東西,也沒有對緩存進行系統的設計,而是直接使用了應用程式緩存對象Cache,但由於系統架構的不斷完善,在分散式系統架構中只依靠Cache明顯不夠,無法實現分散式管理,所以後期我
  • 我們是否可以把從前端接受的JSON字元串轉換成字典集合呢?比如從前端接收:{'size':'10', 'weight':'10kg'}在服務端轉換成:[{size:"10"},{weight:"10kg"}]這樣的字典集合通過Newtonsoft的DeserializeObject<Dictiona
  • 練習教科書第22~25頁單元測試練習,要求自行安裝Visual Studio開發平臺,版本至少在2010以上,要求把程式安裝過程和練習過程寫到博客上,越詳細越好,要圖文並茂,沒有書的同學可以向班內助教同學借閱。 單元測試: 首先新建項目->visual C# 類庫 然後輸入單元測試代碼: 如圖1.1
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...