Spectre.Console
.NET程序員可能都不陌生,寫控制臺(tái)程序美化還是不錯(cuò)的,支持著色,表格,圖標(biāo)等相當(dāng)Nice,如果對(duì)這個(gè)庫(kù)不熟悉我強(qiáng)烈推薦你了解一下,Spectre.Console.Cli
作為Spectre.Console的子集,對(duì)于寫一些CLI小工具還是相當(dāng)方便
本文主要講講 Spectre.Console.Cli
的服務(wù)注入, TA是 Spectre.Console 庫(kù)的一部分,用于創(chuàng)建命令行界面(CLI)應(yīng)用程序。TA提供了一個(gè)強(qiáng)大且易于使用的API來(lái)定義命令、參數(shù)和選項(xiàng),同時(shí)支持 Spectre.Console 的豐富輸出格式化功能。
一個(gè)官方極簡(jiǎn)的CLI例子,定義一個(gè)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;
}
}
接下來(lái),在程序的入口點(diǎn)配置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);
}
}
對(duì)于Spectre.Console.Cli的常規(guī)使用我這里不做過(guò)多介紹,感興趣的同學(xué)可以移步官方文檔,本文主要講一下在CLI程序中如何注入服務(wù)
那么我們需要在GreetCommand中注入服務(wù)應(yīng)該怎么做呢? 比如下面的一個(gè)服務(wù):
public class HelloService(ILogger<HelloService> logger)
{
public Task<string> SayHello(string name, int age)
{
logger.LogInformation("SayHello called with name:{name},age:{age}", name, age);
return Task.FromResult($"Hello,My name is {name}, I`m {age} years old!");
}
}
其實(shí)Spectre.Console.Cli
內(nèi)置了最簡(jiǎn)單的方式,我們只需要在app.Configure
中完成:
var services = new ServiceCollection();
services.AddSingleton<HelloService>();
services.AddLogging(config =>
{
config.AddConsole();
});
var sp = services.BuildServiceProvider();
app.Configure(config =>
{
config.AddCommand<OneCommand>("one");
config.AddCommand<AnotherCommand>("another");
config.Settings.Registrar.RegisterInstance(sp.GetRequiredService<HelloService>());
});
注冊(cè)的服務(wù)就可以直接使用了:
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;
}
}
另外的一個(gè)注入方式是實(shí)現(xiàn)ITypeRegistrar
,官方提供MSDI的用例,自己也可以實(shí)現(xiàn)Autofac
等其他DI,下面是MSDI的實(shí)現(xiàn):
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);
}
}
}
使用的話只需要實(shí)例化CommandApp
時(shí)候傳入MsDITypeRegistrar即可:
var services = new ServiceCollection();
var app = new CommandApp(new MsDITypeRegistrar(services));
app.Configure(config =>
{
});
return app.Run(args);
除了上面的方式,我們其實(shí)還可以使用ICommandInterceptor
切面的方式來(lái)完成注入的操作:
下面我們定義一個(gè)AutoDIAttribute
特性,實(shí)現(xiàn)一個(gè)AutoDIInterceptor
的攔截器,后者主要給標(biāo)記了AutoDI
的屬性服務(wù)賦值
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
public class AutoDIAttribute : Attribute{ }
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);
}
}
}
}
接下來(lái)在CommandSettings中標(biāo)記需要自動(dòng)注入服務(wù)的屬性,如下面的HelloService
:
internal class AutoDICommand : AsyncCommand<AutoDICommand.AnotherInjectSettings>
{
public class AnotherInjectSettings : CommandSettings
{
[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");
});
然后測(cè)試運(yùn)行程序:
dotnet run -- di -n "vipwan"
大功告成:
轉(zhuǎn)自https://www.cnblogs.com/vipwan/p/18321432
該文章在 2024/10/8 15:41:54 編輯過(guò)