Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9707

C#でSqlConnectionの後片付けをする時のusingの使い分け

$
0
0
背景 保守してたら、try-finallyでDBのコネクションの接続をクローズして管理しているコードを発見しました。 ぱっと見usingを使った方が良さげに思え書き換えたけど、例外が発生することに…。 ※. usingはJavaでいうtry-with-resourcesという認識 結論 コネクションの再利用する前提なのにusingで破棄していたことが原因 コネクションの再利用しないならusing、再利用するならtry-finallyするなり自分で管理しよう! 調査 テストコードで検証してみる try-finallyとusingのそれぞれを使用した場合のサンプルコードで実験してみました。 長いので詳細はこちらをご参照ください。 public class SampleDao { // 省略 /// <summary> /// try-finallyでCloseする /// </summary> public void execute() { SqlCommand command = new SqlCommand(); command.CommandText = "SELECT GETDATE()"; command.Connection = this.Connection; try { command.Connection.Open(); command.ExecuteReader(); } finally { command.Connection.Close(); } } /// <summary> /// usingでCloseする /// </summary> public void executeWithUsing() { SqlCommand command = new SqlCommand(); command.CommandText = "SELECT GETDATE()"; command.Connection = this.Connection; command.Connection.Open(); using (command.Connection) { command.ExecuteReader(); } } // 省略 } 上記のサンプルを用いて以下のようなユニットテストで検証してみます。 [TestClass] public class UnitTest1 { // 省略 [TestMethod] public void 複数利用で再利用する場合() { SampleDao dao1 = new SampleDao(); // 初回 try { dao1.execute(); } catch (Exception e) { Assert.Fail(); } Assert.AreEqual(ConnectionState.Closed, dao1.Connection.State); Assert.AreNotEqual("", dao1.Connection.ConnectionString); // 2回目 try { dao1.execute(); } catch (Exception e) { Assert.Fail(); } Assert.AreEqual(ConnectionState.Closed, dao1.Connection.State); Assert.AreNotEqual("", dao1.Connection.ConnectionString); } // 省略 [TestMethod] public void using_複数利用で再利用する場合() { SampleDao dao1 = new SampleDao(); // 初回 try { dao1.executeWithUsing(); } catch (Exception e) { Assert.Fail(); } Assert.AreEqual(ConnectionState.Closed, dao1.Connection.State); Assert.AreEqual("", dao1.Connection.ConnectionString); // 2回目 try { dao1.executeWithUsing(); Assert.Fail(); } catch (Exception e) { Assert.AreEqual("", dao1.Connection.ConnectionString); } } } 結果は以下のようになり、usingを使用したケースではコネクションの再利用はできないことになりました。 usingを使用しない 初回 2回目 単一利用で再利用しない場合 正常終了 N/A 複数利用で再利用しない場合 正常終了 正常終了 複数利用で再利用する場合 正常終了 正常終了 usingを使用 初回 2回目 単一利用で再利用しない場合 正常終了 N/A 複数利用で再利用しない場合 正常終了 正常終了 複数利用で再利用する場合 正常終了 InvalidOperationException そもそもusingってどういうもの? Microsoftのusing ステートメント (C# リファレンス)を引用すると以下のように説明されていました。 IDisposable オブジェクトの正しい使用を保証する簡易構文を提供します。 C# 8.0 以降は、using ステートメントによって IAsyncDisposable オブジェクトが適切に使用されるようになります。 上記の参考サイトから詳細として紹介されているステートメント > using ステートメントでは ステートメントは、 using 1 つまたは複数のリソースを取得し、ステートメントを実行してから、リソースを破棄します。 と説明されていました。 私の言葉でまとめると、リソースは必ずDisposeメソッドによって破棄されるよ、ということが肝のようです。 ※. ただし、MicrosoftSqlConnection.Close メソッド > 注釈では以下のように説明されておりちょっと紛らわしい Close と Dispose は機能的に同等です 今回のケースにおけるusingの使用有無での違い こんな感じになります。 usingを使用しない コネクションをオープン SQL実行 コネクションをクローズ コネクションは破棄されていないので 1. に戻る usingを使用 コネクションをオープン SQL実行 コネクションをクローズ Disposeでコネクションが破棄 コネクションは破棄されているので 1. に戻ろうとすると例外が発生 まとめ 今回は書き換えたusingによってコネクションが破棄されたことが、既存のコードとの違いであり、例外発生の原因でした。 そのため、実装によっては必ずしもusingがベストではない場合もあることがわかりました。 便利ですが仕様を理解しつつ注意して使っていきたいです! 参考 using ステートメント (C# リファレンス) ステートメント > using ステートメント SqlConnection.Close メソッド using文で初期化したDbConnection、Closeを書くべき?書かなくていい? C# using() とは?意味と使い方は?try catch、close、dispose、例外処理、名前空間、自動リソース開放機能

Viewing all articles
Browse latest Browse all 9707

Trending Articles