Skip to content

第8章:データベースアクセスと ORM(Entity Framework Core)を追加 - #54

Merged
takashiuesaka merged 122 commits into
mainfrom
takashiuesaka-chapter-8-ef-core
Sep 12, 2026
Merged

takashiuesaka merged 122 commits into
mainfrom
takashiuesaka-chapter-8-ef-core

Conversation

@takashiuesaka

Copy link
Copy Markdown
Collaborator

概要

Closes #45

C# 以外の言語経験者向けに、第8章の本編と EF Core の付録6本を追加します。本編は ASP.NET Core から利用する際の基本に絞り、詳細な構成や注意点は付録へ分けています。

記事の構成

  • 本編:概要、DbContext と DI、クエリ、保存とトランザクション、マイグレーション、テストとアーキテクチャ
  • 付録1:モデル定義、リレーションシップ、値の変換
  • 付録2:キー、採番、SQL Server 固有のマッピング
  • 付録3:マイグレーションの詳細、クエリ、SQL の直接実行
  • 付録4:更新、トランザクション、同時実行制御、読み取り専用レプリカ
  • 付録5:計測、クエリ・モデル・実行時のパフォーマンス最適化
  • 付録6:実データベース、SQLite、WebApplicationFactory を使うテスト

記述方針と関連変更

  • Microsoft Learn および Microsoft の公式 GitHub 情報を根拠とし、実測は条件を明示した裏付けとして掲載
  • 実測だけに基づく制約の読み替えや性能の一般化を除去
  • Add / AddAsync、HiLo、OUTPUT 句の説明と、対応するコードへの参照を補足
  • README に本編・付録へのリンクを追加
  • 狭い画面での表の折り返し、印刷時のコード折り返し、モバイルメニューの aria-expanded を改善

確認

  • npm run build 成功
  • 記事内アンカー・章間リンク、アラート・Mermaid の構造を確認
  • 掲載内容の動作をローカルおよび必要な Azure 環境で確認し、公式情報と照合

ローカル用の instructions.md と実測成果物は PR に含めていません。

takashiuesaka and others added 30 commits August 28, 2026 18:50
Issue #45 に対応し、EF Core 10 / .NET 10 を対象とした第8章を新規執筆。

- 概要と設計方針、モデル定義と DbContext 設計
- マイグレーションとスキーマ管理(デプロイ戦略の比較)
- クエリ操作と LINQ(N+1、分割クエリ、ページング、生 SQL)
- 更新/変更操作とトランザクション(セーブポイント、楽観的同時実行制御)
- パフォーマンス最適化(プーリング、コンパイル済みクエリ/モデル)
- 読み取り専用レプリカの扱い
- テスト戦略とアーキテクチャ例

記載内容は .NET 10 SDK / EF Core 10.0.11 の実プロジェクトでコンパイル・
実行し、xUnit のテスト実行まで含めて動作を確認済み。

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
実測検証で判明した誤り・記載漏れを修正した。

誤りの修正:
- 楽観的同時実行制御で OriginalValues.SetValues() を「データベース側優先」
  と説明していたが、実測では Client Wins になる。説明を訂正し、
  Reload() との違いを WARNING で明示
- ログのリダクションが LINQ に直書きしたリテラルにも及ぶかのような記述を
  訂正。実測では EF.Constant() 等でインライン化された場合のみ対象

記載漏れの追加:
- EF Core 10 が接続文字列に Application Name を自動注入する破壊的変更
- マルチターゲット時の dotnet ef --framework 必須化
- コレクションのパラメーター化と IN 句の翻訳 (ParameterTranslationMode)
- EF Core のメトリクス 7 種
- 所有型 (OwnsOne) の説明
- EF1003 アナライザーの警告 ID と実際の警告文

整合性の修正:
- コード例間で未定義だったプロパティを補完 (Rating, Contributors,
  TenantId, CreatedAt, ArchivedAt, Comments)
- BlogRepository の二重定義を解消 (BlogQueries にリネーム)
- 同一ブロック内の var blogs 二重宣言を解消
- 新規節を目次に追加、破壊的変更のリンクを参考文献に追加

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
前回と異なる検証角度(他言語 ORM 比較の裏取り、API 選択の一貫性、
数値の全件裏取り、他章との方針整合、ブラウザ実表示)で再レビューした。

