电竞比分网-中国电竞赛事及体育赛事平台

分享

博客園閃存首頁(yè)新隨筆聯(lián)系管理訂閱 隨筆- 0 文章- 0 評(píng)論- 0 VB.NET中LINQ TO List泛型查詢語(yǔ)句(分組,聚合函數(shù))

 昵稱10504424 2013-07-10

支持插件化應(yīng)用的開(kāi)發(fā)框架能給程序帶來(lái)無(wú)窮的生命力,也是目前很多系統(tǒng)、程序追求的重要方向之一,插件化的模塊,在遵循一定的接口標(biāo)準(zhǔn)的基礎(chǔ)上,可以實(shí)現(xiàn)快速集成,也就是所謂的熱插拔操作,可以無(wú)限對(duì)已經(jīng)開(kāi)發(fā)好系統(tǒng)進(jìn)行擴(kuò)展,而且不會(huì)影響已有的功能,不在需要的模塊,通過(guò)修改配置移除即可。我的Winform開(kāi)發(fā)框架一直以來(lái),來(lái)源于多年的項(xiàng)目積累以及客戶的反饋,已經(jīng)具備了眾多很好的特性以及相關(guān)的模塊組合,為了更好擁抱變化,提高基于Winform開(kāi)發(fā)框架基礎(chǔ)上開(kāi)發(fā)新系統(tǒng)的效率,以及為框架融入更多好的特性,故此把我的Winform開(kāi)發(fā)框架在原來(lái)的基礎(chǔ)上進(jìn)行擴(kuò)展,實(shí)現(xiàn)基于插件化應(yīng)用框架特性。

為了引入插件化的應(yīng)用框架特點(diǎn),我在上一篇隨筆《Winform開(kāi)發(fā)框架之權(quán)限管理系統(tǒng)的改進(jìn)》已經(jīng)對(duì)我的通用權(quán)限管理系統(tǒng)進(jìn)行了改進(jìn),其中增加了菜單管理模塊就是為了做插件化做準(zhǔn)備的,我們通過(guò)權(quán)限管理系統(tǒng)配置好菜單的相關(guān)信息,然后在應(yīng)用框架中動(dòng)態(tài)加載菜單功能即可實(shí)現(xiàn)。這個(gè)菜單模塊,是用來(lái)配置基于Web開(kāi)發(fā)框架或者Winform開(kāi)發(fā)框架、WCF開(kāi)發(fā)框架的菜單,通過(guò)預(yù)先的配置,框架程序的動(dòng)態(tài)加載解析,就能實(shí)現(xiàn)插件模塊的熱插拔功能了。實(shí)際插件化框架的菜單配置界面效果如下所示。

最終在Winform開(kāi)發(fā)框架的程序中,實(shí)現(xiàn)基于插件化的應(yīng)用,如下所示。

先來(lái)看看我改造Winform開(kāi)發(fā)框架,最終形成的框架界面效果,然后在逐一進(jìn)行介紹,整個(gè)開(kāi)發(fā)框架的實(shí)現(xiàn)過(guò)程。

1、框架的項(xiàng)目工程規(guī)劃

為了減少框架整體的復(fù)雜性以及提高重用,對(duì)插件化的應(yīng)用框架的項(xiàng)目工程進(jìn)行了劃分,包括“框架基礎(chǔ)界面模塊”、“插件應(yīng)用框架啟動(dòng)模塊”、倉(cāng)庫(kù)管理系統(tǒng)模塊業(yè)務(wù)邏輯、倉(cāng)庫(kù)管理系統(tǒng)模塊窗體界面等幾個(gè)部分。前面兩個(gè)部分是插件化框架的核心,可以認(rèn)為是不需要變化的模塊,提供所有插件應(yīng)用動(dòng)態(tài)創(chuàng)建以及使用的框架支撐;后面兩個(gè)是具體的主業(yè)務(wù)模塊,這里以WInform開(kāi)發(fā)框架中的倉(cāng)庫(kù)管理系統(tǒng)作為主業(yè)務(wù)模塊,它本身也是插件應(yīng)用之一,具體的項(xiàng)目工程結(jié)構(gòu)以及說(shuō)明如下所示。

