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 来做健康检查。 网络在任何检查后都可能失败。 执行操作并处理由此产生的异常。
驱动程序通常报告从闭合到开启以及从开启到闭合的转换。 不要依赖于将Executing、Fetching或Broken视为应用生命周期阶段。
StateChange 事件报告状态转换。 InfoMessage 事件报告的信息消息和服务器警告不会变成异常。 请将这些事件用于诊断,而不要用于协调并发工作。
不要同时共享连接
SqlConnection
SqlCommand, , SqlDataReader, , SqlTransaction 不支持多个线程同时使用。 给每个并发操作单独的连接,让连接池重用物理连接。
多激活结果集(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 处于活动状态时,Open 和 OpenAsync 默认会自动加入。 仅当该操作必须位于当前事务上下文之外时,才设置 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。 记录:
NumberStateClassClientConnectionId- 操作名称以及配置的服务器和数据库标识符
不要记录连接字符串、密码、客户端机密或访问令牌。
释放已断开的连接。 池检测到无效物理连接时会删除它们。 如果凭证、令牌、证书、DNS目标或服务器发生变化,请在重试前修正配置。
仅对瞬态故障使用有界重试逻辑。 初始开放重试、空闲连接恢复和命令重试是不同的机制。 参见 可配置重试逻辑。