誤りの修正:
- Laravel のマイグレーションが「モデルの差分検出まで自動化されている」
  という記述は誤り。公式では make:migration は空のファイルを生成し、
  Schema ファサードで手書きする。Django / EF Core との違いを明示
- Spring の EntityManager を「リクエストスコープのプロキシ」と説明して
  いたが、Jakarta Persistence 仕様の既定はトランザクションスコープ
- Hibernate の @where は 6.3 で非推奨。@SQLRestriction が後継であり、
  @FilterDef / @filter とは静的・動的で別機能である点を区別
- OptimisticLockException が jakarta.persistence の標準例外である
  ことを明示

API 選択の一貫性:
- リポジトリ例の AddAsync を Add に変更。公式が「HiLo など特殊な値
  ジェネレーター以外では非 async 版を使うべき」と明記しているため
- Add と AddAsync の使い分けを解説する NOTE を追加

他章との整合:
- 第6章のリポジトリ例が各メソッドで SaveChangesAsync を呼ぶ形なのに
  対し、本章はトランザクション境界を呼び出し側に置く方針である旨を
  TIP で補足
- Mermaid の改行記法を他章と同じ <br> に統一(19 行)
- 章間リンクの形式を ../<章>/index.md に統一

実測で裏付けた主張(記述は正しいことを確認):
- DbContextPool の poolSize 既定 1024
- await using の破棄で未コミットのトランザクションが自動ロールバック
- トランザクション内の SaveChanges が暗黙のセーブポイントを作る
- ExecuteSqlAsync の補間文字列がパラメーター化される
- EF1003 アナライザーが文字列連結を検出する

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
リンクテキストの実タイトル照合、DI 解決の実測、設定漏れ時の挙動の実測、
アナライザー最大強度実行の 4 つの角度で検証し、以下を修正・追記した。

- 参考リンク 36 本のタイトルを全件照合し、意味が異なる 1 件を含む
  4 件のリンクテキストを実際のページタイトルに合わせて修正
- DbContext の並列実行について、SQLite など同期 I/O のプロバイダーでは
  例外が検出されず本番で初めて落ちる場合があることを実測に基づき追記
- AddDbContextFactory が DbContext 自体も Scoped で登録することを追記
- Singleton への注入時とプロバイダー指定漏れ時の例外メッセージを追記
- EF Core のエンティティが CA1002 / CA2227 / CA1056 と衝突することと、
  .editorconfig での抑制方法(グロブは相対パス指定が必要)を追記

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- 統合テスト例に using System.Data.Common; を追加(欠落によりコンパイル不可だった)
- コントローラー注入の説明が Application Name の小節に埋没していた節構造を修正
- 再試行+明示的トランザクションの例外は SaveChanges 時に発生することを実測して訂正
- EF Core 10 の破壊的変更「Azure SQL で json 型が既定」を追記
- Microsoft.Data.Sqlite 10.0 のタイムゾーン破壊的変更(重大度 High)を実測して追記
- 初期データの投入(シード)の節を新設し、UseSeeding / UseAsyncSeeding の呼び分けを実測して記載
- Store Wins の ReloadAsync のコード例を追加(実測で確認)
- コンパイル済みモデルがクエリフィルターで生成失敗することを実測し逐語メッセージを追加
- 再試行時のバッファリングは未実測である旨と出典を明示
- Mermaid 図のクラス名を本文の実装(BloggingContext / BloggingReadContext)に統一
- SQLite の DateTimeOffset 比較の逐語例外メッセージを追加

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- ページングの異常入力を実測し、pageSize に負値を渡すと Take が無効化され全件返る問題を警告として追記
- 章間リンクの誤り(存在しない ../03-routing-and-endpoints/)を修正
- 同時実行トークンの更新忘れで競合が検出されないことを実測し、SaveChangesAsync の具体例を追加
- 多対多の規約テーブル名が PostTag になることを実測して追記
- 説明なくコードブロックが連続していた 6 箇所に導入文を追加
- EF Core 10 のベクトル検索サポートを追記(API の実在をコンパイラで確認)
- 「ライブラリー」を他章の表記「ライブラリ」に統一
- 他言語比較(Hibernate の @where 非推奨、Django の select_for_update など)を公式で裏取り

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Docker 上の SQL Server 2022 で、これまで検証できていなかった SQL Server 固有の
挙動をすべて実測し、記載の誤りと漏れを修正した。

