首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

Spectre.Console.Cli注入服务的几种姿势

编程知识
2024年07月24日 17:16

Spectre.Console大家可能都不陌生,写控制台程序美化还是不错的,支持着色,表格,图标等相当nice,如果对这个库不熟悉我强烈推荐你了解一下,对于写一些CLI小工具还是相当方便的, 本文主要讲讲 Spectre.Console.Cli的服务注入, TA是 Spectre.Console 库的一部分,用于创建命令行界面(CLI)应用程序。它提供了一个强大且易于使用的API来定义命令、参数和选项,同时支持 Spectre.Console 的丰富输出格式化功能。

一个官方极简的例子,定义一个GreetCommand:

public class GreetCommand : Command<GreetCommand.Settings>
{
    public class Settings : CommandSettings
    {
        [CommandArgument(0, "<name>")]
        [Description("The name of the person to greet.")]
        public string Name { get; set; }

        [CommandOption("-r|--repeat <times>")]
        [Description("The number of times to repeat the greeting.")]
        [DefaultValue(1)]
        public int Repeat { get; set; }
    }
    public override int Execute(CommandContext context, Settings settings)
    {
        for (int i = 0; i < settings.Repeat; i++)
        {
            Console.WriteLine($"Hello, {settings.Name}!");
        }
        return 0;
    }
}

接下来,在程序的入口点配置Command

public class Program
{
    public static int Main(string[] args)
    {
        var app = new CommandApp();
        app.Configure(config =>
        {
            config.AddCommand<GreetCommand>("greet");
        });
        return app.Run(args);
    }
}

对于Spectre.Console.Cli的常规使用我这里不做过多介绍,感兴趣的同学可以移步官方文档,本文主要讲一下在CLI程序中如何注入服务

那么我们需要在GreetCommand中注入服务应该怎么做呢? 比如下面的一个服务:

public class HelloService(ILogger<HelloService> logger)
{
    public Task<string> SayHello(string name, int age)
    {
        //注入的logger
        logger.LogInformation("SayHello called with name:{name},age:{age}", name, age);
        return Task.FromResult($"Hello,My name is {name}, I`m {age} years old!");
    }
}

其实Spectre.Console.Cli内置了最简单的方式,我们只需要在app.Configure中完成:

var services = new ServiceCollection();
//添加服务
services.AddSingleton<HelloService>();
//添加日志
services.AddLogging(config =>
{
    config.AddConsole();
});
var sp = services.BuildServiceProvider();

app.Configure(config =>
{
    //添加Commands
    config.AddCommand<OneCommand>("one");
    config.AddCommand<AnotherCommand>("another");
    //注册Services
    config.Settings.Registrar.RegisterInstance(sp.GetRequiredService<HelloService>());
});

注册的服务就可以直接使用了:

public class HelloCommand(HelloService helloService) : AsyncCommand<HelloCommand.HelloSettings>
{
    private readonly HelloService _helloService = helloService;
    public class HelloSettings : CommandSettings
    {
        [CommandArgument(0, "<name>")]
        [Description("The target to say hello to.")]
        public string Name { get; set; } = null!;
    }
    public override async Task<int> ExecuteAsync(CommandContext context, HelloSettings settings)
    {
        var message = await _helloService.SayHello(settings.Name, settings.Age);
        AnsiConsole.MarkupLine($"[blue]{message}[/]");
        return 0;
    }
}

另外的一个注入方式是实现ITypeRegistrar,官方提供MSDI的用例,自己也可以实现Autofac等其他DI,下面是MSDI的实现:

namespace Infrastructure
{
    public sealed class MsDITypeRegistrar(IServiceCollection services) : ITypeRegistrar
    {
        private readonly IServiceCollection _services =
            services ?? throw new ArgumentNullException(nameof(services));
        public ITypeResolver Build()
        {
            return new TypeResolver(_services.BuildServiceProvider());
        }
        public void Register(Type service, Type implementation)
        {
            _services.AddSingleton(service, implementation);
        }
        public void RegisterInstance(Type service, object implementation)
        {
            _services.AddSingleton(service, implementation);
        }
        public void RegisterLazy(Type service, Func<object> factory)
        {
            _services.AddSingleton(service, (provider) => factory());
        }
    }
    internal sealed class TypeResolver(IServiceProvider provider) : ITypeResolver
    {
        public object? Resolve(Type? type)
        {
            if (provider is null || type is null)
            {
                return null;
            }
            return ActivatorUtilities.GetServiceOrCreateInstance(provider, type);
        }
    }
}

使用的话只需要实例化CommandApp时候传入MsDITypeRegistrar即可:

var services = new ServiceCollection();
//添加服务...

var app = new CommandApp(new MsDITypeRegistrar(services));
app.Configure(config =>
{
   //...
});
return app.Run(args);

除了上面的方式,我们其实还可以使用ICommandInterceptor切面的方式来完成注入的操作:

下面我们定义一个AutoDIAttribute特性,实现一个AutoDIInterceptor的拦截器,后者主要给标记了AutoDI的属性服务赋值

[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class AutoDIAttribute : Attribute{ }

/// <summary>
/// 自动注入的拦截器
/// </summary>
internal class AutoDIInterceptor(IServiceProvider serviceProvider) : ICommandInterceptor
{
    public void Intercept(CommandContext context, CommandSettings settings)
    {
        var type = settings.GetType();
        var properties = type.GetProperties();
        foreach (var property in properties)
        {
            var isAutoInject = property.GetCustomAttributes<AutoDIAttribute>(true).Any();
            if (isAutoInject)
            {
                var service = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, property.PropertyType);
                property.SetValue(settings, service);
            }
        }
    }
}

接下来在CommandSettings中标记需要自动注入服务的属性,如下面的HelloService:

