在 .NET 应用中使用 Microsoft.Data.SqlClient

在这个快速入门中,你创建一个 .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 IDIntegrated 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.NumberClientConnectionId,且不记录凭证或访问令牌。
  • 仅针对瞬时性故障实施重试,并且仅当重复执行该操作是安全的时才这样做。

后续步骤