- ExecuteUpdateAsync / ExecuteDeleteAsync が同時実行トークンを検証せず、
  他者の更新を黙って上書きすることを実測して警告に追記
- ApplicationIntent=ReadOnly には書き込みを禁止する働きがなく、レプリカのない
  サーバーでは書き込みが成功してしまうことを実測して警告を追加
- rowversion の UPDATE 文に OUTPUT INSERTED 句が含まれることを反映
- Contains の翻訳 SQL を実測値(OPENJSON の WITH 句を含む完全な形)に修正
- migrations remove が適用済みマイグレーションを拒否する逐語メッセージを追記し、
  本当に危険なのは別データベースにのみ適用済みの場合であることを明確化
- EnsureCreated 後の MigrateAsync が失敗する逐語メッセージを追記
- マイグレーション適用時の排他ロック取得メッセージを追記
- rowversion 列が INFORMATION_SCHEMA では timestamp と表示される点を補足
- json 型へのマッピングが互換性レベル 170 未満では行われないことを実測で補強
- Application Name の自動注入をサーバー側の program_name で確認した旨に更新

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Azure 上に Windows Server 2022 + SQL Server 2022 の VM と Azure SQL Database
Business Critical (互換性レベル 170) を構築し、これまで未検証だった項目を実測した。

- 分散トランザクションへの昇格: .NET 7 以降は暗黙の昇格が既定で無効のため、
  実際には昇格ではなく NotSupportedException が投げられることを実測して追記。
  ImplicitDistributedTransactions を有効にしたうえで昇格条件を 4 パターン測定し、
  「Application Name だけが違うと、接続を閉じてから開いても昇格する」ことを表で明示。
- 読み取りスケールアウト: ApplicationIntent ごとの Updateability の実測値と、
  レプリカへの書き込みで実際に返る SqlException のメッセージを追記。
- json 型: UseAzureSql + 互換性レベル 170 で実際に json 型になることを DDL 付きで追記。
- ベクトル検索: vector(1536) 列の作成と VectorDistance による並べ替えが
  記事のコード例のまま動作することを確認した旨を追記。

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- LIKE のワイルドカードはパラメーター化でも防げない点を追記(攻撃を実際に再現)
- EF.Constant() がクエリキャッシュのヒット率を下げるという記述を実測に基づき訂正
- MARS とセーブポイントの記述を訂正(無効になるのは EF の自動セーブポイントのみ)
- AsNoTracking / DbContext プーリング / コンパイル済みクエリ / ストリーミングの
  性能主張に実測値を追加(ストリーミングは行数依存である旨も明記)
- InMemory プロバイダーが SQLite より遅い件を公式記述と実測で裏付け
- SQLite の --idempotent 非対応、コンパイル済みモデルの制限を実測メッセージで補強
- マイグレーションが生成する 3 ファイルとスナップショットの実ファイル名を修正
- コードブロックのコンパイルエラー 2 件を修正(CS9035 / CS1061)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- 再試行有効時の結果セットバッファリングを実測 (+7MB → +97MB) し、
  「観測が難しい」としていた記述を実測値に差し替え
- EnableRetryOnFailure は特定のエラー番号のみを再試行対象とすることを
  障害注入 (コンテナー再起動) で確認し、WARNING を新設
- AddDbContextPool の poolSize 超過が例外にもブロックにもならないことを実測
- アラート種別 10 箇所を内容に即して是正 (WARNING 29 → 27)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Azure 上に Windows VM + SQL Server 2022 + Azure SQL BC を構築して検証。

- 記事に欠落していたデッドロックの節を新設。エラー 1205 の逐語、
  実行戦略による自動再試行 (実測で犠牲者側も成功)、予防策の表を追加
- SaveChangesAsync 経由のデッドロックが DbUpdateException に包まれず
  SqlException が直接投げられることを実測し、捕捉方法を明記
- 分散トランザクション昇格の 4 パターンを独立再現し、既存記述の正しさを確認

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- SaveChanges 中のデッドロックは InvalidOperationException →
  DbUpdateException → SqlException の 3 層になることを Azure 上の
  SQL Server 2022 で再実測。前回の「SqlException が直接投げられる」
  という記述は、デッドロックがクエリ側で起きていたことによる誤りだった
