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?文件中,比如:
| using OrchardCore.Modules.Manifest; [assembly: Module( ?Name = "XML-RPC", ?Author = "The Orchard Team", ?Website = "http://orchardproject.net", ?Version = "2.0.0" )] [assembly: Feature( ?Id = "OrchardCore.XmlRpc", ?Name = "XML-RPC", ?Description = "The XML-RPC module enables creation of contents from client applications such as Open Live Writer.", ?Category = "Infrastructure" )] [assembly: Feature( ?Id = "OrchardCore.RemotePublishing", ?Name = "Remote Publishing", ?Description = "The remote publishing feature enables creation of contents from client applications such as Open Live Writer.", ?Dependencies = new [] { "OrchardCore.XmlRpc" }, ?Category = "Infrastructure" )] |
在運行時可將 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 注冊這些服務和中間件。
| /// <summary> /// Adds OrchardCore services to the host service collection. /// </summary> public static OrchardCoreBuilder AddOrchardCore(this IServiceCollection services) { ? ?// If an instance of OrchardCoreBuilder exists reuse it, ? ?// so we can call AddOrchardCore several times. ? ?var builder = services ? ? ? ?.LastOrDefault(d => d.ServiceType == typeof(OrchardCoreBuilder))? ? ? ? ?.ImplementationInstance as OrchardCoreBuilder; ? ?if (builder == null) ? ?{ ? ? ? ?builder = new OrchardCoreBuilder(services); ? ? ? ?services.AddSingleton(builder); ? ? ? ?AddDefaultServices(services); ? ? ? ?AddShellServices(services); ? ? ? ?AddExtensionServices(builder); ? ? ? ?AddStaticFiles(builder); ? ? ? ?AddAntiForgery(builder); ? ? ? ?AddAuthentication(builder); ? ? ? ?AddDataProtection(builder); ? ? ? ?// Register the list of services to be resolved later on ? ? ? ?services.AddSingleton(services); ? ?} ? ?return builder; } |
① 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 。
| namespace Microsoft.AspNetCore.Builder { ?public static class ApplicationBuilderExtensions ?{ ? ? ? /// <summary> ? ? ? /// Enables multi-tenant requests support for the current path. ? ? ? /// </summary> ? ? ? public static IApplicationBuilder UseOrchardCore(this IApplicationBuilder app, Action<IApplicationBuilder> configure = null) ? ? ? { ? ? ? ? ? var env = app.ApplicationServices.GetRequiredService<IHostingEnvironment>(); ? ? ? ? ? var appContext = app.ApplicationServices.GetRequiredService<IApplicationContext>(); ? ? ? ? ? env.ContentRootFileProvider = new CompositeFileProvider( ? ? ? ? ? ? ? new ModuleEmbeddedFileProvider(appContext), ? ? ? ? ? ? ? env.ContentRootFileProvider); ? ? ? ? ? app.UseMiddleware<PoweredByMiddleware>(); ? ? ? ? ? // Ensure the shell tenants are loaded when a request comes in ? ? ? ? ? // and replaces the current service provider for the tenant's one. ? ? ? ? ? app.UseMiddleware<ModularTenantContainerMiddleware>(); ? ? ? ? ? configure?.Invoke(app); ? ? ? ? ? app.UseMiddleware<ModularTenantRouterMiddleware>(); ? ? ? ? ? return app; ? ? ? } ?} } |
四、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://www.cnblogs.com/alby/p/orchardcore-modular-and-multi-tenancy.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的OrchardCore 如何实现模块化( Modular )和 Multi-Tenancy的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core 实战:将 .N
- 下一篇: 【.NET Core项目实战-统一认证平