.NET简谈插件系统开发模式

今天跟大家分享一下我们在日常开发中并不常用的开发模式“插件系统模式”,什么叫插件从大一点的概念讲就是我们开发的软件是由很小的模块组成,每一块都能成功的装卸,使我们的软件成为一个有机体,在发生重大事故、改良优化等等的时候,我们不需要重新编译我们的系统就能很方便的进行升级替换进行使用;这样的开发模式就是插件系统开发模式;这个概念很大,每个人的技术水平不同使用的效果也是不同的;一个大型的系统,不能简简单单的用三层或者MVC来概括,我们要站在一个更高的角度去思考程序,每当我们开始一个新系统的架构设计时,我们不能总是保守的或是“封建”式的使用以往的思想模式,是否深思熟虑过这些开始模式在我们当前系统中是否可用或者说是否能发挥模式的最大效果,但是我们往往很多人,不太喜欢思考,俗话说得好:思考是前进的本质;尽量向“建筑的永横之道”一书中所讲的道理去迈进,这书不仅仅在建筑行业是经典,其实在各行各业道理都是想通的,我们不能停止脚步,我们要时刻保持热身状态,只有这样我们才不会在大难临头时,浑身无力或者由于肌肉长时间不运动导致反应迟钝;我写文章就喜欢乱扯,请大家不要见怪,本人属于一种很讨厌自以为是的那些家伙,

插件系统在很早之前就出现了,包括我们的VisualStudio开发工具的原型都是用的这种模式,我上面也说了每个人的技术水品不同使用的效果也是不同的,我跟大家分享一下我对插件模式的理解和在项目中的使用经验;我将插件系统大体抽象成下图结构:

1:

上图很清晰的描述了我们插件系统的整体执行步骤,我们将应用程序的整个生命周期进行抽象(抽象是我们程序员必备思想,要随时具备抽象化准备),不管什么应用程序都有前后执行顺序,我只是用Main表示一下,在开始执行前我们需要读取插件所在的路径然后进行插件对象的全路径保存,以便在后面运行时方便加载;到了Run开始真正的执行我们的应用程序了,我们需要根据插件的类型进行加载,插件必须具备类型的原则,用在什么地方的,好比汽车的零部件,我们有明确的规定,这里的实现我们可以用接口进行标识;当程序开始结束的时候,我们需要释放各个插件所占用的系统资源,为什么我们要手动释放这些资源呢,有的朋友会说不是用CLR的GC吗?GC只能释放托管资源,非托管资源它管不到,在我们进行P/Invoke平台调用时候,资源释放是比较复杂的,由于托管和非托管的内存分配是不同的,至于怎么释放非托管资源我这里穿插一下我比较喜欢的方法吧,当我们用非托管C++编写核心且高效的系统功能时,往往它占用的资源是非常可观的,所以当我们调用完毕后在第一时间释放是必须的,我们可以进行托管封装,在非托管内部我们定义释放资源的方法,然后在通过P/Invoke调用释放资源的非托管方式进行释放;由于这里牵扯到.NET互操作方面的知识了,这这篇文章中我们就涉及了;有兴趣的朋友可以关注本人的“《.NET程序员学互操作》”分类文章中进行详细的讲解;

大概的原理我就讲这么多了,我们来看代码怎么实现吧;

2:

在我的解决方案中,Plug是插件有关的对象;在PlugInterface里面是所有插件接口,在LoadInterface文件里面是所有加载过程中的插件,我们需要明确插件的类型,只有这样才能很好的控制插件;软件的生命周期随着需求不同而不同,这里是需要我们去收集和提取的,在应用程序的整个生命周期中我们都可以无缝的装卸插件,那是多么美妙的事情;

3:

这是本人在公司的一个项目中的实现代码,由于代码属于公司所有,所以不便于公开;其实代码没什么技术含量就看怎么用了;OK,差不多讲完了;
========

NET实现之(ActiveX插件开发)