- 発生箇所ごとの例外型の違いを表と逐語スタックで明示し、IsDeadlock
  ヘルパーを内側をたどる実装に修正
- LIKE の % が全行に一致する根拠として T-SQL の仕様を明記
- EF.Constant がクエリキャッシュに影響しないのは EF Core 9 以降で
  あることを追記
- エラー 1205 の一時的エラー扱いの出典を明記

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- プロバイダー表に公式一覧の対応バージョン列を追加。Pomelo 9.0.0 を
  EF Core 10 で実行すると MissingMethodException で落ちることを実測し、
  公式の「メジャーバージョンをまたいで動作しない」を裏付け
- Npgsql 10.0.3 は EF Core 10 + PostgreSQL 16 で正常動作することを実測
- Skip/Take の負値の挙動は DB 依存であることが判明。SQL Server では
  SqlException(T-SQL の OFFSET は 0 以上、FETCH は 1 以上)、
  SQLite では全件返却。SQLite 限定の結果を一般化していた誤りを訂正
- マイグレーションロックのメッセージを公式 resx と逐語一致する全文に修正
- SQLite の DateTimeOffset は等価比較のみ翻訳され、大小比較と OrderBy で
  例外の型が異なることを実測して明記
- パフォーマンス節に測定環境の注記を追加
- 10 分間 2,800 反復の負荷試験で active_dbcontexts が 1 のまま推移し
  メモリリークがないことを実測して追記

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
EF Core 10 の公式 What's New / Breaking Changes の全項目、および
DbContextOptionsBuilder のメンバー一覧と記事を突き合わせ、記載漏れを検出した。
検出した項目はすべて Azure 上の SQL Server 2022 で実測し、MS Learn または
dotnet/efcore の公式ソースで裏付けを取ったうえで追記した。

追加
- インターセプターによる横断的な処理(節を新設)
  - 7 種類のインターセプターの一覧、AddInterceptors による登録
  - ISaveChangesInterceptor による監査の実測
  - シングルトンインターセプターを毎回 new すると
    ManyServiceProvidersCreatedWarning が出ることを実測
- DbContext プーリングと接続プーリングの違い(公式は「直交」と明記)
  - EF Core は操作の直前に接続を開き直後に閉じる挙動を実測(3 回で開閉 3 回)
- UseAzureSql は再試行を既定で有効にすること
  - 実行戦略の型を実測(SqlServerRetryingExecutionStrategy)
  - UseAzureSqlDefaults が EF Core 10 で Obsolete になったこと

訂正
- コンパイル済みクエリの制限の記述が公式の誤読だった
  公式が非対応とするのは「インスタンスのメンバー/メソッドアクセスを含む
  複雑なパラメーター式」であり、コレクション型のパラメーター自体は使える。
  SQL Server / SQLite の両方で int[] の Contains が動作することを実測し、
  メンバーアクセス式が InvalidOperationException になることも確認した。

検証
- C# コードブロック 108 件を Roslyn で構文解析(実質エラー 0)
- 記事中の API 名 158 件をリフレクションで実在確認(EF Core 側は全件実在)
- AddDbContextPool の poolSize 既定値 1024 を実行時ダンプで確認
- 他章へのリンクとアンカー、目次アンカー、テーブル列数、カタカナ表記を確認

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- SQLite の AUTOINCREMENT を EF Core 10 の設定で切り替えられることを実測して追記
- EnableThreadSafetyChecks(false) が「予測できない失敗」を招くことを SQL Server 2022 で実測
- 複合型の列名一意化とネストした複合型のフルパス化(EF Core 10 破壊的変更)を DDL 実測付きで追記
- SQL パラメーター名が変数名に簡素化された点を実測し、@p0 と書いていた記述を訂正
- IMaterializationInterceptor の 4 メソッドの呼び出し順序を実測して節を新設

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- Hibernate Interceptor#onSave が 6 で非推奨のため onPersist に修正
- @where が Hibernate 7 で削除された旨を追記
- 日本語混じりの桁揃え 2 箇所をブラウザーで実測し崩れを解消
- AppDbContext を BloggingContext に統一
- MSDTC を初出で正式名称に展開

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- 値変換の落とし穴(ValueComparer なしで変更が失われる)を実測して追加
- クエリタグ(TagWith / TagWithCallSite)の節を新設
- AddDbContext による ASP.NET Core ログ統合の TIP を追加
- 他言語比較の固有名詞 22 件を各公式ドキュメントで再検証
- EF.CompileAsyncQuery の AppDb を BloggingContext に統一

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
公式TOC突合で未言及と判明していた3トピックを実測し、公式ドキュメントの
裏付けが取れたものだけを追加した。

