Home > Backend Development > C#.Net Tutorial > Detailed introduction to ZKEACMS for .Net Core

Detailed introduction to ZKEACMS for .Net Core

零下一度
Release: 2017-05-26 13:34:48
Original
3048 people have browsed it

ZKEACMS.Core is an open source CMS developed based on .Net Core MVC. This article mainly provides an in-depth analysis of ZKEACMS for .Net Core, which has certain reference value. Interested friends You can refer to

ZKEACMS Introduction
ZKEACMS.Core is an open source CMS developed based on .Net Core MVC. ZKEACMS allows users to freely plan the page layout, use visual editing to design "what you see is what you get", and drag and drop content directly on the page.

ZKEACMS uses plug-in design, module separation, and enriches the functions of CMS through horizontal expansion.

Responsive design

ZKEACMS uses the Bootstrap3 grid system to implement responsive design, so that it can be used on different devices can be accessed normally. At the same time, standing on the shoulders of Bootstrap giants, there are rich theme resources available.

Simple demonstration

Let’s take a look at the program design and principles

Project structure

  • EasyFrameWork Underlying framework

  • ZKEACMS CMS core

  • ZKEACMS.Article Plug-in

  • ZKEACMS.Product Product plug-in

  • ZKEACMS.SectionWidget Template component plug-in

  • ZKEACMS. WebHost

Principle - Access request process

RoutingStarts in ZKEACMS Playing a key role, the priority of the route determines the direction of the access process. If a matching route is found, the Controller -> Action -> View corresponding to the route will be taken first. If there is no matching route, the route will be taken by The lowest priority "catch-all" route handles the user's request and finally returns the response.

The "full capture" route with the lowest priority is used to process pages created by users themselves. When a request comes in, it first searches the database to see if the page exists. If it does not exist, a 404 is returned. After finding the page, find all the components and content of the page, and then uniformly call the "Display" method of each component to get the corresponding "ViewModel" and view "View", and finally display them according to the layout of the page.

ZKEACMS request flow chart

Drive page component:

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();
  }
});
Copy after login

Page rendering:

foreach (var widgetPart in Model.ZoneWidgets[zoneId].OrderBy(m => m.Widget.Position).ThenBy(m => m.Widget.WidgetName))
{
  <p style="@widgetPart.Widget.CustomStyle">
    <p class="widget @widgetPart.Widget.CustomClass">
      @if (widgetPart.Widget.Title.IsNotNullAndWhiteSpace())
      {
        <p class="panel panel-default">
          <p class="panel-heading">
            @widgetPart.Widget.Title
          </p>
          <p class="panel-body">
            @Html.DisPlayWidget(widgetPart)
          </p>
        </p>
      }
      else
      {
        @Html.DisPlayWidget(widgetPart)
      }
    </p>
  </p>
}
Copy after login

Plug-in The "most critical" class PluginBase

Every plug-in/module must have a classInheritPluginBase, which serves as the entry point for plug-in initialization. When the program starts, These classes will be loaded and some key initialization work will be done.

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

For specific implementation, please refer to the "article" plug-in ArticlePlug.cs or the "product" plug-in ProductPlug.cs

Load plug-in Startup.cs

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

Component composition

A page is composed of many components. Each component can contain different content (Content), such as text, pictures, videos, etc. The content is determined by the component, and the presentation method is determined by the component. Determined by the template (View).

The relationship and presentation method are roughly as shown in the figure below:

EntityEnity

Each component will correspond An entity used to store some information related to this component. Entities must inherit from the BasicWidget class.

For example, the entity class of the HTML component:

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

The metadata configuration [ViewConfigure(typeof(HtmlWidgetMetaData))] is used in the entity class to control the form page and list page through simple settings. show. If set to text or drop-down box; required, verification of length, etc.

The implementation method here is to add a new ModelMetadataDetailsProviderProvider to MVC. The function of this Provider is to capture the configuration information of these metadata and submit it to MVC.

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

Service Service

WidgetService is the bridge between data and templates. It grabs data through Service and sends it to the page template. Service must inherit from WidgetService. If the business is complex, override the corresponding method of the base class to implement it.

For example, the Service of the HTML component:

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

View Entity ViewModel

ViewModel is not necessary. When the entity (Entity) is passed to the view as a ViewModel, it is insufficient. When the requirements are met, you can create a new ViewModel and pass this ViewModel over, which will require overriding the Display method

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

View/Template Widget.cshtml

Template (Template) is used to display content. The "Model" required by the template is collected through the Service, and finally the template displays them.

动态编译分散的模板

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

public class PluginRazorViewEngineOptionsSetup : ConfigureOptions<RazorViewEngineOptions>
{
  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);
  }
}
Copy after login

看上面代码您可能会产生疑惑,为什么要分开发环境。这是因为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"]);
  }
}
Copy after login

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

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

主题

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

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

最后

关于ZKEACMS还有很多,如果您也感兴趣,欢迎加入我们。

ZKEACMS for .net core 就是要让建网站变得更简单,快速。页面的修改与改版也变得更轻松,便捷。

【相关推荐】

1. .Net Core 之 图形验证码

2. .NET Core配置文件加载与DI注入配置数据

3. .NET Core CLI工具文档dotnet-publish

4. 分享.net MVC中使用forms验证实例代码

5. 在.net core 下如何进行http请求?

6. CentOS上运行ZKEACMS的实例教程

The above is the detailed content of Detailed introduction to ZKEACMS for .Net Core. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template