OrchardCore 如何實現模塊化( Modular )和 Multi-Tenancy

来源:https://www.cnblogs.com/alby/archive/2018/11/26/orchardcore-modular-and-multi-tenancy.html
-Advertisement-
Play Games

OrchardCore 通過將服務和中間件放在不同的程式集以支持模塊化。各個模塊提供類似於 ConfigureServices 和 Configure 的方法供運行時調用。 ...


 

一、概述

通常我們會在 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-TenancyTenant 有如下特性:

  1. 多個 Tenant 運行在同一個應用程式域中,每個 Tenant 幾乎可以看做是獨立的網站;
  2. 根據 Host 、Port 和 Path 的各種組合匹配不同的 Tenant(ModularTenantContainerMiddleware);
  3. 延遲激活,第一次請求 Tenant 才會激活(ModularTenantContainerMiddleware);
  4. 每個 Tenant 有不同的 DI 容器(ModularTenantContainerMiddleware);
  5. 每個 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 註冊這些服務和中間件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/// <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 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 嗯,這是本人的第一篇隨筆,就從最簡單的單例模式開始,一步一步地記錄自己的成長。 單例模式是最常見的設計模式之一,在項目代碼中幾乎隨處可見。這個設計模式的目的就是為了保證實例只能存在一個。單例模式往下還能再細分為懶漢模式和餓漢模式。下麵逐個來看。 1.餓漢模式 餓漢模式的做法是在類載入的時候就完成實例 ...
  • 我的網站的圖片不想被公開瀏覽、下載、盜鏈怎麼辦?本文主要通過解讀一下ASP.NET Core對於靜態文件的處理方式的相關源碼,來看一下為什麼是wwwroot文件夾,如何修改或新增一個靜態文件夾,為什麼新增的文件夾名字不會被當做controller處理?訪問授權怎麼做? 一、靜態文件夾 所謂靜態文件, ...
  •  寫在前面 上篇文章我們講瞭如在在實際項目開發中使用Git來進行代碼的版本控制,當然介紹的都是比較常用的功能。今天我再帶著大家一起熟悉下一個ORM框架Dapper,實例代碼的演示編寫完成後我會通過Git命令上傳到GitHub上,正好大家可以再次熟悉下Git命令的使用,來鞏固上篇文章的知識。本篇文章 ...
  • 首先使用Nugut安裝NLog, NLog.Extensions.Logging,using NLog.Web,並且加上配置文件 ”nlog.config“,配置文件內容網上都可以百度的到。這是我自己的: 創建表: CREATE TABLE `sys_log` ( `Id` int(11) NOT ...
  • C#我只是一個萌新,由於搞過Java,還是可以看懂C#的 偶然間得到賽車游戲Extreme Drift的源碼 接下來我會花一段時間來解讀,這是一個我學習的過程,記錄在博客 等到我完全解讀之後,我也許會考慮再加入聯機功能等 當然,這個游戲用的是Unity引擎 首先,我先展示一下這個游戲的效果: 選車: ...
  • 非泛型集合的類和介面位於System.Collections命名空間 如:列表、隊列、位數組、哈希表和字典的集合 ArrayList 動態數組 可被單獨索引的對象的有序集合可以使用索引在指定的位置添加和移除項目,動態數組會自動重新調整它的大小允許在列表中進行動態記憶體分配、增加、搜索、排序 Capac ...
  • 前幾天我寫了一個UWP圖片裁剪控制項ImageCropper( "開源地址" ),自認為算是現階段UWP社區里最好用的圖片裁剪控制項了,今天就來分享下我編碼的過程。 為什麼又要造輪子 因為開發需要,我們需要使用一個圖片裁剪控制項來編輯用戶上傳的圖片。本著儘量不重覆造輪子的原則,我找了下現在UWP生態圈裡可 ...
  • 找了幾種繪製圖表的辦法,後面選定了ECharts 首先,從NuGet管理中添加ECharts包,然後就可以調用繪製圖表啦! 基本步驟: 1.為ECharts準備一個具備大小(寬高)的Dom 2.ECharts的js文件引入 3.js中為模塊載入器配置echarts和所需圖表的路徑 require.c ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...