- シャドウプロパティ: DDLに列が生成されること、EF.Propertyでクエリできること、
  外部キーを省略すると IsShadowProperty() == true のNULL許容列になることを実測
- バッキングフィールド: 読み出し時に検証メソッドを通らないこと、および
  読み取り専用プロパティはHasFieldなしでは列すら作られないことを実測
- シーケンス: Azure上のSQL Server 2022で、2テーブルが同一シーケンスから
  1000/1005/1010/1015と採番されることを実測

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
角度3.8.1(CLIコマンドを記事の手順どおり実行)を消化し、実際に動かない
記載を3件発見して修正した。

- migrations bundle の対象ランタイム指定が --runtime になっていたのを
  公式どおり --target-runtime に修正。--runtime は「ツールがビルドに使う
  ランタイム」を指す別オプションで、--self-contained と併用すると
  NETSDK1047 で失敗することを実測
- プロジェクト分割時に --startup-project へWebアプリを指定すると
  Design パッケージ不足で失敗する点を追記。公式推奨のマイグレーション
  プロジェクトを両方に指定する形へコマンド例を変更
- DbContext と別アセンブリにマイグレーションを置く場合の
  MigrationsAssembly の必要性を実測して追記
- dbcontext scaffold が接続文字列をソースへ埋め込む点と
  --no-onconfiguring による抑止を追記
- ツールマニフェストの配置パスが実測と食い違うため、パス依存の記述を削除

いずれもAzure上のSQL Server 2022に対する実行、およびMS Learnと
dotnet/efcore のresxで裏付けを確認している。

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- 存在しない `UseAzureSqlDefaults` の記述を削除(EF Core 9/10 の公式 API に不在)
- 実行戦略の既定値を実測値で裏付け(UseSqlServer / UseAzureSql の比較)
- アナライザー EF1002(補間文字列)を EF1003 との対比表で追加
- ConfigureConventions による一括構成の節を新設

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
- `EF.MultipleParameters` は実在しない API のためコード例を削除
  (EF 型の公開メンバーは Constant / Parameter のみ、公式にも記載なし)
- 3 つの翻訳方法の生成 SQL を実測値に差し替え
- クエリ単位では MultipleParameters を指定できない旨を NOTE で明示
- 定義が抜けていた `CreateBlogRequest` を追加

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
TPH / TPT / TPC が記事に 0 件だったため、実測に基づく節を新設した。

- 3 方式の DDL を SQL Server プロバイダーで実測して掲載
- 派生型を登録しない場合の実行時エラーを実測し WARNING に掲載
  (CoreStrings.resx の AbstractLeafEntityType と一致することを確認)
- OfType<T>() が識別子列の述語を付与する SQL を Azure の実機で実測
- 識別子値が既定で CLR のクラス名になることを実データで確認
- Rails STI / Django 多テーブル継承との対比を TIP に追加
- 目次と参考リンク (ja-jp) を更新

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
計算列が 0 件、CommandTimeout が 1 件のみだったため実測に基づき追記した。

- HasComputedColumnSql の virtual / stored(PERSISTED) の DDL を実測
- 計算列への代入が例外なく無視されることを Azure の実機で確認
  (公式の「計算列には明示的な値を指定できない」記述と一致)
- 最終更新日時にはトリガーを使うという公式の案内を NOTE に追加
- CommandTimeout の既定が null であることを実測し、
  SqlCommand.CommandTimeout の既定 30 秒が使われることを公式で確認
- 3 秒設定で 10 秒のコマンドが Number=-2 で打ち切られることを実機で確認
- 目次と参考リンク (ja-jp) を更新

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
HasTrigger / UseSqlOutputClause がともに 0 件だったため追記した。
EF Core 7 の破壊的変更 (影響度 High) で、実務での遭遇率が高い。

