本節介紹服務註冊器的開發.
如果你不需要擴充套件Util應用框架,直接跳過.
當你把某些功能封裝到自己的類庫,並希望啟動時自動執行初始化程式碼進行設定時,定義服務註冊器.
服務註冊器是Util專案啟動時自動執行的程式碼塊.
Util應用框架的某些類庫使用服務註冊器進行設定,比如 Util.ObjectMapping.AutoMapper .
當你參照 Util.ObjectMapping.AutoMapper 類庫,不需要進行任何設定就可以直接使用 AutoMapper 的基本功能.
服務註冊器只需實現 Util.Infrastructure.IServiceRegistrar 介面,啟動器會掃描查詢它.
你可以把服務註冊器放在任何地方,不過放在約定的位置會更容易維護.
約定: 服務註冊器名稱以 ServiceRegistrar 結尾,放到 Infrastructure 目錄中.
/// <summary>
/// 服務註冊器
/// </summary>
public interface IServiceRegistrar {
/// <summary>
/// 排序號
/// </summary>
int OrderId { get; }
/// <summary>
/// 是否啟用
/// </summary>
bool Enabled {
get;
}
/// <summary>
/// 註冊服務,該操作在啟動開始時執行,如果需要延遲執行某些操作,可在返回的Action中執行,它將在啟動最後執行
/// </summary>
/// <param name="context">服務上下文</param>
Action Register( ServiceContext context );
}
/// <summary>
/// 服務上下文
/// </summary>
public class ServiceContext {
/// <summary>
/// 初始化服務上下文
/// </summary>
/// <param name="hostBuilder">主機生成器</param>
/// <param name="assemblyFinder">程式集查詢器</param>
/// <param name="typeFinder">型別查詢器</param>
public ServiceContext( IHostBuilder hostBuilder, IAssemblyFinder assemblyFinder, ITypeFinder typeFinder ) {
HostBuilder = hostBuilder ?? throw new ArgumentNullException( nameof( hostBuilder ) );
AssemblyFinder = assemblyFinder ?? throw new ArgumentNullException( nameof( assemblyFinder ) );
TypeFinder = typeFinder ?? throw new ArgumentNullException( nameof( typeFinder ) );
}
/// <summary>
/// 主機生成器
/// </summary>
public IHostBuilder HostBuilder { get; }
/// <summary>
/// 程式集查詢器
/// </summary>
public IAssemblyFinder AssemblyFinder { get; }
/// <summary>
/// 型別查詢器
/// </summary>
public ITypeFinder TypeFinder { get; }
}
Register 方法用於定義服務設定.
Register 方法傳遞服務上下文 ServiceContext 引數,除了可以獲得主機生成器 IHostBuilder 外,還可以獲取型別查詢器 ITypeFinder.
你可以在服務註冊器中查詢某些型別,並進行設定,比如查詢 ISingletonDependency 介面的實現類,並設定單例依賴關係.
Register 方法的返回型別是一個委託 Action.
如果希望服務設定方法立即執行,返回 null.
如果希望服務註冊器延遲到最後執行,返回委託.
只有已啟用的服務註冊器才會生效 .
通常服務註冊器用來設定服務的依賴注入關係,執行順序不太重要.
不過如果你的服務註冊器依賴執行順序,需要在某些服務註冊器的前面或後面執行,可以設定排序號 OrderId.
啟動器使用排序號對服務註冊器進行排序, OrderId 越小的先執行.
此外,如果你需要將服務註冊器延遲到最後執行,可以讓 Register 方法返回委託.
啟動器在執行服務註冊器 Register 方法後儲存返回的委託範例列表.
在執行委託範例之前,啟動器可能會執行其它操作.
在之前的版本會設定依賴註冊器,不過它的作用有限,已經刪除,未來可能新增其它操作.
當參照包含服務註冊器的類庫,啟動時會自動執行初始化設定程式碼.
對於大部分場景都是預期行為.
不過有些時候自動執行服務註冊器會導致問題,你需要禁用它.
服務註冊器設定 Util.Infrastructure.ServiceRegistrarConfig 用於禁用和啟用服務註冊器.
ServiceRegistrarConfig 使用 AppContext.SetSwitch 方法來完成禁用和啟用.
/// <summary>
/// 服務註冊器設定
/// </summary>
public class ServiceRegistrarConfig {
/// <summary>
/// 服務註冊器設定範例
/// </summary>
public static readonly ServiceRegistrarConfig Instance = new ();
/// <summary>
/// 禁用服務註冊器
/// </summary>
/// <param name="serviceName">服務註冊器名稱</param>
public static void Disable( string serviceName ) {
AppContext.SetSwitch( serviceName, false );
}
/// <summary>
/// 啟用服務註冊器
/// </summary>
/// <param name="serviceName">服務註冊器名稱</param>
public static void Enable( string serviceName ) {
AppContext.SetSwitch( serviceName, true );
}
/// <summary>
/// 是否啟用
/// </summary>
/// <param name="serviceName">服務註冊器名稱</param>
public static bool IsEnabled( string serviceName ) {
var result = AppContext.TryGetSwitch( serviceName, out bool isEnabled );
if ( result && isEnabled == false )
return false;
return true;
}
}
直接傳遞服務名稱不太方便,可以在 ServiceRegistrarConfig 上定義擴充套件方法來禁用和啟用服務註冊器.
下面以 Util.ObjectMapping.AutoMapper 類庫的服務註冊器為例進行說明.
在服務註冊器定義服務名稱,一般使用帶名稱空間的服務註冊器類名.
設定排序號.
Enabled屬性使用 ServiceRegistrarConfig 設定的 IsEnabled 方法,預設為啟用狀態,可以通過 ServiceRegistrarConfig 禁用它.
Register 方法使用型別查詢器查詢所有實現了 IAutoMapperConfig 介面的設定範例並進行設定.
建立 AutoMapper ObjectMapper 物件,並傳遞給 MapTo 擴充套件類.
最後,通過主機生成器 ConfigureServices 設定 IObjectMapper 單例服務.
/// <summary>
/// AutoMapper服務註冊器
/// </summary>
public class AutoMapperServiceRegistrar : IServiceRegistrar {
/// <summary>
/// 獲取服務名
/// </summary>
public static string ServiceName => "Util.ObjectMapping.Infrastructure.AutoMapperServiceRegistrar";
/// <summary>
/// 排序號
/// </summary>
public int OrderId => 300;
/// <summary>
/// 是否啟用
/// </summary>
public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName );
/// <summary>
/// 註冊服務
/// </summary>
/// <param name="serviceContext">服務上下文</param>
public Action Register( ServiceContext serviceContext ) {
var types = serviceContext.TypeFinder.Find<IAutoMapperConfig>();
var instances = types.Select( type => Reflection.CreateInstance<IAutoMapperConfig>( type ) ).ToList();
var expression = new MapperConfigurationExpression();
instances.ForEach( t => t.Config( expression ) );
var mapper = new ObjectMapper( expression );
ObjectMapperExtensions.SetMapper( mapper );
serviceContext.HostBuilder.ConfigureServices( ( context, services ) => {
services.AddSingleton<IObjectMapper>( mapper );
} );
return null;
}
}
在 ServiceRegistrarConfig 擴充套件特定的啟用和禁用方法,並封裝服務名.
/// <summary>
/// AutoMapper服務註冊器設定擴充套件
/// </summary>
public static class ServiceRegistrarConfigExtensions {
/// <summary>
/// 啟用AutoMapper服務註冊器
/// </summary>
/// <param name="config">服務註冊器設定</param>
public static ServiceRegistrarConfig EnableAutoMapperServiceRegistrar( this ServiceRegistrarConfig config ) {
ServiceRegistrarConfig.Enable( AutoMapperServiceRegistrar.ServiceName );
return config;
}
/// <summary>
///禁用AutoMapper服務註冊器
/// </summary>
/// <param name="config">服務註冊器設定</param>
public static ServiceRegistrarConfig DisableAutoMapperServiceRegistrar( this ServiceRegistrarConfig config ) {
ServiceRegistrarConfig.Disable( AutoMapperServiceRegistrar.ServiceName );
return config;
}
}
下面演示禁用 Util.ObjectMapping.Infrastructure.AutoMapperServiceRegistrar 服務註冊器.
ServiceRegistrarConfig.Instance.DisableAutoMapperServiceRegistrar();
builder.AsBuild().AddUtil();
應在 AddUtil 方法之前禁用服務註冊器.