public void EnableModule(string moduleName)
        {
            if (!PluginsLoadContexts.Any(moduleName))
            {
                var context = new CollectibleAssemblyLoadContext();

                var filePath            = $"{AppDomain.CurrentDomain.BaseDirectory}Modules\\{moduleName}\\{moduleName}.dll";
                var referenceFolderPath = $"{AppDomain.CurrentDomain.BaseDirectory}Modules\\{moduleName}";
                using (var fs = new FileStream(filePath, FileMode.Open))
                {
                    var assembly = context.LoadFromStream(fs);
                    _referenceLoader.LoadStreamsIntoContext(context, referenceFolderPath, assembly);

                    var controllerAssemblyPart = new MyAssemblyPart(assembly);
                    AdditionalReferencePathHolder.AdditionalReferencePaths.Add(filePath);

                    _partManager.ApplicationParts.Add(controllerAssemblyPart);
                    PluginsLoadContexts.AddPluginContext(moduleName, context);
                }
            }
            else
            {
                var context = PluginsLoadContexts.GetContext(moduleName);
                var controllerAssemblyPart = new MyAssemblyPart(context.Assemblies.First());
                _partManager.ApplicationParts.Add(controllerAssemblyPart);
            }

            ResetControllActions();
        }
Beispiel #2
0
        public static void MystiqueSetup(this IServiceCollection services, IConfiguration configuration)
        {
            services.AddSingleton <IMvcModuleSetup, MvcModuleSetup>();
            services.AddScoped <IPluginManager, PluginManager>();
            services.AddSingleton <IActionDescriptorChangeProvider>(ActionDescriptorChangeProvider.Instance);
            services.AddSingleton <IReferenceContainer, DefaultReferenceContainer>();
            services.AddSingleton <IReferenceLoader, DefaultReferenceLoader>();
            services.AddSingleton(ActionDescriptorChangeProvider.Instance);

            var mvcBuilder = services.AddMvc();

            var provider = services.BuildServiceProvider();

            using (var scope = provider.CreateScope())
            {
                var pluginManager     = scope.ServiceProvider.GetService <IPluginManager>();
                var allEnabledPlugins = pluginManager.GetAllEnabledPlugins();
                var loader            = scope.ServiceProvider.GetService <IReferenceLoader>();

                foreach (var plugin in allEnabledPlugins)
                {
                    var context             = new CollectibleAssemblyLoadContext();
                    var moduleName          = plugin.Name;
                    var filePath            = $"{AppDomain.CurrentDomain.BaseDirectory}Modules\\{moduleName}\\{moduleName}.dll";
                    var referenceFolderPath = $"{AppDomain.CurrentDomain.BaseDirectory}Modules\\{moduleName}";

                    _presets.Add(filePath);
                    using (var fs = new FileStream(filePath, FileMode.Open))
                    {
                        // LoadFromAssemblyPath改成了LoadFromStream
                        var assembly = context.LoadFromStream(fs);
                        loader.LoadStreamsIntoContext(context, referenceFolderPath, assembly);

                        var controllerAssemblyPart = new MyAssemblyPart(assembly);
                        mvcBuilder.PartManager.ApplicationParts.Add(controllerAssemblyPart);
                        PluginsLoadContexts.AddPluginContext(plugin.Name, context);
                    }
                }
            }

            //Razor视图的运行时编译
            mvcBuilder.AddRazorRuntimeCompilation(o =>
            {
                foreach (var item in _presets)
                {
                    o.AdditionalReferencePaths.Add(item);
                }
                AdditionalReferencePathHolder.AdditionalReferencePaths = o.AdditionalReferencePaths;
            });

            services.Configure <RazorViewEngineOptions>(o =>
            {
                o.AreaViewLocationFormats.Add("/Modules/{2}/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
                o.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
            });
        }
Beispiel #3
0
        public IActionResult Enable(Guid id)
        {
            var module = _pluginManager.GetPlugin(id);

            if (!PluginsLoadContexts.Any(module.Name))
            {
                var context = new CollectibleAssemblyLoadContext();

                _pluginManager.EnablePlugin(id);
                var moduleName = module.Name;

                var filePath = $"{AppDomain.CurrentDomain.BaseDirectory}Modules\\{moduleName}\\{moduleName}.dll";
                using (var fs = new FileStream(filePath, FileMode.Open))
                {
                    var assembly = context.LoadFromStream(fs);

                    var controllerAssemblyPart = new MyAssemblyPart(assembly);

                    AdditionalReferencePathHolder.AdditionalReferencePaths.Add(filePath);
                    _partManager.ApplicationParts.Add(controllerAssemblyPart);
                }

                MyActionDescriptorChangeProvider.Instance.HasChanged = true;
                MyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();

                PluginsLoadContexts.AddPluginContext(module.Name, context);
            }
            else
            {
                var context = PluginsLoadContexts.GetContext(module.Name);
                var controllerAssemblyPart = new AssemblyPart(context.Assemblies.First());
                _partManager.ApplicationParts.Add(controllerAssemblyPart);
                _pluginManager.EnablePlugin(id);

                MyActionDescriptorChangeProvider.Instance.HasChanged = true;
                MyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();
            }

            return(RedirectToAction("Index"));
        }
Beispiel #4
0
        public static IServiceCollection AddPluginCore(this IServiceCollection services, Action <PluginSetupOptions> configAction)
        {
            var pluginSetupOptions = new PluginSetupOptions();

            configAction(pluginSetupOptions);
            //var config = pluginSetupOptions.Configuration;
            var mvcBuilder = pluginSetupOptions.MvcBuilder;

            // 添加配置
            services.AddOptions();
            // 配置触发控制器重新装载
            services.AddSingleton <IActionDescriptorChangeProvider>(MyActionDescriptorChangeProvider.Instance);
            services.AddSingleton(MyActionDescriptorChangeProvider.Instance);

            // 加载安装好的插件
            var provider = services.BuildServiceProvider();

            using (var scope = provider.CreateScope())
            {
                var razorOptions = scope.ServiceProvider.GetService <MvcRazorRuntimeCompilationOptions>();

                // 加载所有已经安装好的插件 TODO 启用禁用 采用一个数据库来保存插件状态数据
                var pluginDir = System.IO.Path.Combine(AppContext.BaseDirectory, "Plugins");
                if (!System.IO.Directory.Exists(pluginDir))
                {
                    System.IO.Directory.CreateDirectory(pluginDir);
                }
                var pluginFiles = System.IO.Directory.GetFiles(pluginDir, "*.dll");
                // TODO 每个插件分配到一个文件夹中,一个文件夹代表一个插件,里面包含`plugin.json`配置文件,内部`config.json`配置文件,`XxxPlugin.dll`插件主体程序,插件依赖的`Dependents.dll`也需要加载
                foreach (var filePath in pluginFiles)
                {
                    _presets.Add(filePath);
                    var context = new CollectibleAssemblyLoadContext();
                    #region 采用 AssemblyLoadContext.LoadFromAssemblyPath(assemblyPath) 方式加载 Assembly
                    //var assembly = context.LoadFromAssemblyPath(filePath);
                    //var controllerAssemblyPart = new Microsoft.AspNetCore.Mvc.ApplicationParts.AssemblyPart(assembly);
                    //mvcBuilders.PartManager.ApplicationParts.Add(controllerAssemblyPart);
                    #endregion
                    //using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open))
                    //{
                    using var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open);
                    var assembly = context.LoadFromStream(fs);
                    var controllerAssemblyPart = new MyAssemblyPart(assembly);
                    mvcBuilder.PartManager.ApplicationParts.Add(controllerAssemblyPart);
                    PluginsLoadContext.Add(System.IO.Path.GetFileNameWithoutExtension(filePath), context);
                    //}
                }
                // 设置插件文件夹监听器
            }

            // 配置`Razor`运行时编译
            mvcBuilder.AddRazorRuntimeCompilation(options =>
            {
                foreach (var item in _presets)
                {
                    options.AdditionalReferencePaths.Add(item);
                }
                AdditionalReferencePathHolder.AdditionalReferencePaths = options.AdditionalReferencePaths;
            });

            // 添加`Razor`页面路由和应用模型约定
            services.Configure <Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions>(options =>
            {
                // 2-PluginName, 1-Controller, 0-Action
                options.AreaViewLocationFormats.Add("/Plugins/{2}/Views/{1}/{0}" + Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine.ViewExtension);
                options.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
            });

            //// 为 IPageConvention 添加委托,以添加应用于 Razor 页面的模型约定。
            //services.Configure<Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions>(options =>
            //{
            //    options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());
            //    options.Conventions.Add(new GlobalHeaderPageApplicationModelConvention());
            //    options.Conventions.Add(new GlobalPageHandlerModelConvention());
            //    // 将 URL www.domain.com/product 映射到Razor 页面 “extras”文件夹“products.cshtml”文件
            //    options.Conventions.AddPageRoute("/extras/products", "product");
            //    // 路由模板(*)通配符表示“全部”。即使使用此配置,磁盘上的现有文件和URL之间的匹配规则仍然正常运行。
            //    options.Conventions.AddPageRoute("/index", "{*url}");
            //    // 重点关注三点:1是路由作用域,2是order路由顺序,3是定义好Template路由规则。
            //    options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
            //    {
            //        //OtherPages文件夹下的页面,都用此路由模板。
            //        var selectorCount = model.Selectors.Count;
            //        for (var i = 0; i < selectorCount; i++)
            //        {
            //            var selector = model.Selectors[i];
            //            model.Selectors.Add(new AspNetCore.Mvc.ApplicationModels.SelectorModel
            //            {
            //                AttributeRouteModel = new AspNetCore.Mvc.ApplicationModels.AttributeRouteModel
            //                {
            //                    //用于处理路由匹配,指定路由处理顺序。按顺序处理的路由 (-1、 0、 1、 2、 … n)
            //                    Order = 2,
            //                    Template = AspNetCore.Mvc.ApplicationModels.AttributeRouteModel.CombineTemplates
            //                    (selector.AttributeRouteModel.Template, "{otherPagesTemplate?}")
            //                }
            //            });
            //        }
            //    });
            //    options.Conventions.AddPageRouteModelConvention("/About", model =>
            //    {
            //        //About页面,用此路由模板。
            //        var selectorCount = model.Selectors.Count;
            //        for (var i = 0; i < selectorCount; i++)
            //        {
            //            var selector = model.Selectors[i];
            //            model.Selectors.Add(new AspNetCore.Mvc.ApplicationModels.SelectorModel
            //            {
            //                AttributeRouteModel = new AspNetCore.Mvc.ApplicationModels.AttributeRouteModel
            //                {
            //                    Order = 2,
            //                    Template = AspNetCore.Mvc.ApplicationModels.AttributeRouteModel.CombineTemplates
            //                    (selector.AttributeRouteModel.Template, "{aboutTemplate?}")
            //                }
            //            });
            //        }
            //    });
            //});

            return(services);
        }