モルモルしている

最近はテスト業務ばっかりで書くことがない

Listからnull要素をすべて削除する

まあ、条件次第だから指定の要素はnullじゃなくてもいいんだけど。
List(T).RemoveAll メソッド (System.Collections.Generic)

public class Sample
{
     public static void Main()
     {
          List<string> sampleList = new List<string>();
          sampleList.Add("ABC");
          sampleList.Add("DEF");
          sampleList.Add(null);
          sampleList.Add("GHI");
          sampleList.Add(null);

          // nullを全部削除する
          sampleList.RemoveALL(MatchNullable);
     }

     private static bool MatchNullable(string s)
     {
          return (s == null) ? true : false;
     }
}

三項演算子使って無駄なif文を削減する

画面上にいくつかのコントロールを配置して、そのコントロールごとに入力されていたら値を取得する、ということをしていました。

if (ComboBox1.SelectedIndex != -1)
{
     var a = ComboBox.SelectedValue;
}

↑的な構文がコントロールの数だけあって無駄にメソッドが長くなってうっとうしい。
以前よくわからないなりに調べながらLINQを使っていた時に「?」と「:」でなんかいい感じに条件分岐ができた気がして、あれは確かラムダとかいうものだ、とぐぐりにぐぐって「ラムダ式 三項演算子」という言葉にたどり着く。*1
第1回 ラムダ式 − @IT

そして大量のif文を↓こうじゃ

var a = (ComboBox1.SelectedIndex == -1) ? null : ComboBox.SelectedValue;

なにこれ超スッキリ。
分岐するためだけに無駄に三行(if文と{})が付随してたのに、一行で解決。
メソッドの行数がっつり削減。これは気持ちいい。
忘れないように(忘れても掘り返せるように)メモメモ。

*1:たどり着いたんだけど、後からよくよく調べたら私の利用しているのは三項演算子だけでラムダ式は関係ないのでした。恥。

TransactionScopeを使うときの注意事項

Transactionはかけるべき、と思っていたけど今までは自分用のツールしか作ってなかったのでまあいいか、してた。
けどさすがに業務用アプリを作るのにそれが許されるわけはなくって。

調べていきついたTransactionScopeが便利で、それを使うことにしたんだけど前記事のTimeout以外にもいくつか引っかかったから覚え書き。

  • System.Transactions.dll を参照追加する。
  • INSERT、UPDATE、DELETE、TRUNCATEなどはまるっとロールバックしてくれる。それだけでなくてDROP TABLEやCREATE TABLEもロールバックしてくれる。


usingで囲ってCompleteしなければ勝手にロールバックしてくれるというのは結構便利で、テストコード書くときに

using(var tran = new TransactionScope())
{
 テストコード;
}

っていうふうにusingで囲ってCompleteなしにしておくと、テスト終了時に勝手にDBへ更新した内容が破棄されるのです。
DBに対して検証環境整えたり処理で変更をかけてもテスト終了時に全部破棄されるから初期化とか必要なくて本当に便利。
最終的にDBと連携する部分のテストはモックに置き換えなきゃいけないなあとは思っているけれど、取り急ぎテストしたかったのでこの方法でDB汚さずにテストできてすごく助かってる。

MSDTCは気を抜いてコード書いてるとデバッグ時に要求されてびっくりするから気を付けながら書かないといけない。
注意注意。

TransactionScopeのTimeout

今日はTimeOutではまった。

前提として、今作っているプログラムはTransactionScopeをかけて順次登録されたストアドを呼び出して実行するプログラム。
実行するストアドが一つでもエラーを返したらロールバックして全部なかったことにする。

そんなプログラム用のストアドを作成していざプログラムを実行したら
Timeout に達しました。操作が完了する前にタイムアウト期間が過ぎたか、またはサーバーが応答していません。
というエラーが返ってきた。

気にしていなかったけどDapperのCommandTimeoutってどうやって設定すんの?と思ったけど

public static int Execute(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null);

↑この通りで普通に引数に指定できたのでラッキーと思って指定した。
CommandTimeoutの単位がわからなかったけど、SqlCommandにTimeOut値渡しているだけだろうしDapperのソースから↓の記述を見つけたのでたぶん「秒」であってる。

/// <param name="commandTimeout">Number of seconds before command execution timeout</param>


これで大丈夫だろうと試しにプログラムを実行してみたら今度は
現在の接続と関連付けられているトランザクションは完了しましたが、破棄されていません。トランザクションは、接続が SQL ステートメントの実行に使用される前に破棄されなければなりません。
というエラーが返ってきて滝汗。

