Spectre.Console-处理依赖注入
引言
之前说的做自动记录 Todo 执行过程中消耗的时间的Todo 项目,由于想持续保持程序执行,就放弃了 Spectre.Console.Cli
,后来随着命令越来越多,自己处理觉得很是麻烦,想了想要不试试怎么将这个东西嵌入程序,然后手动传递参数?
本文完整代码可以从项目中获取。
说干就干,研究了一下,发现核心的 CommandApp
并不需要独占的控制台,我们可以随时 new,参数直接将 ReadLine()
获得的参数传递 args 就可以了。
await _commandApp.RunAsync(cmd.Split(' '));
依赖注入问题
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<TodoHolder>();
services.AddHostedService<TodoCommandService>();
services.AddCommandApp();
});
最后一个是拓展方法:
internal static IServiceCollection AddCommandApp(this IServiceCollection services)
{
return services.AddSingleton(w =>
{
var app = new CommandApp();
app.Configure(config =>
{
config.CaseSensitivity(CaseSensitivity.None);
config.AddBranch<MethodSettings>("del", del =>
{
del.SetDefaultCommand<DelCommand<TodoItem>>();
del.AddCommand<DelCommand<TodoItem>>("todo");
del.AddCommand<DelCommand<Project>>("pro");
del.AddCommand<DelCommand<Tag>>("tag");
});
}
return app;
}
}
一切显得非常美好,但是棘手的问题就来了。Spectre.Console.Cli
自带依赖注入功能,会自动管理 Command 中的依赖关系,如果我们的 Command 需要依赖外部的类,那么需要在 Spectre.Console.Cli
中注册才能正常工作。但是这个东西也不自带注册器,我们在外部 DI 中注册的 TodoHolder
并没有什么用。
放弃 Host
虽然 Spectre.Console.Cli
不提供注册的办法,但是提供了一个构造函数,支持接受一个 ITypeRegistrar
作为参数,直接传递 IServiceCollection
就可以,这样在外部注册的类就传递进去了注册系统。官方提供了这个两个类的实现示例:
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace TodoTrack.Cli
{
public sealed class TypeRegistrar : ITypeRegistrar
{
private readonly IServiceCollection _builder;
public TypeRegistrar(IServiceCollection builder)
{
_builder = builder;
}
public ITypeResolver Build()
{
return new TypeResolver(_builder.BuildServiceProvider());
}
public void Register(Type service, Type implementation)
{
_builder.AddSingleton(service, implementation);
}
public void RegisterInstance(Type service, object implementation)
{
_builder.AddSingleton(service, implementation);
}
public void RegisterLazy(Type service, Func<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
_builder.AddSingleton(service, (provider) => func());
}
}
}
using Spectre.Console.Cli;
namespace TodoTrack.Cli
{
public sealed class TypeResolver : ITypeResolver, IDisposable
{
private readonly IServiceProvider _provider;
public TypeResolver(IServiceProvider provider)
{
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public object? Resolve(Type? type)
{
if (type == null)
{
return null;
}
return _provider.GetService(type);
}
public void Dispose()
{
if (_provider is IDisposable disposable)
{
disposable.Dispose();
}
}
}
}
CommandApp
的初始化语句还得改成这个形式:
public static int Main(string[] args)
{
// Create a type registrar and register any dependencies.
// A type registrar is an adapter for a DI framework.
var registrations = new ServiceCollection();
registrations.AddSingleton<IGreeter, HelloWorldGreeter>();
var registrar = new TypeRegistrar(registrations);
// Create a new command app with the registrar
// and run it with the provided arguments.
var app = new CommandApp<DefaultCommand>(registrar);
return app.Run(args);
}
这种方法放弃了 Host 创建 HostedService,依赖注入的行为会由 TypeRegistrar
与 TypeResolver
控制。
修改注册器行为
由于 Spectre.Console.Cli
是依照 CLI 工具设计的,这类工具往往执行一次就自动退出返回控制台。因此它的注册器会在每次调用时重新创建 IServiceProvider
,如果直接将其改成多次执行,我们会发现所有对象都会重新初始化一遍,和 AddSingleton 之类的行为不同。
修改注册器行为,将其作为一个长期运行的单例执行,这样我们可以继续使用拓展方法注册,并注入到 HostedService 中。
public void Dispose()
{
//if (_provider is IDisposable disposable)
//{
// disposable.Dispose();
//}
}
private ITypeResolver _typeResolver;
public ITypeResolver Build()
{
return _typeResolver ??= new TypeResolver(_builder.BuildServiceProvider());
}
这种方式下,外部的 DI 无法识别 CommandApp 内部注册的 Command 对象,使用时需要小心。
参考
Spectre.Console-处理依赖注入的更多相关文章
- ASP.NET Core 2.2 基础知识(一) 依赖注入
依赖: 类A用到了类B,我们就说类A依赖类B.如果一个类没有任何地方使用到,那这个类基本上可以删掉了. public class Test { private MyDependency md = ne ...
- Console app 里的依赖注入及其实例生命周期
依赖注入是 ASP.NET Core 里的核心概念之一,我们平常总是愉快地在Startup类的ConfigureServices方法里往IServiceCollection里注册各种类型,以致有一些同 ...
- .NET CORE——Console中使用依赖注入
我们都知道,在 ASP.NET CORE 中通过依赖注入的方式来使用服务十分的简单,而在 Console 中,其实也只是稍微绕了个小弯子而已.不管是内置 DI 组件或者第三方的 DI 组件(如Auto ...
- ASP.NET Core中如影随形的”依赖注入”[下]: 历数依赖注入的N种玩法
在对ASP.NET Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注的目光转移到编程层面.在ASP.NET ...
- ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起
我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点.由于前面两章已经涵盖了依赖注入在管道构建过程中以及管道在处理请求过程的应用,但是内 ...
- 模拟AngularJS之依赖注入
一.概述 AngularJS有一经典之处就是依赖注入,对于什么是依赖注入,熟悉spring的同学应该都非常了解了,但,对于前端而言,还是比较新颖的. 依赖注入,简而言之,就是解除硬编码,达到解偶的目的 ...
- angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable
大家好,今天我们要讲是angular2的http功能模块,这个功能模块的代码不在angular2里面,需要我们另外引入: index.html <script src="lib/htt ...
- angularjs 依赖注入--自己学着实现
在用angular依赖注入时,感觉很好用,他的出现是 为了"削减计算机程序的耦合问题" ,我怀着敬畏与好奇的心情,轻轻的走进了angular源码,看看他到底是怎么实现的,我也想写个 ...
- 在.NET Core控制台程序中使用依赖注入
之前都是在ASP.NET Core中使用依赖注入(Dependency Injection),昨天遇到一个场景需要在.NET Core控制台程序中使用依赖注入,由于对.NET Core中的依赖注入机制 ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
随机推荐
- Go - 高并发抢到红包实现
// utils.gopackage mainimport ( "fmt" "math/rand" "sync" "time&qu ...
- PyQt5学习 (2)--QWidget(上)
描述: 1.所有可视控件的基类 2.是一个最简单的空白控件 3.控件时用户界面的最小元素:接收各种事件.绘制在桌面上,展示给用户看 4.每个控件都是矩形的,它们按Z轴顺序排序 5. ...
- 排队论——系统运行指标的R语言实现
排队是在日常生活中经常遇到的现象,如顾客到商店购买物品.病人到医院看病常常要排队.此时要求服务的数量超过服务机构(服务台.服务员等)的容量.也就是说,到达的顾客不能立即得到服务,因而出现了排队现象.这 ...
- day134:2RenMJ:TypeScript的抽象类&装饰器&命名空间&模块&编译配置文件&python中的类型注解
目录 1.抽象类 2.装饰器 3.命名空间 4.模块 5.编译配置文件 6.python的类型注解 1.抽象类 抽象类(abstract class)做为其它派生类的基类使用. 它们一般不会直接被实例 ...
- JS 对输入框进行限制(常用的都有)
文章来源 http://www.soso.io/article/24096.html 1.文本框只能输入数字代码(小数点也不能输入) 代码如下: <input οnkeyup="thi ...
- Linx 阶段一
Linux Linux常用命令 具体演示 1). ls 2). pwd 3). touch 4). mkdir 5). rm 使用技巧 1. 连按 Tab健自动补齐文件名 2. ll 查看当前目录文件 ...
- 批量上传iOS应用程序截图的实用技巧
提交iOS应用程序截图到iTunes Connect是一项非常繁琐的任务,因为你必须上传多达数十张屏幕截图,这是一个重复而枯燥的过程.但是,我们有一个好消息要告诉开发者们,现在有一个工具可以帮助你 ...
- o(nlogn)求最长上升子序列
\(O(nlog_n)\)求最长上升子序列LIS 假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5.下面一步一步试着找出它. 我们定义一个序列B,然 ...
- 5219. 【GDOI2018模拟7.10】B
5219. [GDOI2018模拟7.10]B 题目大意: 考试想法: 正解: 代码: 题目大意: 现在有一个字符串 s s s 当 s [ i ] s[i] s[i]为 I I I时 a n s [ ...
- 逍遥自在学C语言 | 赋值运算符
前言 在C语言中,赋值运算符用于将一个值赋给变量 这个过程分为两个步骤: 计算赋值运算符右侧的表达式 将结果赋给左侧的变量. C语言提供了多个不同的赋值运算符,包括基本的赋值运算符.复合赋值运算符以及 ...