ToB企服应用市场:ToB评测及商务社交产业平台

标题: Spectre.Console.Cli注入服务的几种姿势 [打印本页]

作者: 鼠扑    时间: 2024-7-24 23:14
标题: Spectre.Console.Cli注入服务的几种姿势
Spectre.Console.NET步伐员大概都不陌生,写控制台步伐美化照旧不错的,支持着色,表格,图标等相当Nice,如果对这个库不认识我强烈推荐你相识一下,Spectre.Console.Cli作为Spectre.Console的子集,对于写一些CLI小工具照旧相当方便
本文重要讲讲 Spectre.Console.Cli的服务注入, TA是 Spectre.Console 库的一部门,用于创建命令行界面(CLI)应用步伐。TA提供了一个强大且易于利用的API来定义命令、参数和选项,同时支持 Spectre.Console 的丰富输出格式化功能。
一个官方极简的CLI例子,定义一个GreetCommand:
  1. public class GreetCommand : Command<GreetCommand.Settings>
  2. {
  3.     public class Settings : CommandSettings
  4.     {
  5.         [CommandArgument(0, "<name>")]
  6.         [Description("The name of the person to greet.")]
  7.         public string Name { get; set; }
  8.         [CommandOption("-r|--repeat <times>")]
  9.         [Description("The number of times to repeat the greeting.")]
  10.         [DefaultValue(1)]
  11.         public int Repeat { get; set; }
  12.     }
  13.     public override int Execute(CommandContext context, Settings settings)
  14.     {
  15.         for (int i = 0; i < settings.Repeat; i++)
  16.         {
  17.             Console.WriteLine($"Hello, {settings.Name}!");
  18.         }
  19.         return 0;
  20.     }
  21. }
复制代码
接下来,在步伐的入口点配置Command
  1. public class Program
  2. {
  3.     public static int Main(string[] args)
  4.     {
  5.         var app = new CommandApp();
  6.         app.Configure(config =>
  7.         {
  8.             config.AddCommand<GreetCommand>("greet");
  9.         });
  10.         return app.Run(args);
  11.     }
  12. }
复制代码
对于Spectre.Console.Cli的常规利用我这里不做过多介绍,感爱好的同砚可以移步官方文档,本文重要讲一下在CLI步伐中如何注入服务
那么我们必要在GreetCommand中注入服务应该怎么做呢? 好比下面的一个服务:
  1. public class HelloService(ILogger<HelloService> logger)
  2. {
  3.     public Task<string> SayHello(string name, int age)
  4.     {
  5.         //注入的logger
  6.         logger.LogInformation("SayHello called with name:{name},age:{age}", name, age);
  7.         return Task.FromResult($"Hello,My name is {name}, I`m {age} years old!");
  8.     }
  9. }
复制代码
实在Spectre.Console.Cli内置了最简单的方式,我们只必要在app.Configure中完成:
  1. var services = new ServiceCollection();
  2. //添加服务
  3. services.AddSingleton<HelloService>();
  4. //添加日志
  5. services.AddLogging(config =>
  6. {
  7.     config.AddConsole();
  8. });
  9. var sp = services.BuildServiceProvider();
  10. app.Configure(config =>
  11. {
  12.     //添加Commands
  13.     config.AddCommand<OneCommand>("one");
  14.     config.AddCommand<AnotherCommand>("another");
  15.     //注册Services
  16.     config.Settings.Registrar.RegisterInstance(sp.GetRequiredService<HelloService>());
  17. });
复制代码
注册的服务就可以直接利用了:
  1. public class HelloCommand(HelloService helloService) : AsyncCommand<HelloCommand.HelloSettings>
  2. {
  3.     private readonly HelloService _helloService = helloService;
  4.     public class HelloSettings : CommandSettings
  5.     {
  6.         [CommandArgument(0, "<name>")]
  7.         [Description("The target to say hello to.")]
  8.         public string Name { get; set; } = null!;
  9.     }
  10.     public override async Task<int> ExecuteAsync(CommandContext context, HelloSettings settings)
  11.     {
  12.         var message = await _helloService.SayHello(settings.Name, settings.Age);
  13.         AnsiConsole.MarkupLine($"[blue]{message}[/]");
  14.         return 0;
  15.     }
  16. }
