18

SqlClient.SqlDataReader は .NET 管理オブジェクトですか? Close() メソッドを明示的に呼び出して開いている接続を閉じる必要があるのはなぜですか? そのようなオブジェクトのスコープを使い果たした場合、これは自動的に閉じられるべきではありませんか? とにかくガベージコレクターはそれを片付けるべきではありませんか?

ここでのベストプラクティスは何かを理解するのを手伝ってください。

ここで関連する質問を見ましたが、Web アプリケーションで発生した問題をさらに説明しています。問題は、接続が不足していたことです。詳細なエラーは次のとおりです。

Exception: System.InvalidOperationException
Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Source: System.Data 

at  System.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(SqlConnectionString options, Boolean& isInTransaction)
at  System.Data.SqlClient.SqlConnection.Open()

これを修正するには、すべての SQLDataReader オブジェクトを明示的に閉じる必要がありました。

.NET Framework 3.5 を使用しています

4

8 に答える 8

37

確かに、範囲外になると収集されます(他の参照がない場合)。収集されると、その Dispose() メソッドによって閉じられます。ただし、GC がいつ割り当てを解除するかはわかりません。リーダーを閉じないと、データベースへの接続がすぐになくなります。

参考文献

~ ウィリアム・ライリー・ランド

于 2008-10-30T15:02:00.080 に答える
17

@フロスト中尉

私たちのショップでは原則として、すべてのデータベース呼び出しを Try...Finally ブロックで明示的にラップし、finally セクションでデータ接続をキャッチして閉じます。大きなトラブルシューティングの頭痛の種を避けるために、少しの努力をするだけの価値はあります。

同様のルールがありますが、IDisposable を実装するオブジェクトは「using」ブロックを使用する必要があります。

using (SqlConnection conn = new SqlConnection(conStr))
{
     using (SqlCommand command = new SqlCommand())
     {
        // ETC
     } 
}

using ブロックは、例外があっても、スコープを離れるとすぐに Dispose を呼び出します。

于 2008-10-30T15:13:18.200 に答える
11

(接続を再利用しない限り) 良い方法の 1 つは、Command Behavior を SqlDataReader に追加して、破棄されたときに接続を閉じることです。

SqlDataReader rdr = cmd.ExecuteReader( CommandBehavior.CloseConnection );

これを追加すると、SqlDataReader オブジェクトが閉じられた (またはガベージ コレクションが行われた) ときに、データベースへの接続が確実に閉じられます。

ただし、前に述べたように、同じメソッド内の別の操作でデータベース接続を再利用する予定がある場合は、これを行う必要はありません。

于 2008-10-30T15:18:20.080 に答える
4

他の誰もがこれを言ったと思いますが、私はそれを明確にしたかったのです:

スコープ外は、即時のガベージ コレクションを意味するものではありません。

アプリは、さまざまなレベルで「うまくプレイ」する必要があります。接続を閉じると、それを行うのに役立ちます。それらのレベルのいくつかを調べてみましょう。

1: ガベージ コレクションに依存していません。理想的には、ガベージ コレクションが存在する必要はありません。しかし、そうです。しかし、これに頼るべきではないことは確かです。

2: データベース接続を保持していません。接続は通常プールされますが、制限があることがわかりました。これを必要以上に長く保持すると、アプリは悪いリンゴになります。

3: ネットワーク トラフィックを生成していません。すべてのデータベース接続は、基本的に TCP 接続です。開いたままにしておくと、おそらく、Are you still there? のようなネットワーク トラフィックが生成されます。はい。トラフィックは少ないですが、混雑したネットワークではこれは悪い習慣です。また、SQL Server 自体がリソースを使用して接続を維持しています。そのSQLサーバーにアクセスしようとしている他の人がより有効に活用できるリソース。

データベース アクセスについて考えるときは、ネットワーク リソースについても考える必要があります。データを取得するいくつかの方法は、乗り物に不要なものを持ち込む可能性があるため、良くありません。ADO の以前のバージョンは、この種のもので有名でした。データが必要なときにスキーマ情報を戻す。確かに、接続数が少ない場合、これは問題ではありません。しかし、どのデータベースにも少数の接続しかないのはいつからですか。したがって、このことについて考えて、リソースを乱用しないようにしてください。

Web アプリのユーザーが 100 人しかいない場合、気にする必要はないかもしれません。しかし、100,000 はどうでしょうか。スケーリングするとどうなるかを常に考慮してください。

于 2008-10-30T18:22:00.833 に答える
3

明示的に閉じない場合、ガベージ コレクターがそれを「収集」するのを待ってそこに座っています...それが起こった後にのみ、ADO.Net 接続プールに解放され、別の Connection.Open によって再利用されます。したがって、介在するすべての時間中、接続に対する他のすべての要求は、まったく新しい接続を作成する必要があります。

GC が実行されるまでの時間と、実行されているデータベース リクエストの数によっては、データベースへの使用可能な接続が不足する可能性があります。

ただし、Command.ExecuteReader() メソッドには、CommandBehavior というオプションのパラメーターがあります。これは列挙型で、値は CommandBehavior.Default、CommandBehavior.SingleResult、CommandBehavior.SchemaOnly、CommandBehavior.KeyInfo、CommandBehavior.SingleRow、CommandBehavior.SequentialAccess、および CommandBehavior.CloseConnection です。

この列挙には、メンバー値のビットごとの組み合わせを許可する FlagsAttribute 属性があります。ここで関連するのは、最後の値 (CommandBehavior.CloseConnection) です。これは、データ リーダーが閉じられたときに接続を閉じるよう Command オブジェクトに指示します。 http://msdn.microsoft.com/en-us/library/system.data.commandbehavior.aspx

残念ながら、デフォルトでは、データ リーダーが閉じられたときに接続を閉じることはありませんが、メソッドが完了したらすぐにプールに接続を解放したい場合は、このパラメーターを CommandBehavior.CloseConnection として渡すことができます (また渡す必要があります)。それ...

于 2008-10-30T17:41:50.787 に答える
2

「マネージド コード」という用語で参照される「マネージド」リソースはメモリです。それでおしまい。データベース接続を含め、その他の希少なリソースは、破棄可能なパターンでラップする必要があります。

これが問題となる理由は、ガベージ コレクターがスコープ外になった瞬間にすべてのオブジェクトに対して実行されないためです。より少ない頻度でより多くのアイテムを収集する方がはるかに効率的です。したがって、コレクターがオブジェクトを処分するのを待っていると (そうです、idisposable を実装すれば最終的にそうなるでしょう)、多くのデータベース接続を開いたままにしておくことが、思っているよりずっと長くなる可能性があります。

于 2008-10-30T15:05:05.690 に答える
2

問題は接続ではなく、SqlDataReader によって保持されている SQL カーソルです。最初のものを閉じずに 2 番目のものを開こうとすると、例外がスローされます。

于 2008-10-30T15:15:53.313 に答える
1

また、例外がスローされたときに何が起こるかを考慮してください。実行中のコードから突然追い出された場合、接続が閉じられるかどうかはわかりません。

私たちのショップでは原則として、すべてのデータベース呼び出しを Try...Finally ブロックで明示的にラップし、finally セクションでデータ接続をキャッチして閉じます。大きなトラブルシューティングの頭痛の種を避けるために、少しの努力をするだけの価値はあります。

于 2008-10-30T15:09:28.177 に答える