千家信息网

ZKEACMS for .Net Core的示例分析

发表于:2024-11-13 作者:千家信息网编辑
千家信息网最后更新 2024年11月13日,这篇文章给大家分享的是有关ZKEACMS for .Net Core的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。ZKEACMS 简介ZKEACMS.Core 是
千家信息网最后更新 2024年11月13日ZKEACMS for .Net Core的示例分析

这篇文章给大家分享的是有关ZKEACMS for .Net Core的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

ZKEACMS 简介
ZKEACMS.Core 是基于 .Net Core MVC 开发的开源CMS。ZKEACMS可以让用户自由规划页面布局,使用可视化编辑设计"所见即所得",直接在页面上进行拖放添加内容。

ZKEACMS使用插件式设计,模块分离,通过横向扩展来丰富CMS的功能。

响应式设计

ZKEACMS使用Bootstrap3的栅格系统来实现响应式设计,从而实现在不同的设备上都可以正常访问。同时站在Bootstrap巨人的肩膀上,有丰富的主题资源可以使用。

简单演示

接下来看看程序设计及原理

项目结构

  • EasyFrameWork  底层框架

  • ZKEACMS   CMS核心

  • ZKEACMS.Article   文章插件

  • ZKEACMS.Product  产品插件

  • ZKEACMS.SectionWidget  模板组件插件

  • ZKEACMS.WebHost

原理 - 访问请求流程

路由在ZKEACMS里面起到了关键性的作用,通过路由的优先级来决定访问的流程走向,如果找到匹配的路由,则优先走该路由对应的 Controller -> Action -> View,如果没有匹配的路由,则走路由优先权最低的"全捕捉"路由来处理用户的请求,最后返回响应。

优先级最低的"全捕捉"路由是用来处理用户自行创建的页面的。当请求进来时,先去数据库中查找是否存在该页面,不存在则返回404。找到页面之后,再找出这个页面所有的组件、内容,然后统一调用各个组件的"Display"方法来来得到对应的"ViewModel"和视图"View",最后按照页面的布局来显示。

ZKEACMS 请求流程图

驱动页面组件:

widgetService.GetAllByPage(filterContext.HttpContext.RequestServices, page).Each(widget =>{  if (widget != null)  {    IWidgetPartDriver partDriver = widget.CreateServiceInstance(filterContext.HttpContext.RequestServices);    WidgetViewModelPart part = partDriver.Display(widget, filterContext);    lock (layout.ZoneWidgets)    {      if (layout.ZoneWidgets.ContainsKey(part.Widget.ZoneID))      {        layout.ZoneWidgets[part.Widget.ZoneID].TryAdd(part);      }      else      {        layout.ZoneWidgets.Add(part.Widget.ZoneID, new WidgetCollection { part });      }    }    partDriver.Dispose();  }});

页面呈现:

foreach (var widgetPart in Model.ZoneWidgets[zoneId].OrderBy(m => m.Widget.Position).ThenBy(m => m.Widget.WidgetName)){  
@if (widgetPart.Widget.Title.IsNotNullAndWhiteSpace()) {
@widgetPart.Widget.Title
@Html.DisPlayWidget(widgetPart)
} else { @Html.DisPlayWidget(widgetPart) }
}

插件"最关键"的类 PluginBase

每一个插件/模块都必需要一个类继承PluginBase,作为插件初始化的入口,程序在启动的时候,会加载这些类并作一些关键的初始化工作。