复制代码
另外的一个注入方式是实现ITypeRegistrar,官方提供MSDI的用例,自己也可以实现Autofac等其他DI,下面是MSDI的实现:
  1. namespace Infrastructure
  2. {
  3.     public sealed class MsDITypeRegistrar(IServiceCollection services) : ITypeRegistrar
  4.     {
  5.         private readonly IServiceCollection _services =
  6.             services ?? throw new ArgumentNullException(nameof(services));
  7.         public ITypeResolver Build()
  8.         {
  9.             return new TypeResolver(_services.BuildServiceProvider());
  10.         }
  11.         public void Register(Type service, Type implementation)
  12.         {
  13.             _services.AddSingleton(service, implementation);
  14.         }
  15.         public void RegisterInstance(Type service, object implementation)
  16.         {
  17.             _services.AddSingleton(service, implementation);
  18.         }
  19.         public void RegisterLazy(Type service, Func<object> factory)
  20.         {
  21.             _services.AddSingleton(service, (provider) => factory());
  22.         }
  23.     }
  24.     internal sealed class TypeResolver(IServiceProvider provider) : ITypeResolver
  25.     {
  26.         public object? Resolve(Type? type)
  27.         {
  28.             if (provider is null || type is null)
  29.             {
  30.                 return null;
  31.             }
  32.             return ActivatorUtilities.GetServiceOrCreateInstance(provider, type);
  33.         }
  34.     }
  35. }
复制代码
利用的话只必要实例化CommandApp时候传入MsDITypeRegistrar即可:
  1. var services = new ServiceCollection();
  2. //添加服务...
  3. var app = new CommandApp(new MsDITypeRegistrar(services));
  4. app.Configure(config =>
  5. {
  6.    //...
  7. });
  8. return app.Run(args);
复制代码
除了上面的方式,我们实在还可以利用ICommandInterceptor切面的方式来完成注入的操纵:

下面我们定义一个AutoDIAttribute特性,实现一个AutoDIInterceptor的拦截器,后者重要给标记了AutoDI的属性服务赋值
  1. [AttributeUsage(AttributeTargets.Property, Inherited = true)]
  2. public class AutoDIAttribute : Attribute{ }
  3. /// <summary>
  4. /// 自动注入的拦截器
  5. /// </summary>
  6. internal class AutoDIInterceptor(IServiceProvider serviceProvider) : ICommandInterceptor
  7. {
  8.     public void Intercept(CommandContext context, CommandSettings settings)
  9.     {
  10.         var type = settings.GetType();
  11.         var properties = type.GetProperties();
  12.         foreach (var property in properties)
  13.         {
  14.             var isAutoInject = property.GetCustomAttributes<AutoDIAttribute>(true).Any();
  15.             if (isAutoInject)
  16.             {
  17.                 var service = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider, property.PropertyType);
  18.                 property.SetValue(settings, service);
  19.             }
  20.         }
  21.     }
  22. }
复制代码
接下来在CommandSettings中标记必要自动注入服务的属性,如下面的HelloService:
  1. internal class AutoDICommand : AsyncCommand<AutoDICommand.AnotherInjectSettings>
  2. {
  3.     public class AnotherInjectSettings : CommandSettings
  4.     {
  5.         /// <summary>
  6.         /// 使用切面装载的服务
  7.         /// </summary>
  8.         [AutoDI]
  9.         public HelloService HelloService { get; set; } = null!;
  10.         [Description("user name")]
  11.         [DefaultValue("vipwan"), CommandOption("-n|--name")]
  12.         public string Name { get; set; } = null!;
  13.         [Description("user age")]
  14.         [DefaultValue(12), CommandOption("-a|--age")]
  15.         public int Age { get; set; }
  16.     }
  17.     public override async Task<int> ExecuteAsync(CommandContext context, AnotherInjectSettings settings)
  18.     {
  19.         var message = await settings.HelloService.SayHello(settings.Name, settings.Age);
  20.         AnsiConsole.MarkupLine($"[green]{message}[/]");
  21.         return 0;
  22.     }
  23. }
复制代码
然后在app.Configure中利用AutoDIInterceptor切面:
  1. var services = new ServiceCollection();
  2. //添加服务
  3. services.AddSingleton<HelloService>();
  4. var sp = services.BuildServiceProvider();
  5. app.Configure(config =>
  6. {
  7.     //设置自动注入的拦截器
  8.     config.SetInterceptor(new AutoDIInterceptor(sp));
  9.     config.AddCommand<AutoDICommand>("di");
  10.     //...
  11. });
复制代码
然后测试运行步伐:
  1. dotnet run -- di -n "vipwan"
复制代码
大功告成:

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4