这是一个很久以前做的小ERP的系统架构的一部分。虽然非常简单,但比较实用,写出来,供大家参考、拍砖。
背景:当时需要做一个小型的制造业ERP系统,但客户不按常理出牌,我们需要先做客户最急需的部门子系统,而这个系统是工作流中最后面的一个子系统。
很显然,整个工作流的前面的资料录入部分,暂时也只能放在最后一个子系统录入了。而且,考虑到以后的整个系统完成时,这些暂时放在后面子系统中的模块,肯定还需要再重新分配到前面的子系统中去。
所以,为了减少以后的重复劳动,考虑到各模块的灵活性,及开发方便性,我们决定使用插件架构。
幸好,在.net中有个很好用的特性:反射机制。这对于我们使用插件架构非常方便。
下面是最主要的代码部分:
1 Assembly PlugProgram = Assembly.LoadFrom("A.dll");
2
3
Form frm = new Form();
4
frm = (Form)PlugProgram.CreateInstance("A.Form1",true);
5
frm.MdiParent = this;
6
frm.Show();上面的代码比较简单,就没写注释了。
完整的步骤如下:
1、把上面的代码放在主程序里。
2、将系统细分为多个子模块,每个开发人员具体负责一个(或多个)子模块的编写。
3、各开发人员为子模块单独建立项目,一切都按编写EXE文件的方式来进行,只是调试完毕,准备插入到主程序时,选择“输出类型”为“类库”即可。自然,会生成DLL文件。
4、最后,将所有子模块的DLL文件,与主程序放在一起。运行主程序,用上面的代码,在主程序里的菜单、工具栏、各种按钮中调用所需要的DLL文件。
呵呵,很简单的吧。
这是一个很久以前做的小ERP的系统架构的一部分。虽然非常简单,但比较实用,写出来,供大家参考、拍砖。
背景:当时需要做一个小型的制造业ERP系统,但客户不按常理出牌,我们需要先做客户最急需的部门子系统,而这个系统是工作流中最后面的一个子系统。
很显然,整个工作流的前面的资料录入部分,暂时也只能放在最后一个子系统录入了。而且,考虑到以后的整个系统完成时,这些暂时放在后面子系统中的模块,肯定还需要再重新分配到前面的子系统中去。
所以,为了减少以后的重复劳动,考虑到各模块的灵活性,及开发方便性,我们决定使用插件架构。
幸好,在.net中有个很好用的特性:反射机制。这对于我们使用插件架构非常方便。
下面是最主要的代码部分:
1 Assembly PlugProgram = Assembly.LoadFrom("A.dll");
2
3
Form frm = new Form();
4
frm = (Form)PlugProgram.CreateInstance("A.Form1",true);
5
frm.MdiParent = this;
6
frm.Show();
2

