為了了解應用程式是如何配置和初始化,本文將探討ASP.NET Core和ABP框架最基本的構建模塊,我們將從 ASP.NET Core 的
Startup類開始了解為什么我們需要模塊化系統,以及 ABP 如何提供模塊化方式來配置和初始化應用程式,然后我們將探索 ASP.NET Core 的依賴注入,以及ABP是如何使用預定義規則(predefined rules)自動進行依賴注入,最后,我們將了解 ASP.NET Core 的配置和選項框架,以及其他類別庫,
以下是本文的所有主題:
- 了解模塊化
- 使用依賴注入系統
- 配置應用程式
- 實作選項模式
- 日志系統
一、了解模塊化
模塊化是一種將大型軟體按功能分解為更小的部分,并允許每個部分通過標準化介面進行通信,模塊化有以下主要好處:
- 模塊按規則進行隔離后,大大降低了系統復雜性,
- 模塊之間松散耦合,提供了更大的靈活性,因為模塊是可組裝、可替換的,
- 因為模塊是獨立的,所以它允許跨應用被重用,
大多數企業的軟體被設計成模塊化,但是,實作模塊化并不容易,ABP 框架的主要目標之一是為模塊化提供基礎設施和工具,我們將在后面詳細介紹模塊化開發,本節只介紹 ABP 模塊的基礎知識,
Startup 類
在定義ABP的模塊之前,建議先熟悉 ASP.NET Core 中的StartUp類,我們看下ASP.NET Core 的Startup類:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddTransient<MyService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
ConfigureServices方法用于配置服務并將新服務注冊到依賴注入系統,另一方面,Configure方法用于配置 ASP.NET Core 管道中間件,用于處理 HTTP 請求,
在應用程式啟動之前,我們需要在Program.cs中配置Startup類:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
這個Startup類是獨一無二的,我們只有一個點來配置和初始化所有的服務,但是,在模塊化應用程式中,我們希望每個模塊都能獨立配置和初始化與該模塊相關的服務,此外,一個模塊通常需要使用或依賴于其他模塊,因此模塊配置順序和初始化就非常重要了,我們來看下 ABP 的模塊是如何定義的
模塊定義
ABP 模塊是一組型別(比如類或介面),它們一同開發一同交付的,它是一個程式集(一般來說是Visual Studio 中的一個專案),派生自AbpModule,模塊類負責配置和初始化,并在必要時配置依賴模塊,
下面是一個短信發送模塊的簡單定義:
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace SmsSending
{
public class SmsSendingModule : AbpModule
{
public override void ConfigureServices(
ServiceConfigurationContext context)
{
context.Services.AddTransient<SmsService>();
}
}
}
每個模塊都可以重寫ConfigureServices方法,以便將其服務注冊到依賴注入系統,此示例中的SmsService服務被注冊為瞬態生命周期,該示例和上面Startup類似,但是,大多時候,您不需要手動注冊服務,這要歸功ABP 框架的按約定注冊系統,
OnApplicationInitialization方法用在服務注冊完成后,并且在應用準備就緒后執行,使用此方法,您可以在應用啟動時執行任何操作,例如,您可以初始化一個服務:
public class SmsSendingModule : AbpModule
{
//...
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var service = context.ServiceProvider.GetRequiredService<SmsService>();
service.Initialize();
}
}
這里,我們使用context.ServiceProvider從依賴注入系統請求并初始化服務,可見,此時服務已經完成注冊,
您也可以將
OnApplicationInitialization方法等同于Startup類的Configure方法,
您可以在此處構建 ASP.NET Core 請求管道,但是,通常我們會在啟動模塊中配置請求管道,如下一節所述,
模塊依賴和啟動模塊
一個業務應用通常由多個模塊組成,ABP 框架允許您宣告模塊之間的依賴關系,一個應用必須要有一個啟動模塊,啟動模塊可以依賴于其他模塊,其他模塊可以再依賴于其他模塊,以此類推,
下圖是一個簡單的模塊依賴關系圖:

