在这个快速入门中,你创建一个 .NET 控制台应用程序,内容是:
- 从环境变量中读取连接字符串,而非从源代码中读取。
- 异步开启连接。
- 如果表不存在,则创建该表。
- 使用参数化命令插入一行数据。
- 通过参数化查询读取行。
- 处理SQL和取消错误。
示例中使用了 Microsoft。Data.SqlClient 7.0.3,当前的稳定版本。
先决条件
你需要 .NET 10 SDK 或更晚支持的 .NET SDK。
创建 SQL 数据库
在以下平台之一创建或连接SQL数据库:
快速入门会创建自己的表格,因此不需要样本数据。 数据库身份需要具备连接权限,以及创建表、向表中插入数据和从表中查询数据的权限。
对于 Microsoft Fabric 中的 SQL 数据库,从 SQL 数据库项中复制服务器名和数据库名称。 不要用SQL分析端点。 该标识需要项目读取权限,而工作区角色或项目权限均可授予该权限。 更多信息请参见 SQL数据库中的认证。 不支持SQL认证。
对于 Azure SQL 数据库,配置 Microsoft Entra ID 认证和数据库访问。
创建项目
运行以下命令:
dotnet new console --framework net10.0 --name SqlClientQuickstart
cd SqlClientQuickstart
dotnet add package Microsoft.Data.SqlClient --version 7.0.3
dotnet add package Microsoft.Data.SqlClient.Extensions.Azure --version 7.0.3
扩展包提供驱动程序提供的 Microsoft Entra ID 认证模式。 仅使用 Windows 集成认证或 SQL 认证的应用程序可以省略 Microsoft.Data.SqlClient.Extensions.Azure。
配置连接
为你的数据库设置 SQL_CONNECTION_STRING 环境变量。 不要在源代码中写入密码、访问令牌或生产环境连接字符串。
选择这些起始点之一并替换占位符。
Fabric SQL 或 Azure SQL 的无密码身份验证
请用Microsoft Entra ID登录,该身份拥有数据库访问权限。 本地开发时,可以使用开发者工具,如 Azure CLI:
az login
从Fabric或Azure SQL数据库中的SQL数据库项复制出准确的服务器和数据库名称。 对于 PowerShell:
$env:SQL_CONNECTION_STRING = 'Server=tcp:<server>,1433;Database=<database>;Authentication=Active Directory Default;Encrypt=Strict;MultiSubnetFailover=true;Connect Timeout=30'
对于 Bash:
export SQL_CONNECTION_STRING='Server=tcp:<server>,1433;Database=<database>;Authentication=Active Directory Default;Encrypt=Strict;MultiSubnetFailover=true;Connect Timeout=30'
对于托管在 Azure 中且连接 Azure SQL 的应用,授予其管理身份数据库访问权限,然后使用 Authentication=Active Directory Managed Identity。 有关其他 Microsoft Entra ID 选项,请参见 Microsoft Entra ID 认证。
通过 TCP 的 SQL Server
使用你现有的SQL Server或你遵循的设置指南中的服务器、端口、数据库和登录。 以下SQL认证示例适用于本地开发容器。 对于 PowerShell:
$env:SQL_CONNECTION_STRING = 'Server=tcp:<server>,1433;Database=<database>;User ID=<user_id>;Password=<password>;Encrypt=true;TrustServerCertificate=true;Connect Timeout=30'
对于 Bash:
export SQL_CONNECTION_STRING='Server=tcp:<server>,1433;Database=<database>;User ID=<user_id>;Password=<password>;Encrypt=true;TrustServerCertificate=true;Connect Timeout=30'
Caution
TrustServerCertificate=true 跳过服务器证书验证。 只在没有受信任证书的本地开发实例中使用。 对于共享或生产型 SQL Server 实例,安装客户端信任的证书,使用该证书上的服务器名称,然后移除 TrustServerCertificate=true。
如果环境支持 Windows 集成认证或 Kerberos,请将 和 Password 替换User ID为 Integrated Security=true。 关于设置要求,请参见 SQL Server 认证。
添加应用程序代码
用以下代码替换 Program.cs 的内容:
using System.Data;
using Microsoft.Data.SqlClient;
string? connectionString =
Environment.GetEnvironmentVariable("SQL_CONNECTION_STRING");
if (string.IsNullOrWhiteSpace(connectionString))
{
Console.Error.WriteLine(
"Set the SQL_CONNECTION_STRING environment variable.");
return 1;
}
using var cancellation = new CancellationTokenSource();
Console.CancelKeyPress += (_, eventArgs) =>
{
eventArgs.Cancel = true;
cancellation.Cancel();
};
try
{
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellation.Token);
const string createTableSql = """
IF OBJECT_ID(N'dbo.SqlClientQuickstart', N'U') IS NULL
BEGIN
CREATE TABLE dbo.SqlClientQuickstart
(
Id int IDENTITY(1, 1) PRIMARY KEY,
Message nvarchar(200) NOT NULL,
CreatedAt datetimeoffset NOT NULL
CONSTRAINT DF_SqlClientQuickstart_CreatedAt
DEFAULT sysdatetimeoffset()
);
END;
""";
using (var createCommand =
new SqlCommand(createTableSql, connection) { CommandTimeout = 30 })
{
await createCommand.ExecuteNonQueryAsync(cancellation.Token);
}
const string insertSql = """
INSERT INTO dbo.SqlClientQuickstart (Message)
OUTPUT INSERTED.Id
VALUES (@message);
""";
int insertedId;
using (var insertCommand =
new SqlCommand(insertSql, connection) { CommandTimeout = 30 })
{
insertCommand.Parameters.Add(
new SqlParameter("@message", SqlDbType.NVarChar, 200)
{
Value = "Hello from Microsoft.Data.SqlClient"
});
object? result =
await insertCommand.ExecuteScalarAsync(cancellation.Token);
insertedId = Convert.ToInt32(result);
}
const string querySql = """
SELECT Id, Message, CreatedAt
FROM dbo.SqlClientQuickstart
WHERE Id = @id
ORDER BY Id;
""";
using var queryCommand =
new SqlCommand(querySql, connection) { CommandTimeout = 30 };
queryCommand.Parameters.Add(
new SqlParameter("@id", SqlDbType.Int) { Value = insertedId });
await using SqlDataReader reader =
await queryCommand.ExecuteReaderAsync(cancellation.Token);
while (await reader.ReadAsync(cancellation.Token))
{
Console.WriteLine(
$"{reader.GetInt32(0)}: {reader.GetString(1)} " +
$"at {reader.GetDateTimeOffset(2):O}");
}
return 0;
}
catch (OperationCanceledException)
{
Console.Error.WriteLine("The operation was canceled.");
return 2;
}
catch (SqlException ex)
{
Console.Error.WriteLine(
$"SQL error {ex.Number}, connection {ex.ClientConnectionId}: " +
ex.Message);
return 3;
}
参数类型和大小与表列相匹配。 参数会与 SQL 文本分开发送值,这样可以防止这些值改变命令语法,并帮助 SQL Server 重用查询计划。
await using 即使发生异常,也会释放读取器和连接。 释放连接会将其物理连接返回到连接池中,而不是在整个应用程序生命周期内一直保持一个连接处于打开状态。
运行应用程序
运行应用程序:
dotnet run
应用程序打印它插入的行:
1: Hello from Microsoft.Data.SqlClient at <timestamp>
每个数据库的身份值和时间戳都不同。
如果连接失败,使用错误输出中的SQL错误号和客户端连接ID。 检查服务器和数据库名称、网络访问、数据库权限、认证设置和证书配置。 不要将 TrustServerCertificate=true 作为通用连接修复方法添加到 Azure SQL 或生产连接中。
在应用中使用该图案
当你将样本迁移到API、服务、桌面应用或后台工作程序时,请保持以下界限:
- 通过应用程序的配置系统加载连接信息。
- 打开一个连接来完成一小段工作,然后将其释放。
- 在打开、命令和读取器调用中传递
CancellationToken。 - 根据具体操作设置命令超时时间。
- 为来自SQL语句外的每个值使用参数。
- 记录
SqlException.Number和ClientConnectionId,且不记录凭证或访问令牌。 - 仅针对瞬时性故障实施重试,并且仅当重复执行该操作是安全的时才这样做。