3
Form frm = new Form();4
frm = (Form)PlugProgram.CreateInstance("A.Form1",true);5
frm.MdiParent = this;6
frm.Show();完整的步骤如下:
1、把上面的代码放在主程序里。
2、将系统细分为多个子模块,每个开发人员具体负责一个(或多个)子模块的编写。
3、各开发人员为子模块单独建立项目,一切都按编写EXE文件的方式来进行,只是调试完毕,准备插入到主程序时,选择“输出类型”为“类库”即可。自然,会生成DLL文件。
4、最后,将所有子模块的DLL文件,与主程序放在一起。运行主程序,用上面的代码,在主程序里的菜单、工具栏、各种按钮中调用所需要的DLL文件。
呵呵,很简单的吧。
Feedback
呵呵,我们比较懒。
1、是为了开发方便,几个人各做各的,做完之后,大家的DLL库放在一起就可以跑了。而且可以避免源程序变得很大。
2、我上面说过,我们先做的是最后一个子系统,里面有些子模块,是以后要移动到其它子系统中去的,但我们现在不知道哪些是需要移动的。所以,如果做成DLL文件,以后用动态菜单直接调用就可以了。不需要再重新编译了。维护起来非常方便。
当然,还有很多其它好处。
如:我们使用原型迭代来开发,先开发一小部分给客户先用着,以后,如需要增加新功能,再给客户一个新的DLL库,及一个新的配置文件就可以了。比较不容易出错,安全。
1、是为了开发方便,几个人各做各的,做完之后,大家的DLL库放在一起就可以跑了。而且可以避免源程序变得很大。
2、我上面说过,我们先做的是最后一个子系统,里面有些子模块,是以后要移动到其它子系统中去的,但我们现在不知道哪些是需要移动的。所以,如果做成DLL文件,以后用动态菜单直接调用就可以了。不需要再重新编译了。维护起来非常方便。
当然,还有很多其它好处。
如:我们使用原型迭代来开发,先开发一小部分给客户先用着,以后,如需要增加新功能,再给客户一个新的DLL库,及一个新的配置文件就可以了。比较不容易出错,安全。
思路不错,但是我觉得你不应该CREATEFORM,我觉得应该声明一个接口,然后每个模块实现这个接口,调用的时候也是完全针对接口的。
因为有的时候 一个功能模块不一定就是一个FORM 。而且如果使用接口很多系统的功能很容易处理掉,比如 权限,日志。。。。
因为有的时候 一个功能模块不一定就是一个FORM 。而且如果使用接口很多系统的功能很容易处理掉,比如 权限,日志。。。。
to byrybye :
其实我们最开始也是用接口的,因为想在接口里判断一下,这个DLL库是不是合法的,是不是被受权的?但因为这个ERP实在是比较小,而且调用的也全是FORM,所以,合适就好。呵呵。
如果有人要使用这种方法,还可以再扩展很多的,一个真正的插件架构当然不是这么几句代码就搞定了。还有很多需要考虑的问题。
其实我们最开始也是用接口的,因为想在接口里判断一下,这个DLL库是不是合法的,是不是被受权的?但因为这个ERP实在是比较小,而且调用的也全是FORM,所以,合适就好。呵呵。
如果有人要使用这种方法,还可以再扩展很多的,一个真正的插件架构当然不是这么几句代码就搞定了。还有很多需要考虑的问题。
这个确实是一个好办法, 也比较赞同byrybye , 这样也可以实现窗体show类型的多样化,以及类型的多样化. 还有AlleNny 提的问题,我认为是pansky 没有把他的全部思路说出来的缘故,他肯定有一个地方定义了每个窗体所属的dll, 也就是读取 Assembly PlugProgram = Assembly.LoadFrom("A.dll") 也是动态的,这个东西我以前也做过,对每个入口的Form都做了定义,比如 Form入口的参数,Form所属的Dll,以及Form的全名.
to acloudy:
效率不会有任何问题。
在我们的这个系统中,我们用XML生成了菜单/工具栏的配置文件,程序合法登录后,根据权限,先读取不同的配置文件,再动态生成菜单/工具栏(这样,每一个不同权限的人,看到的菜单/工具栏都不一样的),而在配置文件中,每一个菜单/工具栏都对应一个DLL中的FORM,所以,相应地在点击某个菜单/工具栏时,就会调用相应的FORM。
呵呵,你的每一个子系统的菜单/工具栏不会很多吧?
效率不会有任何问题。
在我们的这个系统中,我们用XML生成了菜单/工具栏的配置文件,程序合法登录后,根据权限,先读取不同的配置文件,再动态生成菜单/工具栏(这样,每一个不同权限的人,看到的菜单/工具栏都不一样的),而在配置文件中,每一个菜单/工具栏都对应一个DLL中的FORM,所以,相应地在点击某个菜单/工具栏时,就会调用相应的FORM。
呵呵,你的每一个子系统的菜单/工具栏不会很多吧?
HH,我们以前的系统就是这样的,delphi下开发的。
所谓的效率就是刚加载窗体的时候可能会比静态的方法慢一点(没测试过具体慢多少),基本无伤大雅。
所谓的效率就是刚加载窗体的时候可能会比静态的方法慢一点(没测试过具体慢多少),基本无伤大雅。
# re: 一个非常简单的“插件”架构
2005-06-20 12:11 by barton131420我觉得这不能算是插件,插件必须承载某种弹性很强的协议。
还有,感觉整个过程并没有用到什么反射。
我很惊讶,你们难道都是这么写程序吗?
Form frm = new Form();//赋值,构造一个空的Form
frm = (Form)PlugProgram.CreateInstance("A.Form1",true);//放弃刚才构造的那个空的Form,换一个指定类型的窗体
其实你可以直接写,既规范又安全:
// 获得来自配置文件中的窗体名称,这是标准的类型限定全名
string formName = "A.Form1, A";
Type formType = Type.GetType(formName);
if (null != formType) {
if (typeof(Form).IsAssignableFrom(formType)) {
Form frm = (Form) Activator.CreateInstance(formType);
frm.MdiParent = this;
frm.Show();
} else {
// 指定的类型不是一个Form类型
}
else {
// 指定的类型不存在
}
还有,感觉整个过程并没有用到什么反射。
我很惊讶,你们难道都是这么写程序吗?
Form frm = new Form();//赋值,构造一个空的Form
frm = (Form)PlugProgram.CreateInstance("A.Form1",true);//放弃刚才构造的那个空的Form,换一个指定类型的窗体
其实你可以直接写,既规范又安全:
// 获得来自配置文件中的窗体名称,这是标准的类型限定全名
string formName = "A.Form1, A";
Type formType = Type.GetType(formName);
if (null != formType) {
if (typeof(Form).IsAssignableFrom(formType)) {
Form frm = (Form) Activator.CreateInstance(formType);
frm.MdiParent = this;
frm.Show();
} else {
// 指定的类型不是一个Form类型
}
else {
// 指定的类型不存在
}
to barton131420:
这位大哥教训的是。我们最开始也用过Type的,呵呵,但后来没用了。
1、关于程序代码规范和安全问题,我上面说过了,这只是最重要的一部分代码,并不是全部,我只是想说清楚我的意思,并不想把我们所有的代码全写出来。(我们的程序有完善的错误机制处理的)
2、关于有没有用到反射,这个我就不多说了,查查MSDN就知道了。
3、“插件必须承载某种弹性很强的协议”。这句话我很认同,所以,我也说了,这只是一个非常简单的插件,因为它毕竟是可以实用的插件架构,也符合插件的原理。
唯一不爽的是,就是太简单了,当然,你可以略做修改,增加很多接口协议,做成一个像CSharpDevelop的IDE那样的插件架构。
谢谢 barton131420!
这位大哥教训的是。我们最开始也用过Type的,呵呵,但后来没用了。
1、关于程序代码规范和安全问题,我上面说过了,这只是最重要的一部分代码,并不是全部,我只是想说清楚我的意思,并不想把我们所有的代码全写出来。(我们的程序有完善的错误机制处理的)
2、关于有没有用到反射,这个我就不多说了,查查MSDN就知道了。
3、“插件必须承载某种弹性很强的协议”。这句话我很认同,所以,我也说了,这只是一个非常简单的插件,因为它毕竟是可以实用的插件架构,也符合插件的原理。
唯一不爽的是,就是太简单了,当然,你可以略做修改,增加很多接口协议,做成一个像CSharpDevelop的IDE那样的插件架构。
谢谢 barton131420!
你的這個插件也太簡陋了.
我也準備做一個插件結構的東西,部分特性描述如下:
1. There is a fold containing some plug-ins.
2. The main program which can be extended by adding some plug-ins.
3. The point of adding plug-ins is to allow a program to extend its functionality.
4. The plug-in derived from a given interface or the assembly that contains the type of the plug-in, have some pre-determined assembly level attribute.
5. The plug-ins can be a .net assembly or a unmanaged DLL library such as a C++ DLL lib.
6. Support multiple plug-ins per assembly.
7. One plug-in can have many files and depends other plug-ins.
8. The main program can load and unload these plug-in automatically in correct order.
9. The main program is responsible for loading all of the plug-ins from the file system, as well as every other detail like starting and stopping the plug-ins.
10. The main program load plug-ins in separate AppDomain, if the AppDomain that loaded the assembly is unloaded, then all of the assemblies loaded by the AppDomain will be unloaded.
11. The main program shadow copy the assemblies and can update the plug-in during runtime. During load time if an assembly fails to load we can roll back to a last known good state and continue running.
我也準備做一個插件結構的東西,部分特性描述如下:
1. There is a fold containing some plug-ins.
2. The main program which can be extended by adding some plug-ins.
3. The point of adding plug-ins is to allow a program to extend its functionality.
4. The plug-in derived from a given interface or the assembly that contains the type of the plug-in, have some pre-determined assembly level attribute.
5. The plug-ins can be a .net assembly or a unmanaged DLL library such as a C++ DLL lib.
6. Support multiple plug-ins per assembly.
7. One plug-in can have many files and depends other plug-ins.
8. The main program can load and unload these plug-in automatically in correct order.
9. The main program is responsible for loading all of the plug-ins from the file system, as well as every other detail like starting and stopping the plug-ins.
10. The main program load plug-ins in separate AppDomain, if the AppDomain that loaded the assembly is unloaded, then all of the assemblies loaded by the AppDomain will be unloaded.
11. The main program shadow copy the assemblies and can update the plug-in during runtime. During load time if an assembly fails to load we can roll back to a last known good state and continue running.
to 无常:
交互数据比较简单,因为我们是一个小型的C/S结构的系统,一般情况下,都是操作数据库,所以,大部分数据都在数据库中交互。
但一些实时数据,如:登录者信息等,我们一般是用XML配置文件来交互的。系统会实时产生一些临时XML文件,当然,这些文件的格式,我们都有专门的规定的,所以,其它子模块需要的开发人员是知道格式,可以顺利解析的。
交互数据比较简单,因为我们是一个小型的C/S结构的系统,一般情况下,都是操作数据库,所以,大部分数据都在数据库中交互。
但一些实时数据,如:登录者信息等,我们一般是用XML配置文件来交互的。系统会实时产生一些临时XML文件,当然,这些文件的格式,我们都有专门的规定的,所以,其它子模块需要的开发人员是知道格式,可以顺利解析的。
不错哦,有些创意。
关于
在我们的这个系统中,我们用XML生成了菜单/工具栏的配置文件,程序合法登录后,根据权限,先读取不同的配置文件,再动态生成菜单/工具栏(这样,每一个不同权限的人,看到的菜单/工具栏都不一样的),而在配置文件中,每一个菜单/工具栏都对应一个DLL中的FORM,所以,相应地在点击某个菜单/工具栏时,就会调用相应的FORM。
能否举个例子?
关于
在我们的这个系统中,我们用XML生成了菜单/工具栏的配置文件,程序合法登录后,根据权限,先读取不同的配置文件,再动态生成菜单/工具栏(这样,每一个不同权限的人,看到的菜单/工具栏都不一样的),而在配置文件中,每一个菜单/工具栏都对应一个DLL中的FORM,所以,相应地在点击某个菜单/工具栏时,就会调用相应的FORM。
能否举个例子?
Pretty good idea.
There is an article in MSDN, which covers most points raised by most people here:
http://msdn.microsoft.com/msdnmag/issues/03/10/Plug-Ins/default.aspx
There is an article in MSDN, which covers most points raised by most people here:
http://msdn.microsoft.com/msdnmag/issues/03/10/Plug-Ins/default.aspx
to 吴建明:
这个东东说起来是很简单,但真要举一个完整的例子,在这里还是比较麻烦的。这里,我就简单说一下步骤吧:
1、在我们的主程序所在文件夹里,有多个不同的子文件夹。每个子文件夹表示一个子系统。如:主文件夹为Main,里面有个login.exe登录程序,其中人事子系统为Main/Person文件夹,市场子系统为Main/Market文件夹。
2、在每个子文件夹里,都有一个config.xml配置文件。基本格式如下:
<MenuButtonItem>
<level>2</level>
<label>录入客户信息 </label>
<IsIcon></IsIcon>
<IsToolBar></IsToolBar>
<icon>录入客户信息.ico</icon>
<assembly>Market.Form1</assembly>
</MenuButtonItem>
其中的意思很明白,这里,我就不多解释了,你也可以根据需要自己定义。
说明一点:Market.Form1表示一个Market.dll库,里面可能有3个窗体,Form1(录入客户信息)、Form2(查询客户信息)、Form3(其它信息)等。在不同的菜单/工具栏里调用即可。
而Market.dll库的来历,我在前面也说过了,就是一个普通的exe文件在最后输出时,选择类库即可。
3、程序在登录时,连接后台的数据库,判断如果用户是使用市场系统的,则读取Main/Market文件夹中的config.xml文件。然后根据里面的配置信息,动态生成菜单/工具栏。如用户使用人事系统,则进入Main/Perosn,读取相应的config.xml文件,以此类推同了。
4、申明一下:在各子系统中的界面,都是只有菜单和工具栏的。整个界面是个MDI窗体,各子模块为子窗体。
5、OK,至此,该市场系统已经运行起来了,有了相应的菜单/工具栏。如果需要某功能,选择相应的菜单/工具栏即可。
至于在选择菜单后,弹出所需的窗体,这个在最上面已给源程序了。参考即可。
总体说来,这个架构是非常简单的,但正因为简单,也就更有操作性、适用性。
这里,不要和我讨论一些很细节的问题,如:XML的安全性,错误处理机制等,因为我相信你自己就能解决这些问题,并不需要我的答案。
如有其它问题,欢迎再讨论,谢谢各位关注。
这个东东说起来是很简单,但真要举一个完整的例子,在这里还是比较麻烦的。这里,我就简单说一下步骤吧:
1、在我们的主程序所在文件夹里,有多个不同的子文件夹。每个子文件夹表示一个子系统。如:主文件夹为Main,里面有个login.exe登录程序,其中人事子系统为Main/Person文件夹,市场子系统为Main/Market文件夹。
2、在每个子文件夹里,都有一个config.xml配置文件。基本格式如下:
<MenuButtonItem>
<level>2</level>
<label>录入客户信息 </label>
<IsIcon></IsIcon>
<IsToolBar></IsToolBar>
<icon>录入客户信息.ico</icon>
<assembly>Market.Form1</assembly>
</MenuButtonItem>
其中的意思很明白,这里,我就不多解释了,你也可以根据需要自己定义。
说明一点:Market.Form1表示一个Market.dll库,里面可能有3个窗体,Form1(录入客户信息)、Form2(查询客户信息)、Form3(其它信息)等。在不同的菜单/工具栏里调用即可。
而Market.dll库的来历,我在前面也说过了,就是一个普通的exe文件在最后输出时,选择类库即可。
3、程序在登录时,连接后台的数据库,判断如果用户是使用市场系统的,则读取Main/Market文件夹中的config.xml文件。然后根据里面的配置信息,动态生成菜单/工具栏。如用户使用人事系统,则进入Main/Perosn,读取相应的config.xml文件,以此类推同了。
4、申明一下:在各子系统中的界面,都是只有菜单和工具栏的。整个界面是个MDI窗体,各子模块为子窗体。
5、OK,至此,该市场系统已经运行起来了,有了相应的菜单/工具栏。如果需要某功能,选择相应的菜单/工具栏即可。
至于在选择菜单后,弹出所需的窗体,这个在最上面已给源程序了。参考即可。
总体说来,这个架构是非常简单的,但正因为简单,也就更有操作性、适用性。
这里,不要和我讨论一些很细节的问题,如:XML的安全性,错误处理机制等,因为我相信你自己就能解决这些问题,并不需要我的答案。
如有其它问题,欢迎再讨论,谢谢各位关注。

浙公网安备 33010602011771号