一瞬ストアド内でTransactionが自動コミットされてしまうような記述があったかと思ったけど特にない。
というかTransactionScopeはCREATE TABLEでもロールバックしてくれるのにまさかそんな、と。

しかもデータ量が少ない時には起こらない。データ量が多いとこのエラーが起きる。
処理内容は変わらないのに何がトランザクションを完了させているのか、わからないままストアドの処理を改善していたらエラーが出るタイミングが変わった。
1本目(一番重い処理)でエラーが出てたけど、データ量や処理方法を検討する中で2本目、3本目と後ろにずれてった。
これもしかして処理方法の問題じゃなくて…と思って「TransactionScopeのTimeout」について調べた。
そもそもTransactionにTimeoutがあること自体正直意識してなかったけど。

調べたところ、TransactisonScopeのTimeout初期値は60秒のようです。
http://msdn.microsoft.com/ja-jp/library/ms973865.aspx#introsystemtran_topic12.NET Framework 2.0 の System.Transactions について

なので↓参考URLさんの通りTransactionScopeに長めのTimeout値を設定したらエラーが出なくなった。
TransactionScopeのタイムアウト設定 (TransactionScope, TransactionScopeOption, TimeSpan) - いろいろ備忘録日記

でも↓の通り、TransactionScopeで何分を設定しても規定で最大値が10分にされており、10分で結局エラーになってしまうそうだ。
MachineSettingsSection.MaxTimeout プロパティ (System.Transactions.Configuration)

最大値を変えたければmachine.configに以下の設定を追加しないといけないのだそうです。

<configuration>
  <system.transactions>
    <machineSettings maxTimeout="01:00:00" />
  </system.transactions>
</configuration> 

そしてそんなmachine.configは↓の場所にあります。

32bitなら C:\Windows\Microsoft.NET\Framework\(.Net Frameworkのバージョン)\config\machine.config
64bitなら C:\Windows\Microsoft.NET\Framework64\(.Net Frameworkのバージョン)\Config\machine.config

とりあえず10分に延ばしたけど、10分で処理が全部完了する保証はないから上司が出張から帰ってきたら相談しなくちゃ。。。
しかしTimeoutに達したならTimeoutになったと言ってくれればいいのにわかりにくいエラーメッセージでいやだなー。

2015.11.16 追記
同僚に「で、machine.configにどう追加すればいいの?」って言われて久しぶりすぎてどこにどう追加したか思い出せなかったのでメモっとく。
machine.configの一番最後に移動しての前に

<system.transactions>
  <machineSettings maxTimeout="01:00:00" />
</system.transactions>

を貼り付ければOK(私は実際には↓15分を指定してみた)
f:id:MoruMe:20151116212357p:plain

OSは64bitでもPGが特に64bit用というわけでなければ見ているのは32bitの「C:\Windows\Microsoft.NET\Framework\(.Net Frameworkのバージョン)\config\machine.config」の方の設定のようです。
設定がちゃんと有効になっているかどうかは TransactionManager.MaximumTimeout で確認することができるので、MessageBox.Show( TransactionManager.MaximumTimeout.ToString())とかで値を見て、異なってたら設定するmachine.configを見直してください。

指定したConfigファイルを読み込む

WPFC#でバッチプログラム作成中。

通常ならApp.Config読めばいいんだけど一つのプログラムに対して複数configファイルを用意して、実行時に引数に指定されたconfigファイルを読んで処理を行うことになった。

参考URL:
特定の構成ファイルを読み込む (app.config, ConfigurationManager, OpenMappedExeConfiguration, ExeConfigurationFileMap) - いろいろ備忘録日記
FlareVanish – プロのマネごとプロマネ! » Blog Archive » 【VB2005】任意のアプリケーション構成ファイルを読み込む

というわけで上記の参考URLさまを参考に実現。

1.参照設定で「System.Configuration.dll」を追加。
  ちゃんとusingにも追加。

2.読み込むconfigファイルを作成。ファイル名は「Hoge.config」とする。
  とりあえずこんな感じで。

<configuration>
 <appSettings>
   <add key="HogeKey" value="hogehoge" />
 </appSettings>
</configuration>

3.↓って感じで設定ファイルを読み込む 

static void Main(string[] args)
        {
            // 既定の構成ファイルとは別のファイルを構成ファイルとして読み込む.
            var configFile = @"Hoge.config";
            var exeFileMap = new ExeConfigurationFileMap { ExeConfigFilename = configFile };
            var config     = ConfigurationManager.OpenMappedExeConfiguration(exeFileMap, ConfigurationUserLevel.None);
            var str        = config.AppSettings.Settings["HogeKey"].Value;

            Console.WriteLine(str);
        }

残しとかないと絶対忘れる。