决定通过简单的方式尽量让初学者少接触底层的东西包括OLE(对象连接与嵌入)、COM(组件对象模型)之类的概念,但是ActiveX插件在开发上有很高的技术要求,虽然.NET为我们封装了很好的实现途径,但是我们也总不能停留在,知自然而不知其所以然的层面上;今天这篇文章我大概构思了一下,我主要会由浅入深的去逐层的讲解,对一些概念性的东西,我会给出参考资料供大家去学习,由于它的发展历史我也就不去讲解了,主要是实现为主;如果喜欢刨根问底的朋友可以去查询相关的文章或者看一些关于OLE、COM方面的书籍,就算我们不从事相关开发,但是对于我们进行.NET互操作方面还是很有帮助的,也不至于让我们觉得.NET的局限性;本人向大家推荐一本OLE方面的好书:《OLE 2高级编程技术》
其实说起ActiveX插件大家都会想起Flash插件,对Flash插件就是一个很典型的COM组件,宿主在以OLE为容器的Web浏览器应用程序中,我们把这个OLE容器称为复合文档应用,复合文档应用程序在微软的系统到处可见如:Office就是一个很强大的复合文档应用程序,为什么要称为复合文档应用程序呢,是因为以容器为宿主的时候,可以进行任何应用集成开发,在office里面我们可以进行图表的编辑、Excel的编辑等等;在这个OLE中使用的对象严格意思上讲都是COM组件,对COM不了解的朋友可以去看一下《COM本质论》也是一本很好的书籍,其实COM就是组件对象模型,是二进制的组件,在任何平台任何语言之间都能使用;它是一套组件规范一套开发标准,大家都去遵守这个约定,才能使不同厂商生产的组件互相调用;而ActiveX插件就是一个COM组件,在早期的开发中对COM的开发是一种望而生畏的感觉,也许我们做.NET开发的程序的感觉开发组件DLL可能并没有多大难度,不需要去了解一些操作系统底层的东西比如:函数在编译过后重新编码对调用产生的影响,不同操作系统对可执行文件的内存分配回收等等问题,都需要开发组件的人了如指掌之后才能开发出出色的COM组件,然而在今天我们站在.NET这艘航母上,不需要害怕这些复杂的技术难题,微软为我们打开一扇通往平坦大道的大门,用最简单的方式去开发最复杂的东西,从某种角度讲是好事,坏事就是让我们越来越远离核心的东西; 这就要看每个人对技术的追求目标了;
由于.NET开发出来的东西都是属于托管的,所以不是正真意义上的二进制标准,这就牵扯到.NET平台调用的技术P/Invoke和互操作Interop技术,但是我们不了解也没关系,这不影响我们开发ActiveX插件,我只是给大家指一个方向;用.NET开发出来的东西要想完全替代COM组件,因为OLE容器只接受COM组件才能使用,所以我们的托管DLL文件怎样才能被OLE容器调用呢,就是通过.NET互操作COM来实现,我们开发的ActiveX插件要被浏览器这么一个大强的复合文档程序加载和使用的,所以我们得将托管的组件包装成非托管的等价的COM组件才行,.NET提供了CCW(COM可调用包装)的机制,将我们的ActiveX插件通过中间转换一下就可以使用了,理论的东西我就到此结束,我们开始动手做一个ActiveX插件吧;先看一下ActiveX插件的运行图;
1:

这是系统在运行过程中要经历的过程调用步骤,有助于我们下面的开发讲解;首先是浏览器接受到远程服务器返回的HTML文本,然后浏览器将HTML转换成DOM对象在通过GDI或者GDI+进行绘制渲染等界面呈现工作,发现在HTML中包含了对本地机器上的COM组件调用,通常也就是我们HTML中的object标签所定义的说明;在标签中会包含COM组件的唯一标识符GUID(全局唯一ID),在我们机器存在着成千上万个COM组件,只有通过GUID才能确定是哪一个组件,浏览器通过GUID到注册表中去查找这个COM组件在本机注册时的地址,得到地址后在将其COM组件加载到内容运行;这个过程被我细化了,只要能说明原理就行了;
下面我们进入到VisualStudio工具进行ActiveX插件开发,我们要新建一个项目这个项目必须得以DLL文件的形式存在,但是要是ActiveX插件必须得有界面啊;所以我们新建一个Windows窗体控件库项目;
2:

新建后会出现一个默认的用户自定义控件我们随便放一个按钮就行了,然后写点测试代码;
3:

每一个ActiveX插件都有一个入口地址,这就是COM入口地址;我们就拿我们上面的这个测试界面作为入口地址;我们切换到代码视图;
4:
 