internal class AutoDICommand : AsyncCommand<AutoDICommand.AnotherInjectSettings>
{
    public class AnotherInjectSettings : CommandSettings
    {
        /// <summary>
        /// 使用切面装载的服务
        /// </summary>
        [AutoDI]
        public HelloService HelloService { get; set; } = null!;

        [Description("user name")]
        [DefaultValue("vipwan"), CommandOption("-n|--name")]
        public string Name { get; set; } = null!;

        [Description("user age")]
        [DefaultValue(12), CommandOption("-a|--age")]
        public int Age { get; set; }
    }

    public override async Task<int> ExecuteAsync(CommandContext context, AnotherInjectSettings settings)
    {
        var message = await settings.HelloService.SayHello(settings.Name, settings.Age);
        AnsiConsole.MarkupLine($"[green]{message}[/]");
        return 0;
    }
}

然后在app.Configure中使用AutoDIInterceptor切面:

var services = new ServiceCollection();
//添加服务
services.AddSingleton<HelloService>();
var sp = services.BuildServiceProvider();

app.Configure(config =>
{
    //设置自动注入的拦截器
    config.SetInterceptor(new AutoDIInterceptor(sp));
    config.AddCommand<AutoDICommand>("di");
    //...
});

然后测试运行程序:

dotnet run -- di -n "vipwan"

大功告成:
image

以上就介绍了几种在Spectre.Console.Cli注入服务的方式,当然没有最优的只有最适合自己的,如果代码存在不足,或者你有更好的建议 欢迎留言交流!

From:https://www.cnblogs.com/vipwan/p/18321432
本文地址: http://www.shuzixingkong.net/article/385
0评论
提交 加载更多评论
其他文章 Nacos 高级详解:提升你的开发和部署效率
Nacos 高级 一 、服务集群 需求 服务提供者搭建集群 服务调用者,依次显示集群中各服务的信息 搭建 修改服务提供方的controller,打印服务端端口号 package com.czxy.controller; import org.springframework.web.bind.anno
Nacos 高级详解:提升你的开发和部署效率 Nacos 高级详解:提升你的开发和部署效率 Nacos 高级详解:提升你的开发和部署效率
Centos7下安装配置最新版本Jenkins(2.452.3)
1、基础环境配置 1.1 服务器下载Jenkins安装包 下载地址:https://www.jenkins.io/download/ 下载命令:wget https://get.jenkins.io/war-stable/2.452.3/jenkins.war 1.2 服务器安装配置JDK Jenk
Centos7下安装配置最新版本Jenkins(2.452.3) Centos7下安装配置最新版本Jenkins(2.452.3) Centos7下安装配置最新版本Jenkins(2.452.3)
AI知识库这事儿FastGPT是专业的
在搭建AI知识库这事儿上,有不少成熟的框架,我推荐使用FastGPT。这篇文章笔者就使用过的两款平台做个比较,FastGPT和百度千帆平台。
AI知识库这事儿FastGPT是专业的 AI知识库这事儿FastGPT是专业的 AI知识库这事儿FastGPT是专业的
2个月搞定计算机二级C语言——真题(1)解析
1. 前言 大家好,我是梁国庆。 这段时间将持续发布计算机二级 C 语言真题的解析,想要同步练习,需要资源包的朋友可以跳转免费获取——《3个月搞定计算机二级C语言——准备工作》。 现在恐怕要改为 2 个月搞定计算机二级 C 语言了,不过没有关系,干就完了! 本篇博客将解析计算机二级 C 语言考试真题
2个月搞定计算机二级C语言——真题(1)解析 2个月搞定计算机二级C语言——真题(1)解析 2个月搞定计算机二级C语言——真题(1)解析
我可以写代码写到退休吗?记录我的10年前端技术之旅
希望通过分享我个人的经历,给技术人一点信心和方向,原来一直做技术也可以做十年,写代码不仅是我赖以谋生的手段,更是一种生活方式,通过写代码我认识很多志同道合的朋友,在写代码的路上,我也在欣赏和探索这世界
我可以写代码写到退休吗?记录我的10年前端技术之旅 我可以写代码写到退休吗?记录我的10年前端技术之旅 我可以写代码写到退休吗?记录我的10年前端技术之旅
[WPF] 脱机环境实现支持拼音模糊搜索的AutoCompleteBox
AutoCompleteBox是一个常见的提高输入效率的组件,很多WPF的第三方控件库都提供了这个组件,但基本都是字符串的子串匹配,不支持拼音模糊匹配,例如无法通过输入ldh或liudehua匹配到刘德华。要实现拼音模糊搜索功能,通常会采用分词、数据库等技术对待匹配数据集进行预处理。某些场景受制于条
[WPF] 脱机环境实现支持拼音模糊搜索的AutoCompleteBox
记录荒废了三年的四年.net开发的第二次面试(进复试了)
这次面试的是小公司,深圳计通智能,面试分为初试和复试。使用腾讯视频会议完成。相比与上次面试,这次有所进步,进复试了。当然,这可能也与面试风格有关。这次面试着重与项目经历和技术,因此回答比较顺畅。 这一周干了什么 我先是研究了上次面试没回答出来,或者回答得不好的技术问题。然后顺着简历上的技术,又复习了
第十二节 JMeter基础-中级地址信息【IF控制器】
文章介绍了在JMeter中核对收货地址信息的操作流程,并深入探讨了JMeter中的IF控制器、日志等组件的使用。特别强调了Groovy和Jexl3在表达式语言上的区别,以及它们在Java平台上的应用场景和语法特性。
第十二节 JMeter基础-中级地址信息【IF控制器】 第十二节 JMeter基础-中级地址信息【IF控制器】 第十二节 JMeter基础-中级地址信息【IF控制器】