如果所示,如果模塊 A 依賴于模塊 B,則模塊 B 總是在模塊 A 之前初始化,這允許模塊 A 使用、設定、更改或覆寫模塊 B 定義的配置和服務,
對于示例圖,模塊初始化的順序應該是:G、F、E、D、B、C、A,
您不必知道確切的初始化順序;只需要知道如果你的模塊依賴于模塊xx,那么模塊xx在你的模塊之前被初始化,
ABP使用[DependsOn](屬性宣告)方式來定義模塊依賴:
[DependsOn(typeof(ModuleB), typeof(ModuleC))]
public class ModuleA : AbpModule
{
}
這里,ModuleA通過[DependsOn]依賴于ModuleB和ModuleC,
本例中,啟動模塊ModuleA負責設定ASP.NET Core 的請求管道:
[DependsOn(typeof(ModuleB), typeof(ModuleC))]
public class ModuleA : AbpModule
{
//...
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
app.UseRouting();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
[代碼塊和之前ASP.NET Core的 Startup類 創建請求管道相同,context.GetApplicationBuilder()和context.GetEnvironment()用于從依賴注入中獲IApplicationBuilder和IWebHostEnvironment服務,
最后,我們在Startup里將ASP.NET Core 和 ABP 框架進行集成:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<ModuleA>();
}
public void Configure(IApplicationBuilder app)
{
app.InitializeApplication();
}
}
services.AddApplication()方法由 ABP 框架定義,用于ABP的模塊配置,它按順序執行了所有模塊的ConfigureServices方法,而app.InitializeApplication()方法也是由 ABP 框架定義,它也是按照模塊依賴的順序來執行所有模塊的OnApplicationInitialization方法,
ConfigureServices和OnApplicationInitialization方法是模塊類中最常用的方法,
模塊生命周期
AbpModule中定義的生命周期方法,除了上面看到的ConfigureServices和OnApplicationInitialization,下面羅列其他生命周期相關方法:
PreConfigureServices: 這個方法在ConfigureServices方法之前被呼叫,它允許您配置服務之前執行的代碼,ConfigureServices:這是配置模塊和注冊服務的主要方法,PostConfigureServices: 該方法在ConfigureServices之后呼叫(包括依賴于您模塊的模塊),這里可以配置服務后執行的代碼,OnPreApplicationInitialization: 這個方法在OnApplicationInitialization之前被呼叫,在這個階段,您可以從依賴注入中決議服務,因為服務已經被初始化,OnApplicationInitialization:此方法用來配置 ASP.NET Core 請求管道并初始化您的服務,OnPostApplicationInitialization: 這個方法在初始化階段后被呼叫,OnApplicationShutdown:您可以根據需要自己實作模塊的關閉邏輯,
帶Pre…和Post…前綴的方法與原始方法具有相同的目的,它們提供了一種在模塊之前或之后執行的一些配置/初始化代碼,一般情況下我們很少使用到,
異步生命周期方法
本節介紹的生命周期方法是同步的,在撰寫本書時,ABP 框架團隊正努力在 框架 5.1 版本引入異步生命周期方法,
如前所述,模塊類主要包含注冊和配置與該模塊相關的服務的代碼,在下一節中,我們將介紹如何使用 ABP 框架注冊服務,
二、使用依賴注入系統
.NET 原生依賴注入
依賴注入是一種獲取類的依賴的技術,它將創建類與使用該類分開,
假設我們有一個UserRegistrationService類,它呼叫SmsService類來發送驗證短信,如下:
public class UserRegistrationService
{
private readonly SmsService _smsService;
public UserRegistrationService(SmsService smsService)
{
_smsService = smsService;
}
public async Task RegisterAsync(
string username,
string password,
string phoneNumber)
{
//...save user in the database
await _smsService.SendAsync(
phoneNumber,
"Your verification code: 1234"
);
}
}
這里的SmsService使用建構式注入來獲取實體,也就是說,依賴注入系統會自動幫我們實體化類的依賴項,并將它們賦值給我們的_smsService,
注意:ABP采用的是ASP.NET Core原生的依賴注入框架,他自己并沒有發明依賴注入框架,
在設計服務時,我們還要考慮另外一件重要的事情:服務生命周期,
ASP.NET Core 為服務注冊提供了三個生命周期選項:
- Transient(瞬態):每次您請求/注入服務時,都會創建一個新實體,
- Scoped(范圍): 通常這由請求生命周期來評估,您只有在同一范圍內才能共享相同的實體,
- Singleton(單例):在應用內有且僅有一個實體,所有請求都使用相同的實體,該物件在第一次請求創建,
以下模塊注冊了兩個服務,一個是瞬態的,另一個是單例的:
public class MyModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<ISmsService, SmsService>();
context.Services.AddSingleton<OtherService>();
}
}
context.Services的型別是IServiceCollection,它是一個擴展方法,
在第一個示例中使用介面注冊,第二個示例使用參考類注冊為單例,
ABP的依賴注入
使用 ABP 框架時,您不必考慮服務注冊,這要歸功于 ABP 框架獨特的服務注冊系統,
1.約定式注冊
在 ASP.NET Core 中,所有服務需要顯式注冊到IServiceCollection,如上一節所示,這些注冊大多重復,完全可以自動化操作,
ABP 對于以下型別采用自動注冊:
- MVC controllers
- Razor page models
- View components
- Razor components
- SignalR hubs
- Application services
- Domain services
- Repositories
以上型別均使用瞬態生命周期自動注冊,如果您還有別的型別,可以考慮介面注冊,
2.介面注冊
您可以實作以下三種介面來注冊:
ITransientDependencyIScopedDependencyISingletonDependency
例如,在下面代碼塊中,我們將服務注冊為單例:
public class UserPermissionCache : ISingletonDependency
{ }
介面注冊很容易并且是推薦的方式,但與下面的屬性注冊相比,它有一定的局限性,
3.屬性注冊
屬性注冊更精細,下面是和屬性注冊相關的配置引數
Lifetime(enum): 服務的生命周期,包括Singleton,Transient和ScopedTryRegister(bool):僅當服務尚未注冊時才注冊ReplaceServices(bool):如果服務已經注冊,則替換之前的注冊
示例代碼:
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace UserManagement
{
[Dependency(ServiceLifetime.Transient, TryRegister = true)]
public class UserPermissionCache
{ }
}
4.介面屬性混合注冊
屬性介面一起使用,如果屬性定義了屬性,屬性比介面優先級更高,
如果一個類可能被注入不同的類或介面,具體取決于暴露的型別,
暴露服務
當一個類沒有實作介面時,只能通過類參考注入,上一節中的UserPermissionCache類就是通過注入類參考來使用的,
假設我們有一個抽象 SMS 發送的介面:
public interface ISmsService
{
Task SendAsync(string phoneNumber, string message);
}
假設您要ISmsService實作 Azure 服務:
public class AzureSmsService : ISmsService, ITransientDependency
{
public async Task SendAsync(string phoneNumber, string message)
{
//TODO: ...
}
}
這里的AzureSmsService實作了ISmsService和ITransientDependency兩個介面,而ITransientDependency介面才是用于自動注冊到依賴注入中的,這里的注入主要通過命名約定來實作,因為AzureSmsService以SmsService作為后綴結尾,
我們再舉一個通過命名約定的例子,假設我們有一個實作多個介面的類:
public class PdfExporter: IExporter, IPdfExporter, ICanExport, ITransientDependency
{ }
PdfExporter服務可以通過注入IPdfExporter和IExporter介面來使用,也可以直接注入PdfExporter類參考來使用,但是,您不能使用ICanExport介面注入它,因為名稱PdfExporter不以CanExport為后綴,
一旦您使用該ExposeServices屬性來暴露服務,如以下代碼塊所示:
[ExposeServices(typeof(IPdfExporter))]
public class PdfExporter: IExporter, IPdfExporter, ICanExport, ITransientDependency
{ }
現在,您只能通過注入IPdfExporter介面來使用PdfExporter類,
我應該為每個服務定義介面嗎?
ABP 不會強迫你這么做,但是通用介面來定義是最佳實踐:如果你想松散地耦合你的服務,比如,在單元測驗中可以輕松模擬測驗資料,
這就是為什么我們將介面與實作物理分離(例如,我們在專案中定義Application.Contracts介面,并在Application專案中實作它們,或者在領域層中定義存盤庫介面,在基礎設施層中實作它們),
我們已經了解了如何注冊和消費服務,另外,某些服務具有選項配置,您需要在使用它們之前對其進行配置,接下來的兩節將展開介紹,
待續
文章有點長,下篇將繼續介紹ABP的配置和選項模式,感謝你的閱讀,
希望以上分享對你有所幫助,感謝您的捧場,作者: 張飛洪[廈門]
QQ群: 共享交流群
我的: 知識星球(VIP) 打賞支持
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/475210.html
標籤:.NET Core
上一篇:20年專業鍋爐資料采集鍋爐遠程監控鍋爐遠程控制鍋爐溫度采集鍋爐壓力采集
下一篇:探索ABP基礎架構
