.NET CORE编写控制台程序应有的优雅姿势(转载)
原文地址:https://www.cnblogs.com/zuowj/p/11107243.html
本文所说的编写控制台程序应有的“正确”方法,我把正确二字加上引号,因为没有绝对的正确,因人而异,因系统设计需求而异,我这里所谓的正确方法是指使用面向对象,依赖注入IOC,切面控制AOP等编码规范来提升程序的性能、整洁度、可读性、可维护性等,最终达到让人感觉有点高大上,有点优雅的样子。
先来说说.NET CORE编写控制台程序,目前网络上大把的讲解ASP.NET CORE的编写规范,反而对于.NET CORE控制台程序编写规范介绍比较少,大多停留在Hello Word 程序中,而本文则来讲讲.NET CORE控制台的编写规范(应有的优雅姿势)^ v ^
如果说不讲什么IOC,DI,AOP等,不讲扩展性,规范性,全部面向过程(方法)编程,那估计没什么好讲的,因为无非就是定义一个class,然后在class中定义一堆的method(方法),如果在方法中需要使用到其它第三方组件,则直接单独引用,引用后进行简单封装util工具类的静态方法,甚至也不用封装,直接使用原生的方法,总之全部都是方法调方法。而这里所演示的编写控制台方法均是尽可能的使用.NET CORE所具有的特性,只有这样才能体现出.NET CORE框架的优势,否则普通控制台程序与.NET CORE控制台程序有什么区别。
编写.NET CORE控制台程序优雅姿势一:(直接使用.NET CORE的 IOC、Logging、Config组件)
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
//Program.cs using Microsoft.Extensions.DependencyInjection; using System; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.Configuration; using System.IO; namespace NetCoreConsoleApp { class Program { static void Main( string [] args) { //设置config文件 var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile( "appSettings.json" , optional: true , reloadOnChange: true ).Build(); //设置依赖注入 var provider = new ServiceCollection() .AddLogging(configLogging => //设置日志组件 { configLogging.SetMinimumLevel(LogLevel.Information); configLogging.AddConsole(); }) .AddScoped<IConfiguration>(p => config) .AddScoped<HostService>() .BuildServiceProvider(); var hostService = provider.GetService<HostService>(); hostService.RunAsync(); //统一入口服务 Console.WriteLine( "提示:程序已正常启动运行,按任意键停止运行并关闭程序..." ); Console.ReadLine(); } } } //HostService.cs<br> using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace NetCoreConsoleApp { public class HostService { private readonly IConfiguration config; private readonly ILogger<HostService> logger; public HostService(IConfiguration config, ILogger<HostService> logger) { this .config = config; this .logger = logger; } public void RunAsync() { Task.Run((Action)Execute); } /// <summary> /// 控制台核心执行入口方法 /// </summary> private void Execute() { //TODO 业务逻辑代码,如下模拟 Stopwatch stopwatch = Stopwatch.StartNew(); for ( int i = 1; i <= 100; i++) { Console.WriteLine( "test WriteLine:" + i); Thread.Sleep(100); } stopwatch.Stop(); logger.LogInformation( "Logging - Execute Elapsed Times:{}ms" , stopwatch.ElapsedMilliseconds); } } } |
因为要使用.NET CORE相关核心组件,故需要引用相关的NuGet包(引用包的方式有多种方式),而且默认的.NET CORE控制台只会生成DLL并不会生成EXE启动程序,故如果仅在WIN系统下使用,还需要设置生成方式等,详细配置属性如下:(项目文件csproj)

- <Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp2.2</TargetFramework>
- <RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
- <SelfContained>false</SelfContained>
- </PropertyGroup>
- <ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
- <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
- </ItemGroup>
- </Project>