項(xiàng)目名稱 項(xiàng)目說(shuō)明
WHC.Framework.BaseUIDx  框架基礎(chǔ)界面模塊,定義窗體界面基類、通用Excel導(dǎo)入模塊、通用高級(jí)查詢模塊等
WHC.Framework.StarterDx  插件應(yīng)用框架啟動(dòng)模塊,集成權(quán)限登錄、動(dòng)態(tài)菜單創(chuàng)建、插件應(yīng)用動(dòng)態(tài)加載、基礎(chǔ)框架功能等
WHC.WareHouseMis  倉(cāng)庫(kù)管理系統(tǒng)模塊的業(yè)務(wù)邏輯
WHC.Framework.WareHouseDx   倉(cāng)庫(kù)管理系統(tǒng)模塊的窗體界面

從上面的表格說(shuō)明中,我們可以看到“WHC.Framework.StarterDx”項(xiàng)目工程,是“插件應(yīng)用框架啟動(dòng)模塊”,它基本上只和權(quán)限管理系統(tǒng)模塊有關(guān)聯(lián)關(guān)系,因?yàn)闄?quán)限系統(tǒng)是框架底層支撐的模塊,包括用戶登錄、菜單管理、權(quán)限控制等都需要從權(quán)限管理系統(tǒng)中獲取數(shù)據(jù),具體的主要業(yè)務(wù)功能如下所示。

 

 

2、框架的菜單動(dòng)態(tài)加載

 本文第一張圖片里面,介紹了菜單的定義信息,其中包括了圖標(biāo)的配置,這些圖片為了方便管理,以及插件需要?jiǎng)討B(tài)添加菜單圖標(biāo),我把它放置在了程序目錄的相對(duì)路徑下面,如下所示,動(dòng)態(tài)創(chuàng)建菜單的時(shí)候,從指定的路徑去獲取圖標(biāo)并加載即可。

動(dòng)態(tài)加載菜單是指在插件化應(yīng)用框架啟動(dòng),用戶登錄后進(jìn)入主界面后,在主界面中動(dòng)態(tài)創(chuàng)建相應(yīng)的菜單(菜單在權(quán)限管理系統(tǒng)中進(jìn)行配置管理),如下代碼所示。

其中是RibbonPageHelper為了方便動(dòng)態(tài)創(chuàng)建菜單而創(chuàng)建的輔助類,部分代碼如下所示。

復(fù)制代碼
    /// <summary>
