• 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模式,应该也可以进行动态加载!

    所有的选择不过是为了下一次选择做准备
  • 相关阅读:
    【前端异常】解决前端引入Bootstrap的dropdowns 菜单时报错,Uncaught TypeError: Bootstrap‘s dropdowns require Popper.js
    @MapperScan注解
    mongoRepository mongoTemplate
    Spring注解中@component
    正则表达式
    832. Flipping an Image
    Java---静态代码块、构造代码块、构造函数以及Java类初始化顺序[未完成]
    轻松学习正则表达式「转」
    771. Jewels and Stones
    java中的栈、堆、常量池和关于String类的理解
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/14671892.html
Copyright © 2020-2023  润新知