- トリガー付きテーブルへの保存が DbUpdateException で失敗することを
  Azure の SQL Server 2022 で実測
- HasTrigger / UseSqlOutputClause(false) の両方で解決することを実測
- 既定の MERGE + OUTPUT 句が 1 行ずつの INSERT + SELECT に変わることを
  ログから実測し、性能への影響を NOTE に明記
- SQLite の AFTER トリガー / 仮想テーブルの同種の制限を TIP に追加
- 目次と参考リンク (ja-jp、疎通確認済み) を更新

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
MaxBatchSize が 0 件だったため実測に基づき追記した。

- 既定で 1 バッチ最大 42 文という公式の説明を 42/43 の境界で実測再現
- MaxBatchSize / MinBatchSize の構成方法を追加
- SqlServerModificationCommandBatchFactory の MaxMaxBatchSize = 1000 を
  実装で確認し、2000 を指定しても 1000 で頭打ちになることを実測
- 1,000 件挿入の所要時間を MaxBatchSize 別に実測 (209s / 5.5s / 0.27s)
  ネットワーク遅延の大きい環境での測定である旨を明記し、
  既定値の変更は必ず自環境で計測するよう WARNING で注意喚起
- ExecuteUpdate / ExecuteDelete への誘導を TIP に追加
- 目次と参考リンク (ja-jp、疎通確認済み) を更新

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
WITH (UPDLOCK) を使う案内は EF Core の公式ドキュメントに存在しないため、
公式が実際に案内している分離レベルによる制御に置き換えた。

- 公式 saving/concurrency の "Using isolation levels for concurrency control"
  に基づき「分離レベルによる同時実行制御」の節を新設
- RepeatableRead では読んだ行に共有ロックが掛かり外部の UPDATE が
  ブロックされること (Number=-2 でタイムアウト) を Azure の実機で実測
- Snapshot では外部の UPDATE が即座に成功し、自分の更新時に
  SqlException Number=3960 になることを実測 (公式の説明と一致)
- 公式が挙げる 2 つの欠点 (長時間のブロック / 全操作を 1 トランザクションに
  含める必要) を WARNING に明記
- ALLOW_SNAPSHOT_ISOLATION の事前設定が必要な点を NOTE に追加
- 参考リンクの重複を 1 件解消
- 目次を更新

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
前回 UPDLOCK の非公式案内が見つかったため、同種の記述を全体点検した。

- 読み取りスケールアウトの表が不正確だった点を修正
  公式は Premium / Business Critical / Hyperscale の新規データベースで
  既定で有効としているが、記事は Business Critical のみに限定していた
- Hyperscale はセカンダリレプリカ 0 の構成では自動的に無効になる旨を追記
- Premium / Business Critical では同時にアクセスできる読み取り専用
  レプリカが 1 つだけである点を NOTE に追加
- 反映遅延の記述を公式の表現 (数十ミリ秒〜1 桁秒、固定の上限なし) に統一
- 1 つのセッション内では読み取りが常にトランザクション整合性を保つ点と、
  大きなトランザクションほど実効遅延が増える点を NOTE に追加
- レプリカのスナップショット分離が「セッションの分離レベル設定やクエリ
  ヒントに関係なく」適用される点を明記

検証のみで変更不要だった項目:
- SQL Server のヒント (NOLOCK / TABLOCK 等) の非公式な案内は 0 件
- UseAzureSynapse はリフレクションで実在を確認

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
「推奨/すべき/必ず」41 行を公式と全件照合した結果、キーセット
ページングの節に公式が扱う重要な落とし穴が抜けていた。

- 並べ替えキーが複数あるとき、単純な > の連鎖では境界の行が欠落する
  点を実測付きで追加 (日付が重複する 6 件で 2 件ずつページングし、
  p.Date > lastDate だけだと Id=3 が丸ごと飛ばされることを確認)
- 公式が示す OR パターン
  p.Date > lastDate || (p.Date == lastDate && p.Id > lastId)
  と、その生成 SQL を掲載
- 行値比較 (Date, Id) > (@d, @i) を EF Core が LINQ で表現できない
  ことを実測 (ValueTuple.Create(...).CompareTo(...) は
  InvalidOperationException)。公式ドキュメントの記載と
  dotnet/efcore#26822 が EF Core 10 でも Backlog である点を明記