public abstract class PluginBase : ResourceManager, IRouteRegister, IPluginStartup{  public abstract IEnumerable RegistRoute(); //注册该插件所需要的路由 可返回空  public abstract IEnumerable AdminMenu(); //插件在后端提供的菜单 可返回空  public abstract IEnumerable RegistPermission(); //注册插件的权限  public abstract IEnumerable WidgetServiceTypes(); //返回该插件中提供的所有组件的类型  public abstract void ConfigureServices(IServiceCollection serviceCollection); //IOC 注册对应的接口与实现  public virtual void InitPlug(); //初始化插件,在程序启动时调用该方法}

具体实现可以参考"文章"插件 ArticlePlug.cs 或者"产品"插件 ProductPlug.cs

加载插件 Startup.cs

public void ConfigureServices(IServiceCollection services){  services.UseEasyFrameWork(Configuration).LoadEnablePlugins(plugin =>  {    var cmsPlugin = plugin as PluginBase;    if (cmsPlugin != null)    {      cmsPlugin.InitPlug();    }  }, null);      }

组件构成

一个页面,由许多的组件构成,每个组件都可以包含不同的内容(Content),像文字,图片,视频等,内容由组件决定,呈现方式由组件的模板(View)决定。

关系与呈现方式大致如下图所示:

实体 Enity

每个组件都会对应一个实体,用于存储与该组件相关的一些信息。实体必需继承于 BasicWidget 类。

例如HTML组件的实体类:

[ViewConfigure(typeof(HtmlWidgetMetaData)), Table("HtmlWidget")]public class HtmlWidget : BasicWidget{  public string HTML { get; set; }}class HtmlWidgetMetaData : WidgetMetaData{  protected override void ViewConfigure()  {    base.ViewConfigure();    ViewConfig(m => m.HTML).AsTextArea().AddClass("html").Order(NextOrder());  }}

实体类里面使用到了元数据配置[ViewConfigure(typeof(HtmlWidgetMetaData))],通过简单的设置来控制表单页面、列表页面的显示。假如设置为文本或下拉框;必填,长度等的验证。

这里实现方式是向MVC里面添加一个新的ModelMetadataDetailsProviderProvider,这个Provider的作用就是抓取这些元数据的配置信息并提交给MVC。

services.AddMvc(option =>  {    option.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider());  })

服务 Service

WidgetService 是数据与模板的桥梁,通过Service抓取数据并送给页面模板。 Service 必需继承自 WidgetService。如果业务复杂,则重写(override)基类的对应方法来实现。

例如HTML组件的Service:

public class HtmlWidgetService : WidgetService{  public HtmlWidgetService(IWidgetBasePartService widgetService, IApplicationContext applicationContext)    : base(widgetService, applicationContext)  {  }   public override DbSet CurrentDbSet  {    get    {      return DbContext.HtmlWidget;    }  }}

视图实体 ViewModel

ViewModel 不是必需的,当实体(Entity)作为ViewModel传到视图不足以满足要求时,可以新建一个ViewModel,并将这个ViewModel传过去,这将要求重写 Display 方法

