OrchardCore 如何动态加载模块?
前言
今天,我们再次讨论下OrchardCore,通过初期调研,我们项目采用OrchardCore底层设施支持模块化,同时根据业务场景,额外还需支持二次开发,于是有了本文,若有不同解决方案,欢迎留言探讨
若对OrchardCore有所了解的童鞋应该知道,OrchardCore本身定位于CMS系统,同时整个架构并非前后分离,采用MVC模式开发,基于此,由于内置需要预编译视图以及考虑其他等等原因,不支持动态加载模块,本文给出我所想到的动态加载模块方案
OrchardCore基本使用示例
OrchardCore采用包管理各个模块,所以有自建NuGet,我们提前配置好OrchardCore程序包源

项目采用前后分离,所以我们创建WebAPi应用程序,为支持模块化开发,如上图下载模块开发应用程序包,紧着在Startup文件中,添加OrchardCore服务以及使用其中间件,如下图

至此一个基本的模块化项目就创建完毕,接下来我们创建模块,官方提供模块包模板引擎

通过对应命令将模板引擎下载至本地
dotnet new -i OrchardCore.ProjectTemplates::1.0.0-rc2-16113 --nuget-source https://nuget.cloudsmith.io/orchardcore/preview/v3/index.json

然后我们在项目解决方案下,继续通过CLI将下载至本地模板引擎来创建模块项目,并引入到项目解决方案中
dotnet new ocmodulecms -n Test

由于我们用不到视图,所以将视图文件夹以及对应默认安装包删除,只需保留模块包【OrchardCore.Module.Targets】就好,同时也一并将项目文件中支持MVC配置给删除,否则会生成视图程序集,猜测应该会引起模块加载依赖需额外加载视图dll

我们将模块默认创建控制器修改为访问接口形式,方便接下来测试验证

那么接下来我们应该如何将开发好的模块进行加载呢?
OrchardCore动态加载模块(前后分离)
了解OrchardCore基本原理的我们应该知道,默认情况下,主项目添加模块引用时,会通过MSBuild在对应模块程序集中,添加模块标识,如下:

如上图所示,一个是模块标识,一个是对应文件路径标识,当启动主项目时,会找到对应程序集模块标识,并注册服务以及其他操作,如此看来,我们只需深入了解源码中是否存在存储对应模块信息的接口呢?
查看底层模块设施源码,得知对外暴露其接口即IModuleNamesProvider,我们将生成模块dll放在主项目程序启动modules目录下,接下来我们实现该接口,如下:
public class DynamicModuleNamesProvider : IModuleNamesProvider
{
private readonly List<string> _moduleNames = new List<string>(); public DynamicModuleNamesProvider()
{
var baseDirectory = AppContext.BaseDirectory; var location = Path.Combine(baseDirectory, "modules"); if (!Directory.Exists(location))
{
return;
} foreach (var file in Directory.EnumerateFiles(location))
{
var assemblyPath = Path.Combine(location, file); var assembly = Assembly.LoadFrom(assemblyPath); _moduleNames.AddRange(assembly.GetCustomAttributes<ModuleMarkerAttribute>().Select(m => m.Name));
}
} public IEnumerable<string> GetModuleNames()
{
return _moduleNames;
}
}
将其以单例形式注入,如下
services.AddSingleton<IModuleNamesProvider, DynamicModuleNamesProvider>();
我们启动主项目验证确认,模块已然进行加载,如下:

但是访问控制器接口却显示404

并未继续深入查看源码,至少可知,通过动态加载内置仅仅只注册了相关服务,猜测是和移除对应视图包有关导致并未激活控制器、视图等等
OrchardCore动态加载模块激活控制器
由于控制器、视图、TagHelper等等相关FeatureProvider并未激活,所以我们借助AssemblyPart来实现,将其作为应用程序的一部分,通过扫描模块,将对应控制器等激活,如下:
var builders = services.AddControllers(); builders.ConfigureApplicationPartManager(apm =>
{
var baseDirectory = AppContext.BaseDirectory; var location = Path.Combine(baseDirectory, "modules"); if (!Directory.Exists(location))
{
return;
} foreach (var file in Directory.EnumerateFiles(location))
{
var assemblyPath = Path.Combine(location, file); var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); var assemblyPart = new AssemblyPart(assembly); apm.ApplicationParts.Add(assemblyPart);
}
});

