実行順序に依存する複数LINQ実行時の遅延評価による副作用

例えば、LINQ1にて元データよりデータを抽出し、これをデータセット1とする。 次に、LINQ2にて、元データからデータセット1を除いたデータセットに対して抽出し、これをデータセット2とする。 これをLINQ3にて、元データからデータセット1とデータセット2を除いたデータセットに対して抽出し、これをデータ・セット3とする。 …. 上記のような処理を1メソッドで行う。 後続のLINQで除外するために、除外するためのデータセットを例えばListとして保持する。 LINQ2はLINQ1の結果が決まらないと決まらない。LINQ3はLINQ2, LINQ1の結果が決まらないと決まらない。だが、後続のLINQに対して結果を伝えるのは(LINQとは無関係の)Listオブジェクト。 このような場合に、上記LINQを順番にコードで記載したとしても、除外Listの値は後続に伝わらない(ことがある?)。おそらく、プログラムの動作として最初にLINQ3の結果を参照した場合にLINQ1,LINQ2の結果を行ってから、という動きはしてくれず、結果、空の除外Listに対してLINQ3を実行してしまっているためと思われる。 遅延されるのが問題なので、LINQの実行結果 IEnumerable型のオブジェクトに対してToList()を実行してやれば即時に確定してこのような副作用は発生しなくなる。遅延評価のメリットは当然なくなるが。

<span title='2018-07-19 00:00:00 +0900 +0900'>July 19, 2018</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;tack41

C#でExcelのバージョンに依存しないCOM経由での操作

C#での社内プログラムでExcelを操作する際、大部分はClosedXMLを利用しているのだが、ActiveXを使用しているなどでうまく動作しない場合にはCOM経由で操作している。 Visual Studioで参照ツリーにExcelのCOM参照を追加するのだが、その時点でPCにインストールされているOfficeのバージョンに対応したCOMを追加する形になる。 ビルド時に参照ツリーにあるCOMを参照するため、参照追加時のPCとビルド時のPCでインストールされているOfficeのバージョンが異なると、ビルド時に警告、またはエラーとなる。 参照追加時には2013、ビルド時に2016だったためにビルド時に警告が出て、そのまま実行すると該当箇所でRuntime Errorでコケる事象が発生した。 社内には、今後2013, 2016が混在する予定のため、どちらかだけしか対応できないとなると困るので、対応方法を調査した。 参照ツリーに追加して開発する形式を事前バインディング、実行時にCOMの名前から該当のCOMを参照する形式を遅延(動的)バインディングというらしい。 事前バインディング Visual StudioでCOMオブジェクトの仕様を把握しているため、補完が効いて開発効率が高い 型情報なども取得済みでコンパイルするため、実行速度は遅延バインディングと比較して速い 使用するOfficeのバージョンを指定する必要がある。 遅延バインディング 使用するOfficeのバージョンを指定する必要がない Visual Studioでの補完は効かず、各オブジェクト、メソッドの情報を調べながら呼び出す必要がある。大変。 実行時に型チェックを行うため、遅い。実行時エラーが出る可能性も。 遅延バインディングは、各メソッドをInvokeMemberで引数を調査しながら呼び出す必要があり、とても大変。以下のサイトにこの大変さをWrapするコードが公開されていた。 https://zenmai.wordpress.com/2011/06/24/excel%E3%81%AE%E5%8F%82%E7%85%A7%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%9B%E3%81%9A%E3%81%ABexcel%E3%82%92%E4%BD%BF%E3%81%86c/ とても素晴らしいのでぜひ利用しようと考えたのだが、(当然ながら)COMオブジェクトのすべてが実装されているわけではないので不足個所を追加実装する必要があり、結構大幅な追加が必要と思われた。 で、たどり着いたのがこちらの記事。 https://teratail.com/questions/109579 なんと、dynamicという宣言に変更するだけで、ビルド時のチェックはやめて実行時に動的に呼び出してくれるとのこと。 (COMオブジェクトの生成部分は固有の書き方への変更が必要)。素晴らしい!! 実際にdyamicに変更したところ、WorkbookオブジェクトへのReleaseComObject呼び出し時にエラーが発生。 http://hiro-syumi.ldblog.jp/archives/36511362.html こちらの記事を参照させてもらってエラー箇所のみobject型へのキャスト処理を追加したところ、問題なく動作するようになった。 このdynamicの利用だが、最初からこれを前提に行うと上記の通りVisualStudioによるサポートが効かないので開発効率はかなり落ちると思われる。今回のようにCOM参照を追加して事前バインディングで実装したうえで、dynamicに書き換えるという形が効率が良いと感じた。

<span title='2018-07-04 00:00:00 +0900 +0900'>July 4, 2018</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;tack41

COM経由でのExcel操作は地獄?

VBAでExcelを操作するプログラムをC#に移行。 印刷に使用するActiveXオブジェクトを含む雛形ファイルを対象とするため、Managedな操作をするClosedXMLやNPOIではうまく動作せず、Interop.Excel経由で行った。 ClosedXML等と比較して圧倒的に動作が遅いのは当然として、シートをある程度(10枚以上?)コピーするとtmpファイルを書き込めない旨のエラーが発生する。 タイミングは実施するたびに違い、運が良ければエラーが発生しないこともある。 同様のエラーを探したが、結論としてはエラー時にリトライする処理を追加するくらいしかないらしい… https://answers.microsoft.com/ja-jp/msoffice/forum/msoffice_excel-mso_winother-mso_2010/vba%E3%81%A7%E3%82%B7%E3%83%BC%E3%83%88%E3%82%B3/b8b84a3e-d1f8-48a5-8623-04023c8510e8 こういった対処方法は、仕方ないとしても凹みますね。 機会があればネックとなっているActiveXオブジェクトをManagedな操作で置き換えていきたい。

<span title='2018-02-24 00:00:00 +0900 +0900'>February 24, 2018</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;tack41

AccessにOleDbアクセス時、日付型のパラメータを使用する場合は型を明示する必要あり

はまった… Accessのときのみ発生。 Parameterを使う際、通常、設定した値の型からよきにはからって処理してくれるが、AccessでDateTime、特に時刻部分が設定されている場合ではエラーとなる。 http://yan-note.blogspot.jp/2008/08/systemdatacommondbparameteraccessdate.html

<span title='2018-02-20 00:00:00 +0900 +0900'>February 20, 2018</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;tack41

C#でのDbTransaction.Rollback

C#のTransactionにて、DbTransactionをusing句で使えば、Disposeの際にRollbackされるので、明示的なRollbackが不要とあったのだが、どうもそのように記載しているサイトのほうが少ないように見える。 MSDNで確認したところ、そうあるべきではるが、ベンダー依存のため前提とするのはだめらしい。 Dispose should rollback the transaction. However, the behavior of Dispose is provider specific, and should not replace calling Rollback. https://msdn.microsoft.com/ja-jp/library/bf2cw321(v=vs.110).aspx SqlTransactionだけならいいかもしれないが、OleDb(Access)やMySQLも共通化している今のコードでは駄目なようだ。try catch使うしかない。 https://msdn.microsoft.com/ja-jp/library/system.data.idbtransaction.rollback(v=vs.110).aspx

<span title='2018-02-02 00:00:00 +0900 +0900'>February 2, 2018</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;tack41