在这个快速入门中,你将使用 Microsoft.Data.SqlClient 7.1.1 构建一个 ASP.NET Core 最小 API。
你从一个空文件夹、.NET SDK和一个可以从电脑访问的SQL Server或Azure SQL数据库开始。 最后你会用一个 Web API 向表中添加消息,并通过 ID 将其读回。
在此过程中,应用程序:
- 从ASP.NET Core配置中读取其连接字符串。 对于本地开发,该值来自用户机密,因此它不会出现在你的项目文件夹里。
- 通过依赖注入对数据类进行注册。
- 为每个请求开启并丢弃连接,允许默认连接池重用物理连接。
- 将请求的
CancellationToken传递给每个数据库调用。
该应用使用与 快速入门:使用 Microsoft.Data.SqlClient 进行连接和查询 相同的 dbo.SqlClientQuickstart 表。 你可以先做任意一个快速入门。
Prerequisites
- .NET 10 SDK 或更高版本的受支持 .NET SDK。 该SDK可在Windows、Linux和macOS上运行。
- 一个你可以连接到的数据库。 要创建一个,请查看 控制台快速入门中的前置条件。
-
curl或者用PowerShell向API发送请求。
当前的 ASP.NET Core 运行在 .NET 上,而不是 .NET 框架。 要使用 .NET Framework 的驱动,请参见 .NET Framework 项目。
数据库身份需要具备连接权限,以及创建表、向表中插入数据和从表中查询数据的权限。
创建项目
在一个空文件夹中,执行以下命令:
dotnet new web --framework net10.0 --name SqlClientWebQuickstart
cd SqlClientWebQuickstart
dotnet add package Microsoft.Data.SqlClient --version 7.1.1
dotnet add package Microsoft.Data.SqlClient.Extensions.Azure --version 7.1.1
dotnet user-secrets init
dotnet user-secrets init 在项目文件中添加UserSecretsId。 ASP.NET Core 仅在应用运行于 Development 环境中时加载用户机密,而这对于 dotnet run 是默认设置。
扩展包提供驱动程序的 Microsoft Entra 认证模式。 如果你的应用只使用 SQL 认证,可以省略 Microsoft.Data.SqlClient.Extensions.Azure。 使用两个软件包时,保持它们的版本相同。
配置连接
将 连接字符串 作为 ConnectionStrings:Quickstart 存储在用户机密中。 用户机密存储在你的用户配置文件里,而不是项目文件夹里,所以它们不会提交到源代码控制。 不要在appsettings.json中放入密码或生产连接字符串。
选择这些起始点之一并替换占位符。
dotnet user-secrets set PowerShell 和 Bash 的命令使用单引号。 在命令提示符中,使用双引号,因为命令提示符将单引号作为键和值的一部分存储。
Azure SQL with Microsoft Entra ID
使用拥有数据库访问权限的 Microsoft Entra 身份登录。 本地开发时,可以使用开发者工具,如 Azure CLI:
az login
然后存储 连接字符串。 从Azure SQL数据库复制出准确的服务器和数据库名称。 同样的 连接字符串 在 Microsoft Fabric 中用于 SQL 数据库,只要你使用来自 SQL 数据库项项的服务器和数据库名称,正如控制台快速启动中描述的那样。
dotnet user-secrets set 'ConnectionStrings:Quickstart' 'Server=tcp:<server>,1433;Database=<database>;Authentication=Active Directory Default;Encrypt=Strict;Connect Timeout=30'
此连接字符串不包含机密。
Active Directory Default 在本地使用你的开发者登录信息。 当你部署到 Azure 应用服务 或 Azure 容器应用 时,授予该应用的托管标识数据库访问权限,将值改为 Authentication=Active Directory Managed Identity,并在主机配置中将其设置为 ConnectionStrings__Quickstart。 有关其他选项,请参见 使用 Microsoft.Data.SqlClient 的 Microsoft Entra 身份验证。
带SQL认证的本地SQL Server
存储一个没有可信证书的本地开发实例的连接字符串。 使用单引号防止 PowerShell 和 Bash 扩展密码中的 $ 字符。
dotnet user-secrets set 'ConnectionStrings:Quickstart' '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。 有关详细信息,请参阅 Microsoft.Data.SqlClient 中的加密和证书验证。
添加代码
用以下代码替换 Program.cs 的内容:
using System.Data;
using Microsoft.Data.SqlClient;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<MessageStore>();
var app = builder.Build();
await app.Services.GetRequiredService<MessageStore>()
.EnsureTableAsync(app.Lifetime.ApplicationStopping);
app.MapPost("/messages", async (
NewMessage input, MessageStore store, CancellationToken cancellationToken) =>
{
if (string.IsNullOrWhiteSpace(input.Text) || input.Text.Length > 200)
{
return Results.BadRequest("Text must be 1 to 200 characters.");
}
Message message = await store.AddAsync(input.Text, cancellationToken);
return Results.Created($"/messages/{message.Id}", message);
});
app.MapGet("/messages/{id:int}", async (
int id, MessageStore store, CancellationToken cancellationToken) =>
await store.GetAsync(id, cancellationToken) is Message message
? Results.Ok(message)
: Results.NotFound());
app.Run();
record NewMessage(string Text);
record Message(int Id, string Text, DateTimeOffset CreatedAt);
sealed class MessageStore(IConfiguration configuration)
{
private readonly string _connectionString =
configuration.GetConnectionString("Quickstart")
?? throw new InvalidOperationException(
"Set the ConnectionStrings:Quickstart configuration value.");
public async Task EnsureTableAsync(CancellationToken cancellationToken)
{
const string sql = """
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;
""";
await using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync(cancellationToken);
await using var command =
new SqlCommand(sql, connection) { CommandTimeout = 30 };
await command.ExecuteNonQueryAsync(cancellationToken);
}
public async Task<Message> AddAsync(
string text, CancellationToken cancellationToken)
{
const string sql = """
INSERT INTO dbo.SqlClientQuickstart (Message)
OUTPUT INSERTED.Id, INSERTED.Message, INSERTED.CreatedAt
VALUES (@message);
""";
await using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync(cancellationToken);
await using var command =
new SqlCommand(sql, connection) { CommandTimeout = 30 };
command.Parameters.Add("@message", SqlDbType.NVarChar, 200).Value = text;
await using SqlDataReader reader =
await command.ExecuteReaderAsync(cancellationToken);
await reader.ReadAsync(cancellationToken);
return new Message(
reader.GetInt32(0), reader.GetString(1), reader.GetDateTimeOffset(2));
}
public async Task<Message?> GetAsync(
int id, CancellationToken cancellationToken)
{
const string sql = """
SELECT Id, Message, CreatedAt
FROM dbo.SqlClientQuickstart
WHERE Id = @id;
""";
await using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync(cancellationToken);
await using var command =
new SqlCommand(sql, connection) { CommandTimeout = 30 };
command.Parameters.Add("@id", SqlDbType.Int).Value = id;
await using SqlDataReader reader =
await command.ExecuteReaderAsync(cancellationToken);
return await reader.ReadAsync(cancellationToken)
? new Message(
reader.GetInt32(0), reader.GetString(1), reader.GetDateTimeOffset(2))
: null;
}
}
运行应用
在固定端口启动应用:
dotnet run --urls http://localhost:5000
启动时,如果表格不存在,应用会生成该表。 当应用准备好时,输出内容包括以下几行:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
如果应用无法访问数据库,启动时会停止并显示SqlException。 常见原因请参见 故障排除。
调用 API
让应用继续运行,打开第二个终端。 通过 POST 请求添加一条消息,然后通过 ID 使用 GET 请求将其读回。 如果表格已经有行,例如来自控制台快速入门的行,请将 GET 请求中的 1 替换为 POST 请求返回的 id。
对于 PowerShell:
Invoke-RestMethod -Method Post -Uri http://localhost:5000/messages -ContentType application/json -Body (@{ text = 'Hello from ASP.NET Core' } | ConvertTo-Json)
Invoke-RestMethod http://localhost:5000/messages/1
PowerShell 将每个 JSON 响应格式化为表。 第一行是 201 Created 对POST请求的响应,第二行是GET响应:
id text createdAt
-- ---- ---------
1 Hello from ASP.NET Core 2026-10-21T00:04:43.3588978+00:00
1 Hello from ASP.NET Core 2026-10-21T00:04:43.3588978+00:00
Windows PowerShell 5.1 显示createdAt为 API 返回的字符串。 PowerShell 7 将其转换为当地时间的日期,例如 10/20/2026 7:04:43 PM。
在 Windows PowerShell 中,curl 是 Invoke-WebRequest 的别名,而 Invoke-WebRequest 不接受 curl 选项。 使用PowerShell命令,或者从命令提示符运行 curl.exe 。
命令提示符:
curl -X POST http://localhost:5000/messages -H "Content-Type: application/json" -d "{\"text\":\"Hello from ASP.NET Core\"}"
curl http://localhost:5000/messages/1
命令提示符不把单引号当作引号字符处理,所以 JSON 正文使用双引号并带反斜杠转义。 每个命令打印 JSON 响应:
{"id":1,"text":"Hello from ASP.NET Core","createdAt":"2026-10-21T00:05:15.4936763+00:00"}
{"id":1,"text":"Hello from ASP.NET Core","createdAt":"2026-10-21T00:05:15.4936763+00:00"}
对于 Bash:
curl -X POST http://localhost:5000/messages -H "Content-Type: application/json" -d '{"text":"Hello from ASP.NET Core"}'
curl http://localhost:5000/messages/1
{"id":1,"text":"Hello from ASP.NET Core","createdAt":"2026-10-20T20:44:17.8634925+00:00"}
{"id":1,"text":"Hello from ASP.NET Core","createdAt":"2026-10-20T20:44:17.8634925+00:00"}
你的ID和时间戳不同。 如果你先运行控制台快速入门,ID 会从其中的记录继续。 请求不存在的 ID 会返回 404 Not Found。 空消息返回 400 Bad Request。
在第一个终端按Ctrl+C可以停止应用。
代码的工作原理
- 该应用将
MessageStore注册为单例。 它只保存连接字符串,并且只从IConfiguration读取一次。 它无法保持连接。 - 每个方法创建一个
SqlConnection,打开它,运行一个命令,并用await using将其释放。 处置时将物理连接返回连接池,下一个请求会重用它。 连接池默认开启,所以不需要配置。 关于调优选项,请参见使用 Microsoft.Data.SqlClient 的 SQL Server 连接池。 - ASP.NET Core 将
CancellationToken参数绑定到请求。 如果客户端断开连接,令牌会取消打开操作、命令和读取操作。 -
@message和@id参数与列类型和大小相匹配。 API 在将该值发送到数据库之前,会验证消息长度。 -
OUTPUT INSERTED.Id, INSERTED.Message, INSERTED.CreatedAt返回插入的行,包括服务器生成的ID和时间戳,无需第二次查询。 - 在其他环境中,ASP.NET Core 从环境变量(如
ConnectionStrings:Quickstart)或其他配置提供程序(如 Azure 密钥保管库)读取相同的ConnectionStrings__Quickstart密钥。
Troubleshoot
| 错误或症状 | 需要检查的事项 | 更多信息 |
|---|---|---|
Set the ConnectionStrings:Quickstart configuration value. |
应用没找到连接字符串。 在项目文件夹里运行 dotnet user-secrets list ,检查应用是否在 Development 环境中运行。 |
|
System.ArgumentException: Cannot find an authentication provider for 'ActiveDirectoryDefault' 启动时 |
连接字符串 使用Active Directory认证模式,但项目不引用 Microsoft.Data.SqlClient.Extensions.Azure。 添加与 Microsoft.Data.SqlClient 相同版本的软件包。 |
Microsoft Entra 认证在 7.0 版本中迁移到扩展包 |
SqlException 启动时 |
应用无法连接或创建该表。 错误编号用于识别原因,例如,登录失败时为 18456。 | 控制台快速入门中的故障排除 |
415 Unsupported Media Type |
请求未设置 Content-Type: application/json。 |
最简 API 快速指南 |
Cannot bind parameter 'Headers' |
你在 Windows PowerShell 中运行了 curl 命令,其中使用 curl 运行 Invoke-WebRequest。 使用“ 调用API”中的PowerShell命令。 |
Invoke-RestMethod |
''' is an invalid start of a value |
你在命令提示符里运行了 Bash curl 命令,它会把单引号作为正文的一部分发送。 在 “调用 API”中使用命令提示符命令。 | 调用API |
清理资源
用任何查询工具删除该表:
DROP TABLE IF EXISTS dbo.SqlClientQuickstart;
移除存储的 连接字符串。 从项目文件夹中运行:
dotnet user-secrets clear
然后删除项目文件夹。 从父文件夹中,在PowerShell中执行以下命令:
Remove-Item -Recurse -Force SqlClientWebQuickstart
对于 Bash:
rm -rf SqlClientWebQuickstart