如上代码虽简单但代码编写顺序很关键,这里进行说明一下:
1.因为一般应用程序都会有config文件,故我们需要先通过new ConfigurationBuilder来设置config文件的方式及路径;
2.因为要使用.NET CORE默认的IOC框架,故new ServiceCollection,然后将相关的依赖服务组件注册到IOC容器中;
3.config、logging 均是一个程序最基本的依赖组件,故将其注册到IOC容器中,注册logging有专门的扩展方法(AddLogging),而config没有则直接使用通过的注册方法(当然也可以基于ServiceCollection写一个AddConfiguration扩展方法)
4.控制台需要一个核心的入口方法,用于处理核心业务,不要直接在Program中写方法,这样就不能使用IOC,同时也没有做到职责分明,Program仅是程序启动入口,业务处理应该有专门的入口,故上述代码中有HostService类(即:核心宿主服务类, 意为存在于控制台中的服务处理类,在这个类的构造涵数中列出所需依赖的服务组件,以便实例化时IOC可以自动注入这个参数),并注册到IOC容器中,当然也可以先定义一个IHostService接口然后实现这个接口。(如果有多个HostService类实例,建议定义一个IHostService接口,接口中只需要入口方法定义即可,如:RunAsync)
5.当各组件初始化设置OK、IOC注册到位后,就应该通过IOC解析获得HostService类实例,并执行入口方法:RunAsync,该方法为异步后台执行,即调用该方法后,会在单独的后台线程处理核心业务,然后主线程继续往下面走,输出关闭提示信息,最后的Console.ReadLine();很关键,这个是等待输入流并挂起当前主线程,目的大家都知道,不要让控制台程序关闭。
通过上述的讲解及源代码展示,有没有感觉优雅呢?如果觉得这样还算优雅,那下面展示的第二种更优雅的姿势
编写.NET CORE控制台程序优雅姿势二:(使用通用主机也称泛型主机HostBuilder)
代码如下:Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using NLog.Extensions.Logging; using Microsoft.Extensions.Configuration; using System.IO; using Polly; using System; namespace NetCoreConsoleApp { class Program { static void Main( string [] args) { var host = new HostBuilder() .ConfigureHostConfiguration(configHost => { configHost.SetBasePath(Directory.GetCurrentDirectory()); }) .ConfigureAppConfiguration(configApp => { configApp.AddJsonFile( "appsettings.json" , optional: false , reloadOnChange: true ); }) .ConfigureServices((context, services) => { //添加数据访问组件示例:services.AddTransient<IDbAccesser>(provider => //{ // string connStr = context.Configuration.GetConnectionString("ConnDbStr"); // return new SqlDapperEasyUtil(connStr); //}); //添加HttpClient封装类示例:services.AddHttpClient<GitHubApiClient>() //.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(3, t => TimeSpan.FromMilliseconds(800))); services.AddHostedService<DemoHostedService>(); }) .ConfigureLogging((context, configLogging) => { configLogging.ClearProviders(); configLogging.SetMinimumLevel(LogLevel.Trace); configLogging.AddNLog(context.Configuration); }) .UseConsoleLifetime() .Build(); host.Run(); } } } |
DemoHostedService类代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace NetCoreConsoleApp { public class DemoHostedService : IHostedService { private readonly IConfiguration config; private readonly ILogger logger; public DemoHostedService(IConfiguration config, ILogger<DemoHostedService> logger) { this .config = config; this .logger = logger; } public Task StartAsync(CancellationToken cancellationToken) { Console.WriteLine(nameof(DemoHostedService) + "已开始执行..." ); //TODO 业务逻辑代码,如下模拟 Stopwatch stopwatch = Stopwatch.StartNew(); for ( int i = 1; i <= 100; i++) { Console.WriteLine( "test WriteLine:" + i); Thread.Sleep(100); } stopwatch.Stop(); logger.LogInformation( "Logging - Execute Elapsed Times:{}ms" , stopwatch.ElapsedMilliseconds); return Task.FromResult(0); } public Task StopAsync(CancellationToken cancellationToken) { Console.WriteLine(nameof(DemoHostedService) + "已被停止" ); return Task.FromResult(0); } } } |
因为要使用HostBuilder类及相关的.NET CORE组件(如上代码主要使用到了:Host、Dapper、Nlog、Polly等),故仍需引用相关的NuGet包,详细配置属性如下:(项目文件csproj)

- <Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp2.2</TargetFramework>
- <RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
- <SelfContained>false</SelfContained>
- </PropertyGroup>
- <ItemGroup>
- <PackageReference Include="Dapper" Version="1.60.6" />
- <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.2.0" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" />
- <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.2.0" />
- <PackageReference Include="NLog.Extensions.Logging" Version="1.5.1" />
- <PackageReference Include="System.Collections.Concurrent" Version="4.3.0" />
- </ItemGroup>
- <ItemGroup>
- <None Update="appsettings.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Update="nlog.config">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- </ItemGroup>
- </Project>

如上代码所示,写过ASP.NET CORE程序的人可能比较眼熟,这与ASP.NET CORE的写法很类似,是的,你没有看错,HostBuilder是通用主机,是可以广泛应用于非HTTP的环境下,而ASP.NET CORE中的WebHostBuilder 主要用于HTTP WEB环境,使用方式基本类似,都是先定义HostBuilder,然后利用扩展方法注册、配置各种组件(中间件),最后调用Host的Run方法,开启后台服务执行,不同的是WebHostBuilder多了属于HTTP专有的一些属性及方法及其适用的中间件。
由于这种写法比较通用,适用于已熟悉.NET CORE或ASP.NET CORE的人群,上手也较简单,故建议采取这种方式来写.NET CORE控制台程序。需要注意的是HostBuilder中最重要的是:注册HostedService 服务,如上代码中的DemoHostedService即是实现了IHostedService接口的宿主后台服务类,可以定义多个,然后都注册到IOC中,最后Host会按注册先后顺序执行多个HostedService服务的StartAsync方法,当停止时同样会执行多个HostedService服务的StopAsync方法
.NET CORE编写控制台程序应有的优雅姿势(转载)的更多相关文章
- .NET CORE与Spring Boot编写控制台程序应有的优雅姿势
本文分别说明.NET CORE与Spring Boot 编写控制台程序应有的“正确”方法,以便.NET程序员.JAVA程序员可以相互学习与加深了解,注意本文只介绍用法,不会刻意强调哪种语言或哪种框架写 ...
- 10分钟 在linux里创建.net core helloworld控制台程序
官方教程 安装linux https://www.cnblogs.com/LittleFeiHu/p/9749455.html 第一步 :选择和你本机适用的Linux版本,我这里用的是18.04. 第 ...
- .net core初试 --- 控制台程序
.net core这个名字对.net程序员来说都不陌生了,但貌似圈子里真正有开发经验的并不多,关键是公司的项目没需求. 今天我就趁着不忙上手玩了玩,搞明白了一些东西,心中也有了十万个为什么.那么现在与 ...
- VB6 如何创建一个标准控制台程序
打开 VB6 并新建一个标准EXE程序,把窗口删掉,然后再加入一个模块. 在模块中加入AllocConsole.FreeConsole.SetConsoleTitle.Sleep的API声明: Pub ...
- [C/C++]宽字符与控制台程序
转自:http://www.cnblogs.com/zplutor/archive/2010/11/27/1889227.html 在我刚开始学C/C++的时候,字符类型使用的都是char.接触Win ...
- Mac/Windows开发跨平台.NET Core 控制台程序
自从微软开始在Github上开源搞.NET Core后,.NET的跨平台逐渐就成真了.多年使用各种语言,说实话还是csharp用起来最舒服.不过现在的工作环境里使用它的机会比较少,大部分时候只是用来写 ...
- 如何在.NET Core控制台程序中使用依赖注入
背景介绍 依赖注入(Dependency Injection), 是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度.在.NET Core MVC中 我们可以在Startup.cs文件的Co ...
- 在.NET Core控制台程序中使用依赖注入
之前都是在ASP.NET Core中使用依赖注入(Dependency Injection),昨天遇到一个场景需要在.NET Core控制台程序中使用依赖注入,由于对.NET Core中的依赖注入机制 ...
- .Net Core 控制台程序错误:Can not find runtime target for framework '.NETCoreApp,Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'.
.Net Core 控制台程序错误:Can not find runtime target for framework '.NETCoreApp,Version=v1.0' compatible wi ...
随机推荐
- Prometheus 监控K8S 资源状态对象
Prometheus 监控K8S 资源状态对象 官方文档:https://github.com/kubernetes/kube-state-metrics kube-state-metrics是一个简 ...
- Nginx的相关问题
1.什么是Nginx的虚拟主机? 答:虚拟主机是一种特殊的软硬件技术,它可以将网络上的每一台计算机分成多个虚拟主机,每个虚拟主机可以独立对外提供www服务,这样就可以实现一台主机对外提供多个web服务 ...
- sso单点登录的入门(Session跨域、Spring-Session共享)
1.单点登录,就是多系统,单一位置登录,实现多系统同时登录的一种技术.单点登录一般是用于互相授信的系统,实现单一位置登录,全系统有效的. 区分与三方登录(第三方登录) ,三方登录:某系统,使用其他系统 ...
- 算法笔记 第6章 C++标准模版库(STL)介绍 学习笔记
6.1 vector的常见用法详解 vector:变长数组,长度根据需要而自动改变的数组 要使用vector,则需要添加vector头文件,即#include<vector>,还需要在头文 ...
- Windows下实现应用程序看门狗软件
有时候,我们需要确保一个应用程序长期稳定地运行,但是在Windows平台上由于种种原因,几乎不可能保障一个应用的绝对可靠运行.那么,我们就需要有一个机制,在应用死机﹑异常﹑系统重启等情况下自我恢复,而 ...
- 高强度学习训练第四天总结:JVM+Redis
JVM 复习了JVM堆内存的几个模块. 复习了JVM的几个控制工具. 复习了JVM发展历史 Redis 复习了Redis的事务控制.
- Properties类按顺序输出加载内容
Properties类按顺序输出加载内容 最近手写工厂的时候,遇到了加载配置文件时不按照properties文件中的数据的顺序来加载. 一.问题代码 import java.io.IOExceptio ...
- 常用的linux命令大全
之前做过两年的运维,用过很多命令,深切体会到某些linux命令熟练掌握后对效率提升有多大.举个简单的例子,在做了研发后经常会有跑一些数据,对于结果数据的处理,我们的产品同学一般都习惯于用excel做统 ...
- 更换Ubuntu软件源
对于Ubuntu系统, 不同的版本的源都不一样,每一个版本都有自己专属的源. 而对于 Ubuntu 的同一个发行版本,它的源又分布在全球范围内的服务器上.Ubuntu 默认使用的官方源的服务器在欧洲, ...
- MNIST数据集上卷积神经网络的简单实现(使用PyTorch)
设计的CNN模型包括一个输入层,输入的是MNIST数据集中28*28*1的灰度图 两个卷积层, 第一层卷积层使用6个3*3的kernel进行filter,步长为1,填充1.这样得到的尺寸是(28+1* ...