/// 動(dòng)態(tài)創(chuàng)建RibbonPage和其下面的按鈕項(xiàng)目輔助類
/// </summary>
public class RibbonPageHelper
{
private RibbonControl control;
public MainForm mainForm;
public RibbonPageHelper(MainForm mainForm, ref RibbonControl control)
{
this.mainForm = mainForm;
this.control = control;
}
public void AddPages()
{
//約定菜單共有3級(jí),第一級(jí)為大的類別,第二級(jí)為小模塊分組,第三級(jí)為具體的菜單
List<MenuNodeInfo> menuList = WHC.Security.BLL.BLLFactory<SysMenu>.Instance.GetTree(Portal.gc.SystemType);
if (menuList.Count == 0) return;
int i = 0;
foreach(MenuNodeInfo firstInfo in menuList)
{
//如果沒(méi)有菜單的權(quán)限,則跳過(guò)
if (!Portal.gc.HasFunction(firstInfo.FunctionId)) continue;
//添加頁(yè)面(一級(jí)菜單)
RibbonPage page = new DevExpress.XtraBars.Ribbon.RibbonPage();
page.Text = firstInfo.Name;
page.Name = firstInfo.ID;
this.control.Pages.Insert(i++, page);
if(firstInfo.Children.Count == 0) continue;
foreach(MenuNodeInfo secondInfo in firstInfo.Children)
{
//如果沒(méi)有菜單的權(quán)限,則跳過(guò)
if (!Portal.gc.HasFunction(secondInfo.FunctionId)) continue;
//添加RibbonPageGroup(二級(jí)菜單)
RibbonPageGroup group = new RibbonPageGroup();
group.Text = secondInfo.Name;
group.Name = secondInfo.ID;
page.Groups.Add(group);
if(secondInfo.Children.Count == 0) continue;
foreach (MenuNodeInfo thirdInfo in secondInfo.Children)
{
//如果沒(méi)有菜單的權(quán)限,則跳過(guò)
if (!Portal.gc.HasFunction(thirdInfo.FunctionId)) continue;
//添加功能按鈕(三級(jí)菜單)
BarButtonItem button = new BarButtonItem();
button.PaintStyle = BarItemPaintStyle.CaptionGlyph;
button.LargeGlyph = LoadIcon(thirdInfo.Icon);
button.Glyph = LoadIcon(thirdInfo.Icon);
button.Name = thirdInfo.ID;
button.Caption = thirdInfo.Name;
..................
group.ItemLinks.Add(button);
}
}
}
}
...............
復(fù)制代碼

菜單為了方便管理,約定分為3級(jí)菜單,三個(gè)層級(jí)的菜單示意圖如下所示。

啟動(dòng)頂部的選項(xiàng)卡級(jí)別為第一級(jí),下面的Ribbon分組為第二級(jí),具體的功能菜單(或者按鈕)為第三級(jí),以上就是通過(guò)菜單數(shù)據(jù)動(dòng)態(tài)創(chuàng)建的菜單界面圖。

3、框架的用戶信息和權(quán)限控制

基礎(chǔ)框架需要傳統(tǒng)的登錄進(jìn)行驗(yàn)證,登錄成功后,把用戶關(guān)聯(lián)的具有的權(quán)限下載到本地,然后由系統(tǒng)邏輯統(tǒng)一判斷即可。

插件應(yīng)用框架系統(tǒng)的登錄代碼和普通的差別不大,登錄后把相關(guān)信息存儲(chǔ)在框架變量中,如下所示。

復(fù)制代碼
        private void btLogin_Click(object sender, EventArgs e)
{
.................
try
{
string ip = NetworkUtil.GetLocalIP();
string macAddr = HardwareInfoHelper.GetMacAddress();
string loginName = this.cmbzhanhao.Text.Trim();
string identity = WHC.Security.BLL.BLLFactory<WHC.Security.BLL.User>.Instance.VerifyUser(loginName, this.tbPass.Text, Portal.gc.SystemType, ip, macAddr);
if (!string.IsNullOrEmpty(identity))
{
UserInfo info = WHC.Security.BLL.BLLFactory<WHC.Security.BLL.User>.Instance.GetUserByName(loginName);
if (info != null)
{
#region 獲取用戶的功能列表
List<FunctionInfo> list = WHC.Security.BLL.BLLFactory<WHC.Security.BLL.Function>.Instance.GetFunctionsByUser(info.ID, Portal.gc.SystemType);
if (list != null && list.Count > 0)
{
foreach (FunctionInfo functionInfo in list)
{
if (!Portal.gc.FunctionDict.ContainsKey(functionInfo.ControlID))
{
Portal.gc.FunctionDict.Add(functionInfo.ControlID, functionInfo.ControlID);
}
}
}
#endregion
bLogin = true;
Portal.gc.UserInfo = info;
Portal.gc.LoginUserInfo = ConvertToLoginUser(info);
this.DialogResult = DialogResult.OK;
}
}
else
{
MessageDxUtil.ShowTips("用戶賬號(hào)密碼不正確");
this.tbPass.Text = ""; //設(shè)置密碼為空
                }
}
catch (Exception err)
{
MessageDxUtil.ShowError(err.Message);
}
}
復(fù)制代碼

為了使框架記錄的權(quán)限信息、用戶數(shù)據(jù)、以及系統(tǒng)的一些配置信息能夠傳遞到每個(gè)插件應(yīng)用的窗體中,設(shè)計(jì)了一個(gè)插件應(yīng)用界面需要實(shí)現(xiàn)的接口,放在了BaseUI項(xiàng)目工程中。

復(fù)制代碼
namespace WHC.Framework.BaseUI
{
/// <summary>
/// 父窗體實(shí)現(xiàn)的權(quán)限控制接口
/// </summary>
public interface IFunction
{
/// <summary>
/// 初始化權(quán)限控制信息
/// </summary>
void InitFunction(LoginUserInfo userInfo, Dictionary<string, string> functionDict);
/// <summary>
/// 是否具有訪問(wèn)指定控制ID的權(quán)限
/// </summary>
/// <param name="controlId">功能控制ID</param>
/// <returns></returns>
bool HasFunction(string controlId);
/// <summary>
/// 登陸用戶基礎(chǔ)信息
/// </summary>
LoginUserInfo LoginUserInfo { get; set; }
/// <summary>
/// 登錄用戶具有的功能字典集合
/// </summary>
Dictionary<string, string> FunctionDict { get; set; }
/// <summary>
/// 應(yīng)用程序基礎(chǔ)信息
/// </summary>
AppInfo AppInfo { get; set; }
}
}
復(fù)制代碼

然后在BaseUI的項(xiàng)目中,界面基類BaseForm實(shí)現(xiàn)這個(gè)接口。

復(fù)制代碼
namespace WHC.Framework.BaseUI
{
public partial class BaseForm : DevExpress.XtraEditors.XtraForm, IFunction
{
public BaseForm()
{
InitializeComponent();
}
...................
復(fù)制代碼

最后,就是我們?nèi)绾蝹鬟f用戶信息以及權(quán)限信息到窗體本身,傳遞到窗體作為其本身的變量后,就可以很方便使用這些關(guān)鍵的信息了。

在我們動(dòng)態(tài)加載插件應(yīng)用的后,我們會(huì)創(chuàng)建對(duì)應(yīng)的Form對(duì)象,然后轉(zhuǎn)換為IFunction接口,賦予該接口相關(guān)的變量屬性即可實(shí)現(xiàn)用戶信息及權(quán)限信息的傳遞,如下代碼所示。

復(fù)制代碼
               Form tableForm = (Form)Activator.CreateInstance(formType);
//如果窗體集成了IFunction接口(第一次創(chuàng)建需要設(shè)置)
IFunction function = tableForm as IFunction;
if (function != null)
{
//初始化權(quán)限控制信息
                    function.InitFunction(Portal.gc.LoginUserInfo, Portal.gc.FunctionDict);
//記錄程序的相關(guān)信息
function.AppInfo = new AppInfo(Portal.gc.AppUnit, Portal.gc.AppName, Portal.gc.AppWholeName, Portal.gc.SystemType);
}
復(fù)制代碼

