Util應用框架核心(三) - 服務註冊器

何鎮汐發表於2023-10-31

本節介紹服務註冊器的開發.

如果你不需要擴充套件Util應用框架,直接跳過.

當你把某些功能封裝到自己的類庫,並希望啟動時自動執行初始化程式碼進行配置時,定義服務註冊器.

服務註冊器概述

服務註冊器是Util專案啟動時自動執行的程式碼塊.

Util應用框架的某些類庫使用服務註冊器進行配置,比如 Util.ObjectMapping.AutoMapper .

當你引用 Util.ObjectMapping.AutoMapper 類庫,不需要進行任何配置就可以直接使用 AutoMapper 的基本功能.

定義服務註冊器

服務註冊器只需實現 Util.Infrastructure.IServiceRegistrar 介面,啟動器會掃描查詢它.

你可以把服務註冊器放在任何地方,不過放在約定的位置會更容易維護.

約定: 服務註冊器名稱以 ServiceRegistrar 結尾,放到 Infrastructure 目錄中.

IServiceRegistrar 介面程式碼

/// <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 方法之前禁用服務註冊器.

相關文章