纸壳CMS是一个开源的可视化设计CMS,通过拖拽,在线编辑的方式来创建网站。

GitHub

https://github.com/SeriaWei/ZKEACMS.Core

欢迎Star,Fork,发PR。:)

插件化设计

纸壳CMS是基于插件化设计的,可以通过扩展插件来实现不同的功能。如何通过插件来扩展,可以参考这篇文章:

http://www.zkea.net/codesnippet/detail/zkeacms-plugin-development.html

纸壳CMS的插件是相互独立的,各插件的引用也相互独立,即各插件都可引用各自需要的nuget包来达到目的。而不用把引用加到底层。

插件存放目录

纸壳CMS的插件的存放目录在开发环境和已发布的程序中是不一样的。在开发环境,插件和其它的项目统一放在src目录下:

而发布程序以后,插件会在wwwroot/Plugins目录下:

所以,如果在开发过程中要使用插件目录时,需要使用特定的方法来获取真实的目录,如:

PluginBase.GetPath<SectionPlug>()

相关代码

有关插件用到的所有相关代码,都在 EasyFrameWork/Mvc/Plugin 目录下:

插件加载

纸壳CMS在程序启动时加载所有启用的插件Loader.cs:

public IEnumerable<IPluginStartup> LoadEnablePlugins(IServiceCollection serviceCollection)
{
var start = DateTime.Now;
Loaders.AddRange(GetPlugins().Where(m => m.Enable && m.ID.IsNotNullAndWhiteSpace()).Select(m =>
{
var loader = new AssemblyLoader();
loader.CurrentPath = m.RelativePath;
var assemblyPath = Path.Combine(m.RelativePath, (HostingEnvironment.IsDevelopment() ? Path.Combine(AltDevelopmentPath) : string.Empty), m.FileName); Console.WriteLine("Loading: {0}", m.Name); var assemblies = loader.LoadPlugin(assemblyPath);
assemblies.Each(assembly =>
{
if (!LoadedAssemblies.ContainsKey(assembly.FullName))
{
LoadedAssemblies.Add(assembly.FullName, assembly);
}
});
return loader;
}));
Console.WriteLine("All plugins are loaded. Elapsed: {0}ms", (DateTime.Now - start).Milliseconds);
return serviceCollection.ConfigurePlugin().BuildServiceProvider().GetPlugins();
}

AssemblyLoader

AssemblyLoader是加载插件DLL的关键,纸壳CMS主要通过它来加载插件,并加载插件的相关依赖,并注册插件。

namespace Easy.Mvc.Plugin
{
public class AssemblyLoader
{
private const string ControllerTypeNameSuffix = "Controller";
private static bool Resolving { get; set; }
public AssemblyLoader()
{
DependencyAssemblies = new List<Assembly>();
}
public string CurrentPath { get; set; }
public string AssemblyPath { get; set; }
public Assembly CurrentAssembly { get; private set; }
public List<Assembly> DependencyAssemblies { get; private set; }
private TypeInfo PluginTypeInfo = typeof(IPluginStartup).GetTypeInfo();
public IEnumerable<Assembly> LoadPlugin(string path)
{
if (CurrentAssembly == null)
{
AssemblyPath = path; CurrentAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
ResolveDenpendency(CurrentAssembly);
RegistAssembly(CurrentAssembly);
yield return CurrentAssembly;
foreach (var item in DependencyAssemblies)
{
yield return item;
}
}
else { throw new Exception("A loader just can load one assembly."); }
} private void ResolveDenpendency(Assembly assembly)
{
string currentName = assembly.GetName().Name;
var dependencyCompilationLibrary = DependencyContext.Load(assembly)
.CompileLibraries.Where(de => de.Name != currentName && !DependencyContext.Default.CompileLibraries.Any(m => m.Name == de.Name))
.ToList(); dependencyCompilationLibrary.Each(libaray =>
{
bool depLoaded = false;
foreach (var item in libaray.Assemblies)
{
var files = new DirectoryInfo(Path.GetDirectoryName(assembly.Location)).GetFiles(Path.GetFileName(item));
foreach (var file in files)
{
DependencyAssemblies.Add(AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName));
depLoaded = true;
break;
}
}
if (!depLoaded)
{
foreach (var item in libaray.ResolveReferencePaths())
{
if (File.Exists(item))
{
DependencyAssemblies.Add(AssemblyLoadContext.Default.LoadFromAssemblyPath(item));
break;
}
}
}
}); } private void RegistAssembly(Assembly assembly)
{
List<TypeInfo> controllers = new List<TypeInfo>();
PluginDescriptor plugin = null;
foreach (var typeInfo in assembly.DefinedTypes)
{
if (typeInfo.IsAbstract || typeInfo.IsInterface) continue; if (IsController(typeInfo) && !controllers.Contains(typeInfo))
{
controllers.Add(typeInfo);
}
else if (PluginTypeInfo.IsAssignableFrom(typeInfo))
{
plugin = new PluginDescriptor();
plugin.PluginType = typeInfo.AsType();
plugin.Assembly = assembly;
plugin.CurrentPluginPath = CurrentPath;
}
}
if (controllers.Count > && !ActionDescriptorProvider.PluginControllers.ContainsKey(assembly.FullName))
{
ActionDescriptorProvider.PluginControllers.Add(assembly.FullName, controllers);
}
if (plugin != null)
{
PluginActivtor.LoadedPlugins.Add(plugin);
}
}
protected bool IsController(TypeInfo typeInfo)
{
if (!typeInfo.IsClass)
{
return false;
} if (typeInfo.IsAbstract)
{
return false;
} if (!typeInfo.IsPublic)
{
return false;
} if (typeInfo.ContainsGenericParameters)
{
return false;
} if (typeInfo.IsDefined(typeof(NonControllerAttribute)))
{
return false;
} if (!typeInfo.Name.EndsWith(ControllerTypeNameSuffix, StringComparison.OrdinalIgnoreCase) &&
!typeInfo.IsDefined(typeof(ControllerAttribute)))
{
return false;
} return true;
}
}
}