 4、插件應(yīng)用的動(dòng)態(tài)加載

上面我們說(shuō)到,只要是實(shí)現(xiàn)基于Form的,我們都可以動(dòng)態(tài)創(chuàng)建方式調(diào)用顯示插件的界面出來(lái),而如果界面實(shí)現(xiàn)了IFucntion的權(quán)限控制接口,那么我們就能夠傳遞給它響應(yīng)的數(shù)據(jù),實(shí)現(xiàn)更加完善的控制功能。

在第一張關(guān)于權(quán)限系統(tǒng)的菜單管理圖片中,我們看到了有個(gè)Winform的窗體類型的字段,里面就是用來(lái)動(dòng)態(tài)構(gòu)造插件的配置信息,我們主要是用來(lái)構(gòu)造插件的窗體,并傳遞給它相關(guān)數(shù)據(jù)即可,下圖是菜單管理里面的 “Winform窗體類型” 信息的具體內(nèi)容。

但我們完成菜單的動(dòng)態(tài)創(chuàng)建后,菜單按鈕的響應(yīng)事件就是觸發(fā)動(dòng)態(tài)加載插件的事件。

我們添加菜單的時(shí)候,對(duì)它的響應(yīng)事件也做了處理,具體代碼如下所示。

復(fù)制代碼
                        //添加功能按鈕(三級(jí)菜單)
BarButtonItem button = new BarButtonItem();
.................
button.Caption = thirdInfo.Name;
button.Tag = thirdInfo.WinformType;
button.ItemClick += (sender, e) =>
{
if (button.Tag != null && !string.IsNullOrEmpty(button.Tag.ToString()))
{
LoadPlugInForm(button.Tag.ToString());
}
else
{
MessageDxUtil.ShowTips(button.Caption);
}
};
group.ItemLinks.Add(button);
復(fù)制代碼

單擊事件的響應(yīng)處理就是動(dòng)態(tài)構(gòu)建插件應(yīng)用的事件,其中就是根據(jù)“Winform窗體類型”的數(shù)據(jù)進(jìn)行解析的。

復(fù)制代碼
                string dllFullPath = Path.Combine(Application.StartupPath, filePath);
Assembly tempAssembly = System.Reflection.Assembly.LoadFrom(dllFullPath);
if (tempAssembly != null)
{
Type objType = tempAssembly.GetType(type);
if (objType != null)
{
LoadMdiForm(this.mainForm, objType, isShowDialog);
}
}
復(fù)制代碼

通過(guò)動(dòng)態(tài)創(chuàng)建菜單模塊,動(dòng)態(tài)加載插件應(yīng)用,以及權(quán)限控制等管理,我們就能隔離框架本身和插件應(yīng)用模塊之間的耦合性關(guān)聯(lián),所有后續(xù)開(kāi)發(fā)或者別人開(kāi)發(fā)的業(yè)務(wù)模塊,都可以很方便的通過(guò)權(quán)限管理系統(tǒng)配置數(shù)據(jù)、自動(dòng)更新模塊更新程序應(yīng)用的方式,把一個(gè)高效、易于擴(kuò)展、動(dòng)態(tài)管理的系統(tǒng)應(yīng)用弄得豐富多彩,有聲有色。

基于插件化應(yīng)用框架的Winform開(kāi)發(fā)框架改造,使得今后開(kāi)發(fā)業(yè)務(wù)系統(tǒng),只是基于一定的接口協(xié)議,開(kāi)發(fā)插件應(yīng)用即可,整體性的框架本身可以有專門的人員進(jìn)行維護(hù),提高團(tuán)隊(duì)對(duì)業(yè)務(wù)模塊的橫向切割和快速開(kāi)發(fā)的效率,更好、統(tǒng)一、高效完成企業(yè)化應(yīng)用框架的搭建和使用。

下面的圖形是之前Winform開(kāi)發(fā)框架的相關(guān)功能點(diǎn)集合,加上目前框架的“支持插件化框架應(yīng)用,能快速開(kāi)發(fā)插件、支持動(dòng)態(tài)擴(kuò)展”的特點(diǎn),就顯得更加豐富完善了。

主要研究技術(shù):代碼生成工具、Visio二次開(kāi)發(fā)、送水管理軟件等共享軟件開(kāi)發(fā)
專注于Winform開(kāi)發(fā)框架、WCF開(kāi)發(fā)框架的研究及應(yīng)用。
  轉(zhuǎn)載請(qǐng)注明出處:
撰寫人:伍華聰  http://www. 
    

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多