我们在这个类的上面添加了两个特性,ComVisible是否对COM公开,这个特性的意思就是说我们的组件是否能用于COM组件的相关调用查找;如果没有这个特性我们的组件是不能用作COM调用的;GUID特性就是用来为我们的这个类型确定一个部署到客户机器的唯一标识符;这个GUID可以通过VisualStudio工具栏中的功能自动创建;我们还差一步就成功完成了这么一个庞大的工程了,我们右击项目选择属性会出来下面这个界面;
5:
 
将为COM互操作注册勾选上,作用就是为了能与COM等组件进行相互调用了,在我们程序目录中会生成一个和组件名称一样的tlb文件,必须有这个文件我们才能使用这个插件,tlb文件是一个类型库文件,作用就是将托管DLL文件的所有信息导出成COM规范的接口文件,在这个tlb文件中定义了COM的调用约定;由于COM和.NET组件无法直接交互所以只能通过tlb文件来确定我们的托管的DLL文件中定义哪些东西;我们编译一下;我们还差一个HTML文件,这个文件随便是来自哪里都可以,这个HTML文件里面要定义调用我们这个ActiveX插件才行;
6:

我红色箭头标记的地方就是Object标记所定义的调用代码,ID就是为调用的ActiveX插件起的一个名字便于在页面中通过JS进行互操作;CLASSID是要调用的ActiveX插件在用户机器上的GUID唯一标识符;CODEBASE如果用户机器上没有安装这个插件则让浏览器帮忙下载的URL地址;其他的标记就是和普通的HTML一样的了;在此我穿插一下,所有的HTML标记最终都会被转换成实实在在的对象,而不是我们所谓的HTML纯文本;
我们保存HTML文件,并且打开这个文件就能看见ActiveX插件被加载运行了;
7:

我这里之间简单的讲解了一下ActiveX插件的基本创建过程,其实ActiveX插件的功能很强大,对B/S一些的功能只能通过插件来解决如:大文件上传、视频采集、照片动态采集等等;插件在部署的时候一定要注意,部署到用户机器上的时候,在属性栏中有一个文件注册方式,一定要选择为COM注册这样才能在注册表中添加相应的节点;ActiveX插件开发大概就讲完了;希望讲的还算清晰明了;

========

.net插件式架构系统

插件式(AddIn)架构,不是一个新名词,应用程序采用插件式拼合,可以更好的支持扩展。很多著名的软件都采用了插件式的架构,如常见的IDE:Eclipse,Visual Studio,SharpDevelop等等。
  这些插件式架构在实现上各有特色,但是基本原理大致相同:
定义插件框架,用来下载,创建,销毁插件,并管理插件间的通信等等。
定义插件契约,定义统一的接口规范。
实现插件组件,组件实现插件契约,在运行时可以被插件框架所发现并集成。
常用的插件在界面打开时即加载,而不常用的在用户点击时才首次加载进来。
  关于插件式架构的实现技术,有很多选择,使用动态链接库的导出函数,使用COM技术或者.net的MEF等等。MEF(Managed Extensibility Framework)是微软集成在.net framework中的扩展性管理框架,Visual Studio2010就是使用了MEF来管理插件,关于MEF的具体使用,请参见MSDN。
插件式架构应用程序不是什么新奇的东西,把UI布局和UI表现分离,并支持插件式架构是设计时的一些想法,具体到项目应用还需要一定的完善。
引文出处:http://www.cnblogs.com/Zhouyongh/archive/2012/02/16/2353498.html
 
MEF 打造的插件系统 
以实例说话,一起体验MEF带来的可扩展性吧
========

SharpDevelop插件架构初探

SharpDevelop AddIn
AddIn Manager Usage
Building Applications with the SharpDevelop Core
1.ICSharpDevelop.Application.addin
    Remove Pads
2.StartPage.addin
    Make clear Complie Output Path

An addin needs three files:
*.addin
*.dll
*.pdb
It has three types of addin:
1) Pre-Installed
2) AddIn Manager add
3) User Defined:
.addin,.dll,.pdb
compress->.zip
change name->.sdaddin
========

c#插件式开发

http://blog.csdn.net/joyhen/article/details/22905481
MEF:http://www.cnblogs.com/tcjiaan/tag/MEF/
http://blog.csdn.net/jam12315/archive/2008/08/18/2791534.aspx
首先,新建一个类库,里面定义接口,这里定义两个方法,一个有返回值的,一个无返回值的。