注册插件时,需要将插件中的所有Controller分析出来,当用户访问到插件的对应Controller时,才可以实例化Controller并调用。

动态编译插件视图

ASP.NET MVC 的视图(cshtml)是可以动态编译的。但由于插件是动态加载的,编译器并不知道编译视图所需要的引用在什么地方,这会导致插件中的视图编译失败。并且程序也需要告诉编译器到哪里去找这个视图。PluginRazorViewEngineOptionsSetup.cs 便起到了这个作用。

由于开发环境的目录不同,对以针对开发环境,需要一个视图文件提供程序来解析视图文件位置:

if (hostingEnvironment.IsDevelopment())
{
options.FileProviders.Add(new DeveloperViewFileProvider(hostingEnvironment));
} loader.GetPlugins().Where(m => m.Enable && m.ID.IsNotNullAndWhiteSpace()).Each(m =>
{
var directory = new DirectoryInfo(m.RelativePath);
if (hostingEnvironment.IsDevelopment())
{
options.ViewLocationFormats.Add($"{DeveloperViewFileProvider.ProjectRootPath}{directory.Name}" + "/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"{DeveloperViewFileProvider.ProjectRootPath}{directory.Name}" + "/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"{DeveloperViewFileProvider.ProjectRootPath}{directory.Name}" + "/Views/{0}" + RazorViewEngine.ViewExtension);
}
else
{
options.ViewLocationFormats.Add($"/wwwroot/{Loader.PluginFolder}/{directory.Name}" + "/Views/{1}/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"/wwwroot/{Loader.PluginFolder}/{directory.Name}" + "/Views/Shared/{0}" + RazorViewEngine.ViewExtension);
options.ViewLocationFormats.Add($"/wwwroot/{Loader.PluginFolder}/{directory.Name}" + "/Views/{0}" + RazorViewEngine.ViewExtension);
}
});
options.ViewLocationFormats.Add("/Views/{0}" + RazorViewEngine.ViewExtension);

为了解决引用问题,需要把插件相关的所有引用都加入到编译环境中:

loader.GetPluginAssemblies().Each(assembly =>
{
var reference = MetadataReference.CreateFromFile(assembly.Location);
options.AdditionalCompilationReferences.Add(reference);
});

纸壳CMS的插件加载机制的更多相关文章

  1. 一种优雅的Golang的库插件注册加载机制

    一种优雅的Golang的库插件注册加载机制 你好,我是轩脉刃. 最近看到一个内部项目的插件加载机制,非常赞.当然这里说的插件并不是指的golang原生的可以在buildmode中加载指定so文件的那种 ...

  2. 插件化框架解读之so 文件加载机制(四)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 提问 本文的结论是跟着 System.loadlibrary() ...

  3. 使用vs code开发纸壳CMS并启用Razor智能提示

    关于纸壳CMS 纸壳CMS是一个开源免费的,可视化设计,在线编辑的内容管理系统.基于ASP .Net Core开发,插件式设计: 下载代码 GitHub:https://github.com/Seri ...

  4. ASP .Net Core路由(Route) - 纸壳CMS的关键

    关于纸壳CMS 纸壳CMS是一个开源免费的,可视化设计,在线编辑的内容管理系统.基于ASP .Net Core开发,插件式设计: GitHub:https://github.com/SeriaWei/ ...

  5. 纸壳CMS列表Grid的配置

    纸壳CMS(ZKEACMS)里的Grid是一个TagHelper,是对jQuery插件datatables的一个配置封装. Easy.Mvc.TagHelpers.GridTagHelper grid ...

  6. Java高级之虚拟机加载机制

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 1.0版本:2016-05-21 SubClass!! 执行结果说明一个问题:子类调用父类变量的时候 ...

  7. 纸壳CMS主题增强,支持主题中加入模板

    背景 在之前,纸壳CMS的主题仅仅只是CSS样式,并不支持在主题下使用模板来构建不同的HTML结构.现在我们对主题功能做了增强,可以在主题下添加各自的模板,这样在制作主题时,就会更加自由.不仅如此,新 ...

  8. Spring Boot 扩展点应用之工厂加载机制

    Spring 工厂加载机制,即 Spring Factories Loader,核心逻辑是使用 SpringFactoriesLoader 加载由用户实现的类,并配置在约定好的META-INF/spr ...

  9. Android之Android apk动态加载机制的研究(二):资源加载和activity生命周期管理

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/23387079 (来自singwhatiwanna的csdn博客) 前言 为了 ...

随机推荐

  1. Django视图View

    1.什么是Django的视图函数 一个视图函数(或者一个类),简称视图 是python的一个简单的函数,他是接受web请求 并且 处理逻辑 进而返回 响应 响应可以是一张网页的HTML内容,一个重定向 ...

  2. Windows 程序 dump 崩溃调试

    Windows 程序捕获崩溃异常 生成dump 概述 事情的起因是,有个同事开发的程序,交付的版本程序,会偶尔随机崩溃了. 悲催的是没有输出log,也没有输出dump文件. 我建议他给程序代码加个异常 ...

  3. 十、api自动化环境问题及解决方案汇总(持续更新)

    1.jenkins报错: Unable to read /root/.jenkins/config.xml at hudson.WebAppMain$3.run(WebAppMain.java:248 ...

  4. Rhythmk 一步一步学 JAVA (22) JAVA 网络编程

    1.获取主机信息 @Test public void GetDomainInfo() throws UnknownHostException { String domain = "www.b ...

  5. Spring security 3.1 +JSF 2.0 . problem with annotating methods in ManagedBeans?

    Hy .What i am trying to do is to integrate Spring security with a Jsf+spring IOC +hibernate applicat ...

  6. HTTP --meta详解

    meta是html语言head区的一个辅助性标签.也许你认为这些代码可有可无.其实如果你能够用好meta标签,会给你带来意想不到的效果,meta标签的作用有:搜索引擎优化(SEO),定义页面使用语言, ...

  7. 【Directshow】IFilterGraph::AddFilter方法参数问题<转>

    看dx里dshow的例子和别人的程序,用IFilterGraph::AddFilter()这个API添加filter到Graph的时候,第二个参数是一个设备友好名称: 我看有的填的是通过IProper ...

  8. 解决SQL将varchar值转换为数据类型为int的列时发生语法错误

    今天遇到一个这样的错误,具体的报错情况如下 解决的方案如下. 数据库MSSQL在比较大小时,出错提示:“将 varchar 值 '24.5' 转换为数据类型为 int 的列时发生语法错!”分析数据库设 ...

  9. 迷你MVVM框架 avalonjs 1.3.5发布

    本版本主要是修复内存泄漏问题,让其在移动端更好的运作. 修正visible BUG 详见这里 修正$fire方法里的正则错误 详见这里 修正ms-attr BUG,在IE9-11,直接用element ...

  10. Windows10系统下安装python2和python3双版本

    Windows10系统下安装Python3的步骤已经演示过(详见:https://www.cnblogs.com/schut/p/8399195.html),此处不再赘述Python的下载,主要介绍在 ...