一人情シスのつぶやき

名古屋の中小企業で一人情シスをしている作者が、日々の業務で思うことをつぶやきます。

IEnumerableで受けてIReadOnlyListで返すなら

tack41tu.hatenablog.com

で記載した方針を受けて、最近はCollectionに関してはIEnumerableで受けて、IReadOnlyListで返す基本方針としている。 一方で、プロパティの変更による予期せぬ誤動作を防ぐために、POCOについてはコンストラクタで初期とを設定し、setterを提供しないことでImmutableな形としている。

ただ、そうなるとCollectionもコンストラクタで渡す必要があるが、アルゴリズムの関係などでCollectionのメンバーは後で追加したいことがある。例えばDBからデータを取得して、鑑データの属性として明細データのCollectionを持たせる場合に、鑑データのclassをいったん生成した後、明細データを都度追加したいことが多い。要は短期間だけメンバーを追加したい。

POCOに渡すCollectionの参照を呼び出し側で保持し、鑑データのコンストラクタに渡した後で呼び出し側で持っているCollectionの参照をもとにメンバーを追加したのだが、なぜか反映されない。

結論としては、POCO内ではCollectionをListとして保持しており、コンストラクタでIEnumerableで受けた際に直ちにtoListで変換していた。これだとその時点でオブジェクトがコピーされてしまい、呼び出し側で持っている参照とは別物になってしまう。 そこで、IEnumerableで受けたオブジェクトは内部的にもIEnumerableで保持し、getterで参照された際にtoListしてIReadOnlyList型として返すことで解決した。こうすれば呼び出し側で持っている参照とPOCOで持っているクラスは全く同じとなり、呼び出し側でメンバーを追加、削除すればPOCOにも反映される。getterで参照する場合はそもそもIReadOnlyList型としてわたるのでメンバーの編集はできない。castすればできなくもないかもしれないが、少なくともそれは保証対象外という意図を明示できる。

