例外がスローされたときに SQL 接続を確実に閉じる適切な方法は何ですか?
-
02-07-2019 - |
質問
こんな感じのパターンをよく使います。これで大丈夫なのか、それともここでは適用しないベストプラクティスがあるのか疑問に思っています。
具体的には、私は疑問に思っています。例外がスローされた場合、finally ブロックにあるコードは、接続が適切に閉じられることを保証するのに十分ですか?
public class SomeDataClass : IDisposable
{
private SqlConnection _conn;
//constructors and methods
private DoSomethingWithTheSqlConnection()
{
//some code excluded for brevity
try
{
using (SqlCommand cmd = new SqlCommand(SqlQuery.CountSomething, _SqlConnection))
{
_SqlConnection.Open();
countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
}
}
finally
{
//is this the best way?
if (_SqlConnection.State == ConnectionState.Closed)
_SqlConnection.Close();
}
//some code excluded for brevity
}
public Dispose()
{
_conn.Dispose();
}
}
解決
データベース処理コードを「using」内にラップします。
using (SqlConnection conn = new SqlConnection (...))
{
// Whatever happens in here, the connection is
// disposed of (closed) at the end.
}
他のヒント
.Net Framework が接続プールを管理するのには理由があります。信じてください!:)データベースに接続して接続をリリースするためだけに、それほど多くのコードを記述する必要はありません。
「using」ステートメントを使用するだけで、「IDBConnection.Release()」によって接続が閉じられるので安心してください。
非常に精巧な「ソリューション」は、バグのあるコードを生成する傾向があります。シンプルな方が良いです。
MSDN ドキュメント これをかなり明確にしてください...
- Close メソッドは、保留中のトランザクションをロールバックします。その後、接続プールへの接続を解放するか、接続プールが無効になっている場合は接続を閉じます。
おそらく接続プーリングを無効にしていない (そして無効にしたくない) ため、「Close」を呼び出した後の接続の状態は最終的にプールによって管理されます。開いているすべての接続をデータベース サーバー側から見ると混乱する可能性があるため、これは重要である可能性があります。
- アプリケーションは Close を複数回呼び出すことができます。例外は生成されません。
では、なぜクローズドのテストをわざわざ行う必要があるのでしょうか?Close() を呼び出すだけです。
- Close と Dispose は機能的に同等です。
このため、 を使用して ブロックすると接続が閉じられます。 を使用して Dispose を呼び出します。
- クラスの Finalize メソッド内の Connection、DataReader、またはその他の管理オブジェクトに対して Close または Dispose を呼び出さないでください。
重要な安全上のヒント。ありがとう、エゴン。
「_SqlConnection.State == ConnectionState.Closed」とは、!= を意味していると思います。
これは確かにうまくいきます。接続オブジェクト自体を using ステートメント内に含めるのが一般的だと思いますが、何らかの理由で同じ接続オブジェクトを再利用したい場合には、この方法が適しています。
ただし、必ず変更する必要があるのは、Dispose() メソッドです。接続オブジェクトはその時点ですでにファイナライズされている可能性があるため、dispose で接続オブジェクトを参照しないでください。代わりに、推奨される破棄パターンに従う必要があります。
とにかく IDisposables を使用しているので。'using' キーワードを使用できます。これは基本的に、finally ブロックで destroy を呼び出すのと同じですが、見た目は良くなります。
答えについては、この質問を参照してください。
接続の有効期間が単一のメソッド呼び出しである場合は、 using
言語の機能を使用して接続を適切にクリーンアップします。その間、 try/finally
ブロックは機能的には同じですが、より多くのコードが必要となり、IMO は読みにくくなります。接続状態を確認する必要はなく、電話をかけることができます。 Dispose
関係なく、接続のクリーンアップが処理されます。
接続の有効期間が、それを含むクラスの有効期間に対応する場合は、次のように実装します。 IDisposable
そして接続をクリーンアップします Dispose
.
表示されているように、接続を閉じるコードを「Finally」ブロック内に配置します。最後に、例外がスローされる前にブロックが実行されます。"using" ブロックを使用することも同様に機能しますが、明示的な "Finally" メソッドの方がより明確であると思います。
多くの開発者にとってステートメントの使用は昔からの習慣ですが、若い開発者はそれを直接知らないかもしれません。
試してみる必要はありません。最後に「using」について説明します。 は 試してみてください。ついに
これを提案してもいいでしょうか:
class SqlOpener : IDisposable
{
SqlConnection _connection;
public SqlOpener(SqlConnection connection)
{
_connection = connection;
_connection.Open();
}
void IDisposable.Dispose()
{
_connection.Close();
}
}
public class SomeDataClass : IDisposable
{
private SqlConnection _conn;
//constructors and methods
private void DoSomethingWithTheSqlConnection()
{
//some code excluded for brevity
using (SqlCommand cmd = new SqlCommand("some sql query", _conn))
using(new SqlOpener(_conn))
{
int countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
}
//some code excluded for brevity
}
public void Dispose()
{
_conn.Dispose();
}
}
それが役立つことを願っています:)