public override WidgetViewModelPart Display(WidgetBase widget, ActionContext actionContext){  //do some thing  return widget.ToWidgetViewModelPart(new ViewModel());}

视图 / 模板 Widget.cshtml

模板 (Template) 用于显示内容。通过了Service收集到了模板所要的"Model",最后模板把它们显示出来。

动态编译分散的模板

插件的资源都在各自的文件夹下面,默认的视图引擎(ViewEngine)并不能找到这些视图并进行编译。MVC4版本的ZKEACMS是通过重写了ViewEngine来得以实现。.net core mvc 可以更方便实现了,实现自己的 ConfigureOptions ,然后通过依赖注入就行。

public class PluginRazorViewEngineOptionsSetup : ConfigureOptions{  public PluginRazorViewEngineOptionsSetup(IHostingEnvironment hostingEnvironment, IPluginLoader loader) :    base(options => ConfigureRazor(options, hostingEnvironment, loader))  {   }  private static void ConfigureRazor(RazorViewEngineOptions options, IHostingEnvironment hostingEnvironment, IPluginLoader loader)  {    if (hostingEnvironment.IsDevelopment())    {      options.FileProviders.Add(new DeveloperViewFileProvider());    }    loader.GetPluginAssemblies().Each(assembly =>    {      var reference = MetadataReference.CreateFromFile(assembly.Location);      options.AdditionalCompilationReferences.Add(reference);            });    loader.GetPlugins().Where(m => m.Enable && m.ID.IsNotNullAndWhiteSpace()).Each(m =>    {      var directory = new DirectoryInfo(m.RelativePath);      if (hostingEnvironment.IsDevelopment())      {        options.ViewLocationFormats.Add($"/Porject.RootPath/{directory.Name}" + "/Views/{1}/{0}" + RazorViewEngine.ViewExtension);        options.ViewLocationFormats.Add($"/Porject.RootPath/{directory.Name}" + "/Views/Shared/{0}" + RazorViewEngine.ViewExtension);        options.ViewLocationFormats.Add($"/Porject.RootPath/{directory.Name}" + "/Views/{0}" + RazorViewEngine.ViewExtension);      }      else      {        options.ViewLocationFormats.Add($"/{Loader.PluginFolder}/{directory.Name}" + "/Views/{1}/{0}" + RazorViewEngine.ViewExtension);        options.ViewLocationFormats.Add($"/{Loader.PluginFolder}/{directory.Name}" + "/Views/Shared/{0}" + RazorViewEngine.ViewExtension);        options.ViewLocationFormats.Add($"/{Loader.PluginFolder}/{directory.Name}" + "/Views/{0}" + RazorViewEngine.ViewExtension);      }    });    options.ViewLocationFormats.Add("/Views/{0}" + RazorViewEngine.ViewExtension);  }}

看上面代码您可能会产生疑惑,为什么要分开发环境。这是因为ZKEACMS发布和开发的时候的文件夹目录结构不同造成的。为了方便开发,所以加入了开发环境的特别处理。接下来就是注入这个配置:

services.TryAddEnumerable(ServiceDescriptor.Transient, PluginRazorViewEngineOptionsSetup>());   

EntityFrameWork

ZKEACMS for .net core 使用EntityFrameWork作为数据库访问。数据库相关配置 EntityFrameWorkConfigure

public class EntityFrameWorkConfigure : IOnConfiguring{  public void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  {    optionsBuilder.UseSqlServer(Easy.Builder.Configuration.GetSection("ConnectionStrings")["DefaultConnection"]);  }}

对Entity的配置依然可以直接写在对应的类或属性上。如果想使用 Entity Framework Fluent API,那么请创建一个类,并继承自 IOnModelCreating

class EntityFrameWorkModelCreating : IOnModelCreating{  public void OnModelCreating(ModelBuilder modelBuilder)  {    modelBuilder.Entity().Ignore(m => m.Description).Ignore(m => m.Status).Ignore(m => m.Title);  }}

主题

ZKEACMS 使用Bootstrap3作为基础,使用LESS,定议了许多的变量,像边距,颜色,背景等等,可以通过简单的修改变量就能"编译"出一个自己的主题。

或者也可以直接使用已经有的Bootstrap3的主题作为基础,然后快速创建主题。

感谢各位的阅读!关于"ZKEACMS for .Net Core的示例分析"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

插件 组件 页面 模板 路由 内容 实体 数据 视图 主题 设计 配置 方法 开发 不同 关键 数据库 文章 方式 流程 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全怎么防护自己的靶机 2016服务器系统硬件要求 双阳区通用网络技术咨询售后服务 数据库安全机制源代码 计算机网络技术组建局域网 dell 服务器安装教程 哈尔滨 网络技术 巨耀 战神引擎数据库记录 学籍管理连接服务器失败 成都千猫互联网科技骗子 证券期货业网络安全管理办法 根服务器很强吗 宁夏惠普服务器维修维保哪家便宜 软件开发的前端是指什么 石林专业性软件开发 平板上可以使用的数据库 数据库期末试卷南京大学金陵学院 最常见索引数据库 imc服务器换了网卡还能用吗 郑州西亚斯网络安全专业 网络安全作文他们 上海虹口区数据库防篡改 新办公软件开发市场分析怎么写 平板电脑软件开发定制 珠海镁成软件开发公司 网络技术相关技术 数据库中的二维数据库有哪些特点 数据库怎么查询每个科目最高分 数据库连接池java 2021上海网络安全博览会
0