we may not be unfamiliar, write the console program beautification or good, support for coloring, tables, icons, etc. quite nice, if this library is not familiar with I strongly recommend that you learn about it, for the writing of a number of CLI gadgets or quite convenient, this article is mainly about the
service injection, TA is part of a library for creating command line interface (CLI) applications. It provides a powerful and easy-to-use API for defining commands, parameters, and options, as well as support for rich output formatting.
For an official minimalist example, define a GreetCommand.
public class GreetCommand : Command<>
{
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 < ; i++)
{
($"Hello, {}!");
}
return 0;
}
}
Next, configure Command at the entry point of the program
public class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
(config =>
{
<GreetCommand>("greet");
});
return (args);
}
}
So what should we do if we need to inject a service into GreetCommand? For example, a service like the following.
public class HelloService(ILogger<HelloService> logger)
{
public Task<string> SayHello(string name, int age)
{
//infusedlogger
("SayHello called with name:{name},age:{age}", name, age);
return ($"Hello,My name is {name}, I`m {age} years old!");
}
}
indeedThe easiest way to do this is built in, we just need to add the
Done in.
var services = new ServiceCollection();
//Add Service
<HelloService>();
//Add Log
(config =>
{
();
});
var sp = ();
(config =>
{
//increaseCommands
<OneCommand>("one");
<AnotherCommand>("another");
//enrollmentServices
(<HelloService>());
});
Registered services can then be used directly:.
public class HelloCommand(HelloService helloService) : AsyncCommand<>
{
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(, );
($"[blue]{message}[/]");
return 0;
}
}
Another way to inject is to implement theITypeRegistrar
,Official MSDI use casesYou can do it yourself.Autofac
Other DIs, such as MSDI, are implemented as follows.
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 (provider, type);
}
}
}
To use it, you only need to instantiate theCommandApp
Just pass in MsDITypeRegistrar when.
var services = new ServiceCollection();
//Add Service...
var app = new CommandApp(new MsDITypeRegistrar(services));
(config =>
{
//...
});
return (args);
In addition to the above, we can actually use theICommandInterceptor
The injection is accomplished by means of a cutout: the
Below we define aAutoDIAttribute
feature, which implements aAutoDIInterceptor
interceptor, the latter mainly gives a chance to tag theAutoDI
The attribute service assignment of the
[AttributeUsage(, Inherited = true)]
public class AutoDIAttribute : Attribute{ }
/// <summary>
/// Interceptors for automatic injection
/// </summary>
internal class AutoDIInterceptor(IServiceProvider serviceProvider) : ICommandInterceptor
{
public void Intercept(CommandContext context, CommandSettings settings)
{
var type = ();
var properties = ();
foreach (var property in properties)
{
var isAutoInject = <AutoDIAttribute>(true).Any();
if (isAutoInject)
{
var service = (serviceProvider, );
(settings, service);
}
}
}
}
Next, mark the properties of the service that need to be injected automatically in the CommandSettings, as followsHelloService
:
internal class AutoDICommand : AsyncCommand<>
{
public class AnotherInjectSettings : CommandSettings
{
/// <summary>
/// Services loaded using facets
/// </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 (, );
($"[green]{message}[/]");
return 0;
}
}
after thathit the nail on the head
AutoDIInterceptor
Sections.
var services = new ServiceCollection();
//Add Service
<HelloService>();
var sp = ();
(config =>
{
//Setting up interceptors for automatic injection
(new AutoDIInterceptor(sp));
<AutoDICommand>("di");
//...
});
Then test run the program.
dotnet run -- di -n "vipwan"
It's done.
/blog/127598/202407/)
The above describes a few of the ways in whichinjected into the service, of course, there is no optimal only the most suitable for their own, if there are shortcomings in the code, or you have a better proposal Welcome to leave a message to exchange!