使用 Microsoft.Data.SqlClient 連線到資料來源

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 一指令執行
CancellationToken 來電者請求取消非同步操作

逾時或取消並不能證明伺服器已回滾該項操作。 當多個變更必須作為一個單位提交或回滾時,使用交易,並根據操作的冪性與交易結果做出重試決策。

了解連線狀態

State 屬性會傳回 ConnectionState 列舉的快照。

State Meaning
Closed 邏輯連結並不開放。
Connecting 一場公開行動正在進行中。
Open 邏輯上的連結是開放的。

不要在每次指令前用 State 來做健康檢查。 網路在任何檢查後都可能故障。 執行操作並處理產生的例外。

驅動程式通常會回報從關閉到開啟以及從開啟到關閉的轉換。 不要將觀察到的 ExecutingFetchingBroken 視為應用程式生命週期階段。

StateChange 事件會回報狀態轉換。 InfoMessage 事件會報告資訊性訊息和伺服器警告,但不會演變成例外狀況。 這些事件應該用來做診斷,而不是協調同時進行的工作。

不要同時分享連線

SqlConnectionSqlCommandSqlDataReaderSqlTransaction不支援多個執行緒同時使用。 讓每個並行操作各自使用自己的連線,並讓連線集區重複使用實體連線。

多重主動結果集(MARS)允許在支援的情境下,在同一連線上同時有多個主動批次。 它不會讓 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 啟用時,OpenOpenAsync 預設會自動加入。 僅在操作必須保持在環境交易之外時才設定 Enlist=false

測量一個邏輯連結

StatisticsEnabled 設為 true,以收集單一 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。 紀錄:

  • Number
  • State
  • Class
  • ClientConnectionId
  • 操作名稱以及已設定的伺服器與資料庫識別碼

不要記錄連線字串、密碼、用戶端密碼或存取權杖。

釋放中斷的連線。 池子偵測到無效的實體連線時會移除它們。 如果憑證、憑證、憑證、DNS 目標或伺服器有變更,請在重試前先修正設定。

僅對暫態故障使用有界重試邏輯。 初始開啟重試、閒置連線恢復與指令重試是不同的機制。 請參考 可設定重試邏輯