- ランダムアクセスが必要な場合の「次へ/前へはキーセット、任意ページへの
  ジャンプはオフセット」という公式の併用案内を NOTE に追加
- ページングの並べ替えに対応するインデックス (複合インデックス) の
  必要性を TIP に追加

検証のみで変更不要だった項目:
- 「推奨/すべき/必ず」の他の記述は公式ドキュメントの記載と一致

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
「既定」の主張 37 件を公式と全件照合した結果、スコープ検証まわりに
実務上重要な抜けがあった。

- スコープの検証が開発環境でしか働かない点を実測で追加
  環境名だけを Production に変えると、Scoped の DbContext を
  BackgroundService に直接注入しても例外なく起動してしまう
- 公式が案内する IServiceScopeFactory による BackgroundService の
  実装例を追加 (これまで名前が出るだけで実装例がなかった)
- 直接注入と IServiceScopeFactory の実測比較表を追加
- スコープ検証のもう 1 つの項目「Scoped をルートプロバイダーから
  解決していないか」を TIP に追加
  Cannot resolve scoped service 'BloggingContext' from root provider.

検証のみで変更不要だった項目:
- poolSize の既定 1024 を AddDbContextPool の公式 API リファレンスで確認
- 「既定」の他の主張は公式ドキュメントの記載および実測と一致

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Blazor に関する記述が 0 件だったため節を新設した。公式には
「Entity Framework Core を使用した ASP.NET Core Blazor」という
専用ページがあり、DbContext の扱いに特別な配慮が要るとされている。

- サーバー側 Blazor はサーキット単位で状態を保持するため、Scoped の
  DbContext がコンポーネント間で共有される点を説明
- Singleton / Scoped / Transient のいずれも適さない理由を公式の
  表現で表にまとめた
- 1 つの DbContext に 2 つの操作を同時実行して例外を実測
  A second operation was started on this context instance...
  同じ操作を IDbContextFactory 経由にすると例外が出ないことも実測
- 公式の 4 つの指針 (操作ごとに 1 コンテキスト / Loading フラグ /
  複数スレッドの可能性があればファクトリー / 長い操作では
  コンポーネントの寿命に合わせる) を掲載
- Blazor WebAssembly は公式でも対象外である点を NOTE に明記
- 参考リンクに公式ページを追加 (ja-jp で 200 を確認)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Copilot AI review requested due to automatic review settings September 11, 2026 14:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

再試行付きトランザクションとDbContextプールの状態管理に関する説明を修正する必要があります。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/content/docs/appendix-efcore-02/index.md:518

  • この例は、後段で Azure SQL 向けに推奨している UseAzureSql(既定で再試行を有効化)と組み合わせると、ユーザー開始トランザクションが実行戦略の外にあるため最初のコマンドで拒否されます。CreateExecutionStrategy().ExecuteAsync(...) の中で試行ごとに新しい DbContext とトランザクションを作り、IDENTITY_INSERT ON から OFF、コミットまでを一単位にする例にするか、このコードは再試行無効時専用であることを明記してください。
  • Files reviewed: 8/13 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/content/docs/appendix-efcore-05/index.md Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Copilot AI review requested due to automatic review settings September 11, 2026 15:05
@takashiuesaka

Copy link
Copy Markdown
Collaborator Author

レビュー #54 (review) の抑制コメント(IDENTITY_INSERT と自動再試行の併用)も確認し、d2737a3 で対応しました。

前提条件の不足という指摘は妥当です。既存コードは維持し、コードの前に「EF Coreの自動再試行を有効にしていない構成が前提」と明記しました。UseAzureSqlは既定で再試行が有効なので、そのまま適用せず、付録4の実行戦略内で試行ごとにContext・トランザクションを作る例へ誘導しています。Azure SQLの再試行設定を外すことを勧めるものではありません。既存のAzure実測段落にも、実行済みソースを再確認してUseSqlServer・再試行有効化なしという条件を補足しました。

ただし「最初のコマンドで拒否される」は正確ではありません。ExecuteSqlRawAsyncの公式API説明では、このメソッドは現在の実行戦略を使用しません。今回、記事のコードを機械抽出し、SQLiteの実トランザクションにSqlServerRetryingExecutionStrategyを設定した確認例では、ON呼出し後のSaveChangesAsyncでユーザー開始トランザクションの例外となり、finallyのOFF呼出しを観測しました。SET文はインターセプターで捕捉・実行抑止しており、Azure SQLのセッション状態を今回再測定したという意味ではありません。実行戦略内にトランザクション全体を置く対照では保存が成功しています。