总结
虽然官方并未提供动态加载模块示例,但我们依然可以借用其对外暴露接口来实现,理论上若是采用MVC模式,应该也可以进行动态加载!
OrchardCore 如何动态加载模块?的更多相关文章
- stevedore动态加载模块
stevedore动态加载模块,stevedore使用setuptools的entry points来定义并加载插件.entry point引用的是定义在模块中的对象,比如类.函数.实例等,只要在im ...
- Python_getattr+__import__ 实现动态加载模块、类对象或函数
__import__() 语法 __import__(name[, globals[, locals[, fromlist[, level]]]]) 参数 name -- 字符串,模块的导入路径 说明 ...
- AngularJs 动态加载模块和依赖
最近项目比较忙额,白天要上班,晚上回来还需要做Angular知识点的ppt给同事,毕竟年底要辞职了,项目的后续开发还是需要有人接手的,所以就占用了晚上学习的时间.本来一直不打算写这些第三方插件的学习笔 ...
- [driver]linux内核动态加载模块
问题: 1. 把编译好的模块放到板子/lib/modules对应文件夹下,并且执行了depmod -a, 比如pl2303.ko, 那么下一次插入pl2303的串口线,是否可以识别,也就是自动加载pl ...
- angular-ui-router动态加载模块
1.定义index.html主页,对于通用的js就不用require依赖加载了,其中main.js作为主模块,用require添加系统路由模块. <!DOCTYPE html> <h ...
- nginx平滑升级、在线添加模块(tengine 动态加载模块)
http://www.orzace.com/how-to-upgrade-nginx/ 下面是nginx替换成tengine再加上lua 模块,(tengine-2.0.1版本暂时无法动态加载lua模 ...
- Apache动态加载模块
添加步骤:如要额外安装cgi,先找到mod_cgi.c及mod_cgid.c.一般在apache安装包目录下,如 ./httpd-2.2.25/modules/generators .#编译安装 cg ...
- ubuntu动态加载模块简单模板
1:简单代码 #include<linux/init.h> #include<linux/module.h> MODULE_LICENSE("GPL"); ...
- 配置动态加载模块和js分模块打包,生产环境和开发环境公共常量配置
1. 话不多少 先上代码: route.js // 引用模板 分模块编译 const main = r => require.ensure([], () => r(require('. ...
随机推荐
- ES进行date_histogram时间聚合,聚合结果时间不正确问题
在做项目中,有一个需求是统计本周内每天的漏洞数量,我选用的是ES中的date_histogram函数来进行聚合统计: 但是出现了一个问题,聚合出来的结果和想要统计的结果时间不一致,如下图所示 时间区间 ...
- Ubuntu 下安装Anaconda + 显卡驱动 + CUDA + CUDNN + 离线安装环境
写来给自己备忘,并不是什么教程- .- 下载安装包 Anaconda:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 显卡驱动:https ...
- JS遍历对象的属性和值
对于需要动态获取对象的某些属性和对应的值的时候,就需要遍历对象的属性和值. const user = { name: '张三', age: 20, addr: '湖北武汉', sex: '男' } / ...
- Vue中Jsx的使用
什么是JSX? JSX就是Javascript和XML结合的一种格式.React发明了JSX,利用HTML语法来创建虚拟DOM.当遇到<,JSX就当HTML解析,遇到{就当JavaScript解 ...
- EFCodeFirst 数据库连接
EFCodeFirst 数据库连接 EFCodeFirst 数据库连接 1.NuGet安装实体命令 PM> Install-Package EntityFramework 2.数据库连接字符串 ...
- 学习java之基础语法(一)
学习java之基础语法(一) 基本语法 编写 Java 程序时,应注意以下几点: 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的. 类名:对于所有的类来说 ...
- if...else和switch...case
一.位运算 class Demo01 { public static void main(String[] args) { int a = 5; int b = 3; /* 0000 0101 |00 ...
- POJ-1511(Dijkstra+优先队列优化+向前星)
Invitation Cards POJ-1511 从这道题我还是发现了很多的问题,首先就是快速输入输出,这里的ios::---这一行必须先放在main函数第一行,也就是输入最开始的前面,否则系统疯狂 ...
- Javascript学习,DOM对象,方法的使用
JavaScript: ECMAScript: BOM: DOM: 事件 DOM的简单学习 功能:控制html文档内容 代码:获取页面标签(元素)对象和Element document.getElem ...
- shell脚本,mysql数据库的备份,并压缩
db_user="root"db_passwd="xxx"db_host="localhost" databases="xxx-p ...