using System;
using System.Collections.Generic;
using System.Text;  namespace IMsg {  ///<summary>  /// 这是插件必须实现的接口,也是主程序与插件通信的唯一接口  /// 换句话说,主程序只认识插件里的这些方法  ///</summary>  publicinterface IMsgPlug {  void OnShowDlg();  string OnShowInfo();  }
} 

将上面的类库生成IMsg.dll, 新建一个类库MYPlugin1,添加刚出的引用,分别新建两个类来实现IMsg中定义的接口。

using System;
using System.Collections.Generic;
using System.Text;
using IMsg;
namespace MYPlugin1 {  publicclass myConsole: IMsgPlug {#region IMsgPlug成员publicvoid OnShowDlg() {  Console.WriteLine("控制台调用插件的OnShowDlg方法");  }  publicstring OnShowInfo() {  return "myConsole";  }#endregion  }
} 
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using IMsg;
namespace MYPlugin1 {  publicclass MYDlg: Form,  IMsgPlug {#region IMsgPlug成员  publicvoid OnShowDlg() {  this.Text = "插件子窗体";  this.ShowDialog(); //调用Form的ShowDialog,显示窗体  }  publicstring OnShowInfo() {  return "MyDlg";  }#endregion  }
} 

将上面的都生成dll, 生成目录可以设置为新建exe工程的bin目录plugins文件夹下。Plugins文件夹是新建的,专门存放插件的。 新建一个 WinForm项目来使用刚才的插件.

using System;
using System.IO;
using System.Linq;
using System.Collections;
using System.Windows.Forms;
using System.Reflection;  namespace myConsole
{  public partial class Form1 : Form  {  /// <summary>  /// 应用程序的主入口点。  /// </summary>  [STAThread]  static void Main()  {  Application.EnableVisualStyles();  Application.SetCompatibleTextRenderingDefault(false);  Application.Run(new Form1());  }  public Form1()  {  InitializeComponent();  }  /// <summary>  /// 存放插件的集合  /// </summary>  private ArrayList plugins = new ArrayList();  //载入所有插件  private void btnLoadPlug_Click(object sender, EventArgs e)  {  string[] files = Directory.GetFiles(Application.StartupPath + "\\plugins");  if (files != null)  this.listBox1.Items.Clear();  foreach (var f in files)  {  if (!f.ToUpper().EndsWith(".DLL"))  continue;  try  {  Assembly ab = Assembly.LoadFile(f);  Type[] t = ab.GetTypes();  foreach (var x in t)  {  if (x.GetInterface("IMsgPlug") != null)  {  plugins.Add(ab.CreateInstance(x.FullName));  this.listBox1.Items.Add(x.FullName);  }  }  }  catch (Exception ex)  {  MessageBox.Show(ex.Message);  }  }  }  //调用插件的方法  private void btnExecute_Click(object sender, EventArgs e)  {  if (this.listBox1.SelectedIndex == -1)  return;  object selObj = this.plugins[this.listBox1.SelectedIndex];  Type t = selObj.GetType();  MethodInfo OnShowDlg = t.GetMethod("OnShowDlg");  MethodInfo OnShowInfo = t.GetMethod("OnShowInfo");  OnShowDlg.Invoke(selObj, null);  object returnValue = OnShowInfo.Invoke(selObj, null);  this.lblMsg.Text = returnValue.ToString();  }  }
} 

运行结果:

此方法用了反射,个人感觉效果不是很好。另外,注意dll对象的依赖,这种问题我在手写上面demo的时候发现了,首先要保证依赖文件的存在,再一个依赖文件的路径需要正确。另一点要说下,对反射后的验证可以做一些加强处理,listbox对象显示的名字可以通过给dll中对象添加特性标记,然后获取显示出来。下一节我们看看MEF的小例子。
最后想说一下,这不是正在意义上的插件式开发,请参阅微软的MEF和MAF的设计
========

C#实现插件式架构的方法

插件式架构,一种全新的、开放性的、高扩展性的架构体系.插件式架构设计近年来非常流行,基于插件的设计好处很多,把扩展功能从框架中剥离出来,降低了框架的复杂度,让框架更容易实现。扩展功能与框架以一种很松的方式耦合,两者在保持接口不变的情况下,可以独立变化和发布。基于插件设计并不神秘,相反它比起一团泥的设计更简单,更容易理解。下面已C# .Net简要介绍一下插件式架构的方法.

定义插件接口,将其编译成dll

namespace PluginInterface
{public interface IPlugin{string Show();}
}

编写插件,引用上面的DLL,实现上面定义的接口,也编译为DLL

//插件A
namespace PluginA
{public class PluginA:IPlugin{public string Show(){return "插件A";}}
}
//插件B
namespace PluginB
{public class PluginB : IPlugin{public string Show(){return "插件B";}}
}

新建一个控制台程序,需要引用定义插件接口的dll,生成之后,需要在exe所在的目录里建一个Plugins子文件夹,将上面生成的PluginA.dll,和PluginB.dll拷贝进去。

namespace ConsolePluginTest
{class Program{static void Main(string[] args){Program p = new Program();List<string> pluginpath =  p.FindPlugin();pluginpath = p.DeleteInvalidPlungin(pluginpath);foreach (string filename in pluginpath){try{//获取文件名string asmfile = filename;string asmname = Path.GetFileNameWithoutExtension(asmfile);if (asmname != string.Empty){// 利用反射,构造DLL文件的实例Assembly asm = Assembly.LoadFile(asmfile);//利用反射,从程序集(DLL)中,提取类,并把此类实例化Type[] t = asm.GetExportedTypes();foreach (Type type in t){if (type.GetInterface("IPlugin") != null){IPlugin show = (IPlugin)Activator.CreateInstance(type);Console.Write(show.Show());}}}}catch(Exception ex){Console.Write(ex.Message);}}}//查找所有插件的路径private List<string> FindPlugin(){List<string> pluginpath = new List<string>();try{//获取程序的基目录string path = AppDomain.CurrentDomain.BaseDirectory;//合并路径,指向插件所在目录。path = Path.Combine(path,"Plugins");foreach (string filename in Directory.GetFiles(path, "*.dll")){pluginpath.Add(filename);}}catch(Exception ex){Console.Write(ex.Message);}return pluginpath;}//载入插件,在Assembly中查找类型private object LoadObject(Assembly asm, string className, string interfacename, object[] param){try{//取得className的类型Type t =asm.GetType(className);if (t == null|| !t.IsClass|| !t.IsPublic|| t.IsAbstract|| t.GetInterface(interfacename) == null){return null;}//创建对象Object o = Activator.CreateInstance(t,param);if (o == null){//创建失败,返回nullreturn null;}return o;}catch{return null;}}//移除无效的的插件,返回正确的插件路径列表,Invalid:无效的private List<string> DeleteInvalidPlungin(List<string> PlunginPath){string interfacename = typeof(IPlugin).FullName;List<string> rightPluginPath = new List<string>();//遍历所有插件。foreach (string filename in PlunginPath){try{Assembly asm = Assembly.LoadFile(filename);//遍历导出插件的类。foreach (Type t in asm.GetExportedTypes()){//查找指定接口Object plugin = LoadObject(asm,t.FullName,interfacename,null);//如果找到,将插件路径添加到rightPluginPath列表里,并结束循环。if (plugin != null){rightPluginPath.Add(filename);break;}}}catch{throw new Exception(filename+"不是有效插件");}}return rightPluginPath;}}
}

========

MEF

实战MEF(5):导出元数据
实战MEF(4):搜索范围
实战MEF(3):只导出类的成员
实战MEF(2):导出&导入
实战MEF(1):一种不错的扩展方式
========

开放工厂

开放工厂是一个敏捷、开放、共享、协作、社会化的软件生产线平台。它向开发者提供了规范化的OSGi.NET插件框架和可复用的插件仓库,致力于解决应用系统的模块化敏捷开发、团队无缝协作和自动化部署问题。开放工厂向软件开发商提供了共享的插件仓库。目前共享插件仓库拥有超过100个的可直接使用插件,涵盖了桌面界面框架、Web界面框架、数据库访问、数据挖掘、数据集成、大数据支撑、消息队列、数据加密、文件访问等各类插件。 软件生产线 开放工厂向软件开发商构建了一条自动化的敏捷软件生产线平台。这个生产线平台能够极大提高软件开发效率、团队协作效率,使软件的生产变得和生产线组装一样。 在这里,开发团队从插件仓库获取基础插件并进行组装,在此基础上根据实际需求开发业务逻辑插件,并将业务插件通过自动化部署工具发布/更新到插件仓库。 测试团队则从插件仓库获取需要测试的插件及其更新包组装成软件进行测试,一旦发现问题则反馈给开发团队。 部署团队从插件仓库获取需要部署的软件系统的插件进行安装,并利用开放工厂自动升级功能实现自动化的部署。 软件生产线 开放工厂为软件开发商提供了自动化部署机制,为软件提供商提供了持续部署和持续交付能力,实现了最高级别的“DevOps”。 在这里,当插件变更需要发布时,开发团队通过鼠标右键即可将插件及其后续升级包持续的发布到插件仓库;管理人员则可以对插件仓库进行管理。 一旦插件仓库产生变更后,实际部署环境则通过自动升级/降级机制保持与插件仓库版本一致,实现应用系统的自动化持续部署。 自动化部署 开放工厂所有插件基于OSGi.NET面向服务插件框架构建。该框架是国际上第一个完整迁移了OSGi R4规范的OSGi.NET框架,提供了动态模块化、面向服务和模块扩展三大功能,支持WinForm桌面应用、WPF桌面应用、ASP.NET Web应用、ASP.NET MVC应用、Silverlight RIA应用、手机应用等任意.NET应用环境。 自动化部署 开放工厂架构
开放工厂架构如下所示。开放工厂为用户提供了公共/私有插件仓库和插件SDK,允许用户使用“搭积木”的方式创建 模块化应用程序。开放工厂为用户提供的私有插件仓库允许用户以项目的形式来管理自己的应用系统的所有插件及其升级包。 此外,它还暴露了开放接口,允许用户通过开放接口来检测插件框架等内核文件的变更情况及相应的文件,允许用户访问插件仓库、获取 插件仓库插件的变更情况、获取某个插件的文件。基于开放工厂的应用系统使用插件SDK来开发应用程序。利用SDK创建的程序可以通过开放接口来获取插件框架 等内核文件的最新版本并实现自动更新,此外,自带的系统插件——自动升级与降级插件、插件仓库访问与插件管理服务 利用开放接口使应用系统自动与插件仓库保持同步,并允许我们在任何时候通过插件中心和插件管理界面来安装、启动、 停止、卸载插件。开放·工厂结构图 开放工厂由OSGi.NET插件框架和插件仓库组成
(1)OSGi.NET插件框架:它是OSGi R4规范移植到.NET平台的实现,提供了动态模块化、面向服务架构和模块扩展三大功能。在这里,模块具备物理隔离性、热插拔、动态性和物理重用性。 (2)插件仓库:它是一个在线的插件仓库,所有插件均符合OSGi.NET规范,可运行在OSGi.NET插件框架之上。您可以从插件仓库中任意挑选插件进行组装,也可以开发新的插件进行功能扩展。 开放·工厂结构图 开放工厂具有六方面的价值
开放工厂价值 规范的模块化框架和“积木式”软件生产线
通过iOpenWorks,软件开发工程师可以更加关注与业务结合,而无需考虑底层逻辑与实现,彻底解放了系统设计师,方便灵活的开发多领域定制化产品,开发软件产品(项目)就像“搭积木”,而且由于OSGi采用微内核机制,又可保证系统的稳定高效。 显著提升系统的设计能力
iOpenWorks能够更加规范的让设计师去完成系统设计工作,更加容易先分模块设计业务架构,后进行模块详细设计。所有的设计师都基于一种设计规范,避免百花齐放,每个设计师都有自已一套设计方式,设计上无法通用的弊端。 实现了动态可管理的系统(即插即用)
基于iOpenWorks开发的系统功能模块(Plugin)可以动态加载或者卸载,而系统本身无需停止,还可以通过远程管理工具进行功能模块的远程安装、启动、停止和卸载模块,或者订阅模块仓库中模块变更并同步。 建立了规范的团队并行开发方式、培训体系
基于iOpenWorks,软件开发企业所有的项目都可以按照统一的基础架构、开发方式以及部署方式来完成,很容易形成团队的并行开发模式,自然而然的很容易形成在开发知识方面的培训体系。 系统维护成本大大降低
由于基于iOpenWorks开发的系统模块具有严格意义上的模块化,相对于传统开发方式,系统模块的组织、复用和扩展变得容易,也简化了模块的测试。当一个系统功能需要修改或者发现Bug时,开发人员只需要修改或者Debug他自己的功能模块,模块之间没有耦合,互不影响,这可以大大降低系统的维护成本。开发新功能也是一个“独立”的模块(Plugin),部署只需拷贝到指定目录,系统即可拥有新功能。 建立企业级可复用软件资产(构件)库变得容易
由于所有的系统设计、模块开发都遵循统一的技术规范,企业所开发项目的所有模块都可作为高可复用的软件资产积累下来,企业可建立企业级可复用软件资产库,做新项目的时候只需要下载相应的功能模块进行集成就能实现功能。
========

.net 插件式开发学习总结相关推荐

  1. C\S结构的插件式开发思想以及向B\S结构的架构延伸(二)

    忙碌的好多天过去了,自从上一贴发出后,不少同仁都对插件式开发提出了自己的看法以及分享一些插件式开发比较典型的例子,感谢李锋.肖明等 同仁的分享,也感谢所有顶贴的同志,今天来分享一下插件式开发著名的产品 ...

  2. 基于AppDomain的插件式开发

    很多时候,我们都想使用(开发)USB式(热插拔)的应用,例如,开发一个WinForm应用,并且这个WinForm应用能允许开发人员定制扩展插件,又例如,我们可能维护着一个WinService管理系统, ...

  3. 从零开始实现ASP.NET Core MVC的插件式开发(五) - 插件的删除和升级

    标题:从零开始实现ASP.NET Core MVC的插件式开发(五) - 使用AssemblyLoadContext实现插件的升级和删除 作者:Lamond Lu 地址:https://www.cnb ...

  4. 从零开始实现ASP.NET Core MVC的插件式开发(四) - 插件安装

    标题:从零开始实现ASP.NET Core MVC的插件式开发(四) - 插件安装 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11343141. ...

  5. 从零开始实现 ASP.NET Core MVC 的插件式开发(九) - 如何启用预编译视图

    标题:从零开始实现 ASP.NET Core MVC 的插件式开发(九) - 升级.NET 5及启用预编译视图 作者:Lamond Lu 地址:https://www.cnblogs.com/lwql ...

  6. 从零开始实现 ASP.NET Core MVC 的插件式开发(七) - 问题汇总及部分问题解决方案...

    标题:从零开始实现 ASP.NET Core MVC 的插件式开发(七) - 问题汇总及部分问题解决方案 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/ ...

  7. 从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用

    标题:从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用. 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/1171 ...

  8. 从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件

    标题:从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/112 ...

  9. 从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板

    标题:从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11155 ...

最新文章

  1. java devexpress_Coolite与DevExpress比较
  2. 独家 | 用于数据清理的顶级R包(附资源)
  3. 雷观(十五):提高生产力和程序员价值的2种方法
  4. 如何判断UIWebView是否loading完全
  5. 【原创】QT简单计算器
  6. linux防火墙开启某端口命令行,linux上防火墙 开启某个端口
  7. go语言defer的作用
  8. HDOJ 1713 相遇周期 (最大公约数与最小公倍数)
  9. 恒生电子offer含金量_收获8个Offer,来给大家分享备战和面试经验
  10. Hadoop四大组件介绍
  11. 人工神经网络 图像分割 matlab,MATLAB图像分割毕业设计开题报告
  12. 基于动力学前馈加反馈线性化的机器人动力学控制实现
  13. 测试了一周总结的模拟器玩刺激战场延迟卡顿完美解决办法
  14. MatConvNet的excise 3改为网络配置文件形式
  15. Brainfuck语言入门
  16. 云原生|kubernetes|minikube的部署安装完全手册(修订版)
  17. 计算机不显示磁盘阵列,识别不到硬盘?bios中怎么改raid为ahci硬盘模式。
  18. LiteFlowNet3:解决对应歧义以获得更准确的光流估计
  19. Vue2.x + element ui 导入导出excel
  20. java 项目启动初始化_Spring项目启动时执行初始化方法

热门文章

  1. is NULL , is NOT NULL 有时索引失效 || in 走索引, not in 索引失效 ||单列索引和复合索引 || 查看索引使用情况
  2. jQuery 基础选择器/层级选择器/隐式迭代
  3. SpringMVC入门案例
  4. 类型转换与采样 || SMOTE算法
  5. jquery的checked以及disabled
  6. Python 使用CORS跨域资源共享解决flask服务器跨域问题、浏览器同源策略
  7. 【upc5020】 Knight
  8. K-Means算法理论及Python实现
  9. %r或者{!r}在python中的意思
  10. 第二十一讲 卷积公式