実SQL Serverプロバイダーでの再試行既定値3ケース、トランザクション境界3ケース、個別スレッドのプーリング6ケースを、編集前後にそれぞれ実行し12ケース成功しています。両記事のコードブロックと見出しは不変で、追加リンクも生成HTMLで確認しています。今回はAzure操作を行っていません。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The timestamp-based concurrency token can remain unchanged and allow a conflicting update to overwrite data.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 8/13 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/content/docs/appendix-efcore-04/index.md Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Copilot AI review requested due to automatic review settings September 11, 2026 15:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

付録3のSQL例に宣言されていない変数参照があり、そのままでは実行できません。

Review details
  • Files reviewed: 8/13 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Copilot AI review requested due to automatic review settings September 11, 2026 15:30
@takashiuesaka

Copy link
Copy Markdown
Collaborator Author

レビュー #54 (review) を確認しました。掲載SQLをそのまま実行できないという指摘は妥当で、b719a4d で対応しています。

付録3「列名は動的にできない」の DECLARE p0 / DECLARE p1 を、公式のT-SQL宣言構文に合わせ DECLARE Pavel Avgustinov (@p0) / DECLARE Chad (@p1) に修正しました。説明のためローカル変数宣言を付けたSQLであることも明記しています。

ただし、元のC#コードの実行時にパラメーターが未設定という問題ではありません。修正前にEF Core SqlServer 10.0.11で記事の式を抽出して確認したところ、元の掲載内容は ToQueryString() の診断出力と一致しました。一方、CreateDbCommand() で生成された実コマンドにはDECLAREは含まれず、SELECTの@p0/Chad (@p1)に対応する2つのDbParameterが設定されています。ToQueryStringの公式API説明も、出力はデバッグ用で直接実行に適さない場合があるとしています。記事にもこの区別を補足しました。

修正前後の3ケースで、診断出力と実パラメーター、宣言名と参照名の対応、SQLiteでの「列名まで値として補間すると0件/実際のOwner列を指定すると1件」を確認しています。今回、SQL Server用のDECLARE付きバッチをSQL Server/Azure SQLで再実行したわけではありません。宣言修正は公式文法に基づき、既存のC#コードとSQL Server 2022の実測結果の説明は維持しています。Azure操作はありません。

個別コメントのないレビューのため、判定と対応をこちらへ記録します。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

広範なプロバイダー固有の説明と実行可能なコード例を含むため、最終的な人手による検証が必要です。

Review details
  • Files reviewed: 8/13 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

キャンセル時のロールバック処理と章間リンクに修正が必要です。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 8/13 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/content/docs/08-entity-framework-core/index.md Outdated
Comment thread src/content/docs/appendix-efcore-02/index.md Outdated
Correct model-dependent SQL, complex-type version history, Azure SQL options, query examples, and test fixture assumptions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Copilot AI review requested due to automatic review settings September 11, 2026 16:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

キーなしエンティティ型における ToTableToView の説明を訂正する必要があります。

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/content/docs/appendix-efcore-02/index.md:225

  • キーなしエンティティ型は ToTable でマップしても追跡・更新されないため、「ToTableToView の違いは読み取り専用として扱うかどうか」という説明は誤りです。ToTable なら書き込み可能だと読者が誤解しないよう、読み取り専用性はキーなし型自体の特性であることを明記してください。
  • Files reviewed: 8/13 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Use a non-canceled token for explicit rollback and restore the Markdown file target in the cross-chapter link.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb9e357f-a50f-48d7-a2f0-e878f7bfc777
Copilot AI review requested due to automatic review settings September 12, 2026 01:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

提示された変更を通して確認し、承認を妨げる未解決の具体的な問題は見つかりませんでした。

Review details
  • Files reviewed: 8/13 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@takashiuesaka
takashiuesaka merged commit 2317edb into main Sep 12, 2026
4 checks passed
@takashiuesaka
takashiuesaka deleted the takashiuesaka-chapter-8-ef-core branch September 12, 2026 01:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

第8章の新規執筆

2 participants