SqlConnectionはSQL Server、Azure SQL、または他のサポートされているSQL Server互換エンドポイントへの論理的な接続の一つを表します。 オブジェクトを開くと、接続プールから物理的な接続が得られます。 閉じたり処分したりすると、その物理的な接続がプールに戻されます。
作業単位には短命の SqlConnection オブジェクトを使いましょう。 アプリケーション用にグローバル接続を一つだけ開いておかないでほしいです。
接続構成を構築する
アプリケーションの設定システムから接続文字列を読み込みます。 コードが検証や設定を追加する必要がある場合は SqlConnectionStringBuilder を使いましょう:
string configuredConnectionString =
configuration.GetConnectionString("Orders")
?? throw new InvalidOperationException(
"Connection string 'Orders' wasn't configured.");
var builder = new SqlConnectionStringBuilder(configuredConnectionString)
{
ApplicationName = "Orders.Api",
};
builder.ConnectionStringからSqlConnectionを作りましょう。 ユーザーの入力を文字列に連結しないでください。 認証パターン、安全な保存方法、構文については 「接続文字列」を参照してください。
接続を開いて破棄する
同期コードでは Open を呼び出し、非同期コードでは OpenAsync を呼び出します。 各独立操作ごとに新しい論理接続を開きます:
public static async Task<string?> LoadOrderStatusAsync(
string connectionString,
int orderId,
CancellationToken cancellationToken)
{
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);
const string sql = """
SELECT Status
FROM Sales.Orders
WHERE OrderId = @orderId;
""";
using var command =
new SqlCommand(sql, connection) { CommandTimeout = 30 };
command.Parameters.Add(
new SqlParameter("@orderId", SqlDbType.Int) { Value = orderId });
object? value =
await command.ExecuteScalarAsync(cancellationToken);
return value is null or DBNull ? null : (string)value;
}
await using文は、成功、エラー、またはキャンセルのいずれかで接続を処理します。 プーリングが有効な場合、ディスポーザーは通常リセットされ、ネットワークソケットを閉じる代わりに物理接続を戻します。
リーダーやコマンドは、それらを所有している接続より先に破棄してください。 ガベージコレクションやファイナライザーによって接続がプールに返されることを当てにしないでください。
非同期 API の使用
ウェブサーバー、サービス、ユーザーインターフェース、ワーカーでのネットワークバウンドデータベース作業には非同期呼び出しを使用する:
OpenAsync(cancellationToken)ExecuteNonQueryAsync(cancellationToken)ExecuteReaderAsync(cancellationToken)ExecuteScalarAsync(cancellationToken)ReadAsync(cancellationToken)
Asynchronous Processing=trueは必要ありません。 Microsoft。Data.SqlClient 4.0以降のバージョンでは、その接続文字列キーワードをサポートしていません。
現在の非同期操作が完了する前に、接続、コマンド、リーダーの操作を始めないでください。
キャンセルとタイムアウトを適用してください
呼び出し元の CancellationToken を、すべての非同期データベース呼び出しに渡してください。 キャンセルは、提供者に保留中の作業の停止を求めることですが、即時完了が保証されるわけではありません。 制限付き接続とコマンドのタイムアウトを使い続けてください。
これらの制御には別々のスコープがあります:
| 制御 | Scope |
|---|---|
Connect Timeout |
接続確立またはプール接続の待機 |
SqlCommand.CommandTimeout |
1回のコマンド実行 |
CancellationToken |
発信者からの非同期操作のキャンセルリクエスト |
タイムアウトやキャンセルはサーバーが操作をロールバックしたことを証明しません。 複数の変更を一つの単位としてコミットまたはロールバックする必要があるトランザクションを使い、その操作の冪等性やトランザクション結果から再試行の判断を行います。
接続状態の理解
StateプロパティはConnectionState列挙からスナップショットを返します。
| State | Meaning |
|---|---|
Closed |
論理接続が開かれていません。 |
Connecting |
公開作戦が進行中です。 |
Open |
論理的なつながりは開かれています。 |
コマンドの前に State を健康チェックとして使わないでください。 ネットワークはチェック後に故障する可能性があります。 操作を実行し、その結果生じた例外を処理します。
ドライバーは通常、閉鎖から開閉および開閉への切り替えを報告します。
Executing、Fetching、Brokenをアプリケーションのライフサイクル段階として観察することに頼らないでください。
StateChangeイベントは状態遷移を報告します。 InfoMessageイベントは例外にはならない情報メッセージやサーバー警告を報告します。 これらのイベントは、並行作業の調整ではなく診断用に使ってください。
同時に接続を共有しないようにしましょう
SqlConnection、 SqlCommand、 SqlDataReader、 SqlTransaction は複数のスレッドによる同時使用をサポートしていません。 各同時操作に独自の接続を与え、接続プーリングで物理接続を再利用できるようにします。
マルチプルアクティブ結果セット(MARS)は、サポートシナリオにおいて1つの接続で複数のアクティブバッチを許可します。 SqlClientオブジェクトをスレッドセーフにするわけではなく、セッションやトランザクションのルールを追加します。 特定の操作が必要でない限り、無効にしておくのが良いでしょう。
依存注入のシングルトンとしてオープン SqlConnection を登録しないでください。 接続文字列、不変オプションオブジェクト、または新しい接続を作成するファクトリーを登録します。
トランザクションは慎重に使用する
ローカルトランザクションはその接続に属します。 トランザクション内のすべてのコマンドはその接続を使用し、 Transaction プロパティを設定しなければなりません。
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);
await using SqlTransaction transaction =
(SqlTransaction)await connection.BeginTransactionAsync(cancellationToken);
using var command = new SqlCommand(sql, connection, transaction);
command.Parameters.Add(
new SqlParameter("@value", SqlDbType.Int) { Value = value });
await command.ExecuteNonQueryAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken);
もし操作が CommitAsync前に失敗した場合、トランザクションを破棄するとロールバックされます。 取引は短くしましょう。 データベースのトランザクションがロックを保持している間は、ネットワーク通話やユーザー操作、無関係な計算はしないでください。
System.Transactions.Transaction.Currentがアクティブ時は、OpenとOpenAsyncが自動的に自動的に入隊します。 操作がアンビエントトランザクションの外に留まる必要がある場合にのみ Enlist=false 設定します。
論理接続を1つ測定する
StatisticsEnabledをtrueに設定して、1つのSqlConnectionオブジェクトのプロバイダー統計を収集します:
await using var connection = new SqlConnection(connectionString)
{
StatisticsEnabled = true,
};
await connection.OpenAsync(cancellationToken);
connection.ResetStatistics();
using var command = new SqlCommand(sql, connection);
await command.ExecuteNonQueryAsync(cancellationToken);
System.Collections.IDictionary statistics =
connection.RetrieveStatistics();
long roundTrips =
Convert.ToInt64(statistics["ServerRoundtrips"]);
RetrieveStatistics スナップショットを返します。
ResetStatistics 新しい測定境界が始まります。
StatisticsEnabled=falseを収集停止に設定してください。これまでに収集した値は引き続き利用可能です。 統計は接続オブジェクトごとに割り当てられ、オーバーヘッドが増えるため、すべての本番リクエストではなくターゲット診断に有効にしてください。
プロセス全体のプールおよび接続測定には 、SqlClientの診断カウンターを使用します。
接続エラーを処理する
故障を記録、翻訳、または再試行できる境界線で SqlException を捉えましょう。 レコード:
NumberStateClassClientConnectionId- 操作名と設定されたサーバーおよびデータベース識別子
接続文字列、password、client secret、access tokenはログにしないでください。
切断された接続は処分しましょう。 プールは無効な物理接続を検出すると削除します。 認証情報、トークン、証明書、DNSターゲット、サーバーが変更された場合は、再試行前に設定を修正してください。
一時的な故障には限定リトライロジックを使用してください。 初期オープンリトライ、アイドル接続リカバリー、コマンドリトライは異なるメカニズムです。 詳細は 「設定可能な再試行ロジック」を参照してください。