OrchardCore 如何实现模块化( Modular )和 Multi-Tenancy
一、概述
通常我们会在 Startup
类通过 void ConfigureServices(IServiceCollection services)
配置应用的服务。常见的形如 AddXXX 的方法,实际上调用的都是 IServiceCollection
或直接说是 ServiceCollection
的 AddSingleton 等方法。调用ApplicationBuilder
的 RequestDelegate Build()
方法会调用 IServiceCollection
的扩展方法 BuildServiceProvider
会创建并返回一个 ServiceProvider 对象。
还会在 Startup
类通过 void Configure(IApplicationBuilder app, IHostingEnvironment env)
配置请求管道,在该方法内进行的主要操作是添加中间件。常见的形如 UseMiddleware 或 UseXXX 的方法,实际上调用的都是 IApplicationBuilder
或直接说是 ApplicationBuilder
的 IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
方法,Use 方法并不是马上将中间件配置入请求管道,而是将“实例化中间件的方式”保存到 ApplicationBuilder 内部一个列表的操作。调用ApplicationBuilder
的 RequestDelegate Build()
方法会实例化中间件并把各个中间件串联起来。
OrchardCore
通过将服务和中间件放在不同的程序集以支持模块化。各个模块提供类似于 ConfigureServices 和 Configure 的方法供运行时调用。
OrchardCore
还支持 Multi-Tenancy
。Tenant
有如下特性:
- 多个 Tenant 运行在同一个应用程序域中,每个 Tenant 几乎可以看做是独立的网站;
- 根据 Host 、Port 和 Path 的各种组合匹配不同的 Tenant(ModularTenantContainerMiddleware);
- 延迟激活,第一次请求 Tenant 才会激活(ModularTenantContainerMiddleware);
- 每个 Tenant 有不同的 DI 容器(ModularTenantContainerMiddleware);
- 每个 Tenant 有不同的请求管道,可以共享中间件,还可以使用特定中间件(ModularTenantRouterMiddleware)。
二、模块定义
模块是依赖于 OrchardCore.Modules.Targets
程序集的程序集,可以有各自的配置、选项、DI 服务和中间件等,还可以有各自的路由、视图、控制器、 Filter 和 ModelBinder 等,看起来像是一个 MVC Area
。
模块包含 0 或多个 Feature
。Feature 是功能的逻辑组合,可单独开启或禁用。
Feature 之间可有依赖关系,并且支持跨模块的依赖。
备注:
OrchardCore 中,一个程序集只包含一个模块。
模块可以看做是特殊的 Feature 。
用于定义Theme
的OrchardCore.Theme.Targets
程序集也依赖于OrchardCore.Modules.Targets
程序集。
1、Mainifest.cs 文件
模块有个以程序集特性的形式嵌入程序集中的 Mainifest
,用于描述模块的基本信息、拥有的 Feature
和依赖的其他 Feature
等。一般写在 Mainifest.cs
文件中,比如:
1 |
using OrchardCore.Modules.Manifest; |
在运行时可将 Mainifest 读取至 MainifestInfo
对象中。ModuleAttribure
用于描述模块基本信息,只能用于程序集并且只能使用一次,在运行时可读取至 ModuleInfo 对象中。
一个模块可以包含 0 或多个 Feature 。FeatureAttribure
用于描述模块提供的 Feature,只能用于程序集并且可以使用多次,在运行时可读取至 FeatureInfo 对象中。
备注:ModuleAttribute 继承自 FeatureAttribute ,都位于OrchardCore.Abstractions程序集、OrchardCore.Modules.Manifest命名空间中。
从类或对象来看,1 个 MainifestInfo 对象包含1个 ModuleInfo 对象,1 个 ModuleInfo 对象包含 0 或多个 FeatureInfo 对象。
2、ManifestInfo 和 FeatureInfo 类
ExtensionManager类用于获取 Mainifest ,并将相关数据反序列化入 ManifestInfo 和 FeatureInfo对象中。
3、ModuelNameAttribute 类
如果一个项目引用了一些模块,MSBuild 在生成项目时会针对每个模块添加一个程序集级的 ModuleNameAttrbute ,用于保存引用的模块名称。
AssemblyAttributeModuleNamesProvider 类的 IEnumerable<string> GetModuleNames()
方法能够收集到 ModuleNameAttrbute 。
备注:AssemblyAttributeModuleNamesProvider 位于OrchardCore.Abstractions程序集、OrchardCore.Modules命名空间中。
4、ModuleMarkerAttribute 类
MSBuild 在生成模块的项目时会自动添加程序集级的 ModuleMarkerAttribute 。ModuleMarkerAttribute 继承 ModuleAttribute),位于OrchardCore.Abstractions程序集、OrchardCore.Modules.Manifest命名空间中
5、ModuleAssetAttribute类
MSBuild 在生成模块的项目时会自动添加程序集级的 ModuleAssetAttribute。ModuleAssetAttribute继承 ModuleAttribute),位于OrchardCore.Abstractions程序集、OrchardCore.Modules.Manifest命名空间中
6、Module 类
在创建 Module 对象时,传入模块程序集的名称,构造函数会通过 Assembly.Load 加载模块程序集,并且收集模块的 ModuleAttribute、ModuleAssetAttribute 和 ModuleMarkerAttribute 放入自身属性中。
注意:这里说的是 Module 类不是 ModuleAttribute 类。
备注:ModularApplicationContext 位于 OrchardCore.Abstractions程序集、OrchardCore.Modules命名空间中。
7、IStartup 接口
每个模块可能需要注册一些服务至 DI 容器中,也可能需要注册一些中间件。OrchardCore 定义了一个 OrchardCore.Modules.IStartup, OrchardCore.Modules.Abstractions
接口,以及实现了该接口的 OrchardCore.Module s.StartupBase, sOrchardCore.Modules.Abstractions
抽象类。OrchardCore 模块通常有一个 Startup.cs
文件,实现了继承自 SetupBase
抽象类的名为 Startup
的具体类。
注意:OrchardCore 的
Startup
类不是指通常 ASP.NET Core 中的那个类,IStartup
接口也不是通常 ASP.NET Core 中的那个接口,尽管它们的确很相似。
通常,对于 ASP.NET Core 应用的 Startup 类我们不直接实现 IStartup 接口,而采用更灵活的基于方法名约定的方式。另外,通过 IHostingStartup(承载启动)实现,在启动时从外部程序集向应用添加增强功能。但是使用 IHostingStartup 无法控制各个模块注册服务和添加中间件的顺序,也不支持延迟加载。OrchardCore.Modules.IStartup,OrchardCore.Modules.Abstractions
相较于 Microsoft.AspNetCore.Hosting.IStartup,Microsoft.AspNetCore.Hosting.Abstractions
多了个 Order
属性,并且前者的 Configure 方法签名为void Configure(IApplicationBuilder app, IRouteBuilder routes, IServiceProvider serviceProvider)
,后者为 void Configure(IApplicationBuilder app)
。因为模块通常位于不同的程序集, Order 属性的作用是控制向 DI 容器注册服务、添加中间件、添加配置和添加路由的顺序。
备注:
void Configure(IApplicationBuilder app, IRouteBuilder routes, IServiceProvider serviceProvider)
的 routes 和 serviceProvier 是为了支持模块化和Multi-Tenancy
。
三、模块引擎
事实上没有一种明确的组件叫模块引擎。OrchardCore 提供了一些由于支持模块的基础设施,并提供将分散于各个模块的服务收集起来注册至 DI 容器,以及中间件添加至请求管道的机制。
1、AddOrchardCore
AddOrchardCore 不准确地说就是将服务注册至 DI 容器中以及将中间件添加至请求管道的,并返回一个 OrchardCoreBuilder 对象。
OrchardCoreBuilder 严格来说不是生成器模式,它类似于 Startup 类有 ConfigureServices 和 Configure方法。但是当调用这两类方法时,并不是直接将服务注册到 DI 容器中或注册中间件,而是将注册的方式通过委托保存在集合中(通过 StartupAction )。这样做的目的是为了将来给每个 Tenant 注册这些服务和中间件。
1 |
/// <summary> |
① AddDefaultServices
添加默认服务,比如 Logging、Localization 和 Web Encoders (Web Encoders 是指 Html、Url 和 Javascript 的编码器)。
重要的是添加 Routing 服务。IServiceCollection 的扩展方法 AddMvc/AddMvcCore 会添加 Routing 服务。就算不是 MVC 应用也可以是使用路由,并且 OrchardCore 的路由可配置在不同的模块,所以在这里注册是因为后续会使用 Routing 相关服务。
② AddShellServices
添加用于支持 Tenant 的相关服务。Shell 涉及众多的类,这里暂时不分析。
③ AddExtensionServices
添加用于支持模块化的相关服务。主要是 AssemblyAttributeModuleNamesProvider : IModuleNamesProvider
和 ModularApplicationContext : IApplicationContext
。
AssemblyAttributeModuleNamesProvider 提供了一种从程序集的 Attribute 获取模块名称的方式。
ModularApplicationContext 提供了一个 OrchardCore.Modules.Application
对象,可在某些情况下指代应用。使用 ModularApplicationContext 的属性 Application 时,会触发 Application 对象的构造过程。
④ AddStaticFiles
添加静态文件服务中间件,主要是增加 ModuleEmbeddedStaticFileProvider 的支持。
⑤ AddAntiForgery
主要是提供对 Multi-Tenancy 的支持。为不同的 Tenant 的 Antiforgery Cookie 设置的名称和路径。
⑥ AddAuthentication
主要是提供对 Multi-Tenancy 的支持。
⑦ AddDataProtection
主要是提供对 Multi-Tenancy 的支持。
2、AddMvc
AddMvc 主要作用是添加和 Mvc 相关的中间件。请注意这是 OrchardBuilder 而不是 IServiceCollection 的扩展方法。
类似的方法 AddNancy 用于提供对 Nancy 的支持。
3、UseOrchardCore
UseOrchardCore
是一个 IApplicationBuilder
的扩展方法,主要作用是添加中间件 ModularTenantContainerMiddleware 和 ModularTenantRouterMiddleware 。
1 |
namespace Microsoft.AspNetCore.Builder |
四、Multi-Tenancy
在 ModularTenantContainerMiddleware 中间件中,根据 Host 、Port 和 Path 的各种组合匹配不同的 Tenant 。Tenant 的激活延迟性的,在第一次请求 Tenant 才会激活。每个 Tenant 可以有不同的 DI 容器。
在 ModularTenantRouterMiddleware 中间件中,为当前 Tenant 配置单独的请求管道。
五、服务和中间件注册点
总结一下目前为止遇见的服务和中间件注册点。
1、服务注册点
包含名为 ConfigureServices 或类似的方法的接口和类:
类/接口 | 程序集 | 命名空间 | 备注 |
---|---|---|---|
IStartup | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 接口。 |
IStartup | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 接口。 |
Startup | 自定义 | 自定义 | 定义于应用。不继承任何接口或类,实现 Configure 和 ConfigureServices 等方法。 |
IWebHostBuilder | Microsoft.AspNetCore.Hosting.Abstractions | 接口。 | |
WebHostBuilder : IWebHostBuilder | Microsoft.AspNetCore.Hosting | Microsoft.AspNetCore.Hosting | ConfigureServices 不会进行实际的服务注册操作,当调用 Build 方法时才注册。 |
IStartup | OrchardCore.Modules.Abstractions | OrchardCore.Modules | 接口。 |
StartupBase: IStartup | OrchardCore.Modules.Abstractions | OrchardCore.Modules | 抽象类。 |
Startup: SetupBase | 自定义 | 自定义 | 定义于 OrchareCore 模块。 |
OrchardCoreBuilder | OrchardCore .Modules.Abstractions | Microsoft.Extensions.DependencyInjection | 注册 Tenant 服务和中间件。 |
StartupActions | OrchardCore.Modules.Abstractions | Microsoft.Extensions.DependencyInjection | 包含 ConfigureServicesActions 属性,而非方法。 |
2、中间件注册点
包含名为 Configure 或类似方法的接口和类:
类/接口 | 程序集 | 命名空间 | 备注 |
---|---|---|---|
IStartup | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 接口。 |
IHostingStartup | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 接口。 |
HostingStartup:IHostingStartup | 自定义 | 自定义 | 定义于应用。 |
Startup | 自定义 | 自定义 | 定义于应用。不继承任何接口或类,实现 Configure 和 ConfigureServices 等方法。 |
IWebHostBuilder | Microsoft.AspNetCore.Hosting.Abstractions | 接口。 | |
WebHostBuilder : IWebHostBuilder | Microsoft.AspNetCore.Hosting | Microsoft.AspNetCore.Hosting | Configure 不会进行实际的添加注册操作,当调用 Build 方法时才注册。 |
IStartup | OrchardCore.Modules.Abstractions | OrchardCore.Modules | 接口。 |
StartupBase: IStartup | OrchardCore.Modules.Abstractions | OrchardCore.Modules | 抽象类。 |
Startup: SetupBase | 自定义 | 自定义 | 定义于 OrchareCore 模块。 |
OrchardCoreBuilder | OrchardCore .Modules.Abstractions | Microsoft.Extensions.DependencyInjection | 注册 Tenant 服务和中间件。 |
StartupActions | OrchardCore.Modules.Abstractions | Microsoft.Extensions.DependencyInjection | 包含 ConfigureActions 属性,而非方法。 |
IStartupFilter | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 定义于应用或 OrchareCore 模块。需注册为服务。 |
参考资料
https://orchardcore.readthedocs.io/en/latest/
http://docs.orchardproject.net/en/latest/
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/startup?view=aspnetcore-2.1
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/host/platform-specific-configuration?view=aspnetcore-2.1
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/host/web-host?view=aspnetcore-2.1
排版问题:https://blog.tubumu.com/2018/11/26/orchardcore-modular-and-multi-tenancy/
OrchardCore 如何实现模块化( Modular )和 Multi-Tenancy的更多相关文章
- 模块化JavaScript设计模式(一)
在可扩展JavaScript的世界里,假设我们说一个应用程序是模块化(modular)的,那么通常意味着它是由一系列存储于模块中的高度解耦,不同的功能片段组成. 在可能的情况下.通过一处依赖性.松耦合 ...
- 【转载】Java 9 新特性——模块化
来自 <http://www.jianshu.com/p/053a5ca89bbb#> 前言 年,我们将迎来 Java 语言的 22 岁生日,22岁,对于一个人而言,正是开始大展鸿图的年纪 ...
- (三)学习了解OrchardCore笔记——灵魂中间件ModularTenantContainerMiddleware的第一行①的模块部分
了解到了OrchardCore主要由两个中间件(ModularTenantContainerMiddleware和ModularTenantRouterMiddleware)构成,下面开始了解Modu ...
- ABP文档 :Overall - Introduction
介绍 我们基于不同的需求创建不同的应用,但却在一次又一次地实现相同或相似的结构.至少在某种程度上,授权.验证.异常处理.日志.本地化.数据库连接管理.配置管理.审计日志属于通用的结构. 另外我们总是在 ...
- ABP框架系列之八:(Introduction-介绍)
Introduction We are creating different applications based on different needs. But implementing commo ...
- BlackArch-Tools
BlackArch-Tools 简介 安装在ArchLinux之上添加存储库从blackarch存储库安装工具替代安装方法BlackArch Linux Complete Tools List 简介 ...
- Phoenix综述(史上最全Phoenix中文文档)
个人主页:http://www.linbingdong.com 简书地址:http://www.jianshu.com/users/6cb45a00b49c/latest_articles 网上关于P ...
- Magicodes.WeiChat——多租户的设计与实现
概要 多租户(Multi Tenancy/Tenant)是一种软件架构,其定义是:在一台服务器上运行单个应用实例,它为多个租户提供服务. 本框架使用的是共享数据库.共享 Schema.共享数据表的数据 ...
- [Architect] ABP(现代ASP.NET样板开发框架) 翻译
所有翻译文档,将上传word文档至GitHub 本节目录: 简介 代码示例 支持的功能 GitHub 简介 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目) ...
随机推荐
- (模拟) codeVs1160 蛇形矩阵
题目描述 Description 小明玩一个数字游戏,取个n行n列数字矩阵(其中n为不超过100的奇数),数字的填补方法为:在矩阵中心从1开始以逆时针方向绕行,逐圈扩大,直到n行n列填满数字,请输出该 ...
- gzy的摄影梦
好像当身高180的摄影师啊. 记录一下素材,等自己有相机了,就一一实现. 竟然有人在看哎. 一袭红衣的女子,将手伸出窗外,开心的看这漫天大雪. 今天的晚霞,近处是蓝色,颜色逐渐变淡,一直延伸到远处.( ...
- 三、TensorFlow模型的保存和加载
1.模型的保存: import tensorflow as tf v1 = tf.Variable(1.0,dtype=tf.float32) v2 = tf.Variable(2.0,dtype=t ...
- Daily Codeforces 计划 训练时录
时间 场次 过题数目 补题数目 难易度 2019.4.7 codeforces-1141 ...
- 策略模式-Strategy(Java实现)
策略模式-Strategy 在策略模式中,一个类(策略使用者)可以更改自己的执行策略. 比如以排序算法为例子, 多种排序算法都归属于排序算法, 但是实现的算法细节不同, 使用者可以很轻松地替换策略, ...
- xls 打乱序列 -和给拼接字符串加上双引号
打乱 给空列 添加函数 =RAND() ,下拉,排序,即可打乱 添加双引号: =""""&C1&"""" ...
- “放到桌面”的Servlet实现
复习下Servlet下载文件, 当response把ContentType设置成application/xxxx的时候呢,浏览器会默认启动下载,而不是试图打开. 通过给httpHeader里面加入内容 ...
- 基于Python的Webservice开发(一)-简介
之前为了解决Webservice的开发,直接用Python自带的CGI模块挂在IIS上. 但是该方式开发Soap的接口,需要大量的开发,而且安全方面也存在很多问题. 我推荐关于用Python开发Web ...
- Java基础14-缓冲区字节流;File类
作业解析 阐述BufferedReader和BufferedWriter的工作原理, 是否缓冲区读写器的性能恒大于非缓冲区读写器的性能,为什么,请举例说明? 答: BufferedReader对Rea ...
- “Excel-建议不可用于您所选择的数据”错误提示
Excel作为一项的数据图表绘制软件,在工作中经常用到.“建议不可用于您所选择的数据这一错误提示,我也遇到了几次了,但是每次遇到总是忘了上次是什么原因.这次,写下博客记录.一般都是在复制网页或者其他数 ...