C# C/S结构数据库系统框架详解(二) ModuleManager详解[vjsdn]
一个大的应用程序由大量模块构成, 因人而异模块的理解和分类各有不同. 在C/S框架内我将模块(Module)定义为一个DLL程序(Class Library). 而每个DLL程序按业务分类. 如进销存系统我们可定义3个模块:采购模块(Purchase Module),销售模块(Sales Module)和库存模块(Inventory Module).而每个模块的业务代码封装在
DLL Class Library内. 则构成3个独立的DLL和一个可执行EXE文件. 如要细分还有公共模块(Common Module), 数据访问层(Data Access Layer Module),系统安全(System Security Module)和系统设置模块(System Setups Module)等模块. 这样就构成一个简单的多模块系统框架了.
C/S框架内定义了8个模块(DLL)和一个主程序(EXE).
从设计角度上讲模块也要分类,可分为系统模块和业务模块.
系统模块是框架组成部分,如Business.dll,Common.dll,DataAccess.dll,Library.dll,Models.dll.
业务模块封装核心业务,包括业务窗体,图片资源等信息. 如上所述的进销存系统:进,销,存3个模块为业务模块.
我们对模块有了初步认识. 由此可见一个系统是由很多模块构成的,而系统越大分的模块越多,模块越多程序越复杂和难以控制.当然安全问题也应运而生了.
假设A用户仅能使用采购模块(Purchase Module),另两个模块不可见, 在设计上我们可以这么做:
1.设计权限,隐藏其它两个模块.
2.在客户机仅发布exe和purchase.dll
3.其它设计.
假设不按业务独立模块,我想设计权限来隐藏其它两个模块是件复杂而危险的事. 可想而知你需要在每个窗体每个业务类都要判断A用户是否有Sales和Inventory的权限,成百上千种判断会使你疯狂!
但是只给客户机安装一个purchase.dll,即使在设计上不判断权限也用不着Sales和Inventory任何功能!
假设我们选择第2种方法, 因客户不同发布的dll也不同,这里牵涉到动态加载dll的问题!本文的重点就是介绍如何动态加载模块.下面且看相关源代码:
ModuleID类:模块编号,给每个模块一个唯一编号.
/// <summary>
/// 模块编号,给每个模块一个唯一编号.
/// </summary>
public enum ModuleID
{
None = 0,
TestModule1 = 1, //测试模块1
TestModule2 = 2,//测试模块2
SystemManage = 3, //系统管理模块
}
模块信息类:实体类,封装与模块相关信息.
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 模块信息类
/// </summary>
public class ModuleInfo
{
private Assembly _ModuleAssembly = null;
private ModuleID _ModuleID = ModuleID.None;
private string _ModuleName = string.Empty;
private string _ModuleFile = string.Empty;
/// <summary>
/// 构造器
/// </summary>
public ModuleInfo(Assembly asm, ModuleID id, string name, string file)
{
_ModuleAssembly = asm;
_ModuleFile = file;
_ModuleID = id;
_ModuleName = name;
}
/// <summary>
/// 加载DLL文件后程序集指针
/// </summary>
public Assembly ModuleAssembly
{
get { return _ModuleAssembly; }
set { _ModuleAssembly = value; }
}
/// <summary>
/// 模块文件(*.dll)
/// </summary>
public string ModuleFile
{
get { return _ModuleFile; }
set { _ModuleFile = value; }
}
/// <summary>
/// 模块名称
/// </summary>
public string ModuleName
{
get { return _ModuleName; }
set { _ModuleName = value; }
}
/// <summary>
/// 模块编号
/// </summary>
public ModuleID ModuleID
{
get { return _ModuleID; }
set { _ModuleID = value; }
}
}
转载请注明来自易学网 www.vjsdn.net 易学原创作品
ModuleLoader:模块加载器. 通过ModuleLoader类动态加载模块dll, 将程序集Assembly保存在
_ModuleAssembly变量.
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 模块加载器
/// </summary>
public class ModuleLoader
{
private string _ModuleFileName;//DLL 文件名
private frmModuleBase _ModuleMainForm;//如果加載了模塊. 返回該模塊的主窗體對象.
private Assembly _ModuleAssembly;
public static readonly string MODULE_FILTER = "易学网C/S系统框架模块|VJSDN C/S Modules|*.dll";
public static readonly string MODULE_PATH = Application.StartupPath;
public ModuleLoader()
{
//
}
/// <summary>
/// 模块所在的程序集
/// </summary>
public Assembly ModuleAssembly
{
get { return _ModuleAssembly; }
}
/// <summary>
/// 返回AssemblyModuleEntry,自定义模块特性
/// </summary>
public string GetCurrentModuleName()
{
return ModuleLoader.GetModuleEntry(_ModuleAssembly).ModuleName;
}
/// <summary>
/// 模块主窗体
/// </summary>
public frmModuleBase ModuleMainForm
{
get { return _ModuleMainForm; }
}
/// <summary>
/// 加载模块的菜单
/// </summary>
/// <param name="menuStrip">程序主窗体的菜单</param>
public void LoadMenu(MenuStrip menuStrip)
{
if (_ModuleMainForm != null && _ModuleMainForm.GetModuleMenu() != null)
{
menuStrip.Items.Insert(menuStrip.Items.Count - 1, _ModuleMainForm.GetModuleMenu().Items[0]);
}
}
/// <summary>
/// 加载模块主方法
/// </summary>
/// <param name="moduleinfo">模块信息</param>
/// <returns></returns>
public bool LoadModule(ModuleInfo moduleinfo)
{
_ModuleFileName = moduleinfo.ModuleFile;
_ModuleAssembly = moduleinfo.ModuleAssembly;
string entry = GetModuleEntryNameSpace(_ModuleAssembly);
if (string.Empty == entry) return false;
Form form = (Form)_ModuleAssembly.CreateInstance(entry);
_ModuleMainForm = null;
if (form is IModuleBase)
_ModuleMainForm = (frmModuleBase)form;
return _ModuleMainForm != null;
}
/// <summary>
/// 获取模块列表,转换为ModuleInfo集合.
/// </summary>
public IList<ModuleInfo> GetModuleList()
{
try
{
string[] files = null; //模塊文件(*.dll)
IList<ModuleInfo> list = new List<ModuleInfo>();
if (Directory.Exists(MODULE_PATH))
files = Directory.GetFiles(MODULE_PATH, "*.dll");
foreach (string mod in files)
{
Assembly asm = null;
try
{
//.net framework dll
asm = Assembly.LoadFile(mod);
}
catch { continue; }
ModuleID id = GetModuleID(asm);
string name = GetCurrentModuleName();
if (id != ModuleID.None)
{
ModuleInfo m = new ModuleInfo(asm, id, name, mod);
list.Add(m);
}
}
SortModule(list); //模塊排序.
return list;
}
catch (Exception ex)
{
Msg.ShowException(ex);
return null;
}
}
/// <summary>
/// 获取程序集自定义特性。是否用户自定义模块由AssemblyModuleEntry特性确定。
/// </summary>
/// <returns></returns>
public AssemblyModuleEntry GetModuleEntry()
{
return ModuleLoader.GetModuleEntry(_ModuleAssembly);
}
/// <summary>
/// 判断加载的文件是否模块文件,因目录下可能有不同类别的DLL文件。
/// 判断DLL为模块的条件是检查Assembly特性。
/// </summary>
public bool IsModuleFile(string moduleFile)
{
try
{
Assembly asm = Assembly.LoadFile(moduleFile);
return (ModuleLoader.GetModuleID(asm) != ModuleID.None);
}
catch { return false; }
}
/// <summary>
/// 每一个模块对应一个XtraTabPage,然后將模块主窗体的Panel容器放置在XtraTabPage内。
/// 因此,所有加载的模块主窗体的Panel容器都嵌套在主窗体的XtraTabControl内。
/// </summary>
public void LoadGUI(XtraTabControl mainTabControl)
{
try
{
IModuleBase moduleBase = _ModuleMainForm as IModuleBase;
Panel container = moduleBase.GetContainer();//模块主窗体的Panel容器
if (null != container)
{
container.Dock = DockStyle.Fill;
XtraTabPage page = new XtraTabPage();
page.Text = this.GetCurrentModuleName();
page.Controls.Add(container);
mainTabControl.TabPages.Add(page);
}
}
catch (Exception ex) { Msg.ShowException(ex); }
}
/// <summary>
/// 模块排序
/// </summary>
/// <param name="list"></param>
public void SortModule(IList<ModuleInfo> list)
{
int i, j;
ModuleInfo temp;
bool done = false;
j = 1;
while ((j < list.Count) && (!done))
{
done = true;
for (i = 0; i < list.Count - j; i++)
{
if ((list[i] as ModuleInfo).ModuleID > (list[i + 1] as ModuleInfo).ModuleID)
{
done = false;
temp = list[i];
list[i] = list[i + 1];
list[i + 1] = temp;
}
}
}
}
#region 类公共表态方法
/// <summary>
/// 查找子菜单,深度搜索
/// </summary>
public static ToolStripMenuItem GetMenuItem(ToolStrip mainMenu, string menuName)
{
ToolStripItem[] items = mainMenu.Items.Find(menuName, true);
if (items.Length > 0 && items[0] is ToolStripMenuItem)
return (ToolStripMenuItem)items[0];
else
return null;
}
/// <summary>
/// 获取程序集自定义特性。是否用户自定义模块由AssemblyModuleEntry特性确定。
/// </summary>
public static AssemblyModuleEntry GetModuleEntry(Assembly asm)
{
AssemblyModuleEntry temp = new AssemblyModuleEntry(ModuleID.None, "", "");
if (asm == null) return temp;
object[] list = asm.GetCustomAttributes(typeof(AssemblyModuleEntry), false);
if (list.Length > 0)
return (AssemblyModuleEntry)list[0];
else
return temp;
}
/// <summary>
/// 获取模块主窗体名字空间
/// </summary>
public static string GetModuleEntryNameSpace(Assembly asm)
{
return GetModuleEntry(asm).ModuleEntryNameSpace;
}
/// <summary>
/// 獲取模塊編號
/// </summary>
public static ModuleID GetModuleID(Assembly asm)
{
return ModuleLoader.GetModuleEntry(asm).ModuleID;
}
#endregion
}
转载请注明来自易学网 www.vjsdn.net 易学原创作品
模块控制类
1.管理ModuleLoader加载的模块.
2.动态创建菜单及导航条按钮.
3.将所有模块主窗体组合在XtraTabControl 控件内. 每个XtraTabPage对应一个模块.
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 模块管理器
/// </summary>
public class ModuleManager
{
//程序主窗体的TabControl,用于放置模块主窗体
private XtraTabControl _tabControlModules = null;
//程序主窗体
private Form _MDIMainForm = null;
private static IList<ModuleInfo> _Modules = null;
/// <summary>
/// 获取回载的模块
/// </summary>
public static IList<ModuleInfo> GetAvailableModules()
{
return _Modules;
}
/// <summary>
/// 构造器
/// </summary>
public ModuleManager(Form mdiMainForm, XtraTabControl tabControlModules)
{
_tabControlModules = tabControlModules;
_MDIMainForm = mdiMainForm;
}
/// <summary>
/// Navigator bar 导航条按鈕事件.
/// </summary>
public static void OnNavBarLinkClick(object sender, NavBarLinkEventArgs e)
{
if (e.Link.Item.Tag is ToolStripItem)
{
ToolStripItem item = (ToolStripItem)(e.Link.Item.Tag);
if (null != item) item.PerformClick(); //執行菜單事件.
}
}
/// <summary>
/// 是否有子菜單.
/// </summary>
private bool IsSubMenuOwner(ToolStripItem item)
{
if (item is ToolStripMenuItem)
{
if (((ToolStripMenuItem)item).DropDownItems.Count > 0)
return true;
}
return false;
}
/// <summary>
/// 由菜单创建导航条控件按钮
/// </summary>
private NavBarItem CreateNavBarItem(NavBarControl navBar, ToolStripItem item)
{
NavBarItem newItem = new NavBarItem();
newItem.Caption = item.Text;
newItem.Tag = item; //綁定一個菜單項,newItem.Click事件會使用這個菜單項
newItem.LinkClicked += new NavBarLinkEventHandler(ModuleManager.OnNavBarLinkClick);
navBar.Items.Add(newItem);
return newItem;
}
/// <summary>
/// 导航条控件按钮分组
/// </summary>
private NavBarGroup CreateNavBarGroup(NavBarControl navBar, string caption)
{
NavBarGroup group = navBar.Groups.Add();//增加一個Button組.
group.Caption = caption; //模塊的顯示名稱
group.LargeImageIndex = 0;
if (group.Caption.Equals(string.Empty)) group.Caption = "Unknown Name";
return group;
}
/// <summary>
/// 当点击导航条分组按钮时触发该事件
/// </summary>
private void OnActiveGroupChanged(object sender, DevExpress.XtraNavBar.NavBarGroupEventArgs e)
{
try
{
string moduleName = e.Group.Caption.ToString();
this.ActiveModule(moduleName);
}
catch (Exception ex) { Msg.ShowException(ex); }
}
/// <summary>
/// 显示指定的模块主窗体
/// </summary>
public void ActiveModule(string moduleDisplayName)
{
foreach (XtraTabPage page in this._tabControlModules.TabPages)
{
if (page.Text.Equals(moduleDisplayName))
{
_tabControlModules.SelectedTabPage = page;
return;
}
}
}
/// <summary>
/// 创建导航条分组按钮 Navigator NavBarGroup
/// </summary>
/// <param name="navBar">NavBarControl对象</param>
/// <param name="moduleMenu">模块主菜单</param>
/// <param name="moduleDisplayName">模块名称</param>
public NavBarGroup CreateNavBarButton(NavBarControl navBar, ToolStripMenuItem moduleMenu, string moduleDisplayName)
{
navBar.ActiveGroupChanged += new NavBarGroupEventHandler(OnActiveGroupChanged);
NavBarGroup group = CreateNavBarGroup(navBar, moduleDisplayName); //根据模块名称创建Group
if (moduleMenu == null) return group; //组没有菜单则返回,实为一个空组无子按钮.
foreach (ToolStripItem menuItem in moduleMenu.DropDownItems)
{
if (menuItem is ToolStripSeparator) continue;
if (IsSubMenuOwner(menuItem)) continue;
if (menuItem.Enabled)
{
NavBarItem item = CreateNavBarItem(navBar, menuItem);
item.SmallImageIndex = 1;//Sub Function
group.ItemLinks.Add(item);
}
}
return group;
}
/// <summary>
/// 初始化NavBar控件.绑定组NavGroup.ActiveGroupChanged事件
/// </summary>
public void CreateNavBarButtons(NavBarControl navBar, MenuStrip mainMenu)
{
try
{
navBar.SuspendLayout();
navBar.Items.Clear();
navBar.Groups.Clear();
foreach (XtraTabPage page in this._tabControlModules.TabPages)
{
//取主窗体对应的顶级菜单
ToolStripMenuItem moduleMenu = FindMenuItemByText(mainMenu, page.Text);
if (moduleMenu != null && moduleMenu.Enabled)//创建该模块的NavBarButton.
this.CreateNavBarButton(navBar, moduleMenu, page.Text);
}
navBar.ResumeLayout();
}
catch (Exception ex) { Msg.ShowException(ex); }
}
private ToolStripMenuItem FindMenuItemByText(MenuStrip mainMenu, string text)
{
foreach (ToolStripMenuItem item in mainMenu.Items)
if (item.Text == text) return (ToolStripMenuItem)item;
return null;
}
/// <summary>
/// 加载模块主方法
/// </summary>
public void LoadModules(StatusLable msg)
{
try
{
ModuleLoader loader = new ModuleLoader();
_Modules = loader.GetModuleList();//從目錄裝載模塊
foreach (ModuleInfo m in _Modules) //枚舉文件名列表.
{
if (!loader.LoadModule(m)) continue;
msg.UpdateMessage("正在加载:" + loader.GetCurrentModuleName());
loader.LoadGUI(_tabControlModules);
loader.LoadMenu(_MDIMainForm.MainMenuStrip);
loader.ModuleMainForm.MdiParent = _MDIMainForm; //设置模块主窗体的父窗体.
}
GC.Collect();
}
catch (Exception ex) { Msg.ShowException(ex); }
}
}
转载请注明来自易学网 www.vjsdn.net 易学原创作品
为了规范模块共有的特性,设计一个模块主窗体接口.
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 模块主窗体接口
/// </summary>
public interface IModuleBase
{
ModuleID GetModuleID(); //获取模块编号
ModuleInfo GetModuleInfo(); //
Panel GetContainer(); //模块主窗体容器
MenuStrip GetModuleMenu();
void InitButton();
void InitMenu();
}
转载请注明来自易学网 www.vjsdn.net 易学原创作品
定义一个模块主窗体基类来实现这个接口.
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 模块主窗体基类
/// </summary>
public partial class frmModuleBase : frmBase, IModuleBase
{
public frmModuleBase()
{
InitializeComponent();
}
#region IModuleBase Members
//模块的主菜单
public virtual MenuStrip GetModuleMenu()
{
return null;
}
//模块编号
public virtual ModuleID GetModuleID()
{
return ModuleID.None;
}
//模块信息
public virtual ModuleInfo GetModuleInfo()
{
return null;
}
//模块主窗体容器
public virtual Panel GetContainer()
{
return this.pnlContainer;
}
//初始化按钮
public virtual void InitButton()
{
}
//初始化菜单
public virtual void InitMenu()
{
}
#endregion
}
转载请注明来自易学网 www.vjsdn.net 易学原创作品
全部链接:
1.C/S结构数据库系统框架详解
http://www.vjsdn.net/bbs/bbsTopicDetails.aspx?pid=109201218
2.ModuleManager详解
http://www.vjsdn.net/bbs/bbsTopicDetails.aspx?pid=109201219
3.如何新建一个模块
http://www.vjsdn.net/bbs/bbsTopicDetails.aspx?pid=109201220
4.C/S结构数据库系统框架源代码
http://www.vjsdn.net/bbs/bbsTopicDetails.aspx?pid=109201221