前情提要 筆者收到主管的指示,要來寫一個執行檔(exe)程式,筆者離這種exe程式好久遠了,自從轉換到Web領域後幾乎都是寫API為主,一開始從.net MVC5開始進入Web領域,寫了幾年的Razor View後,前一份工作剛好前後端分離,前幾年都在寫dotnet core web api及angular為主,Console Application離我好遙遠阿,但筆者已經被dotnet core的內建DI機制及AppSettings當Config已經習以為常,對於Console Application來說這些都是要自己實作上去的,不妨藉由這次機會來實作看看。
筆者看了幾篇部落格後,想法是仿照dotnet core web api標準配置那樣
有一個Startup類別
Startup類別中有public get的Configuration,令其他有需求的人可以存取Configuration物件
Startup中宣告ConfigureServices,並將配置好的ServiceCollection回傳
宣告一個App類別,主要有Run這個Method,最終於Program中執行
製作商業邏輯Service 筆者這支的主要用意是檢查某個服務是否正常,可以想見驗證方式分為
ping test
http request test
對於應用程式來說,由注入時決定該用哪個方式即可,直接來看Code吧
1 2 3 4 public interface IPingTestService { Task<bool > PingHost (string hostName ) ; }
接著實作上面列到的兩種方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class PingTestByPingService : IPingTestService { public async Task<bool > PingHost (string hostName ) { bool pingable = false ; Ping pinger = null ; try { pinger = new Ping(); PingReply reply = await pinger.SendPingAsync(hostName); pingable = reply.Status == IPStatus.Success; } finally { if (pinger != null ) { pinger.Dispose(); } } return pingable; } }
1 2 3 4 5 6 7 8 9 10 11 public class PintTestByHttpRequestService : IPingTestService { public async Task<bool > PingHost (string hostName ) { var result = false ; using var httpClient = new HttpClient(); var response = await httpClient.GetAsync(hostName); result = response.StatusCode == HttpStatusCode.OK; return result; } }
到這邊商業邏輯實作告一段落了,就先放在一邊,回頭把基礎建設都用好
製作Startup類別 我們要製作Startup類別中的AppSettings及DI機制需要爾外安裝相關的微軟提供套件,就先來安裝nuget套件們吧
1 2 3 4 5 6 dotnet add package Microsoft.Extensions.Configuration dotnet add package Microsoft.Extensions.Configuration.Binder dotnet add package Microsoft.Extensions.Configuration.Json dotnet add package Microsoft.Extensions.DependencyInjection
安裝好nuget套件後,要來製作Startup類別了
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 public class Startup { public IConfiguration Configuration { get ; private set ; } public Startup () { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json" , optional: false ); var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT" ); if (!string .IsNullOrEmpty(environment)) { builder = builder.AddJsonFile($"appsettings.{environment} .json" , optional: true , reloadOnChange: false ); } this .Configuration = builder.Build(); } public ServiceCollection ConfigureServices () { var serviceCollection = new ServiceCollection(); serviceCollection.AddTransient<App>(); serviceCollection.AddTransient<IPingTestService, PintTestByHttpRequestService>(); return serviceCollection; } }
配置AppSettings 主要是新增各個環境的AppSettings檔案,這邊主要是要提醒大家,要將新增的appsettings檔案要去設定Copy always,筆者以Development檔案為準,列出筆者這邊的設定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { "EmailSetting" : { "SmtpServer" : "SMTP Host" , "SmtpPort" : 587 , "Account" : "SMTP Account" , "Password" : "SMTP Password" } , "TestUrl" : "https://google.com.tw" , "MailToDo" : { "MailTo" : "test@gmail.com;test2@gmail.com" , "MailCc" : "" , "Subject" : "Test Subject" } }
LaunchSettings配置 筆者這邊習慣調整LaunchSettings來切換Development, Staging, UAT等環境,Console Application的專案預設是沒有任何設定的,看不到Properties/LaunchSettings.json的,就動手加上去吧
加上去後會在Solution Explorer中會看到LaunchSettings.json了
製作主要運作程式App類別 基礎建設都用完了,要來撰寫主要執行的App類別了,邏輯觀念為
從Appsettings讀取EmailSetting及Mail相關設定
宣告MySmtpClient物件
執行主要Ping Test程式
若回傳錯誤或者Exception則寄信通知對應的信箱
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 public class App { private readonly IPingTestService _pingTestService; public App (IPingTestService pingTestService ) { _pingTestService = pingTestService; } public async Task Run (IConfiguration config ) { var emailSetting = config.GetSection("EmailSetting" ).Get<EmailSetting>(); var mailTodo = config.GetSection("MailToDo" ).Get<MailToDoItem>(); var mySmtpClient = new MySmtpClient(mailTodo, emailSetting); mySmtpClient.MySendCompleted += MailSendCompleted; try { var result = await _pingTestService.PingHost(config["TestUrl" ]); if (!result) { mailTodo.Message = "Monitor作業偵測到連線異常通知" ; await mySmtpClient.SendMailProcess(null , null ); } } catch (Exception ex) { mailTodo.Subject = $"Monitor作業異常通知" ; mailTodo.Message = $"{ex} " ; await mySmtpClient.SendMailProcess(null , null ); } } private void MailSendCompleted (MailToDoItem mailToDoItem, AsyncCompletedEventArgs e ) { } }
上述程式中用到的MySmtpClient、EmailSetting、MailToDoItem類別宣告就不特別在此列出,可以參考筆者這篇文章
[DotnetCore]SMTP寄信服務設計
Program:Main程式 最後要來撰寫Main程式了,直接用Code說話
1 2 3 4 5 6 7 8 9 10 11 12 class Program { public static async Task Main (string [] args ) { var startUp = new Startup(); var serviceProvider = startUp.ConfigureServices().BuildServiceProvider(); var app = serviceProvider.GetRequiredService<App>(); await app.Run(startUp.Configuration); } }
結論 藉由這次,平常直接透過dotnet new webapi這種指令建置出Web API專案,基本上那些相關的設定都配置好了,這次撰寫過程,就會知道,之所以可以使用IConfiguration則,需要安裝哪些套件,DI注入宣告之ServiceCollection需要的是哪個對應的nuget套件,對於筆者來說,是滿大的收穫。
參考