implementing-dapper-queries について
Bitwarden の Dapper リポジトリと MSSQL ストアドプロシージャの実装を支援するコーディングスキルです。マイグレーションやテストの手順に加え、プロジェクト固有の制約も示します。
主な機能
- 01ストアドプロシージャとマイグレーションの手順
- 02簡潔な Dapper リポジトリメソッドのパターン
- 03SSDT とマイグレーションの構文規則
- 04MSSQL スキーマと移行時の注意点
- 05EF Core との動作一致要件
活用シーン
- 01Bitwarden のデータベースプロシージャの追加・更新
- 02Dapper リポジトリメソッドの実装
- 03安全な MSSQL マイグレーションの作成
- 04プロジェクト規約に沿った SQL 変更のレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bitwarden/ ai-plugins --skill implementing-dapper-queriesインストール後、Claude Code で/implementing-dapper-queriesを入力して読み込まれたか確認します。
詳細
リポジトリパターン
Dapper の実装はすべて src/Infrastructure/Dapper/Repositories/ に配置します。各リポジトリクラスは src/Core/ のインターフェースを実装し、すべてのデータベース操作にストアドプロシージャを使用します。リポジトリメソッドは意図的に薄く設計されています。C# のパラメーターを SQL パラメーターにマッピングし、結果セットをドメインオブジェクトにマッピングします。
インライン SQL ではなくストアドプロシージャを使用
Dapper のデータベース操作では、すべてストアドプロシージャを使用するのがデフォルトのパターンです。インライン SQL を使用する例外もありますが、これらはリポジトリの基底クラスや親パターンによって自動的に提供されるものであり、個々のリポジトリメソッドに場当たり的に記述するものではありません。
ワークフロー
src/Sql/dbo/Stored Procedures/にストアドプロシージャを定義/更新します。プレーンなCREATE PROCEDURE(SSDT 構文)を使用します。util/Migrator/DbScripts/に、ストアドプロシージャをデプロイするマイグレーションスクリプトを作成します。CREATE OR ALTER PROCEDURE(べき等)を使用します。src/Infrastructure/Dapper/Repositories/に、DapperServiceProviderを使用してプロシージャを呼び出すリポジトリメソッドを実装します。[DatabaseData]属性を使用して統合テストを作成します。
MSSQL のクエリ動作を決定する正となる情報源はストアドプロシージャです。Dapper のリポジトリメソッドは薄く、パラメーターと結果をマッピングします。
ストアドプロシージャの命名規則
プロシージャは {Entity}_{Action} のパターンに従います。例:User_Create、Cipher_ReadManyByUserId、Organization_DeleteById。ツールとコード生成はこの規則に依存し、リポジトリメソッドをプロシージャにマッピングします。
AI アシスタントが間違えやすい重要な決定事項
CREATE OR ALTER と CREATE PROCEDURE の使い分け — ファイルの場所によって異なります
Bitwarden では、ツールチェーンの制約が異なる複数のコンテキストで、各ストアドプロシージャのコピーを 2 つ管理しています。
| コンテキスト | 場所 | 必須の構文 |
|---|---|---|
| SSDT スキーマソース | src/Sql/dbo/Stored Procedures/ | CREATE PROCEDURE(プレーン) |
| マイグレーションスクリプト | util/Migrator/DbScripts/ | CREATE OR ALTER PROCEDURE |
異なる理由:
- SSDT プロジェクトは
CREATE OR ALTERをサポートしていないため、使用するとビルドエラーになります。SSDT は独自のデプロイモデルを通じてオブジェクトのライフサイクルを管理するため、各ソースファイルにはプレーンなCREATE PROCEDUREを記述する必要があります。 - マイグレーションスクリプトは再実行される可能性があるため、べき等でなければなりません。
CREATE OR ALTERは、プロシージャの有無にかかわらず動作します。マイグレーションでプレーンなCREATE PROCEDUREを使用してはいけません。
SSDT のテーブルファイルでは GO バッチ区切りが必要です
src/Sql/dbo/Tables/ では、SSDT により CREATE TABLE と、それに続く CREATE INDEX または CREATE NONCLUSTERED INDEX ステートメントの間に GO バッチ区切りを置く必要があります。
-- CORRECT — GO separates DDL statements for SSDT
CREATE TABLE [dbo].[Example] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[Name] NVARCHAR(256) NOT NULL,
CONSTRAINT [PK_Example] PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
CREATE NONCLUSTERED INDEX [IX_Example_Name]
ON [dbo].[Example] ([Name] ASC)
GO
新しいパラメーターにはデフォルト値を指定し、NULL を許容する必要があります
既存のストアドプロシージャにパラメーターを追加するときは、必ず @NewParam DATATYPE = NULL を使用してください。既存の呼び出し元は新しいパラメーターを渡しません。デフォルト値がないと、呼び出し元でエラーが発生します。
NOT NULL 列:ALTER-UPDATE-ALTER ではなくインラインのデフォルト値を使用
まず NULL を許容する列を追加し、すべての行を更新してから NOT NULL に変更すると、テーブル全体のスキャンが発生します。代わりに ADD [Column] INT NOT NULL CONSTRAINT DF_Table_Column DEFAULT 0 を使用してください。これは SQL Server ではメタデータのみを変更する操作です。これは Bitwarden のマイグレーションで AI アシスタントが最もよく犯す間違いです。
マイグレーションスクリプトで大規模なテーブルにインデックスを作成してはいけません
dbo.Cipher、dbo.OrganizationUser などの大規模なテーブルにマイグレーションスクリプトでインデックスを作成すると、障害が発生する可能性があります。スクリプトで ONLINE = ON を指定してはいけません。本番環境ではこの処理が自動的に行われ、このオプションはサポートされていない SQL Server エディションでは失敗します。大規模なインデックス操作は DbScripts_manual に配置してください。
数値型にのみデフォルト値を使用する
BIT、TINYINT、INT、BIGINT にはデフォルト値を使用してください。VARCHAR、NVARCHAR、または MAX 型にはデフォルト値を使用しないでください。SQL Server ではこれらの型の扱いが異なり、文字列にデフォルト値を設定すると、EF Core のマイグレーションで予期しない動作が発生します。
ビューにはメタデータの更新が必要
テーブルを変更すると、それを参照しているビューのメタデータが古くなります。影響を受けるビューに対して sp_refreshview を呼び出してください。ビューを変更した後は、依存するプロシージャに対して sp_refreshsqlmodule を呼び出してください。これは最も忘れられやすい手順です。
GUID 列には UNIQUEIDENTIFIER を使用する
すべてのエンティティ ID は UNIQUEIDENTIFIER とし、SQL Server ではなく、アプリケーションコードで CoreHelpers.GenerateComb() を使って値を設定します。ストアドプロシージャで NEWID() や NEWSEQUENTIALID() を使用しないでください。
EF との同等性に関する要件
すべてのストアドプロシージャの動作を、EF Core の実装で完全に再現する必要があります。新しいストアドプロシージャを作成する際は、EF の実装で同じフィルタリング、並べ替え、副作用をどのように再現するかを検討してください。ストアドプロシージャが複雑な処理(条件付き更新や複数テーブルの操作など)を行う場合は、EF の実装で同じ動作を再現できるよう、期待される動作を明確に記述してください。
重要なルール
これらは最も頻繁に違反される規約です。Claude は実行時にリンク先のドキュメントを取得できないため、ここに内容を記載しています。
- すべてのストアドプロシージャの冒頭に
SET NOCOUNT ONを記述する - パラメーターの命名規則: C# のプロパティ名に合わせた PascalCase の
@ParamName - マイグレーションスクリプトは冪等であること —
util/Migrator/DbScripts/ではCREATE OR ALTERを使用し、SSDT ソース(src/Sql/dbo/)では通常のCREATE PROCEDUREを使用する - 制約の命名規則:
PK_TableName、FK_Child_Parent、IX_Table_Column、DF_Table_Column - ストアドプロシージャのファイル命名規則: 1 ファイルにつきプロシージャ 1 つとし、
{Entity}_{Action}.sqlという名前を付ける
例
ストアドプロシージャの作成 — SSDT ソースとマイグレーションスクリプト
-- SSDT source file: src/Sql/dbo/Stored Procedures/User_ReadById.sql
-- Use plain CREATE PROCEDURE (SSDT does not support CREATE OR ALTER)
CREATE PROCEDURE [dbo].[User_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT * FROM [dbo].[User] WHERE [Id] = @Id
END
-- Migration script: util/Migrator/DbScripts/YYYY-MM-DD_00_AddUser_ReadById.sql
-- Use CREATE OR ALTER for idempotency
CREATE OR ALTER PROCEDURE [dbo].[User_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT * FROM [dbo].[User] WHERE [Id] = @Id
END
NOT NULL 列の追加
-- CORRECT — metadata-only operation, no table scan
ALTER TABLE [dbo].[Organization]
ADD [UseCustomPermissions] BIT NOT NULL CONSTRAINT DF_Organization_UseCustomPermissions DEFAULT 0
-- WRONG — causes full table scan on large tables
ALTER TABLE [dbo].[Organization] ADD [UseCustomPermissions] BIT NULL
UPDATE [dbo].[Organization] SET [UseCustomPermissions] = 0
ALTER TABLE [dbo].[Organization] ALTER COLUMN [UseCustomPermissions] BIT NOT NULL
既存のプロシージャへのパラメーターの追加
-- CORRECT — existing callers won't break
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create]
@Id UNIQUEIDENTIFIER,
@NewField NVARCHAR(MAX) = NULL -- default protects existing callers
-- WRONG — breaks all existing callers immediately
CREATE OR ALTER PROCEDURE [dbo].[Cipher_Create]
@Id UNIQUEIDENTIFIER,
@NewField NVARCHAR(MAX) -- no default = required parameter