一、概述
通常我们会在 Startup
类通过 void ConfigureServices(IServiceCollection services)
配置应用的服务。常见的形如 AddXXX
的方法,实际上调用的都是 IServiceCollection
或直接说是 ServiceCollection
的 AddSingleton
、AddTransient
和 AddScoped
等方法。调用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
程序集的程序集,可以有各自的配置
、选项
、服务
和中间件
等,还可以有各自的路由
、视图
、控制器
、 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 容器中或注册中间件,而是将注册的方式通过委托保存在集合中(通过 StartupActions
)。这样做的目的是为了将来给每个 Tenant
注册这些服务和中间件。
1 | /// <summary> |
① AddDefaultServices
添加默认服务,比如 Logging
、Localization
和 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 | 接口。 实现类定义于应用。 配合 IWebHostBuilder.UseStartup 方法。 |
Startup | 自定义 | 自定义 | 定义于应用。不继承任何接口或类,实现 Configure 和 ConfigureServices 等方法。 配合 IWebHostBuilder.UseStartup 方法。 |
IWebHostBuilder | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 接口。 |
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 | 接口。实现类定义于应用。 配合 IWebHostBuilder.UseStartup 方法。 |
IHostingStartup | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 接口。 |
Startup | 自定义 | 自定义 | 定义于应用。不继承任何接口或类,实现 Configure 和 ConfigureServices 等方法。 配合 IWebHostBuilder.UseStartup 方法。 |
IWebHostBuilder | Microsoft.AspNetCore.Hosting.Abstractions | Microsoft.AspNetCore.Hosting | 接口。提供该接口的 Configure 扩展方法。 |
WebHostBuilderExtensions | Microsoft.AspNetCore.Hosting | Microsoft.AspNetCore.Hosting | Configure 是 IWebHostBuilder 的扩展方法。 |
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