NopCommerce MVC 插件机制分析

基本原理

插件话的应用程序一般都是先定义插件接口,然后把插件编译的dll放到固定的目录中,应用程序主程序通过加载那些实现了插件接口的dll来实现插件的使用。NopCommerce 也是这样,但作为 MVC Web 应用程序会有一些不一样,首先是不同信任级别(Full Trust,Medium Trust)的时候加载dll的策略会有不一样,另外就是怎样显示插件中的 View 的问题。

 

放插件的文件夹

NopCommerce 的插件放在网站主目录的 Plugins 目录下,Plugins 下面有很多文件夹,一个插件类库就是一个文件夹。在插件类库中修改编译输出的地址为网站主目录的 Plugins 文件夹,这样插件生成的 dll 就能自动在目标文件夹下面。如下图:

另一个文件夹是 ShadowCopy 文件夹,就在 Plugins/bin 文件夹下。关于为什么要用 ShadowCopy ,住这篇文章中有所叙述,NopCommerce 就是参考它的实现。里面也详细叙述了信任级别的问题。

 

网站启动

我们先来看 PluginManager.Initialize 方法。通过在 PluginManager 类上定义如下属性保证 PluginManager 的 Initialize 方法在网站开始的时候运行,早于Application_Start 运行。

[assembly: PreApplicationStartMethod(typeof(PluginManager), "Initialize")]

在 Application_Start 之前运行初始化代码主要是为了让网站应用程序可以引用到加载的 dll。

 

核心类

IPluginFinder.cs接口:获取插件的信息接口,在 ioc 里的 Nop.Web.Framework.DependencyRegistrar 注册此接口。系统启动的时候会加载到内存里。

IPlugin.cs:插件的操作接口,主要有设置插件的属性信息,安装插件接口,卸载插件接口。

BasePlugins.cs 实现 IPlugin.cs 的方法。

PluginDescriptor.cs  插件的实体类,包含了插件的版本、描述,类型,文件名称,作者,等等一系列状态。

PluginFileParser.cs 包含对插件的实体操作方法,主要是写入插件的描述信息。

PluginFinder.cs 加载所有的插件,并获取它们的信息.

PluginManager.cs 插件管理的主类,看里面的注释,它的插件机制应该是参考的 Umbraco 这个 cms 的。

 

加载插件

首先加载插件的描述。每一个插件都必须定义一个插件描述文件,用文本文件 Description.txt 来定义,名字也是约定的,不能是其他名字。在插件的类库中添加Description.txt,对插件进行描述。Description 中的文本字段的格式是固定的,PluginManager 会把 Description.txt 文件转化成 PluginDescriptor 类,然后存储在内存中。Description 中的 DisplayOrder 字段表示了这个插件的顺序,以便在界面上显示。获取所有的插件描述文件后,它就会去 InstalledPlugins.txt 里面看,在InstalledPlugins.txt 里面有的就是已经安装的查件,没有的话就是没有安装的。PluginDescriptor.Installed 属性描述了这个信息。

把需要加载的dll复制到 Plugins/bin 文件夹下,当然都是要继承自 IPlugIn 接口的,通过 Assembly.Load 在家这个 dll,再用BuildManager.AddReferencedAssembly 把这个 dll 加载到网站这个应用程序中。要注意的是 BuildManager.AddReferencedAssembly 必须早网站程序的Application_PreStartInit 过程中加入,也就是在 Application_Start 前。这是要引用的插件都被放在了 Plugins/bin 下,并被 CLR 引用了。被引用的插件的 Assembly 引用将被保存在 PluginManager 的静态列表中 ReferencedPlugins。PluginManager 的 Initialize 方法到此结束。

 

在界面上显示插件

举例来说,我们在定义插件的时候会定义一种类型的插件,比如送货方式。那么我们在定义插件的时候会继承2个接口,一个是 IPlugin 接口,一个是IShippingMethod 接口。在需要显示送货方式插件的时候通过 PluginFinder.GetPlugins<T>().ToList() 方法去获取。PluginFinder 会去上一步的PluginManager.ReferencedPlugins 列表里面去寻找。返回的是 IShippingMethod 的实例。NopCommerce 有个txt文件:InstalledPlugs.txt。只有在这个里面的插件最终会加载到界面上去。可以通过 NopCommerce 的查件管理页面把最终需要作用于网站的插件加入到这个文件中。

 

配置插件

NopCommerce 的 admin 网站可以对插件进行配置。如下图:可以配置 Display Order 和 IsActive 等。主要的逻辑是更新该插件的 Description.txt 文件和内存中的IPlugin.Descriptor 里面的属性。

 

定义插件中的 Controller,Action 和 View

稍微复杂的插件基本都包含自己要处理的界面和逻辑。所以在插件的类库中可以定义插件的界面 View 和相关 Controller 和 Action。在建立 Controller 和 View 的时候,不一定要按照规范的 Controllers 文件夹和 Views 文件夹来定义,可以定义自己的风格。

在 Action 中返回 View 的时候,要输入 View 的名称,这个名称要包含名称空间,例如:

return View("Nop.Plugin.Payments.CashOnDelivery.Views.PaymentCashOnDelivery.Configure", model);

因为,在编译过后的插件 dll 中,作为嵌入资源的 View 会被编译成名叫 Nop.Plugin.Payments.CashOnDelivery.Views.PaymentCashOnDelivery.Configure.cshtml的资源文件。

NopCommerce 通过一些插件的 Configure 界面,把一些插件的配置信息保存到数据库中。然后在前台页面显示的时候再从数据库获取。

 

读取嵌入的资源 View

插件作为一个类库被加载到应用程序域中。而在定义插件的 View 的时候,需要把 cshtml 文件的属性修改成 Embedded Resource。它是作为嵌入式资源放到AppDomain 中去的。我们可以通过 VirtualPathProvider,使 Web 应用程序可以从虚拟文件系统中检索资源,您可以在这篇文章中找到相关知识,NopCommerce的实现和这篇文章是一样的。在 NopCommerce 中的 Nop.Web.Framework 类库中有个 EmbeddedViews 文件夹,里面包含了如果处理嵌入的 View 的一些类。最后需要在 Global.asax 进行注册。NopCommerce 的代码如下:

1 //register virtual path provider for embedded views
2 var embeddedViewResolver = EngineContext.Current.Resolve<IEmbeddedViewResolver>();
3 var embeddedProvider = new EmbeddedViewVirtualPathProvider(embeddedViewResolver.GetEmbeddedViews());
4 HostingEnvironment.RegisterVirtualPathProvider(embeddedProvider);

 

编写 NopCommerce 插件

可以参考官方文档。其中有一条建议非常好,就是 Copy 原来的插件,在上面修改。

 

把 NopCommerce 相关插件的部分代码摘取了出来,可以从这里下载。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。