久しぶりに C# での開発現場に戻ってからテストコードを書く時にやっていること - dotnet script -

dany1468.hatenablog.com

上記の記事の続きです。

といいつつ、上記では触れていなかった話題です 😅

Rails Console が欲しい

Ruby on Rails で開発・運用をしていて一番便利だったと記憶しているのが Rails Console でした。
今の現場の人に話すと「リモートデバッガで済む話じゃない?」とか「SQL 書けばいいのでは」とか言われるのですが、Rails Console のアプリ内のコードをそのまま実行できる感じは、単に ActiveRecordSQL を実行するだけという感じとは違って、調査やデータ抽出においてもかなりパワフルだったなと感じています。(もちろん、エンタープライズになれば、そうそう Rails Console を production 環境で実行とかできないし、実際できなくなってはいくのですが、それでも)

C# の REPL 環境

.NET Framework, .NET Core のいずれでもいくつか REPL は存在します。 ( 検索すれば csi.exe とか C# Interactive とか出てくると思います)

私は Rails Console のように、production でも使えるようなものが欲しかったので、IDE への依存がなく、さくっとインストールできるものが良かったので、.NET Core の global tool としてもインストール可能な filipw/dotnet-script を利用しています。

dotnet script

github.com

README に全部書いてあるので詳細は省きますが、dotnet script では、 csx ファイルだけでなく、 dll の参照や NuGet パッケージのインストールも実行中に可能です。

私の場合は以下のような context.csx を用意し、 dotnet script context.csx -i でプロジェクトを操作できる状態で REPL を起動するようにしています。

#r "プロジェクトの dll"

using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Data;
using System.Data.Common;
using Dapper; # プロジェクトの dll に依存しているものなら読み込める
using Npgsql;

using プロジェクト固有の名前空間

# 設定ファイルへの依存をうまく扱えない場合には環境変数からも設定できるようにしておき、ここで解決させる
Environment.SetEnvironmentVariable(プロジェクトの実行に必要な環境変数);

# 場合によってはショートカット用の関数や設定済みの変数を用意しておくと便利
public ....
{ 

}

ちょっとしたツールにも使いやすい

.NET Core なら dotnet run で実行できるのでさほど差はないのですが、ちょっとした運用ツールも dotnet script の csx + α ぐらいで書いておくと、仮に動かなくてもその場で REPL を起動して試しながら直しながら動かしていけるのでいいのではないかと思っています。

正直、それまでは「実行ファイルだと動かない時面倒だし、とりあえず Ruby で書いておくか。。」みたいな気持ちで小さなスクリプトを書くことが多かったのですが、 dotnet script を使いだしてからは C# で書くようになりました。(C# の現場なので、当然そうすべきなのですが)

その場合は、設定系は環境変数から読めるようにしておき、開発中は direnv を使っています。(私は IDE での作業以外は WSL 上で作業をしているため)

csi.exe を使う場合

csi.exe は今は使っていませんが利用を検討していた時は以下のような bat ファイルを用意して使っていました。

 "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Roslyn\csi.exe" /r:System /r:System.Core /r:Microsoft.CSharp /r:Syste     m.ValueTuple.dll /u:System /u:System.IO /u:System.Collections.Generic /u:System.Console /u:System.Diagnostics /u:System.Dynamic /u:System.Linq /u:System.Linq.Expressions /u:System.Text /u:System.Threading.Tasks @context.rsp /i context.csx

csi.exe の場合は rsp ファイルでコンテキストを分けて定義するので、 rspcsx を分けています。この辺は Visual StudioC# Interactive が使っている rsp とかを見つつ作っていましたが、調べるといろいろ出てきます。