引言

之前说的做自动记录 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,依赖注入的行为会由 TypeRegistrarTypeResolver 控制。

修改注册器行为

由于 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-处理依赖注入的更多相关文章

  1. ASP.NET Core 2.2 基础知识(一) 依赖注入

    依赖: 类A用到了类B,我们就说类A依赖类B.如果一个类没有任何地方使用到,那这个类基本上可以删掉了. public class Test { private MyDependency md = ne ...

  2. Console app 里的依赖注入及其实例生命周期

    依赖注入是 ASP.NET Core 里的核心概念之一,我们平常总是愉快地在Startup类的ConfigureServices方法里往IServiceCollection里注册各种类型,以致有一些同 ...

  3. .NET CORE——Console中使用依赖注入

    我们都知道,在 ASP.NET CORE 中通过依赖注入的方式来使用服务十分的简单,而在 Console 中,其实也只是稍微绕了个小弯子而已.不管是内置 DI 组件或者第三方的 DI 组件(如Auto ...

  4. ASP.NET Core中如影随形的”依赖注入”[下]: 历数依赖注入的N种玩法

    在对ASP.NET Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注的目光转移到编程层面.在ASP.NET ...

  5. ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起

    我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点.由于前面两章已经涵盖了依赖注入在管道构建过程中以及管道在处理请求过程的应用,但是内 ...

  6. 模拟AngularJS之依赖注入

    一.概述 AngularJS有一经典之处就是依赖注入,对于什么是依赖注入,熟悉spring的同学应该都非常了解了,但,对于前端而言,还是比较新颖的. 依赖注入,简而言之,就是解除硬编码,达到解偶的目的 ...

  7. angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable

    大家好,今天我们要讲是angular2的http功能模块,这个功能模块的代码不在angular2里面,需要我们另外引入: index.html <script src="lib/htt ...

  8. angularjs 依赖注入--自己学着实现

    在用angular依赖注入时,感觉很好用,他的出现是 为了"削减计算机程序的耦合问题" ,我怀着敬畏与好奇的心情,轻轻的走进了angular源码,看看他到底是怎么实现的,我也想写个 ...

  9. 在.NET Core控制台程序中使用依赖注入

    之前都是在ASP.NET Core中使用依赖注入(Dependency Injection),昨天遇到一个场景需要在.NET Core控制台程序中使用依赖注入,由于对.NET Core中的依赖注入机制 ...

  10. ASP.NET Core中的依赖注入(2):依赖注入(DI)

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...

随机推荐

  1. Centos 7 配置Tomcat跳转Https

    前言:在网络安全盛行的时代下,有时业务为了安全需求要使用https协议,包括http.nginx.tomcat等,本篇简单分享一下tomcat跳转https配置. 1.环境 Centos 7.9 2. ...

  2. 从pcap文件中提取pcma音频

    操作系统 :Windows10_x64 .CentOS 7.6.1810_x64 wireshark版本:3.6.12 Python 版本  :  3.9.12 一.背景描述 工作中有时候会遇到需要从 ...

  3. 【Visual Leak Detector】QT 中 VLD 输出解析(四)

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记. 目录 说明 1. 使用方式 2. 测试代码 3. 使用 32 bit 编译器时的输出 4. 使用 64 bit 编译器时的输出 5. 输 ...

  4. 项目优化-CDN缓存

    名次解释 CDN(Content Delivery Network)内容分发网络. CDN出现背景: 客户端从源站点获取数据,当服务端访问流量较为拥挤的时候 可能出现缓慢卡顿的现象,为了解决这个问题, ...

  5. Nmap基本使用【速查版】

    列举远程机器开放的端口 nmap [域名] 列举远程机器开放的端口和服务 nmap --dns-servers 8.8.8.8 [域名] nmap进行探测之前要把域名通过DNS服务器解析为IP地址,我 ...

  6. MATLAB计算变异函数并绘制经验半方差图

      本文介绍基于MATLAB求取空间数据的变异函数,并绘制经验半方差图的方法.   由于本文所用的数据并不是我的,因此遗憾不能将数据一并展示给大家:但是依据本篇博客的思想与对代码的详细解释,大家用自己 ...

  7. CVE-2015-5254漏洞复现

    1.漏洞介绍. Apache ActiveMQ 是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持 Java 消息服务,集群,Spring Framework 等.Apache ...

  8. Go中的有限状态机FSM的详细介绍

    1.FSM简介 1.1 有限状态机的定义 有限状态机(Finite State Machine,FSM)是一种数学模型,用于描述系统在不同状态下的行为和转移条件. 状态机有三个组成部分:状态(Stat ...

  9. 前端 本地缓存localStorage/sessionStorage

    当我们刷新页面时,除了路由,页面的当前状态及数据会全部清空/重置,包括浏览器标题. 如果想保存刷新前的一些数据,可以通过window.localStorage/sessionStorage,在浏览器里 ...

  10. Typora+MinIO+Python代码打造舒适协作环境

    作者:IT王小二 博客:https://itwxe.com 不知不觉大半年没更新了...前面小二介绍过使用Typora+MinIO+Java代码打造舒适写作环境,然后有很多大佬啊,说用Java来实现简 ...