検索
タグ
ASP.NET
.NET
ASP.NET MVC
Visual Studio
F#
Azure
ASP.NET Core
ライトニングトーク
Plone
Selenium
AJAX
C#
jQuery
SQL Server
JavaScript
ADO.NET Entity Framework
EFCore
WebMatrix
LINQ
Fizz-Buzz
カテゴリ
最新の記事
最新のコメント
記事ランキング
最新のトラックバック
以前の記事
2024年 11月 2024年 10月 2024年 09月 2024年 08月 2024年 04月 2024年 03月 2024年 02月 2024年 01月 2023年 12月 2023年 11月 2023年 10月 2023年 09月 2023年 08月 2023年 07月 2023年 06月 2023年 05月 2023年 04月 2023年 03月 2023年 02月 2023年 01月 2022年 12月 2022年 11月 2022年 10月 2022年 09月 2022年 08月 2022年 07月 2022年 06月 2022年 05月 2022年 04月 2022年 03月 2022年 02月 2022年 01月 2021年 12月 2021年 11月 2021年 10月 2021年 09月 2021年 08月 2021年 07月 2021年 06月 2021年 05月 2021年 04月 2021年 03月 2021年 02月 2021年 01月 2020年 12月 2020年 11月 2020年 10月 2020年 09月 2020年 08月 2020年 07月 2020年 06月 2020年 05月 2020年 04月 2020年 03月 2020年 02月 2020年 01月 2019年 12月 2019年 11月 2019年 10月 2019年 09月 2019年 08月 2019年 07月 2019年 06月 2019年 05月 2019年 04月 2019年 03月 2019年 02月 2019年 01月 2018年 12月 2018年 11月 2018年 10月 2018年 09月 2018年 08月 2018年 07月 2018年 06月 2018年 05月 2018年 04月 2018年 03月 2018年 02月 2018年 01月 2017年 12月 2017年 11月 2017年 10月 2017年 09月 2017年 08月 2017年 07月 2017年 06月 2017年 05月 2017年 04月 2017年 02月 2017年 01月 2016年 12月 2016年 11月 2016年 10月 2016年 09月 2016年 08月 2016年 07月 2016年 06月 2016年 05月 2016年 04月 2016年 03月 2016年 02月 2016年 01月 2015年 12月 2015年 11月 2015年 10月 2015年 09月 2015年 08月 2015年 07月 2015年 05月 2015年 04月 2015年 03月 2015年 02月 2015年 01月 2014年 12月 2014年 11月 2014年 10月 2014年 09月 2014年 08月 2014年 06月 2014年 04月 2014年 03月 2014年 02月 2014年 01月 2013年 12月 2013年 10月 2013年 09月 2013年 08月 2013年 07月 2013年 06月 2013年 05月 2013年 04月 2013年 03月 2013年 02月 2013年 01月 2012年 12月 2012年 11月 2012年 10月 2012年 09月 2012年 08月 2012年 07月 2012年 06月 2012年 05月 2012年 04月 2012年 03月 2012年 02月 2012年 01月 2011年 12月 2011年 11月 2011年 10月 2011年 09月 2011年 08月 2011年 07月 2011年 06月 2011年 05月 2011年 04月 2011年 03月 2011年 02月 2011年 01月 2010年 12月 2010年 11月 2010年 10月 2010年 09月 2010年 08月 2010年 07月 2010年 06月 2010年 05月 2010年 04月 2010年 03月 2010年 02月 2010年 01月 2009年 12月 2009年 10月 2009年 09月 2009年 07月 2009年 06月 2009年 05月 2009年 04月 2009年 03月 2009年 02月 2009年 01月 2008年 12月 2008年 11月 2008年 10月 2008年 09月 2008年 08月 2008年 07月 2008年 06月 2008年 05月 2008年 04月 2008年 03月 2008年 02月 2008年 01月 2007年 12月 2007年 11月 2007年 04月 2007年 03月 2007年 02月 2007年 01月 2006年 11月 2006年 10月 2006年 09月 2006年 08月 2006年 07月 |
2023年 12月 30日
C#/.NET プログラミングの話。 とある要件で、ASP.NET Core Web アプリケーションを起動し、HTTP リッスン状態になるまでを待機して、それからその ASP.NET Core Web アプリに HTTP 要求を送信して応答を得るという、そのようなコンソールアプリケーションを作成する需要が発生した。 もちろん、.NET の標準ライブラリにある System.Diagnostics.Process クラスを使えば実装できる。Process クラスに対し、起動したい実行可能ファイルパスやコマンドライン引数を指定するのに加えて、標準出力や標準エラー出力のリダイレクトを指定し、および Process クラスから発火される、標準出力や標準エラー出力への書き込み発生のイベントをハンドルすればよい。 ASP.NET Core Web アプリケーションであれば、HTTP リッスン状態になると「Now listening on: https://localhost:7241」といったテキストが標準出力への書き込まれるので (下図例)、これを検知すればよい。 具体的な実装例は以下のとおり。 using System.Diagnostics; // コマンドで起動する Process オブジェクトを準備。 // その際、標準出力のリダイレクトを指定。 var process = new Process { StartInfo = new ProcessStartInfo { FileName = "dotnet", Arguments = "run", WorkingDirectory = @"...", RedirectStandardOutput = true } }; // タスク完了ソースを用意 var tcs = new TaskCompletionSource(); // 標準出力発生のイベントをハンドルし、"Now listening on..." が出力 // されたら、タスクを完了させる process.OutputDataReceived += (_, e) => { var output = e.Data?.Trim() ?? string.Empty; if (output.StartsWith("Now listening on: http://localhost:")) { tcs.TrySetResult(); } }; // プロセスを開始 & 標準出力読み取りを開始 process.Start(); process.BeginOutputReadLine(); // "Now listening on..." の出力が検出されるまで非同期で待機 await tcs.Task; // ここまで来たら、起動した ASP.NET Core Web アプリは、 // HTTP リッスン開始している Console.WriteLine("DETECT START LISTENING!"); ちゃんと実装するとなると大変!ただ、上記実装例は、もう既にずいぶんと冗長な感じがすることに加え、標準エラー出力については実装していない。また、正常に動作しているぶんにはよいが、起動した ASP.NET Core Web アプリケーションプロセスが何らかのエラーを発生していた場合に、コンソールには標準出力や標準エラー出力の内容は表示されないため (出力をリダイレクトしてイベントで拾うようになってしまっているので)、何かあったときのトラブルシューティングが困難極める。タイムアウトの仕組みもないので、"Now listening on:..." の出力がされないと永遠に await し続けてしまい、エラーの発生に気づくことすらできなさそうだ。その他にも、このコンソールアプリケーション自体が例外を発生した時に、起動した ASP.NET Core Web アプリケーションプロセスをどう始末するのか、といったことも考慮が必要だろう。 それら考慮点をすべて網羅していくと、結構なコード量に膨れ上がることになる。 Process クラスの強化版的なライブラリ世の中には、こういった需要に応えるような、Process クラスの強化版的なライブラリが、無償利用可能な NuGet パッケージとして公開されていたりする。それらライブラリのうち、自分の中での推しのひとつは、ProcessX というライブラリだ (下記リンク先)。 非同期 Stream で標準出力を扱えたり、文字列に対する await 構文で外部コマンドを手軽に実行、完了を待機できたりと、なかなかに便利で興味深い機能が提供されている。 ただ、この ProcessX をもってしても、「指定の出力が現れるまで待機」や「トラブル発生時に、それまでの標準出力・標準エラー出力を見たい」といった今回の要件に伴う需要に応えるには、もう一息、実装を積み重ねる必要があった。 結局自作したそこで、今回要件のような、エッジケースに特化した、新たな "Process クラスの強化版" ライブラリを、もうひとつ新たに作成して OSS として世に送り出すことにした。 そうして作成したのが "XProcess" である (下記リンク先、MIT license)。 この XProcess は、前述のとおり今回の自分の要件に特化してあるため、先に挙げた「ASP.NET Core Web アプリケーションを起動し、HTTP リッスン状態になるまで待機」のコードは、以下にまで端的に書けるようになる。 using Toolbelt.Diagnostics; // 作業フォルダ指定して "dotnet run" で ASP.NET Core Web アプリ // プロジェクトを起動 using var process = XProcess.Start("dotnet", "run", workingDirectory: @"..."); // 指定した Predection が true を返す出力があるまで非同期で待機、 // 但し、最大 5 秒のタイムアウトを指定する。 var found = await process.WaitForOutputAsync(output => { output = output.Trim(); return output.StartsWith("Now listening on: http://localhost:"); }, millsecondsTimeout: 5000); // true が返されたら、目的の出力があったということ if (found) { Console.WriteLine("DETECT START LISTENING!"); } else { Console.WriteLine("SOME ERROR OCCURED!"); // Output プロパティを参照すれば、それまでの標準出力と標準エラー出力 // の内容を参照できる Console.WriteLine(process.Output); } まとめこのように、特定のユースケースに特化された XProcess は、ProcessX ほどの柔軟性と高度な機能は備えていないものの、その利用シナリオに適合した場合は、より簡潔にそのやりたいことを実現可能だ。 XProcess は、もっぱら自分の要件を満たすために作成した、特化されたライブラリであり、とはいえどうせ作ったのなら OSS & 無償利用可能なパッケージとして世の中に公開したら、同じ需要を持つ誰かの便利に役立つかもしれない、というノリでリリースしている。なので、"打倒、ProcessX!" みたいなつもりはないので悪しからず。 なので、XProcess に関しては、機能追加要望やバグ修正の Issue を上げられても、あまり積極的には対応しないかもしれない。その代わり、自分の新たな需要に対して機能不足やバグがあった場合は、速やかに改善・修正されるかもしれない、そんな性格のゆるい感じのライブラリだ。
by developer-adjust
| 2023-12-30 15:35
| .NET
|
ファン申請 |
||