クイックスタート: ASP.NET Core アプリで Microsoft.Data.SqlClient を使用する

このクイックスタートでは、Microsoft.Data.SqlClient 7.1.1 を使用して ASP.NET Core のミニマル API を構築します。

まずは空のフォルダ、.NET SDK、そしてパソコンからアクセス可能なSQL ServerやAzure SQLデータベースから始めます。 最後に、メッセージをテーブルに追加し、IDで読み返すウェブAPIで締めくくります。

その過程で、アプリは次のことを行います:

  • ASP.NET Core構成から接続文字列を読み込みます。 ローカル開発の場合、値はユーザーシークレットから得られるため、プロジェクトフォルダには含まれません。
  • 依存性注入でデータ クラスを登録します。
  • 各リクエストごとに接続を開いて破棄し、デフォルトの接続プールが物理接続を再利用できるようにします。
  • リクエストの CancellationToken をすべてのデータベースコールに渡します。

このアプリは、Quickstart: Connect and query with Microsoft.Data.SqlClient と同じ dbo.SqlClientQuickstart テーブルを使用しています。 2つのクイックスタートのどちらを先にやってもいいですよ。

Prerequisites

現在のASP.NET Coreは、.NET Framework上ではなく、.NET上で動作しています。 .NETフレームワークのドライバーを使用するには、.NETフレームワークプロジェクトを参照してください。

データベースのアイデンティティは接続やテーブルの作成、挿入、選択の許可が必要です。

プロジェクトを作成する

空のフォルダー内で、以下のコマンドを実行してください:

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に入れないでください。

これらの開始点から1つを選び、プレースホルダーを置き換えてください。 dotnet user-secrets setコマンドはPowerShellとBashでシングルクォートを使っています。 コマンドプロンプトでは、キーと値の一部としてシングルクォートを保存するため、代わりに二重引用符を使ったほうがいいです。

Azure SQL with Microsoft Entra ID

データベースにアクセスできるMicrosoft EntraのIDでサインインしてください。 ローカル開発の場合は、Azure CLIのような開発者ツールをご利用ください。

az login

その後、接続文字列を保存します。 Azure SQLデータベースから正確なサーバー名とデータベース名をコピーしてください。 同じ接続文字列は、コンソールのクイックスタートで説明されているSQLデータベースアイテムのサーバー名とデータベース名を使うと、Microsoft Fabricの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 App ServiceやAzure Container Appsに展開する際、アプリのマネージド ID にデータベースへのアクセスを許可し、値を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 を呼び出す

アプリはそのまま実行したままにして、2つ目のターミナルを開いてください。 POSTリクエスト付きのメッセージを追加し、それをGETリクエストでIDで読み返します。 テーブルにすでに行がある場合、例えばコンソールのクイックスタートから行がある場合は、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応答をテーブルとしてフォーマットします。 最初の行はPOSTリクエストへの 201 Created 応答で、2行目は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

PowerShell 5.1 Windows、APIが返す文字列としてcreatedAtを表示します。 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を作成し、開いて1つのコマンドを実行し、await usingで破棄します。 Disposingは物理接続を接続プールに戻し、次のリクエストでその接続が再利用されます。 接続プーリングはデフォルトでオンになっているので、設定する必要はありません。 チューニング オプションについては、Microsoft.Data.SqlClient を使用した SQL Server 接続プーリングを参照してください。
  • ASP.NET Core は CancellationToken パラメーターをリクエストにバインドします。 クライアントが切断されると、トークンによってオープン操作、コマンド、読み取りがキャンセルされます。
  • @messageと@idパラメータはカラム型とサイズに一致します。 APIは値をデータベースに送信する前に、メッセージの長さを検証します。
  • OUTPUT INSERTED.Id, INSERTED.Message, INSERTED.CreatedAt 挿入された行(サーバー生成のIDとタイムスタンプを含む)を、2つ目のクエリなしで返します。
  • 他の環境では、ASP.NET Core は、ConnectionStrings:Quickstart などの環境変数や、Azure Key Vault のような別の構成プロバイダーから同じ ConnectionStrings__Quickstart キーを読み取ります。

Troubleshoot

エラーまたは症状 確認すべきこと 詳細情報
Set the ConnectionStrings:Quickstart configuration value. アプリは接続文字列を見つけられませんでした。 プロジェクトフォルダで dotnet user-secrets list を実行し、アプリが Development 環境で動作しているか確認してください。 ASP.NET Core でのアプリシークレットの安全な開発用ストレージ
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

次のステップ