Spectre.Console-處理依賴註入

来源:https://www.cnblogs.com/podolski/archive/2023/06/01/17450308.html
-Advertisement-
Play Games

## 引言 之前說的做自動記錄 Todo 執行過程中消耗的時間的[Todo 項目]( https://github.com/circler3/TodoTrack ),由於想持續保持程式執行,就放棄了 `Spectre.Console.Cli`,後來隨著命令越來越多,自己處理覺得很是麻煩,想了想要不試 ...


引言

之前說的做自動記錄 Todo 執行過程中消耗的時間的Todo 項目,由於想持續保持程式執行,就放棄了 Spectre.Console.Cli,後來隨著命令越來越多,自己處理覺得很是麻煩,想了想要不試試怎麼將這個東西嵌入程式,然後手動傳遞參數?

本文完整代碼可以從項目中獲取。

說乾就乾,研究了一下,發現核心的 CommandApp 並不需要獨占的控制台,我們可以隨時 new,參數直接將 ReadLine() 獲得的參數傳遞 args 就可以了。

await _commandApp.RunAsync(cmd.Split(' '));

依賴註入問題

        static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();

        }
        public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            services.AddSingleton<TodoHolder>();
            services.AddHostedService<TodoCommandService>();
            services.AddCommandApp();
        });

最後一個是拓展方法:


internal static IServiceCollection AddCommandApp(this IServiceCollection services)
{
	return services.AddSingleton(w =>
	{
		var app = new CommandApp();
		app.Configure(config =>
		{
			config.CaseSensitivity(CaseSensitivity.None);
			config.AddBranch<MethodSettings>("del", del =>
			{
				del.SetDefaultCommand<DelCommand<TodoItem>>();
				del.AddCommand<DelCommand<TodoItem>>("todo");
				del.AddCommand<DelCommand<Project>>("pro");
				del.AddCommand<DelCommand<Tag>>("tag");
			});
		
		}
		return app;
	}
}

一切顯得非常美好,但是棘手的問題就來了。Spectre.Console.Cli 自帶依賴註入功能,會自動管理 Command 中的依賴關係,如果我們的 Command 需要依賴外部的類,那麼需要在 Spectre.Console.Cli 中註冊才能正常工作。但是這個東西也不自帶註冊器,我們在外部 DI 中註冊的 TodoHolder 並沒有什麼用。

放棄 Host

雖然 Spectre.Console.Cli 不提供註冊的辦法,但是提供了一個構造函數,支持接受一個 ITypeRegistrar 作為參數,直接傳遞 IServiceCollection 就可以,這樣在外部註冊的類就傳遞進去了註冊系統。官方提供了這個兩個類的實現示例:

using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;

namespace TodoTrack.Cli
{
    public sealed class TypeRegistrar : ITypeRegistrar
    {
        private readonly IServiceCollection _builder;

        public TypeRegistrar(IServiceCollection builder)
        {
            _builder = builder;
        }

        public ITypeResolver Build()
        {
            return new TypeResolver(_builder.BuildServiceProvider());
        }

        public void Register(Type service, Type implementation)
        {
            _builder.AddSingleton(service, implementation);
        }

        public void RegisterInstance(Type service, object implementation)
        {
            _builder.AddSingleton(service, implementation);
        }

        public void RegisterLazy(Type service, Func<object> func)
        {
            if (func is null)
            {
                throw new ArgumentNullException(nameof(func));
            }

            _builder.AddSingleton(service, (provider) => func());
        }
    }
}

using Spectre.Console.Cli;

namespace TodoTrack.Cli
{

    public sealed class TypeResolver : ITypeResolver, IDisposable
    {
        private readonly IServiceProvider _provider;

        public TypeResolver(IServiceProvider provider)
        {
            _provider = provider ?? throw new ArgumentNullException(nameof(provider));
        }

        public object? Resolve(Type? type)
        {
            if (type == null)
            {
                return null;
            }

            return _provider.GetService(type);
        }

        public void Dispose()
        {
            if (_provider is IDisposable disposable)
            {
                disposable.Dispose();
            }
        }
    }
}

CommandApp 的初始化語句還得改成這個形式:

    public static int Main(string[] args)
    {
        // Create a type registrar and register any dependencies.
        // A type registrar is an adapter for a DI framework.
        var registrations = new ServiceCollection();
        registrations.AddSingleton<IGreeter, HelloWorldGreeter>();
        var registrar = new TypeRegistrar(registrations);

        // Create a new command app with the registrar
        // and run it with the provided arguments.
        var app = new CommandApp<DefaultCommand>(registrar);
        return app.Run(args);
    }

這種方法放棄了 Host 創建 HostedService,依賴註入的行為會由 TypeRegistrarTypeResolver 控制。

修改註冊器行為

由於 Spectre.Console.Cli 是依照 CLI 工具設計的,這類工具往往執行一次就自動退出返回控制台。因此它的註冊器會在每次調用時重新創建 IServiceProvider,如果直接將其改成多次執行,我們會發現所有對象都會重新初始化一遍,和 AddSingleton 之類的行為不同。

修改註冊器行為,將其作為一個長期運行的單例執行,這樣我們可以繼續使用拓展方法註冊,並註入到 HostedService 中。

        public void Dispose()
        {
            //if (_provider is IDisposable disposable)
            //{
               // disposable.Dispose();
            //}
        }
        private ITypeResolver _typeResolver;

        public ITypeResolver Build()
        {
            return _typeResolver ??= new TypeResolver(_builder.BuildServiceProvider());
        }

這種方式下,外部的 DI 無法識別 CommandApp 內部註冊的 Command 對象,使用時需要小心。

參考


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

-Advertisement-
Play Games
更多相關文章
  • # 1. 前言 最近有點時間,就隨便找點東西弄弄,倒也碰到了一些問題,在此記錄下 # 2. 環境 Python3.11.3 + selenium4.9.1 + opencv4.7 + PyAutoGUI0.9.54 + windows11 # 3. 開始 ## 3.1 賬號密碼輸入 ![image] ...
  • 有時候我們使用python自動化框架的時候,打開一個網頁的時候,它會出現出線這一種登錄框,我們f12檢查不了,用開發者工具強制檢查裡面沒有任何屬性. 那這時候我們就可以用到python第三方庫:pyautogui PyAutoGUI:是一個Python庫,可用於自動化GUI(圖形用戶界面)程式的任務 ...
  • 本文介紹基於**Python**中**ArcPy**模塊,對大量柵格遙感影像文件**批量**進行**無效值**(**NoData**值)填充的方法。 在處理柵格圖像文件時,我們經常會遇到圖像中存在有無效值(即**NoData**值)的情況。如下圖所示,這裡有一個**矢量面要素圖層**和該矢量圖層範圍 ...
  • 我們在初學 Java 編程的時候,總是感覺很枯燥乏味,想著做點可以交互的小系統,可以讓用戶自由輸入,系統可以接收做出反映。這就要介紹一下 Java 中的 Scanner 類了。 ...
  • 通過python來開發web應用,可以產簡化了web開發的流程,功能和函數庫也是非常豐富,我們也是開箱即用,目前比較流程的WEB框架就是Flask和django。 > 根據2020年JetBrains Python開發人員調查,Django和Flask是迄今為止最受歡迎的兩個Python Web框架 ...
  • 在今年2月14日的時候,Keycloak 團隊宣佈他們正在棄用大多數 Keycloak 適配器。其中包括Spring Security和Spring Boot的適配器,這意味著今後Keycloak團隊將不再提供針對Spring Security和Spring Boot的集成方案。但是,如此強大的Ke ...
  • # 1.初識列表(list) **列表由一系列按特定順序排列的數據元素組成**。可以將任何類型數據元素加入列表中,其中的數據元素之間沒有任何關係。鑒於列表通常包含多個數據元素,給列表指定一個表示覆數的名稱是個不錯的選擇。 在python中,用方括弧[]來表示列表,並用逗號來分隔其中的數據元素。編寫程 ...
  • # 出錯信息 ``` 我使用flask時,運行後出現下麵的錯誤:Traceback (most recent call last): File "D:/github/lind-python/test-web.py", line 1, in from flask import Flask File " ...
一周排行
    -Advertisement-
    Play Games
  • 前言 插件化的需求主要源於對軟體架構靈活性的追求,特別是在開發大型、複雜或需要不斷更新的軟體系統時,插件化可以提高軟體系統的可擴展性、可定製性、隔離性、安全性、可維護性、模塊化、易於升級和更新以及支持第三方開發等方面的能力,從而滿足不斷變化的業務需求和技術挑戰。 一、插件化探索 在WPF中我們想要開 ...
  • 歡迎ReaLTaiizor是一個用戶友好的、以設計為中心的.NET WinForms項目控制項庫,包含廣泛的組件。您可以使用不同的主題選項對項目進行個性化設置,並自定義用戶控制項,以使您的應用程式更加專業。 項目地址:https://github.com/Taiizor/ReaLTaiizor 步驟1: ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • Channel 是乾什麼的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consume ...
  • efcore如何優雅的實現按年分庫按月分表 介紹 本文ShardinfCore版本 本期主角: ShardingCore 一款ef-core下高性能、輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學習成本、零業務代碼入侵適配 距離上次發文.net相關的已經有很久了,期間一直在從事java相關的 ...
  • 前言 Spacesniffer 是一個免費的文件掃描工具,通過使用樹狀圖可視化佈局,可以立即瞭解大文件夾的位置,幫助用戶處理找到這些文件夾 當前系統C盤空間 清理後系統C盤空間 下載 Spacesniffer 下載地址:https://spacesniffer.en.softonic.com/dow ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • 一、ReZero簡介 ReZero是一款.NET中間件 : 全網唯一開源界面操作就能生成API , 可以集成到任何.NET6+ API項目,無破壞性,也可讓非.NET用戶使用exe文件 免費開源:MIT最寬鬆協議 , 一直從事開源事業十年,一直堅持開源 1.1 純ReZero開發 適合.Net Co ...
  • 一:背景 1. 講故事 停了一個月沒有更新文章了,主要是忙於寫 C#內功修煉系列的PPT,現在基本上接近尾聲,可以回頭繼續更新這段時間分析dump的一些事故報告,有朋友微信上找到我,說他們的系統出現了大量的http超時,程式不響應處理了,讓我幫忙看下怎麼回事,dump也抓到了。 二:WinDbg分析 ...
  • 開始做項目管理了(本人3年java,來到這邊之後真沒想到...),天天開會溝通整理需求,他們講話的時候忙裡偷閑整理一下常用的方法,其實語言還是有共通性的,基本上看到方法名就大概能猜出來用法。出去打水的時候看到外面太陽好好,真想在外面坐著曬太陽,回來的時候好兄弟三年前送給我的鍵盤D鍵不靈了,在打"等待 ...