王建祥,王洪澤
(河南省濟源鋼鐵(集團)有限公司 信息化儀表處, 河南 濟源 454650)
隨著信息化技術(shù)的發(fā)展,管理信息系統(tǒng)得到普遍應(yīng)用,而權(quán)限管理系統(tǒng)是各類管理信息系統(tǒng)的前提和基礎(chǔ)。本文權(quán)限管理系統(tǒng)的設(shè)計按EF模型生成數(shù)據(jù)庫、各層深度解耦、設(shè)計T4模板自動生成代碼、設(shè)計前端等步驟進(jìn)行[1-2]。
根據(jù)ASP.Net MVC系統(tǒng)的特性,權(quán)限管理系統(tǒng)的設(shè)計目標(biāo)是:
1.UserInfo---RoleInfo---ActionInfo;用戶擁有角色,權(quán)限擁有角色,從而用戶擁有權(quán)限。
2.UserInfo---R_UserInfo_ActionInfo;用戶直接擁有特殊權(quán)限。
具體實現(xiàn)過程如圖1所示。
圖1 權(quán)限管理的目標(biāo)
本系統(tǒng)使用Visual Studio 2019 Enterprise 16.11版本軟件進(jìn)行權(quán)限管理軟件開發(fā)和調(diào)試,使用ASP.NET MVC技術(shù)進(jìn)行設(shè)計,其中DAL層到BLL層的解耦使用復(fù)雜工廠技術(shù),BLL層到UI層的解耦使用Spring.Net技術(shù)[3]。
2.1.1 建立方案并組織管理各層項目
新建解決方案文件夾: 解決方案文件夾是虛擬文件夾,其作用是便于項目的管理。
表現(xiàn)層——項目類型w e b 應(yīng)用程序---.N E T Framework
jygt.AutoWeightSystem.UI.Portal
2.1.2 Model 層添加實體數(shù)據(jù)模型并生成數(shù)據(jù)庫
使用EF技術(shù)的Model First方式生成數(shù)據(jù)庫: 項目開始沒有數(shù)據(jù)庫,根據(jù)EF設(shè)計模型,進(jìn)行模型同步完成數(shù)據(jù)庫及表的創(chuàng)建。
添加ADO.NET實體數(shù)據(jù)模型---空EF設(shè)計器,命名DataModel
1、在EF設(shè)計器DataModel中添加實體及屬性、關(guān)聯(lián)
(1) 添加實體模型UserInfo
(2) 添加實體模型OrderInfo
(3) 建立實體UserInfo與實體OrderInfo兩個實體之間1對多的關(guān)聯(lián)。
2、根據(jù)模型生成數(shù)據(jù)庫
右擊EF設(shè)計器空白處---添加代碼生成項,把名稱改成DataModel.tt,即生成上述實體模型UserInfo、實體模型OrderInfo對應(yīng)的實體類文件。
右擊EF設(shè)計器空白處---根據(jù)模型生成數(shù)據(jù)庫,生成DataModel.edmx.sql文件,執(zhí)行DataModel.edmx.sql文件生成數(shù)據(jù)庫及表。EF技術(shù)生成的數(shù)據(jù)庫如圖2所示。
圖2 EF 技術(shù)生成的數(shù)據(jù)庫
2.1.2 設(shè)計DAL 數(shù)據(jù)訪問層
查詢方法的設(shè)計思路:
1.方法選擇。方法需能接受用戶輸入的任意條件,返回值應(yīng)是b o o l 類型,因此可以用委托Func
2.方法返回值類型選擇。返回值是用戶,由于返回的用戶個數(shù)不確定,不能用UserInfo類型,可使用List
3.繼續(xù)深入考慮,如果返回值是List
繼續(xù)深入思考方法,F(xiàn) u n c
綜上,DAL層使用如下方法實現(xiàn)查詢:
2.1.3 設(shè)計封裝數(shù)據(jù)訪問層基類BaseDAL
DAL層的各個方法,經(jīng)測試全部正確后,目前只是針對UserInfoDAL,針對OrderInfoDAL同樣也有增刪改查等類似方法,僅數(shù)據(jù)實體對象不同,還有其他實體都要寫類似的增刪改查方法,不經(jīng)專門處理就會有很多重復(fù)工作。因此把相同的方法提出來,封裝成一個基類,所有類似通用方法寫在此基類里面,然后讓子類去繼承使用該基類的增刪改查方法。
需要使用泛型類,即把BaseDAL類改成泛型類,然后加上泛型約束,
public class BaseDAL
2.1.4 添加數(shù)據(jù)訪問層的接口IDAL
在數(shù)據(jù)訪問層建立的基礎(chǔ)上,創(chuàng)建業(yè)務(wù)邏輯層(BLL)類庫。在jygt.AutoWeightSystem.BLL層中新建一個類UserInfoBLL,然后添加方法,
上述做法需要改進(jìn)的地方:
1.上述做法會使jygt.AutoWeightSystem.BLL層與jygt.AutoWeightSystem.EFDAL層緊密耦合在一起,EFDAL層發(fā)生變化BLL層就必須跟著變,比如UserInfoDAL的名稱變了,BLL層必須跟著變。
2.DAL層的可能變化點:(1)跨數(shù)據(jù)庫,如SqlServer、Oracle、 mysql,對于采用EF技術(shù),本身就支持跨數(shù)據(jù)庫(2)數(shù)據(jù)庫訪問驅(qū)動層驅(qū)動變化,比如不采用EFDAL,采用ADODAL或者NHDAL。
3.項目設(shè)計原則:模塊內(nèi)高內(nèi)聚,模塊間低耦合。期望DAL層變化的情況下,BLL層不需要變化或者變化最小。
于是進(jìn)行如下改進(jìn):
D A L 層中就添加一個類庫項目j y g t .AutoWeightSystem.IDAL(注意命名規(guī)范),表示DAL層的接口,創(chuàng)建IuserInfoDAL接口。
然后在IuserInfoDAL接口里定義抽象的增刪改查的方法。同理,添加OrderInfoDAL等實體的DAL接口IOrderInfoDAL,里面同樣添加抽象的增刪改查方法。這時由IuserInfoDAL接口和IOrderInfoDAL接口可以抽象出一個基類接口。
2.1.5 抽象出數(shù)據(jù)訪問層的基類接口
p u blic interf ace IBaseD A L
接著讓IUserInfoDAL、IOrderInfaDAL繼承IBaseDAL,原有的方法結(jié)構(gòu)注釋或刪除,IorderInfaDAL類不存在就新建,并導(dǎo)入Model命名空間。然后修改EFDAL層下的UserInfoDAL和OrderInfoDAL,讓其分別實現(xiàn)IUserInfoDAL、IOrderInfaDAL,同時添加對IDAL層jygt.AutoWeightSystem. IDAL的引用。這樣UserInfoDAL實例用接口IUserInfoDAL來接收,使用接口接收就可以做到不依賴具體實現(xiàn)。
2.1.6 用接口隔離業(yè)務(wù)邏輯層BLL 對DAL 的依賴
切換到業(yè)務(wù)邏輯層UserInfoBLL,用接口類型來接收new UserInfoDAL(),這個前提是UserInfoDAL類要實現(xiàn)IUserInfoDAL接口,否則不能接收,其好處是下面的方法依賴的是接口,因為接口中的成員都是抽象的,具體實現(xiàn)發(fā)生變化不影響接口。如此new UserInfoDAL()即數(shù)據(jù)訪問驅(qū)動層采用其他技術(shù)來實現(xiàn)對數(shù)據(jù)庫的訪問,下面的方法代碼不發(fā)生變化。UserInfoDAL();//具體實現(xiàn)方法;
例如,數(shù)據(jù)訪問驅(qū)動層改用Nhibernate實現(xiàn)對數(shù)據(jù)庫的訪問。實現(xiàn)方法如下。
添加類庫項目,命名為jygt.AutoWeightSystem.NHDAL,在該類庫項目下添加NHUserInfoDAL類,讓其去實現(xiàn)IUserInfoDAL接口。
2.1.7 通過簡單工廠進(jìn)一步隔離BLL 層對DAL 的依賴
在DAL層下新建類庫項目jygt.AutoWeightSystem.DALFactory,下面新建靜態(tài)的StaticDALFactory類,類下面創(chuàng)建GetUserInfoDAL方法,因為下面new UserInfoDAL()、new NhUserInfoDAL等都可以接受,即能接受下面的變化。
即由:
簡單工廠的優(yōu)勢在于,如果數(shù)據(jù)訪問驅(qū)動層的實現(xiàn)技術(shù)發(fā)生改變,只需更改簡單工廠中的return new UserInfoDAL(),而BLL層程序不需要做修改。如果程序已經(jīng)發(fā)布,只需要把簡單工廠的程序集重新生成后,把服務(wù)器上的相應(yīng)的程序集替換即可,BLL層代碼等不做改動。
2.1.8 通過抽象工廠實現(xiàn)BLL 層與DAL 層徹底解耦
至此,通過簡單工廠使業(yè)務(wù)邏輯層與數(shù)據(jù)訪問層已經(jīng)解耦,即通過簡單工廠隔離了BLL層對DAL層的依賴。但是依然需要修改程序,即通過修改配置文件其他程序不做修改,不通過new實例化對象。這時要實現(xiàn)BLL層與DAL層徹底解耦,可使用抽象工廠的反射方式來實現(xiàn)。要使用反射就要使用當(dāng)前程序集。
數(shù)據(jù)訪問驅(qū)動層的不同實現(xiàn)方法其實就是程序集名稱不同,前提是不同數(shù)據(jù)訪問驅(qū)動層封裝同一個實體的DAL名稱相同,比如都為UserInfoDAL,不要一個是UserInfoDAL,另一個是NhUserInfoDAL,所以這里把jygt.AutoWeightSystem.NHDAL層下的NhUserInfoDAL重命名為UserInfoDAL與jygt.AutoWeightSystem.EFDAL下的名稱相同,這就是約定大于配置的思想。
以后如果實現(xiàn)數(shù)據(jù)訪問驅(qū)動層的技術(shù)改變,只要修改配置文件web.Config中的
2.1.9 保證線程內(nèi)共享一個上下文實例
數(shù)據(jù)訪問層BaseDAL中上下文實例通過new方法產(chǎn)生,語句如下:
只要執(zhí)行這句代碼就會產(chǎn)生一個上下文實例,不能保證線程內(nèi)共享一個上下文實例。
這時也需要把new方法去掉,不通過new 來實例化出一個上下文對象。也可以用一個單獨類來產(chǎn)生上下文實例,由于本項目DAL主要用EF來實現(xiàn),所以在jygt.AutoWeightSystem.E F D A L 層下單獨再新建一個D b C o n te n t F a c t o r y類,專門用來保證線程內(nèi)共享一個上下文實例。
由于使用DbContext作為返回值類型,Model層中如果還有其他的上下文對象,只要把new 后面的具體上下文對象名稱做對應(yīng)修改就可以實現(xiàn)切換上下文對象。
然后在BaseDAL中的
2.1.10 封裝數(shù)據(jù)訪問層的統(tǒng)一入口DbSession 類
封裝DBSession類,讓其擁有所有DAL的實例(或者說是用來生成DAL實例對象的類/工廠)和更新到數(shù)據(jù)庫的方法,也即是讓DBSession類封裝成整個數(shù)據(jù)訪問層與數(shù)據(jù)庫的會話類,像EF上下文封裝了所有表對應(yīng)實體集合DbSet
接下來把BaseDAL.cs中所有DB.SaveChanges()代碼全部刪除或注釋掉,即不在數(shù)據(jù)訪問層使每一操作都與數(shù)據(jù)庫交互。
優(yōu)勢:數(shù)據(jù)提交的權(quán)利從數(shù)據(jù)庫訪問層提到了業(yè)務(wù)邏輯層。批量操作可以一次性提交到數(shù)據(jù)庫,如果在數(shù)據(jù)庫訪問層每個方法都有SaveChanges()方法,那么每操作一次都會與數(shù)據(jù)庫發(fā)生交互。而到了業(yè)務(wù)層來了就非常靈活,可以批量操作后一起提交,需要時也可以一個操作結(jié)束就提交,只需加上DBSession.SaveChanges();即可。
2.1.11 建立IDBSession 接口,讓業(yè)務(wù)層依賴接口
建立IDBSession接口,讓業(yè)務(wù)層依賴接口,保證一次請求共用一個dbsession實例
上面業(yè)務(wù)邏輯層中有語句:DBSession dbsession =new DBSession();
即DBSession所在層與BLL層之間緊密依賴。項目一般要求層與層之間的調(diào)用最好通過接口隔離,不要依賴于具體實現(xiàn),即使這個接口沒有其他意義。
因此再定義一個IDBSession,用來隔離DBSession所在層與BLL層之間的依賴。
在數(shù)據(jù)訪問接口層jygt.AutoWeightSystem.IDAL中添加新項目接口IDbSession,代碼如下:
這樣整個設(shè)計方案程序代碼在抽象與解耦方面性能得以很大改善。
2.2.1 Spring.Net 與MVC 配合使用
目的是把控制器下的生成B L L 實例的代碼,如userInfoBLL由
2.2.2 配置UI 層下的web.config 文件
2.2.3 修改UI 層下的Global.asax 文件的繼承類先添加Spring.net的引用。
2.2.4 在Config 文件夾下新建BLLs.xml
其配置如下。
這兩個X M L 配置語句順序不能對調(diào),因為controllers.x ml中用到B L Ls.x ml中的內(nèi)容,即controllers.xml配置文件需要引用(ref="UserInfoBLL")UserInfoBLL類,而UserInfoBLL類在BLLs.xml中配置。
綜上,由2.1節(jié)和2.2節(jié),基于DAL層到BLL層的解耦、BLL層到UI層的解耦,如圖3所示。
圖3 DAL 層到BLL 層及BLL 層到UI 層的解耦過程示意圖
2.3.1 設(shè)計IDAL 的T4 模板IDALs.tt[5]
2.3.2 設(shè)計DAL層下的T4模板IDbSession.tt
2.3.3 設(shè)計EFDAL 的T4 模板DALs.tt
復(fù)制IDALs.tt一份到j(luò)ygt.AutoWeightSystem.EFDAL項目下,并重命名為DALs.tt。修改DALs.tt文件如下。
2.3.4 設(shè)計DbSession 的T4 模板DbSession.tt
復(fù)制DALs.tt一份到j(luò)ygt.AutoWeightSystem.DALFactoty項目下,并重命名為Dbsession.tt。修改Dbsession.tt文件代碼,方法與設(shè)計DALs.tt類似,代碼省略。
2.3.5 設(shè)計StaticDALFactory的 T4 模板StaticDALFactory.tt代碼省略。
2.3.6 設(shè)計IBLL 的T4 模板IBLL.tt
復(fù)制一份DbSession.tt到j(luò)ygt.AutoWeightSystem.IBLL項目下,重命名為IBLL.tt并修改代碼。方法與設(shè)計DALs.tt類似,代碼省略。
保存下模板,即可生成相應(yīng)的類。
2.3.7 設(shè)計BLL 的T4 模板BLL.tt
復(fù)制一份IBLL.tt到j(luò)ygt.AutoWeightSystem.BLL項目下,把那個重命名為BLL.tt并修改代碼,方法與設(shè)計DALs.tt類似,代碼省略。
2.3.8 設(shè)計UI層下controllers.xml的T4 模板BLLxmlSpring.tt
到j(luò)ygt.AutoWeightSystem.IBLL項目復(fù)制一份IBLL.tt到當(dāng)前項目jygt.AutoWeightSystem.IBLL下,并重命名為BLLxmlSpring.tt并進(jìn)行修改,方法與設(shè)計DALs.tt類似,代碼省略。
保存下模板,即可生成相應(yīng)的XML文件。
最后,單擊生成---轉(zhuǎn)換所有T4模板,所有層的相應(yīng)代碼就自動生成。
1.打開UserInfo的View頁面Index.cshtml,刪除body中間的所有內(nèi)容;
2.復(fù)制jquery-easyui-1.3.1文件到項目的Content文件夾下[6];
3.在前臺添加展示數(shù)據(jù)表格的table元素;
4.添加添加樣式和js腳本等;
4. 角色管理、權(quán)限管理設(shè)計
與上節(jié)用戶管理設(shè)計類似進(jìn)行角色管理、權(quán)限管理設(shè)計。整個權(quán)限管理系統(tǒng)的軟件結(jié)構(gòu)如圖4所示。
圖4 權(quán)限管理系統(tǒng)的軟件結(jié)構(gòu)
系統(tǒng)權(quán)限管理測試方案:
1.給用戶管理設(shè)置角色。用戶管理:勾選“高級管理員”角色;
2.給權(quán)限管理設(shè)置角色。權(quán)限管理:勾選“高級管理員”“管理員”角色;
3.用戶登錄測試當(dāng)以高級管理員admin登錄時,桌面的圖標(biāo)有用戶管理、權(quán)限管理、角色管理圖標(biāo),可執(zhí)行相關(guān)操作;當(dāng)用戶以管理員ZhangSan身份登錄時,桌面的圖標(biāo)只有權(quán)限管理、角色管理可執(zhí)行相關(guān)操作,沒有用戶管理圖標(biāo),不能執(zhí)行用戶管理相關(guān)操作。
經(jīng)上述操作測試和試運行,本項目基于MVC的權(quán)限管理系統(tǒng)實現(xiàn)了權(quán)限管理的功能。