rm /* やってしまった

やってしまいました。

rm ${HOGE}/*

HOGEが未定義のため、ルートディレクトリのファイルが全削除。 幸い、rfオプションは付けていなかったので、消されたのはファイルのみ。

CentOS7において、ルートディレクトリに存在するファイルは以下のシンボリックリンク

  • /bin -> /usr/bin
  • /lib -> /usr/lib
  • /lib64 -> /usr/lib64
  • /sbin/ -> /usr/sbin

組み込み系のコマンドしかまともに動作せず、/usr/binフォルダに移動してコマンドを実行しても

-bash: /usr/bin/ln: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter:

となってしまう...

なんとかググって見つけた以下のサイト

ja.stackoverflow.com

# /usr/lib64/ld-linux-x86-64.so.2 --library-path /usr/lib64 /usr/bin/ln -s /usr/lib64 /lib64
# /usr/bin/ln -s /usr/bin /bin
# ln -s /usr/lib /lib
# ln -s /usr/lib64 /lib64
# ln -s /usr/sbin /sbin

上記コマンドで復旧した。神!! 感謝!!! 死ぬほど焦った...

今覚えば、/usr/bin/busyboxを使えばよかったのかもしれない。

ちなみに、shebangにて

#!/bin/bash -eu

とやっていたのだが、該当ファイルを/bin/bashの引数で呼び出したので効いていなかった... shebangではなく本文中にset -euと書くことが大事だと、死ぬほど理解した.

PowershellのToStringの書式指定ではまった

PowerShellのToStringで数値を書式指定で出力する際にはまった2点。どちらもエラーメッセージが適切ではなく、原因特定に苦労した。 PSVersion 5.1.14393.2969, Windows Server 2016上のISEで開発。Set-StrictMode -Version latest

書式#,#はダメ

$num.ToString('#,#') 

にて$numが0だと「"ToString" のオーバーロードで、引数の数が "1" であるものが見つかりません。」と出る。

$num.ToString('#,0')

とすることで、正しく出力される。

キャスト後のtoString

$s_num="99,999,999,99" 

の場合に書式指定で出力したい場合、いったんlongにキャストするので

[long]$s_num.ToString("#,0")

と書くと、同様に「"ToString" のオーバーロードで、引数の数が "1" であるものが見つかりません。」とでる。正解は

([long]$s_num).ToString("#,0")

キャスト演算子よりもToStringの方が優先度が高いのか...

.netで利用可能な帳票ツール

内製開発している.net(C#)で利用可能な帳票ツールについて調査した。結論としては、高いお金を出さないとまともなツールは手に入らないということ。 価格はすべて税抜き。

使えるが、高い

  • Create!Form: 1帳票設計ライセンス200,000円、1WindowsServerランタイム400,000円。帳票ツールは使いやすそうで機能も十分そうだが、高い...
  • Active Reports for .Net 12.0: 1開発ライセンス300,000円、サーバーライセンス(2core)120,000円。Create!Formと同様、機能は十分だが高い...
  • iTextSharp: 保守されている最新のVer.7(iText)では、ライセンスがAGPLかCommercial License。Commercial Licenseの費用は見積もりを取らないとわからないっぽいが、このサイトによると、1920ポンド(26万程度)..
  • JasperReport: Jaspersoft Studioという設計ツールで設計は簡単そう。.Netから利用しようとすると、Serverを立ててAPI経由での利用だがCommunity ServerのライセンスがAGPLか商用ライセンスの購入が必要。価格は見つけられなかったが、上記製品と同価格帯だろう。... LibraryはLGPLなのに... クライアントをJavaで開発するのは、今の自分の開発スキル的につらい、つらすぎる...

安いが、あまり使えない

  • Reports.net: 1開発ライセンス60,000円、ランタイムは無料。ヘルプ等を見る限り、ヘッダ・フッタやグルーピングという概念がなく、すべての項目を項目名を指定して出力し、ページ送りも自分で行う感じに見える。Excelに自分で出力するのと大差ない。
  • VB-Report 8: 1開発ライセンス85,000円、ランタイムは無料。Reports.netよりもさらに原始的で、ひな形Excelのセル番号を指定して出力する感じ。ひな形ExcelにClosedXMLあたりで出力したほうが早いやん。
  • Access: 14,800円。ランタイムは無料。一部内製アプリで使用しているが、 VBAは見捨てられた言語だし、ソースコードはいったんExportする必要があり、かつ元のバイナリには戻せない。開発しづらい。

無料だが、つらい

  • [Microsoft ReportViewer]: Visual Studio Proに添付。どっちにしろうちの開発用途ではCommunityは使えないので。ただ、社内帳票でよくある、1明細が複数行にわたる場合に対応できない。
  • ClosedXML.Report: 無料。よさそうだが、こちらも、1明細が複数行にわたる場合に対応できないっぽい。
  • 帳票.NET: 無料。最終リリースが2016年。継続性に不安
  • Excel: 全利用者に配布済みなので追加費用不要。ClosedXML等を利用して普通にセル指定で出力。プログラムで頑張ってセルを指定して出力するか、ひな形にてデータの表示とは別にデータの入力個所をまとめておいてプログラム側の負荷を下げるか、どちらにしてもめんどい。帳票の種類が増えてくるとつらさが増してくる。

まともなツールは1開発者でも300,000円から400,000円程度は最低でも必要なのだろう。が、中小企業においてこの金額はおいそれとは出ない。今後も継続的にバージョンアップする必要があるだろうし。 ひとまず、帳票部分のみAccessで開発、そのAccessを.netプログラムに同梱して帳票部分で呼び出す形にできないか調査、ダメならExcelのひな型をなるべく工夫する。あまりにつらくて上記費用が安いと感じられるようであれば購入を申請する形か。

Get-ChildItemからのLengthプロパティでのファイル容量取得ではまる

PowerShellで特定のフォルダ配下に存在する一定容量以上のファイルをリストアップするスクリプトを作成。

Get-ChildItem -Recurse  . | Where-Object{$_.Length -ge 10*1024*1024 }

よくあるお題であちこちにサンプルがあるのだが、なぜかLengthプロパティがないというエラーが出る。

色々調べた結果、Set-Strictのversionを2以上にすると発生することが分かった。1の場合には存在しないプロパティでも無視することで動作するようだ。

winscript.jpstackoverflow.com によると、対象がdirectoryの場合に配下のFileInfoとDirectoryInfo配列のサイズを返すところ、配下が1つのみの場合だと存在するFileInfo,またはDirectoryInfoのLengthを返そうとするためで、無理やり配列にすればいけるとあるが、ダメ。Directoryに対しては配下にファイルが無くても、何個あってもLengthプロパティへの問い合わせはエラーとなる。PowerShell Ver.5.1.17763.503 on Win10 1809(64bit)で確認。

結論としては、Directoryの中の個数をカウントしてもしょうがないので、対象をファイルのみにすることだった。

Get-ChildItem -Recurse -File . | Where-Object{$_.Length -ge 10*1024*1024 }

これであれば、Set-StrictのVersionがlatestでも通る。

ClosedXMLにて印刷範囲が設定され、「(」を含むシート名を保存するとファイルが壊れる

ClosedXML 0.93.1, 0.94.2で確認。

印刷範囲を設定したファイルを開いて、「(」を含むシートを作成して保存すると、ファイルが壊れてしまいExcelでは開けなくなる。 workbook.xmlを見る限り、シート名をシングルクォーテーションでエスケープできていないのが問題のようだ。 再現ケースを確定してIssueに挙げるか。

Form Load時にRadioButtonのCheckedChangedイベント発生

C# WinFormにてForm Load時にRadioButtonのCheckedChangedイベントが発生する事態に遭遇。 LoadイベントではCheckedをFalseにしているだけだし、他に怪しいイベントも見当たらない。

原因はTabStopが設定された、最小のTabIndexを持つフォームがRadioButtonになっていたため。