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

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

如果你不需要擴展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 方法之前禁用服務註冊器.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章