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);
}
}

那么我们需要在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"

大功告成:

24.cnblogs.com/blog/127598/202407/127598-20240724180421239-1298412615.png)

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

Spectre.Console.Cli注入服务的几种姿势的更多相关文章

  1. Angular服务的5种创建方式

    config配置块 Angular应用的运行主要分为两部分:app.config()和app.run(),config是你设置任何的provider的阶段,从而使应用可以使用正确的服务,需要注意的是在 ...

  2. 介绍Angular的注入服务

    其实angular的注入服务是挺复杂的,目前看源码也只看懂了一半,为了不误导大家,我也不讲敢讲太复杂,怕自己都理解错了. 首先我们要知道angular的三种注入方式: 第一种:inference va ...

  3. angularJS自定义服务的几种方式

    在angularJS中定义服务共有四种常见的方式:factory,service,provider,constant,value 使用形式的不同: 1)factory以返回对象的形式定义服务: mya ...

  4. 【aspnetcore】在过滤器(Filter)中使用注入服务(ServiceFilter|TypeFilter)

    在MVC中,AOP是很常用的功能,我们经常会使用如 ActionFilter,IAuthorizeFilter 等描述对Controller和Action进行约束和扩展,一般做法如下: public ...

  5. ASPNETCOREAPI 跨域处理 SQL 语句拼接 多条件分页查询 ASPNET CORE 核心 通过依赖注入(注入服务)

    ASPNETCOREAPI 跨域处理 AspNetCoreApi 跨域处理 如果咱们有处理过MV5 跨域问题这个问题也不大. (1)为什么会出现跨域问题:  浏览器安全限制了前端脚本跨站点的访问资源, ...

  6. .net core 注入中的三种模式:Singleton、Scoped 和 Transient

    从上篇内容不如题的文章<.net core 并发下的线程安全问题>扩展认识.net core注入中的三种模式:Singleton.Scoped 和 Transient 我们都知道在 Sta ...

  7. 避免在ASP.NET Core 3.0中为启动类注入服务

    本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇. Part 1 - 将.NET Standard 2.0类库转换为.NET Core 3.0类库 Part 2 - IHostingE ...

  8. .NET 使用自带 DI 批量注入服务(Service)和 后台服务(BackgroundService)

    今天教大家如何在asp .net core 和 .net 控制台程序中 批量注入服务和 BackgroundService 后台服务 在默认的 .net 项目中如果我们注入一个服务或者后台服务,常规的 ...

  9. springboot+dubbo之多端口注入服务

    前面介绍了,springboot+dubbo基础整合,这篇介绍多端口注入服务. springboot使用@Bean注入dubbo服务,当你是单一的ProviderConfig实例,dubbo的@Ser ...

  10. Guice 学习(六)使用Provider注入服务( Provider Inject Service)

    1.定义接口 package com.guice.providerInject; import com.google.inject.ProvidedBy; public interface Servi ...

随机推荐

  1. 【终极指南】使用Python可视化分析文本情感倾向

    本文分享自华为云社区<Python理解文本情感倾向的终极指南>,作者: 柠檬味拥抱. 情感分析是一种通过自然语言处理技术来识别.提取和量化文本中的情感倾向的方法.Python在这一领域有着 ...

  2. 支持表格识别,PaddleOCRSharp最新发布

    PaddleOCRSharp 2.3.0已经发布nuget包. 项目开源地址:https://gitee.com/raoyutian/paddle-ocrsharp 2.3.0更新内容: 1.增加表格 ...

  3. 开源云原生平台对比 KubeSphere vs Rainbond

    最近因为工作需要,需要找一个功能完善的云原生应用平台,经过自己筛选和朋友推荐,剩下 KubeSphere和Rainbond ,这两个产品都是基于 Kubernetes 之上构建的云原生应用平台,功能都 ...

  4. 拼多多面试:Netty如何解决粘包问题?

    粘包和拆包问题也叫做粘包和半包问题,它是指在数据传输时,接收方未能正常读取到一条完整数据的情况(只读取了部分数据,或多读取到了另一条数据的情况)就叫做粘包或拆包问题. 从严格意义上来说,粘包问题和拆包 ...

  5. k8s——kubctl命令基础

    语法 kubevtl [command] [type] [name] [flags] command: 指定要对一个或多个资源执行的操作,例如,`create`,`get`,`describe`,`d ...

  6. Java异常中throw 与throws的区别

    throw 与 throws区别 在Java中,throws和throw是两个不同的关键字,它们在异常处理中起着不同的作用. throws关键字: throws用于声明一个方法可能会抛出的异常.当一个 ...

  7. nfs笔记整理

    NFS---共享存储系统 #network file system 网络文件系统 ​ #NFS主要使用在局域网下,让不同的主机之间可以共享文件.或者目录数据.主要用于linux系统上实现文件共享的一种 ...

  8. 《Android开发卷——HTTP网络通信,HTTP网络连接》

    为了访问互联网,需要设置应用程序获取"androd.permission.INTERNET"权限的许可. 一.使用Apache接口(org.apache.http)并实现网络连接的 ...

  9. 增补博客 第三篇 python 英文统计

    编写程序实现对特定英文文章(文本文件)的单词数和有效行数的统计,其中要求空行不计数: def count_words_and_lines(file_path): word_count = 0 line ...

  10. 为什么https要使用证书

    为什么https要使用证书 什么是httpshttps不是一种新的协议,只是http的通信接口部分使用了ssl和tsl协议替代,加入了加密.证书.完整性保护的功能. 加密:共享密钥加密加密和解密公用一 ...