diff --git a/.github/ISSUE_TEMPLATE/quality.md b/.github/ISSUE_TEMPLATE/quality.md
index 75c466431..d6ce2e73d 100644
--- a/.github/ISSUE_TEMPLATE/quality.md
+++ b/.github/ISSUE_TEMPLATE/quality.md
@@ -36,7 +36,7 @@ labels: quality improvement
.\2_RunAllTests.ps1
.\3_SmokeTest.ps1
-合格の目安は 全ステップ OK / 8-8 差分 0 / 22-22。
+合格の目安は 全ステップ OK / 8-8 差分 0 / 25-25。
-->
## 利用者への影響
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 5b2af8c41..fdeff7416 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -25,7 +25,7 @@
.\2_RunAllTests.ps1
.\3_SmokeTest.ps1
-合格の目安は 全ステップ OK / 8-8 差分 0 / 22-22。
+合格の目安は 全ステップ OK / 8-8 差分 0 / 25-25。
文書だけの変更なら「不要」と書いてください。
-->
diff --git a/.gitignore b/.gitignore
index e8c067635..e05dd6615 100644
--- a/.gitignore
+++ b/.gitignore
@@ -341,3 +341,9 @@ __pycache__/
/root/programs/*/NuGet/in/*/*.json
/root/programs/*/NuGet/in/*/*/
*.snupkg
+
+# エージェント用スキル(#577)
+# 本体は OpenTouryoCodingAgentAssets にあり、ここに在るのは複製である。
+# **コミットすると、向こうが更新されたときに古くなる。**
+# 取得は root/programs/GetAgentSkills.ps1 で行う。
+/.claude/skills/
diff --git a/AGENTS.md b/AGENTS.md
index d208ad5cf..232dff858 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -192,6 +192,87 @@ cd root\programs
`2_RunAllTests.ps1` はワーキング ツリーの `Result*.txt` を書き換える(従来のバッチ運用と同じ)。
**コミットの要否は人が判断する**ため、エージェントは差分を報告するに留める。
+**エージェントは、この 3 本を個別に実行することを検討する。**(#576)
+`0_RunAll.ps1` は 3 本をまとめて回す**利用者向けの入口**であり、
+**必要な 1 本だけを選ぶ、という判断が入らない。**
+どれを回すかは、次節の対応表で決める。
+
+```powershell
+.\1_BuildAll.ps1 -Only "Framework_Tool" # ビルドするものだけ
+.\2_RunAllTests.ps1 -Only "TestBatch" # テスト結果 Result*.txt が絞られる
+.\3_SmokeTest.ps1 -Only "DeployZip" # 確かめるものだけ
+```
+
+**`-Only` に何を指定できるかは `-List` で出す。**
+**文書には一覧を書かない。** 対象が増減したときに古くなるため、
+スクリプト自身を一次情報にする(ツールの `/HELP` と同じ考え方)。
+
+```powershell
+.\1_BuildAll.ps1 -List -Lang Both # 45 ステップ
+.\2_RunAllTests.ps1 -List # 8 件
+.\3_SmokeTest.ps1 -List -Lang VB # 6 件(-Lang が効く)
+```
+
+**3 本とも `-Only` が空振りしたら終了コード 1 で止まる。**
+打ち間違いが「全ステップ OK」になることはない。
+
+#### 通しで回す前に、依存関係を見る(#576)
+
+**全部回すのは「安全」ではない。遅いだけのことがある。**
+
+依存の向きは一方向で、段は 2 つしかない。
+
+```
+基盤 NuGet / Business / Business.RichClient / CopyAssemblies
+ ↓ (ここが変われば、下は全部やり直し)
+末端 Tools / 各サンプル / Tests
+```
+
+**ツールと個別サンプルは末端である。** そこを変えても、基盤も他のサンプルも変わらない。
+
+| 変更した場所 | `1_BuildAll` | `2_RunAllTests` | `3_SmokeTest` |
+|---|---|---|---|
+| `Infrastructure/`(基盤) | **通し** | **通し** | **通し** |
+| `Tools/`(ツール) | `-Only Framework_Tool` | **不要** | `-Only <そのツール>` |
+| 個別サンプル | `-Only <サンプル>` | **不要** | `-Only <サンプル>` |
+| `Tests/` | `-Only <対象>` | `-Only <対象>` | **不要** |
+| `.ps1` / `.md` のみ | **不要** | **不要** | **不要** |
+
+**`2_RunAllTests.ps1` の対象はフレームワークのテストだけ**である。
+`Tools/` や個別サンプルを変えても、ここは動かない。回す理由が無い。
+
+実測(`0_RunAll.ps1 -Lang Both` は 24.6 分)。
+
+```
+基盤のビルド 122.8 秒
+Framework_Tool 系だけ 74.8 秒 ← ツールの変更で必要なのはこちら
+2_RunAllTests 通し 111.6 秒
+2_RunAllTests -Only 35 秒ほど
+```
+
+**ツールだけの変更なら、1/16 ほどで終わる。**
+
+#### 時間だけの問題ではない
+
+`2_RunAllTests.ps1` は**ワーキング ツリーの `Result*.txt` を書き換える。**
+関係の無い対象まで回すと、**人が確認してコミットする差分が増える。**
+
+`-Only` で絞れば、書き換わるのは絞った対象の分だけになる。
+
+#### それでも通しを回す場面
+
+- **基盤(`Infrastructure/`)に触れたとき**
+- **リリース前**(`RELEASE.md`)
+- **絞り込みで妙なエラーが出たとき**(再現するかを見る)
+
+3 本目は制約による誤検知が実際にある。
+`-Only` は前段が用意した状態に依存するステップを落とすことがあり、
+**変更と無関係なエラーに見える。** 詳細は
+[`CHEATSHEET.md`](root/programs/CHEATSHEET.md) 1 節の
+「`-Only` + `-SkipClean` は万能ではない」。
+
+**判断に迷ったら通しでよい。** ただし**迷っていないのに通すのは、ただの浪費である。**
+
**上記の既定は C# 側である。VB 側に手を入れたときは `-Lang` で回す。**
```powershell
@@ -211,6 +292,51 @@ cd root\programs
前提となるサービスや DB の状態が足りない場合は、**勝手に変えず、対処方法とともに報告する。**
+### サンプルを実装するときは、スキル リポジトリのスキルを使う(#577)
+
+**サンプルは「フレームワークを使うアプリ」である。**
+その書き方は本体の規約とは別に、専用のスキルとしてまとめられている。
+
+https://github.com/OpenTouryoProject/OpenTouryoCodingAgentAssets
+
+**`AGENTS.md` に書くだけでは使えない。**
+Claude Code は `.claude/skills/<名前>/SKILL.md` を探すため、実体が要る。
+
+```powershell
+cd root\programs
+.\GetAgentSkills.ps1 # main から取得して .claude/skills へ配置
+.\GetAgentSkills.ps1 -List # 何が対象になるかだけ見る
+```
+
+**`.claude/skills/` は `.gitignore` の対象である。**
+ここに在るのは複製で、**本体は向こうのリポジトリにある。**
+コミットすると、向こうが更新されたときに古くなり、どちらが正か分からなくなる。
+**使う前に取得する。**
+
+#### 向こうの `install.ps1` は使わない
+
+あちらにも導入スクリプトがあるが、**フレームワークの利用者(アプリ開発)向け**で、
+導入先の `AGENTS.md` と `CLAUDE.md` も生成する。
+**本書は手で書いたものなので、スキルだけを取りに行く。**
+
+#### 既定で除外しているもの
+
+| 除外 | 理由 |
+|---|---|
+| `opentouryo-project-setup*`(6 件) | アプリの新規構築手順。本体の開発では使わない |
+| `opentouryo-project-policy` | **プロジェクト方針は本書(`AGENTS.md`)が正** |
+| `opentouryo-project-transform` | 既存資産の移行。本体側では別の話 |
+| `opentouryo-comment-convention` | **コメント規約は [`CODING.md`](root/programs/CODING.md) が正** |
+| `opentouryo-base2-customize` | 利用者による基底クラスの改造。本体側では別の話 |
+
+**除外を増やしたら、取得し直せば取り残しも消える。**
+前回配置したものが除外に回った場合、`GetAgentSkills.ps1` が削除する
+(消すのは向こうに在るスキル名だけで、無関係なものは触らない)。
+
+**規約が競合したら、本体の文書が優先である。**
+スキルはアプリ開発を前提に書かれているため、
+[`CODING.md`](root/programs/CODING.md) や各 `ANALYSIS.md` と食い違うことがある。
+
### 付属ツールを CLI で使うときは、各ツールの README に従う
エージェントから実行できる(非対話の)ツールには README を置く。
diff --git a/root/README.md b/root/README.md
index a336dd936..2b137494c 100644
--- a/root/README.md
+++ b/root/README.md
@@ -185,7 +185,6 @@ C:\root\programs\CS\Samples\WS_sample\WSClient_sample\WSClientWinCone_sample\WSC
#### Infrastructure:
- C:\root\programs\CS\Frameworks\Infrastructure
-- C:\root\programs\CS\Frameworks\Infrastructure\ServiceInterface\ASPNETWebServiceCore
#### Tools:
- C:\root\programs\CS\Frameworks\Tools
diff --git a/root/Readme.ja.md b/root/Readme.ja.md
index 4170bf382..68c80d97e 100644
--- a/root/Readme.ja.md
+++ b/root/Readme.ja.md
@@ -183,7 +183,6 @@ C:\root\programs\CS\Samples\WS_sample\WSClient_sample\WSClientWinCone_sample\WSC
#### 基盤:
- C:\root\programs\CS\Frameworks\Infrastructure
-- C:\root\programs\CS\Frameworks\Infrastructure\ServiceInterface\ASPNETWebServiceCore
#### ツール:
- C:\root\programs\CS\Frameworks\Tools
diff --git a/root/programs/0_RunAll.ps1 b/root/programs/0_RunAll.ps1
index 7e36e53ad..f2781f7d1 100644
--- a/root/programs/0_RunAll.ps1
+++ b/root/programs/0_RunAll.ps1
@@ -71,7 +71,7 @@ foreach ($s in $scripts)
if ($Lang -eq "VB" -and -not $s.UseLang)
{
Write-Host ("{0} は VB 版の対象外のため飛ばします。" -f $s.Name) -ForegroundColor Yellow
- $results += [pscustomobject]@{ スクリプト = $s.Name; 終了コード = "対象外" }
+ $results += [pscustomobject]@{ スクリプト = $s.Name; 終了コード = "対象外"; 秒 = "-" }
continue
}
@@ -79,8 +79,20 @@ foreach ($s in $scripts)
if ($s.UseLang) { $splat.Lang = $Lang }
if ($s.UseIgnore) { $splat.IgnoreErrors = $IgnoreErrors }
+ # **実行時間を測る。**(#571)
+ # 通しは長い。合計だけ見ても、どこを短くすればよいかが分からない。
+ $sw = [Diagnostics.Stopwatch]::StartNew()
+
& (Join-Path $PSScriptRoot $s.Name) @splat
- $results += [pscustomobject]@{ スクリプト = $s.Name; 終了コード = $LASTEXITCODE }
+ $code = $LASTEXITCODE
+
+ $sw.Stop()
+
+ $results += [pscustomobject]@{
+ スクリプト = $s.Name
+ 終了コード = $code
+ 秒 = ("{0:N1}" -f $sw.Elapsed.TotalSeconds)
+ }
# --- bindingRedirect の突き合わせ(警告のみ)---(#556)
#
@@ -100,6 +112,25 @@ foreach ($s in $scripts)
Write-Host " .\CompareRedirect.ps1 -Check で内容を確認してください。"
Write-Host ""
}
+
+ # --- パッケージの版の突き合わせ(警告のみ)---(#569)
+ #
+ # **版は 4 か所に散らばる。**(#566 / #568)
+ # packages.config を基準に、csproj のパス表記(②)と
+ # Reference の Version(③)が外れていないかを見る。
+ #
+ # ③ は HintPath の DLL を読むため、**復元してからでないと判定できない。**
+ # ここ(ビルドの直後)なら材料が揃っている。
+ & (Join-Path $PSScriptRoot "ComparePackage.ps1") -Check 6>$null | Out-Null
+ $packageOk = ($LASTEXITCODE -eq 0)
+
+ if (-not $packageOk)
+ {
+ Write-Host ""
+ Write-Host "【警告】packages.config と csproj で、パッケージの版が食い違っています。" -ForegroundColor Yellow
+ Write-Host " .\ComparePackage.ps1 -Check で内容を確認してください。"
+ Write-Host ""
+ }
}
}
@@ -110,6 +141,12 @@ Write-Host ""
Write-SummaryTable $results
Write-Host ""
+# **合計も出す。** 1 本ずつの秒を足す手間を省く。
+$totalSec = ($results | Where-Object { $_.秒 -ne "-" } |
+ ForEach-Object { [double]$_.秒 } | Measure-Object -Sum).Sum
+Write-Host (" 合計 : {0:N1} 秒({1:N1} 分)" -f $totalSec, ($totalSec / 60))
+Write-Host ""
+
# **終了コードをそのまま合否として読んでよい。**(#555)
# 既知の署名エラー(MSB3482)は -IgnoreErrors の既定値で除外しているため、
# 1_BuildAll.ps1 が 1 を返したら、それは**別の理由**である。
@@ -142,5 +179,12 @@ if ($null -ne $redirectOk -and -not $redirectOk)
Write-Host " (CompareRedirect.ps1 -Check)。合否には数えていません。"
}
+if ($null -ne $packageOk -and -not $packageOk)
+{
+ Write-Host ""
+ Write-Host "【警告】packages.config と csproj で、パッケージの版が食い違っています" -ForegroundColor Yellow
+ Write-Host " (ComparePackage.ps1 -Check)。合否には数えていません。"
+}
+
# --- 画面を残すための処理 ---
Read-Host "`nEnterキーを押すとウィンドウを閉じます"
diff --git a/root/programs/1_BuildAll.ps1 b/root/programs/1_BuildAll.ps1
index 8dc99b32e..890777763 100644
--- a/root/programs/1_BuildAll.ps1
+++ b/root/programs/1_BuildAll.ps1
@@ -31,6 +31,8 @@
.PARAMETER Only
ステップ名の部分一致で対象を絞る(例: -Only "net48")。動作確認用。
+.PARAMETER List
+ -Only に指定できるステップ名を一覧表示して終わる。**ここが一次情報。**
.PARAMETER SkipClean
クリーン処理(1_DeleteDir / 1_DeleteFile)を省略する。
※ リリース判定では省略しないこと。前回のビルド成果物が残っていると、
@@ -73,7 +75,9 @@ param(
[ValidateSet("CS", "VB", "Both")]
[string]$Lang = "CS",
[string]$Only,
+ [switch]$List,
[switch]$SkipClean,
+ [switch]$WarnDetail,
[string]$OutputDir = (Join-Path $env:TEMP "OpenTouryoBuildLogs"),
[string[]]$IgnoreErrors = @()
)
@@ -305,6 +309,34 @@ $total = [Diagnostics.Stopwatch]::StartNew()
# 区画の区切りとしての表示は「実行済み」として残す(黙って消さない)。
$executed = @{}
+
+# **-Only に何を指定できるかは、ここが一次情報である。**(#576)
+# 文書に書き写すと二重管理になり、対象が増減したときに古くなる。
+if ($List)
+{
+ Write-Host ("=== -Only に指定できる名前(部分一致){0} ===" -f "(-Lang $Lang)") -ForegroundColor Cyan
+ foreach ($x in $steps) { Write-Host (" " + $x.Name) }
+ Write-Host (" ---- {0} 件 ----" -f $steps.Count)
+ exit 0
+}
+
+# **-Only が空振りしたら止める。**(#576)
+# 1 件も選ばれないまま進むと「全ステップ OK」と表示され、
+# **打ち間違いが緑になる。** 何も建てていないのに成功に見えるのが最も悪い。
+if ($Only)
+{
+ $matched = @($steps | Where-Object { ($_.Name -like "*$Only*") -or ($_.Bat -like "*$Only*") })
+
+ if ($matched.Count -eq 0)
+ {
+ Write-Host (" **-Only '$Only' に一致するステップがありません。**") -ForegroundColor Red
+ Write-Host (" -List で一覧を出せます。") -ForegroundColor Yellow
+ exit 1
+ }
+
+ Write-Host (" -Only '$Only' : {0} ステップに絞りました" -f $matched.Count) -ForegroundColor Yellow
+}
+
foreach ($s in $steps)
{
if ($Only -and ($s.Name -notlike "*$Only*") -and ($s.Bat -notlike "*$Only*"))
@@ -388,6 +420,7 @@ foreach ($s in $steps)
エラー = $stepErrors.Count
既知 = $stepKnown.Count
警告 = $diag.Warnings.Count
+ 警告詳細 = $diag.Warnings
秒 = [Math]::Round($sw.Elapsed.TotalSeconds, 1)
}
}
@@ -400,7 +433,55 @@ $total.Stop()
Write-Host ""
Write-Host "================ サマリ ================"
Write-Host ""
-Write-SummaryTable $results
+# **警告詳細は列にしない。** 表が壊れるので、集計にだけ使う。
+Write-SummaryTable ($results | Select-Object * -ExcludeProperty 警告詳細)
+
+# --- 警告の内訳(#571)---
+#
+# **件数だけでは何を直せばよいか分からない。** 種類ごとにまとめる。
+# 既定では出さない。毎回出ると本題(エラー)が埋もれるため。
+if ($WarnDetail)
+{
+ $withWarn = @($results | Where-Object { $_.警告詳細 -and $_.警告詳細.Count -gt 0 })
+
+ if ($withWarn.Count -eq 0)
+ {
+ Write-Host ""
+ Write-Host " 警告はありません。"
+ }
+ else
+ {
+ Write-Host ""
+ Write-Host "================ 警告の内訳 ================"
+
+ foreach ($r in ($withWarn | Sort-Object { -$_.警告詳細.Count }))
+ {
+ Write-Host ""
+ Write-Host (" {0}({1} 件)" -f $r.ステップ, $r.警告詳細.Count)
+
+ # 「: warning XXnnnn :」から種類を取り出す。取れないものは (種類不明) にまとめる。
+ $byCode = $r.警告詳細 | ForEach-Object {
+ $m = [regex]::Match($_, ':\s*warning\s+([A-Za-z]+\d+)\s*:')
+ if ($m.Success) { $m.Groups[1].Value } else { "(種類不明)" }
+ } | Group-Object | Sort-Object Count -Descending
+
+ foreach ($g in $byCode)
+ {
+ # 代表を 1 つ出す。**同じ種類でも中身が違うことがある**ので、目印になる。
+ $sample = @($r.警告詳細 | Where-Object { $_ -match [regex]::Escape($g.Name) })[0]
+ if ($null -eq $sample) { $sample = "" }
+ $sample = ($sample -replace '\s+', ' ')
+ if ($sample.Length -gt 96) { $sample = $sample.Substring(0, 96) + " …" }
+
+ Write-Host (" {0,4} {1,-12} {2}" -f $g.Count, $g.Name, $sample)
+ }
+ }
+
+ Write-Host ""
+ Write-Host " **同じ種類は、たいてい 1 か所の対処でまとめて消える。**"
+ Write-Host " MSB3277 は版の混在(CompareRedirect.ps1 / ComparePackage.ps1 も参照)。"
+ }
+}
Write-Host ""
Write-Host (" 所要時間 : {0:N1} 分" -f $total.Elapsed.TotalMinutes)
Write-Host (" ログ : {0}" -f $OutputDir)
diff --git a/root/programs/2_RunAllTests.ps1 b/root/programs/2_RunAllTests.ps1
index 65781fafc..fa018d64f 100644
--- a/root/programs/2_RunAllTests.ps1
+++ b/root/programs/2_RunAllTests.ps1
@@ -38,6 +38,11 @@
.PARAMETER OutputDir
HEAD 版の期待値とビルド ログの出力先。既定は %TEMP%\OpenTouryoTestResults。
+.PARAMETER Only
+ 対象名の部分一致で絞る(例: -Only "TestBatch")。
+ **Result*.txt を書き換えるのは、絞った対象だけになる。**
+.PARAMETER List
+ -Only に指定できる対象名を一覧表示して終わる。**ここが一次情報。**
.PARAMETER SkipBuild
バッチの実行を省略し、ワーキング ツリーにある既存の Result*.txt を比較する。
@@ -47,6 +52,9 @@
.EXAMPLE
.\2_RunAllTests.ps1 -SkipBuild
+.EXAMPLE
+ .\2_RunAllTests.ps1 -Only "TestBatch"
+
.NOTES
作成者 :玄人 幸道
更新履歴 :
@@ -58,6 +66,8 @@
[CmdletBinding()]
param(
[string]$OutputDir = (Join-Path $env:TEMP "OpenTouryoTestResults"),
+ [string]$Only,
+ [switch]$List,
[switch]$SkipBuild
)
@@ -189,11 +199,11 @@ $tests = @(
Result = "TestDataAccess\ResultCore100.txt"; SkipLog4net = $false; NormBase64 = $false
}
@{
- Name = "SimpleBatch (net48)"; Bat = "y_Build_TestCode_Batch.bat"
+ Name = "TestBatch (net48)"; Bat = "y_Build_TestCode_Batch.bat"
Result = "TestBatch\ResultSimpleBatch48.txt"; SkipLog4net = $true; NormBase64 = $false
}
@{
- Name = "SimpleBatch (net10.0)"; Bat = "y_Build_TestCode_Batch.bat"
+ Name = "TestBatch (net10.0)"; Bat = "y_Build_TestCode_Batch.bat"
Result = "TestBatch\ResultSimpleBatchCore100.txt"; SkipLog4net = $true; NormBase64 = $false
}
@{
@@ -206,6 +216,37 @@ $tests = @(
}
)
+# ------------------------------------------------------------------
+# 対象の絞り込み(#576)
+# ------------------------------------------------------------------
+# **依存関係の外まで回す必要はない。**
+# このスクリプトはワーキング ツリーの Result*.txt を書き換えるため、
+# 不要な対象まで回すと、人が確認・コミットする差分が増える。
+
+# **-Only に何を指定できるかは、ここが一次情報である。**(#576)
+# 文書に書き写すと二重管理になり、対象が増減したときに古くなる。
+if ($List)
+{
+ Write-Host ("=== -Only に指定できる名前(部分一致){0} ===" -f "") -ForegroundColor Cyan
+ foreach ($x in $tests) { Write-Host (" " + $x.Name) }
+ Write-Host (" ---- {0} 件 ----" -f $tests.Count)
+ exit 0
+}
+
+if ($Only)
+{
+ $tests = @($tests | Where-Object { $_.Name -like "*$Only*" })
+
+ if ($tests.Count -eq 0)
+ {
+ Write-Host (" **-Only '$Only' に一致する対象がありません。**") -ForegroundColor Red
+ Write-Host (" -List で一覧を出せます。") -ForegroundColor Yellow
+ exit 1
+ }
+
+ Write-Host (" -Only '$Only' : {0} 件に絞りました" -f $tests.Count) -ForegroundColor Yellow
+}
+
# ------------------------------------------------------------------
# 期待値(HEAD 版)の取り出し
# ------------------------------------------------------------------
diff --git a/root/programs/3_SmokeTest.ps1 b/root/programs/3_SmokeTest.ps1
index b152fc46b..ecde145b0 100644
--- a/root/programs/3_SmokeTest.ps1
+++ b/root/programs/3_SmokeTest.ps1
@@ -47,6 +47,8 @@
.PARAMETER Only
対象名の部分一致で絞る(例: -Only "Rerunnable")。
+.PARAMETER List
+ -Only に指定できる対象名を一覧表示して終わる。**ここが一次情報。**
.PARAMETER SkipBuild
ビルドを省略し、既存のバイナリで疎通のみ行う。
@@ -78,6 +80,7 @@ param(
[ValidateSet("CS", "VB", "Both")]
[string]$Lang = "CS",
[string]$Only,
+ [switch]$List,
[switch]$SkipBuild,
[string]$OutputDir = (Join-Path $env:TEMP "OpenTouryoSmokeTest")
)
@@ -143,779 +146,18 @@ if ([Console]::OutputEncoding.CodePage -ne 65001)
{
[Console]::OutputEncoding = New-Object Text.UTF8Encoding $false
}
-
-# ------------------------------------------------------------------
-# 接続文字列
-# ------------------------------------------------------------------
-# サンプルが実際に使う App.config から読む。
-# ここで別途ハードコードすると、サンプル側の変更に追随できなくなる。
-function Get-SampleConnectionString
-{
- $config = Join-Path $configRoot "Samples\Bat_sample\SimpleBatch_sample\App.config"
- if (-not (Test-Path $config)) { return $null }
-
- $xml = [xml](Get-Content $config -Raw)
- $node = $xml.configuration.connectionStrings.add |
- Where-Object { $_.name -eq "ConnectionString_SQL" }
- return $node.connectionString
-}
-
-$connString = Get-SampleConnectionString
-
-function Invoke-Sql([string]$sql)
-{
- $c = New-Object System.Data.SqlClient.SqlConnection $connString
- $c.Open()
- try
- {
- $cmd = $c.CreateCommand()
- $cmd.CommandText = $sql
- return $cmd.ExecuteScalar()
- }
- finally { $c.Close() }
-}
-
# ------------------------------------------------------------------
-# 対象の定義
+# 分割したファイルを読む(#571)
# ------------------------------------------------------------------
-# Name : 表示名(ログのファイル名にもなるため、言語をまたいで重複させない)
-# Dir : バッチとサンプルのあるフォルダ(root\programs からの相対)。
-# 省略時は対象言語のフォルダ。
-# Bat : ビルドに使うバッチ(Dir 配下)
-# Exe : 実行ファイル(net48)。Dll を指定した場合は dotnet で実行する。
-# Args : コマンドライン引数
-# Pre : 実行前に行う準備(スクリプト ブロック)
-# Expect : 標準出力がこの正規表現に一致すれば成功
-# Verify : 追加の検証(スクリプト ブロック)。$true を返せば成功
+# **ドット ソースで読む。** 関数と変数を、この実行スコープへ入れるため。
#
-# ※ サンプルは末尾に Console.ReadKey() を持つため、出力をリダイレクトすると
-# 必ず例外で終わる。これはテスト内容とは無関係なので、判定から除外する。
-$batchArgs = @("/DAP", "SQL", "/MODE1", "individual", "/MODE2", "static", "/EXROLLBACK", "-")
-
-# ------------------------------------------------------------------
-# Orders2(Northwind 標準には無い表)
-# ------------------------------------------------------------------
-# instnwnd.sql に含まれないため、**DB を作り直すたびに消える**。
-# 無いまま実行すると「オブジェクト名 'Orders2' が無効です」で事前準備が落ち、
-# 原因が読み取れない。ここで作ってしまう。
-#
-# DDL は同梱の CREATE ORDERS2.sql をそのまま流す。**ここに書き写さない。**
-# (同じ DDL がサンプル配下に 9 つ重複しており、さらに増やす意味がない)
-function Initialize-Orders2
-{
- $exists = Invoke-Sql "SELECT OBJECT_ID('dbo.Orders2', 'U')"
- if ($null -ne $exists -and $exists -isnot [DBNull]) { return }
-
- $ddl = Join-Path $configRoot "Samples\Bat_sample\RerunnableBatch_sample\CREATE ORDERS2.sql"
- if (-not (Test-Path $ddl))
- {
- throw "Orders2 が無く、DDL も見つかりません : $ddl"
- }
-
- Write-Host " Orders2 がありません。作成します($ddl)。"
-
- # sqlcmd ではなく SqlClient で流すため、GO(バッチ区切り)は自前で分ける。
- # SqlClient は GO を解釈できず、構文エラーになる。
- foreach ($batch in ((Get-Content $ddl -Raw) -split '(?im)^\s*GO\s*$'))
- {
- # USE は流さない。接続先は接続文字列に従う(別 DB を指していても壊さない)。
- if ($batch -match '\S' -and $batch -notmatch '(?im)^\s*USE\s')
- {
- Invoke-Sql $batch | Out-Null
- }
- }
-}
-
-# RerunnableBatch 系は Orders → Orders2 の INSERT。実行前に Orders2 を空にする。
-$clearOrders2 = {
- Initialize-Orders2
- Invoke-Sql "DELETE FROM [Orders2]" | Out-Null
-}
-# 実行後は Orders と同数(830 件)になっていること。
-$verifyOrders2 = {
- $src = [int](Invoke-Sql "SELECT COUNT(*) FROM [Orders]")
- $dst = [int](Invoke-Sql "SELECT COUNT(*) FROM [Orders2]")
- Write-Host (" Orders {0} 件 → Orders2 {1} 件" -f $src, $dst)
- return ($dst -eq $src -and $src -gt 0)
-}
-
-# ------------------------------------------------------------------
-# DaoGen_Tool(墨壺)の CUI モード
-# ------------------------------------------------------------------
-# #508 で追加された CUI。2 モードを DAODEFGEN → DAOSQLGEN の順に実行し、
-# 前段が出力した定義 CSV を後段の入力に使う。
-#
-# <パス区切りの注意>
-# コマンドライン解析(StringVariableOperator.GetCommandArgs)は
-# 「\」をエスケープ文字として扱うため、パスの区切りは「/」にする。
-# 「C:\temp\out」と書くと「\」が消えて別のパスになる(ツールの /HELP にも記載)。
-#
-# <テンプレート>
-# root/files/tools/DGenTemplates(DaoTemplate*.cs / *Template.xml などの平置き)。
-
-$daoGenTemplate = ((Join-Path $csRoot "..\..\files\tools\DGenTemplates" | Resolve-Path).Path) -replace '\\', '/'
-
-# 対象を 2 テーブルに絞る。全テーブルを回すと時間がかかるだけで、
-# 疎通の確認としては同じことを見ている。
-$daoGenTables = "Shippers,Orders"
-
-# 作業フォルダを作る(net48 / Core で分ける)
-function New-DaoGenWork([string]$tag)
-{
- $work = Join-Path $OutputDir "daogen_$tag"
- New-Item -ItemType Directory -Force (Join-Path $work "gen") | Out-Null
- # 前回の生成物を残さない(残っていると「生成された」の判定が甘くなる)
- Get-ChildItem $work -Recurse -File -EA SilentlyContinue | Remove-Item -Force -EA SilentlyContinue
- return $work
-}
-
-function New-DaoGenArgs([string]$tag, [string]$mode)
-{
- $work = Join-Path $OutputDir "daogen_$tag"
- $csv = ($work + "/DaoDef.csv").Replace("\", "/")
- $gen = ($work + "/gen").Replace("\", "/")
-
- if ($mode -eq "DAODEFGEN")
- {
- return @("/CUI", "/MODE", "DAODEFGEN", "/OUTPUT", $csv, "/DAP", "SQL", "/TABLES", $daoGenTables)
- }
- return @("/CUI", "/MODE", "DAOSQLGEN", "/DAODEF", $csv,
- "/TEMPLATE", $daoGenTemplate, "/OUTPUT", $gen,
- "/DAP", "SQL", "/LANG", "CS", "/ENTITY")
-}
-
-# 定義 CSV に対象テーブルが並んでいること
-function Test-DaoDef([string]$tag)
-{
- $csv = Join-Path (Join-Path $OutputDir "daogen_$tag") "DaoDef.csv"
- if (-not (Test-Path $csv)) { Write-Host " 定義 CSV が生成されていない"; return $false }
-
- $text = Get-Content $csv -Raw
- $ok = ($text -match 'Shippers') -and ($text -match 'Orders')
- Write-Host (" 定義 CSV : {0} 行" -f (Get-Content $csv).Count)
- return $ok
-}
-
-# Dao・DTO・SQL が生成されていること
-function Test-DaoGen([string]$tag)
-{
- $gen = Join-Path (Join-Path $OutputDir "daogen_$tag") "gen"
- $files = @(Get-ChildItem $gen -Recurse -File -EA SilentlyContinue)
- Write-Host (" 生成ファイル : {0} 件" -f $files.Count)
-
- # Dao クラス(.cs)と 動的 SQL(.xml)と 静的 SQL(.sql)が揃っていること
- $hasCs = @($files | Where-Object { $_.Name -eq "DaoShippers.cs" }).Count -gt 0
- $hasXml = @($files | Where-Object { $_.Extension -eq ".xml" }).Count -gt 0
- $hasSql = @($files | Where-Object { $_.Extension -eq ".sql" }).Count -gt 0
- return ($hasCs -and $hasXml -and $hasSql)
-}
-
-$prepareDaoGen48 = { [void](New-DaoGenWork "net48") }
-$prepareDaoGenCore = { [void](New-DaoGenWork "core") }
-$daoDefArgs48 = New-DaoGenArgs "net48" "DAODEFGEN"
-$daoSqlArgs48 = New-DaoGenArgs "net48" "DAOSQLGEN"
-$daoDefArgsCore = New-DaoGenArgs "core" "DAODEFGEN"
-$daoSqlArgsCore = New-DaoGenArgs "core" "DAOSQLGEN"
-$verifyDaoDef48 = { Test-DaoDef "net48" }
-$verifyDaoGen48 = { Test-DaoGen "net48" }
-$verifyDaoDefCore = { Test-DaoDef "core" }
-$verifyDaoGenCore = { Test-DaoGen "core" }
-
-# ------------------------------------------------------------------
-# DeployZipPackWithHTTP の CUI モード
-# ------------------------------------------------------------------
-# #528 で /MFTGEN(マニュフェスト生成)を追加し、生成から配置まで CUI で通せる。
-#
-# <配置先を C:\ 直下にしない>
-# 同梱サンプルのマニフェストは c:\FormAppRoot\ を指すが、
-# 疎通確認で環境を汚さないよう、$OutputDir 配下を指すマニフェストを作り直す。
-#
-# <引数の癖>(Tools\DeployZipPackWithHTTP\README.md 3.4 節)
-# ・「\」はエスケープ文字として食べられる → パスは「/」で渡す
-# ・ただし /INSDIR だけは「\」を残す(ins 行がそのまま配置先になるため)
-# ・空白を含む値は、自分で引用符を付ける
-$deployWebPort = 51084
-$deploySampleSrc = Join-Path $csRoot "Frameworks\Tools\DeployZipPackWithHTTP\Sample\FormAppRoot"
-
-# **配布物(ZIP)は追跡していない。** FormAppRoot から毎回作る。
-# /ZIPGEN … ルート直下(/TOPONLY)と各フォルダ(/ROOTINZIP)を別々の ZIP にする
-# /MFTGEN … その ZIP からマニュフェストを作る
-# 作り置きを追跡すると、元を直したときの作り直し漏れで MD5 が合わなくなる。
-$deployZipNames = @("root", "aaa", "bbb", "ccc")
-
-function Get-DeployWork([string]$tag)
-{
- return (Join-Path $OutputDir "deploy_$tag")
-}
-
-# 配信フォルダを用意し、FormAppRoot から ZIP を作る
-function New-DeployWeb([string]$tag, [string]$exe)
-{
- $work = Get-DeployWork $tag
- $web = Join-Path $work "web"
- $ins = Join-Path $work "ins"
-
- Get-ChildItem $work -Recurse -File -EA SilentlyContinue | Remove-Item -Force -EA SilentlyContinue
- New-Item -ItemType Directory -Force $web | Out-Null
- New-Item -ItemType Directory -Force $ins | Out-Null
-
- # .mft は既定で MIME 未登録のため 404.3 になる
- $conf = '' + "`r`n" +
- '' +
- '' +
- '' +
- ''
- Set-Content (Join-Path $web "web.config") $conf -Encoding UTF8
-
- #region ZIP を作る(/ZIPGEN)
-
- # **パスの区切りは「/」で渡す。** コマンドライン解析が「\」を食べる。
- $src = $deploySampleSrc.Replace("\", "/")
-
- foreach ($name in $deployZipNames)
- {
- $out = (Join-Path $web $name).Replace("\", "/")
-
- if ($name -eq "root")
- {
- # ルート直下だけ(サブフォルダは各 ZIP が持つ)。書庫内ルートは作らない。
- $a = @("/ZIPGEN", "/SRCDIR", $src, "/ZIPFILE", $out, "/TOPONLY")
- }
- else
- {
- # フォルダごと。書庫内ルートをフォルダ名にする(個別のフォルダ圧縮)。
- $a = @("/ZIPGEN", "/SRCDIR", "$src/$name", "/ZIPFILE", $out, "/ROOTINZIP", $name)
- }
+# **順序が要る。** st_Targets.ps1 は他の 3 つが定義したものを参照する。
+. (Join-Path $PSScriptRoot "st_Utility.ps1")
+. (Join-Path $PSScriptRoot "st_Server.ps1")
+. (Join-Path $PSScriptRoot "st_Flow.ps1")
+. (Join-Path $PSScriptRoot "st_Targets.ps1")
- $log = Join-Path $OutputDir "deploy_zipgen_$tag`_$name.log"
- Start-Process $exe -ArgumentList $a -NoNewWindow -Wait `
- -WorkingDirectory (Split-Path $exe) -RedirectStandardOutput $log
- if (-not (Test-Path (Join-Path $web ($name + ".zip"))))
- {
- Write-Host (" ZIP を生成できない : {0}.zip" -f $name)
- return $work
- }
- }
-
- #endregion
-
- return $work
-}
-
-function New-MftGenArgs([string]$tag)
-{
- $work = Get-DeployWork $tag
- $web = Join-Path $work "web"
-
- # **ファイル一覧を採ってはいけない。**
- # 引数は対象定義の時点(Pre より前)で組み立てられるため、ZIP はまだ無い。
- # 名前は決まっているので、そこから組み立てる。
- $zips = @($deployZipNames | Sort-Object |
- ForEach-Object { (Join-Path $web ($_ + ".zip")).Replace("\", "/") }) -join ","
-
- # ins 行はそのまま配置先になるので「\」を残す(「\\」でエスケープ)
- $ins = (Join-Path $work "ins") + "\"
- $insArg = $ins.Replace("\", "\\")
-
- $mft = (Join-Path $web "FormAppRoot.mft").Replace("\", "/")
-
- # **exe 行はインストール先からの相対パス。** サブフォルダのものは、そう書く。
- # 二重起動チェックがこれを使うため、実在しないパスを書くと検出が効かない。
- return @("/MFTGEN", "/ZIPFILES", $zips, "/INSDIR", $insArg,
- "/EXENAME", '"top.exe, aaa\\top1.exe, bbb\\top2.exe"', "/MFTFILE", $mft)
-}
-
-# /NB … マニフェストの exe 行で指定されたアセンブリを起動しない
-# /FORCE … 履歴を消して毎回やり直す(前回の結果に依存しない)
-$deployArgs = @("/CUI", "/NB", "/FORCE", "/WWWURL",
- ("http://localhost:{0}/FormAppRoot.mft" -f $deployWebPort))
-
-# マニュフェストが生成され、MD5 が実ファイルのものと一致すること
-function Test-MftGen([string]$tag)
-{
- $web = Join-Path (Get-DeployWork $tag) "web"
- $mft = Join-Path $web "FormAppRoot.mft"
- if (-not (Test-Path $mft)) { Write-Host " マニュフェストが生成されていない"; return $false }
-
- $lines = @(Get-Content $mft -Encoding UTF8)
- Write-Host (" マニュフェスト : {0} 行" -f $lines.Count)
-
- $zips = @($lines | Where-Object { $_ -like "zip *" })
- $md5s = @($lines | Where-Object { $_ -like "md5 *" })
-
- if (($zips.Count -ne $deployZipNames.Count) -or ($md5s.Count -ne $deployZipNames.Count))
- {
- Write-Host " zip / md5 の組数が合わない"
- return $false
- }
-
- # **書かれた MD5 を、実ファイルから計算し直して突き合わせる。**
- # ここが合わないと配布時に弾かれる。作り置きの ZIP を使っていた頃は、
- # 元を直したのに ZIP を作り直さず、ここで気付けなかった。
- $md5 = [System.Security.Cryptography.MD5]::Create()
-
- for ($i = 0; $i -lt $zips.Count; $i++)
- {
- $name = $zips[$i].Substring(4).Trim()
- $path = Join-Path $web $name
-
- if (-not (Test-Path $path)) { Write-Host (" ZIP が無い : {0}" -f $name); return $false }
-
- $want = $md5s[$i].Substring(4).Trim()
- $got = [Convert]::ToBase64String($md5.ComputeHash([IO.File]::ReadAllBytes($path)))
-
- if ($want -ne $got) { Write-Host (" MD5 が一致しない : {0}" -f $name); return $false }
- }
-
- return $true
-}
-
-# 配置結果が、圧縮前のフォルダと一致すること
-function Test-Deploy([string]$tag)
-{
- $ins = Join-Path (Get-DeployWork $tag) "ins"
- $md5 = [System.Security.Cryptography.MD5]::Create()
-
- $src = @(Get-ChildItem $deploySampleSrc -Recurse -File)
- $dst = @(Get-ChildItem $ins -Recurse -File -EA SilentlyContinue)
- Write-Host (" 配置 : {0} / {1} ファイル" -f $dst.Count, $src.Count)
-
- if ($dst.Count -ne $src.Count) { return $false }
-
- foreach ($f in $src)
- {
- $rel = $f.FullName.Substring($deploySampleSrc.Length + 1)
- $t = Join-Path $ins $rel
- if (-not (Test-Path $t)) { Write-Host (" 欠落 : {0}" -f $rel); return $false }
-
- $a = [Convert]::ToBase64String($md5.ComputeHash([IO.File]::ReadAllBytes($f.FullName)))
- $b = [Convert]::ToBase64String($md5.ComputeHash([IO.File]::ReadAllBytes($t)))
- if ($a -ne $b) { Write-Host (" 内容相違 : {0}" -f $rel); return $false }
- }
-
- return $true
-}
-
-# 配信用の IIS Express を起動・停止する
-#
-# Web 系の対象(Kind = "Web")は本文側が起動・停止するが、こちらは
-# **EXE を実行する対象なので、その仕組みに乗らない。**
-# Pre で起動し、Verify の最後で止める。
-$script:deployWebProc = $null
-
-function Start-DeployWeb([string]$tag)
-{
- $iis = Join-Path $env:ProgramFiles "IIS Express\iisexpress.exe"
- if (-not (Test-Path $iis)) { return $false }
-
- # **前回の残りを先に止める。**
- # 起動に失敗した IIS Express が URL の登録を握ったままだと、
- # 次の起動が 0x800700b7(既に存在する)で失敗し続ける。
- Stop-DeployWeb
-
- $web = Join-Path (Get-DeployWork $tag) "web"
- $log = Join-Path $OutputDir "deploy_web_$tag.log"
-
- $script:deployWebProc = Start-Process $iis `
- -ArgumentList "/path:`"$web`"", "/port:$deployWebPort", "/systray:false" `
- -PassThru -WindowStyle Hidden `
- -RedirectStandardOutput $log -RedirectStandardError "$log.err"
-
- # 起動を待つ(接続できるまで最大 15 秒)
- #
- # **コンテンツを要求して待ってはいけない。** 404 でも「起動している」ため、
- # 応答の内容で判定すると、ファイルが無いときに待ち続けて
- # 起動したままのプロセスが残る。TCP で繋がるかだけを見る。
- for ($i = 0; $i -lt 30; $i++)
- {
- Start-Sleep -Milliseconds 500
-
- $client = New-Object System.Net.Sockets.TcpClient
- try
- {
- $client.Connect("localhost", $deployWebPort)
- if ($client.Connected) { $client.Close(); return $true }
- }
- catch { }
- finally { $client.Dispose() }
- }
-
- Write-Host " 配信サーバが応答しない"
- Stop-DeployWeb
- return $false
-}
-
-function Stop-DeployWeb
-{
- if ($null -ne $script:deployWebProc)
- {
- try { $script:deployWebProc | Stop-Process -Force -EA SilentlyContinue } catch { }
- $script:deployWebProc = $null
- }
-}
-
-# ZIP を作るのは対象と同じ実行ファイル(net48 / .NET 10 のそれぞれで確かめる)
-$deployExe48 = Join-Path $csRoot "Frameworks\Tools\DeployZipPackWithHTTP\bin\Debug\OpenTouryo.DeployZipPackWithHTTP.exe"
-$deployExeCore = Join-Path $csRoot "Frameworks\Tools\DeployZipPackWithHTTP\bin\Debug\net10.0-windows7.0\OpenTouryo.DeployZipPackWithHTTP.exe"
-
-$prepareDeploy48 = { [void](New-DeployWeb "net48" $deployExe48) }
-$prepareDeployCore = { [void](New-DeployWeb "core" $deployExeCore) }
-$mftGenArgs48 = New-MftGenArgs "net48"
-$mftGenArgsCore = New-MftGenArgs "core"
-$verifyMftGen48 = { Test-MftGen "net48" }
-$verifyMftGenCore = { Test-MftGen "core" }
-# 配置の確認が終わったら、配信サーバを止める(起動しっぱなしにしない)
-$startDeployWeb48 = { [void](Start-DeployWeb "net48") }
-$startDeployWebCore = { [void](Start-DeployWeb "core") }
-$verifyDeploy48 = { $r = Test-Deploy "net48"; Stop-DeployWeb; return $r }
-$verifyDeployCore = { $r = Test-Deploy "core"; Stop-DeployWeb; return $r }
-
-# ------------------------------------------------------------------
-# HTTP 要求
-# ------------------------------------------------------------------
-# リダイレクトを追わずに状態コードを見たいが、Invoke-WebRequest は
-# -MaximumRedirection 0 で 3xx を受け取ると、-SkipHttpErrorCheck を付けていても
-# 「The maximum redirection count has been exceeded」で終了エラーになる。
-# ここで捕まえ、3xx を正常な結果として返す。
-# Cookie を引き継ぐため、セッションは呼び出し側で作って渡す。
-function New-WebSession
-{
- return New-Object Microsoft.PowerShell.Commands.WebRequestSession
-}
-
-function Invoke-Http
-{
- param(
- [string]$Uri,
- [string]$Method = "GET",
- $Body,
- $Session
- )
-
- $p = @{
- Uri = $Uri; Method = $Method; WebSession = $Session
- MaximumRedirection = 0
- # 5.1 は既定で Internet Explorer のエンジンを使い、未構成だと解析に失敗する。
- # 7 では受け付けられて無視されるため、常に付けてよい。
- UseBasicParsing = $true
- }
- # -SkipHttpErrorCheck は PowerShell 7 以降にしかない。
- # 5.1 に渡すとパラメータ束縛で失敗するので付けない。
- # (5.1 では 4xx/5xx が例外になるが、下の catch で状態コードを取り出す)
- if ($PSVersionTable.PSVersion.Major -ge 6) { $p.SkipHttpErrorCheck = $true }
- if ($Body) { $p.Body = $Body }
-
- try
- {
- $r = Invoke-WebRequest @p -ErrorAction Stop
- return [pscustomobject]@{ Status = [int]$r.StatusCode; Content = $r.Content; Length = $r.RawContentLength }
- }
- catch
- {
- $resp = $_.Exception.Response
- if ($resp) { return [pscustomobject]@{ Status = [int]$resp.StatusCode; Content = ""; Length = 0 } }
- return [pscustomobject]@{ Status = -1; Content = $_.Exception.Message; Length = 0 }
- }
-}
-
-# ------------------------------------------------------------------
-# Web アプリの疎通手順
-# ------------------------------------------------------------------
-# 引数でベース URL を受け取り、@{ Ok = $bool; Detail = $string } を返す。
-#
-# <確認の深さを揃える>
-# 3 つとも「ログインを通して、認証が要る画面に到達できること」まで見る。
-# 入口ページが 200 を返すだけでは、ホスティングと構成しか確認できない。
-# 認証が要る画面まで通せば、ルーティング・認証・セッションまでを一度に確認できる。
-#
-# | 対象 | 認証の実装 | 到達を確認する画面 |
-# |----------------------|---------------------|----------------------|
-# | MVC_Sample (net48) | FormsAuthentication | /Crud1/Index |
-# | MVC_Sample (net10.0) | Cookie 認証 | /Crud1/Index |
-# | WebForms_Sample | FormsAuthentication | Aspx/start/menu.aspx |
-#
-# いずれのサンプルも「ユーザー名が空でなければ認証する」実装のため、資格情報は不要。
-
-# MVC_Sample : net48 は FormsAuthentication、net10.0 は Cookie 認証と実装は異なるが、
-# 画面構成と URL は同じなので同じ手順で確認できる。
-# 認証後の応答は net48 が 302(RedirectFromLoginPage)、net10.0 が 200(View を返す)
-# と分かれるため、ここでは 4xx/5xx でないことだけを見る。
-$mvcLoginFlow = {
- param($base)
-
- $ses = New-WebSession
-
- $r1 = Invoke-Http "$base/Home/Login" -Session $ses
- if ($r1.Status -ne 200) { return @{ Ok = $false; Detail = "GET /Home/Login = $($r1.Status)" } }
-
- # ValidateAntiForgeryToken のため、画面からトークンを取り出して送り返す。
- $tok = ([regex]::Match($r1.Content, 'name="__RequestVerificationToken"[^>]*value="([^"]+)"')).Groups[1].Value
- if (-not $tok) { return @{ Ok = $false; Detail = "__RequestVerificationToken が取得できない" } }
-
- # 500 はセッション状態サービスの停止など、環境側の問題であることが多い。
- $body = @{ UserName = "smoke"; Password = "smoke"; normal = "ログイン"; __RequestVerificationToken = $tok }
- $r2 = Invoke-Http "$base/Home/Login" -Method POST -Body $body -Session $ses
- if ($r2.Status -ge 400) { return @{ Ok = $false; Detail = "POST /Home/Login = $($r2.Status)" } }
-
- # 認証が要る画面。未認証ならログイン画面へ 302 されるため、200 なら認証が通っている。
- $r3 = Invoke-Http "$base/Crud1/Index" -Session $ses
- if ($r3.Status -ne 200) { return @{ Ok = $false; Detail = "GET /Crud1/Index = $($r3.Status)(認証が通っていない)" } }
-
- return @{ Ok = $true; Detail = "ログイン後 /Crud1/Index = 200" }
-}
-
-# WebForms_Sample (net48) : Web.config で のため全画面が要認証。
-# ログイン後は defaultUrl の menu.aspx へ遷移する。
-$webFormsFlow = {
- param($base)
-
- $ses = New-WebSession
-
- $r1 = Invoke-Http "$base/Aspx/start/login.aspx" -Session $ses
- if ($r1.Status -ne 200) { return @{ Ok = $false; Detail = "GET login.aspx = $($r1.Status)" } }
-
- # WebForms のポストバックには、画面が発行した状態フィールドをそのまま返す必要がある。
- # __VIEWSTATE が取れること自体、ページのライフサイクルが動いている証拠でもある。
- $fields = @{}
- foreach ($n in @("__VIEWSTATE", "__VIEWSTATEGENERATOR", "__EVENTVALIDATION"))
- {
- $m = [regex]::Match($r1.Content, ('name="' + $n + '"[^>]*value="([^"]*)"'))
- if (-not $m.Success) { return @{ Ok = $false; Detail = "$n が取得できない" } }
- $fields[$n] = $m.Groups[1].Value
- }
-
- # マスタ ページ配下のため、コントロール名は ctl00$ContentPlaceHolder_A$ が付く。
- # btnButton1 がログイン ボタン(btnButton2 は「外部ログイン」。CS / VB とも同じ)。
- $fields["ctl00`$ContentPlaceHolder_A`$txtUserID"] = "smoke"
- $fields["ctl00`$ContentPlaceHolder_A`$txtPassword"] = "smoke"
- $fields["ctl00`$ContentPlaceHolder_A`$btnButton1"] = "ログイン"
-
- $r2 = Invoke-Http "$base/Aspx/start/login.aspx" -Method POST -Body $fields -Session $ses
- if ($r2.Status -ge 400) { return @{ Ok = $false; Detail = "POST login.aspx = $($r2.Status)" } }
-
- # 認証が要る画面。未認証なら login.aspx へ 302 されるため、200 なら認証が通っている。
- $r3 = Invoke-Http "$base/Aspx/start/menu.aspx" -Session $ses
- if ($r3.Status -ne 200) { return @{ Ok = $false; Detail = "GET menu.aspx = $($r3.Status)(認証が通っていない)" } }
-
- return @{ Ok = $true; Detail = "ログイン後 menu.aspx = 200" }
-}
-
-$targetsCS = @(
- # --- バッチ (net48) ---
- @{
- Name = "SimpleBatch_sample (net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\SimpleBatch_sample\bin\Debug\SimpleBatch_sample.exe"
- Args = $batchArgs; Expect = '\d+件のデータがあります'
- }
- @{
- Name = "RerunnableBatch_sample (net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\RerunnableBatch_sample\bin\Debug\RerunnableBatch_sample.exe"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
- @{
- Name = "RerunnableBatch_sample2 (net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\RerunnableBatch_sample2\bin\Debug\RerunnableBatch_sample2.exe"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
- @{
- Name = "RerunnableBatch_sample3 (net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\RerunnableBatch_sample3\bin\Debug\RerunnableBatch_sample3.exe"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
-
- # --- バッチ (net10.0) ---
- @{
- Name = "SimpleBatch_sample (net10.0)"; Bat = "5_Build_BatCore_sample.bat"
- Exe = "Samples4NetCore\Legacy\Bat_sample\SimpleBatch_sample\bin\Debug\net10.0\SimpleBatch_sample.dll"
- Args = $batchArgs; Expect = '\d+件のデータがあります'
- }
- @{
- Name = "RerunnableBatch_sample (net10.0)"; Bat = "5_Build_BatCore_sample.bat"
- Exe = "Samples4NetCore\Legacy\Bat_sample\RerunnableBatch_sample\bin\Debug\net10.0\RerunnableBatch_sample.dll"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
- @{
- Name = "RerunnableBatch_sample2 (net10.0)"; Bat = "5_Build_BatCore_sample.bat"
- Exe = "Samples4NetCore\Legacy\Bat_sample\RerunnableBatch_sample2\bin\Debug\net10.0\RerunnableBatch_sample2.dll"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
- @{
- Name = "RerunnableBatch_sample3 (net10.0)"; Bat = "5_Build_BatCore_sample.bat"
- Exe = "Samples4NetCore\Legacy\Bat_sample\RerunnableBatch_sample3\bin\Debug\net10.0\RerunnableBatch_sample3.dll"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
-
- # --- CLI (net10.0) ---
- # net48 版は System.CommandLine / Sharprompt の .NET Fx サポート終了により
- # ドロップされている(5_Build_CLI_sample.bat 参照)。
- # interactive サブコマンドは Prompt を使うため対象外とし、非対話のものを使う。
- @{
- Name = "Simple_CLI (net10.0)"; Bat = "5_Build_CLICore_sample.bat"
- Exe = "Samples4NetCore\Legacy\CLI_sample\Simple_CLI\Simple_CLI\bin\Debug\net10.0\Simple_CLI.dll"
- Args = @("cmd1", "--an-int", "123"); Expect = 'Sub command cmd1: 123'
- }
-
- # --- DaoGen_Tool(墨壺)の CUI モード ---
- # #508 で追加された /HELP と /CUI。GUI 側の確認は手作業に残る。
- # DAODEFGEN → DAOSQLGEN の順に実行し、前段の出力を後段の入力に使う。
- @{
- Name = "DaoGen_Tool /HELP (net48)"; Bat = "4_Build_Framework_Tool.bat"
- Exe = "Frameworks\Tools\DaoGen_Tool\bin\Debug\OpenTouryo.DaoGen_Tool.exe"
- Args = @("/HELP"); Expect = 'DaoGen_Tool(D層自動生成ツール/墨壺)'
- }
- @{
- Name = "DaoGen_Tool DAODEFGEN (net48)"; Bat = "4_Build_Framework_Tool.bat"
- Exe = "Frameworks\Tools\DaoGen_Tool\bin\Debug\OpenTouryo.DaoGen_Tool.exe"
- Args = $daoDefArgs48; Expect = '生成が完了しました。'
- Pre = $prepareDaoGen48; Verify = $verifyDaoDef48
- }
- @{
- Name = "DaoGen_Tool DAOSQLGEN (net48)"; Bat = "4_Build_Framework_Tool.bat"
- Exe = "Frameworks\Tools\DaoGen_Tool\bin\Debug\OpenTouryo.DaoGen_Tool.exe"
- Args = $daoSqlArgs48; Expect = '生成が完了しました。'
- Verify = $verifyDaoGen48
- }
- @{
- Name = "DaoGen_Tool /HELP (net10.0)"; Bat = "4_Build_Framework_ToolCore.bat"
- Exe = "Frameworks\Tools\DaoGen_Tool\bin\Debug\net10.0-windows7.0\OpenTouryo.DaoGen_Tool.exe"
- Args = @("/HELP"); Expect = 'DaoGen_Tool(D層自動生成ツール/墨壺)'
- }
- @{
- Name = "DaoGen_Tool DAODEFGEN (net10.0)"; Bat = "4_Build_Framework_ToolCore.bat"
- Exe = "Frameworks\Tools\DaoGen_Tool\bin\Debug\net10.0-windows7.0\OpenTouryo.DaoGen_Tool.exe"
- Args = $daoDefArgsCore; Expect = '生成が完了しました。'
- Pre = $prepareDaoGenCore; Verify = $verifyDaoDefCore
- }
- @{
- Name = "DaoGen_Tool DAOSQLGEN (net10.0)"; Bat = "4_Build_Framework_ToolCore.bat"
- Exe = "Frameworks\Tools\DaoGen_Tool\bin\Debug\net10.0-windows7.0\OpenTouryo.DaoGen_Tool.exe"
- Args = $daoSqlArgsCore; Expect = '生成が完了しました。'
- Verify = $verifyDaoGenCore
- }
-
- # --- DeployZipPackWithHTTP の CUI モード ---
- # #528 で /MFTGEN を追加し、生成 → 配布 → 配置 まで CUI で通せるようになった。
- # マニュフェストを作り、IIS Express で配信し、実際に配置して突き合わせる。
- # ※ /NB を付けないと、配置した EXE が起動して止まる。
- @{
- Name = "DeployZip /MFTGEN (net48)"; Bat = "4_Build_Framework_Tool.bat"
- Exe = "Frameworks\Tools\DeployZipPackWithHTTP\bin\Debug\OpenTouryo.DeployZipPackWithHTTP.exe"
- Args = $mftGenArgs48; Expect = 'マニュフェスト ファイルを生成しました。'
- Pre = $prepareDeploy48; Verify = $verifyMftGen48
- }
- @{
- Name = "DeployZip 配置 (net48)"; Bat = "4_Build_Framework_Tool.bat"
- Exe = "Frameworks\Tools\DeployZipPackWithHTTP\bin\Debug\OpenTouryo.DeployZipPackWithHTTP.exe"
- Args = $deployArgs; Expect = '履歴に新規追加しました。'
- Pre = $startDeployWeb48; Verify = $verifyDeploy48
- }
- @{
- Name = "DeployZip /MFTGEN (net10.0)"; Bat = "4_Build_Framework_ToolCore.bat"
- Exe = "Frameworks\Tools\DeployZipPackWithHTTP\bin\Debug\net10.0-windows7.0\OpenTouryo.DeployZipPackWithHTTP.exe"
- Args = $mftGenArgsCore; Expect = 'マニュフェスト ファイルを生成しました。'
- Pre = $prepareDeployCore; Verify = $verifyMftGenCore
- }
- @{
- Name = "DeployZip 配置 (net10.0)"; Bat = "4_Build_Framework_ToolCore.bat"
- Exe = "Frameworks\Tools\DeployZipPackWithHTTP\bin\Debug\net10.0-windows7.0\OpenTouryo.DeployZipPackWithHTTP.exe"
- Args = $deployArgs; Expect = '履歴に新規追加しました。'
- Pre = $startDeployWebCore; Verify = $verifyDeployCore
- }
-
- # --- 通信制御の接続オプション(#546) ---
- #
- # CallController の接続オプション(ProxyUrl / UserName / UserAgent / Compression 等)が、
- # 実際の HTTP 要求に反映されているかを見る。
- #
- # <外部環境が要らない>
- # オリジンとプロキシを、テスト側が TcpListener で自前に立てる。
- # 1 プロセスに閉じているので、起動・停止の面倒を見る必要がない。
- #
- # <net48 だけ>
- # ASP.NET WebAPI の経路が .NET Framework 限定である(BinarySerialize を使うため)。
- #
- # <判定>
- # 対象側が項目ごとに OK / NG を出し、末尾に件数を出す。
- @{
- Name = "TestTransmission (net48)"; Bat = "y_Build_TestTransmission.bat"
- Exe = "Frameworks\Tests\TestTransmission\net48\bin\Debug\TestTransmissionFx.exe"
- Expect = 'NG : 0 件'
- }
-
- # --- Web アプリ ---
- @{
- Name = "MVC_Sample (net48)"; Bat = "10_Build_WebApp_sample.bat"
- Kind = "Web"; WebHost = "IISExpress"; Port = 51081
- Site = "Samples\WebApp_sample\MVC_Sample\MVC_Sample"
- Need = "aspnet_state"
- Flow = $mvcLoginFlow
- }
- @{
- Name = "WebForms_Sample (net48)"; Bat = "10_Build_WebApp_sample.bat"
- Kind = "Web"; WebHost = "IISExpress"; Port = 51082
- Site = "Samples\WebApp_sample\WebForms_Sample\WebForms_Sample"
- Need = "aspnet_state"
- Flow = $webFormsFlow
- }
- @{
- Name = "MVC_Sample (net10.0)"; Bat = "10_Build_WebAppCore_sample.bat"
- Kind = "Web"; WebHost = "Kestrel"; Port = 51083
- Exe = "Samples4NetCore\Backend\MVC_Sample\MVC_Sample\bin\Debug\net10.0\MVC_Sample.dll"
- Flow = $mvcLoginFlow
- }
-)
-
-# ------------------------------------------------------------------
-# VB 版の対象
-# ------------------------------------------------------------------
-# VB にあるのは Bat_sample と WebApp_sample だけで、Core 版・CLI_sample・
-# ツール(Frameworks\Tools)は無い(#542)。
-#
-# 判定(Args / Pre / Verify / Flow)は C# 版と同じものを使い回す。
-# 上の定義と同じ変数を指しているので、片方だけ直すことができない。
-#
-# <ポートを分ける>
-# -Lang Both では C# 版と続けて起動するため、51081/51082 とは別にする。
-$targetsVB = @(
- # --- バッチ (net48) ---
- @{
- Name = "SimpleBatch_sample (VB net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\SimpleBatch_sample\bin\Debug\SimpleBatch_sample.exe"
- Args = $batchArgs; Expect = '\d+件のデータがあります'
- }
- @{
- Name = "RerunnableBatch_sample (VB net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\RerunnableBatch_sample\bin\Debug\RerunnableBatch_sample.exe"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
- @{
- Name = "RerunnableBatch_sample2 (VB net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\RerunnableBatch_sample2\bin\Debug\RerunnableBatch_sample2.exe"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
- @{
- Name = "RerunnableBatch_sample3 (VB net48)"; Bat = "5_Build_Bat_sample.bat"
- Exe = "Samples\Bat_sample\RerunnableBatch_sample3\bin\Debug\RerunnableBatch_sample3.exe"
- Args = $batchArgs; Pre = $clearOrders2; Verify = $verifyOrders2
- }
-
- # --- Web アプリ ---
- @{
- Name = "MVC_Sample (VB net48)"; Bat = "10_Build_WebApp_sample.bat"
- Kind = "Web"; WebHost = "IISExpress"; Port = 51085
- Site = "Samples\WebApp_sample\MVC_Sample\MVC_Sample"
- Need = "aspnet_state"
- Flow = $mvcLoginFlow
- }
- @{
- Name = "WebForms_Sample (VB net48)"; Bat = "10_Build_WebApp_sample.bat"
- Kind = "Web"; WebHost = "IISExpress"; Port = 51086
- Site = "Samples\WebApp_sample\WebForms_Sample\WebForms_Sample"
- Need = "aspnet_state"
- Flow = $webFormsFlow
- }
-)
# 省略されている Dir を、その言語のフォルダで補う。
function Add-DefaultDir($items, [string]$dir)
@@ -1038,8 +280,33 @@ function Start-WebHost($t, [string]$log)
# ------------------------------------------------------------------
Write-Host ("対象 : {0}" -f $Lang) -ForegroundColor Cyan
+
+# **-Only に何を指定できるかは、ここが一次情報である。**(#576)
+# 文書に書き写すと二重管理になり、対象が増減したときに古くなる。
+if ($List)
+{
+ Write-Host ("=== -Only に指定できる名前(部分一致){0} ===" -f "(-Lang $Lang)") -ForegroundColor Cyan
+ foreach ($x in $targets) { Write-Host (" " + $x.Name) }
+ Write-Host (" ---- {0} 件 ----" -f $targets.Count)
+ exit 0
+}
+
$selected = @($targets | Where-Object { -not $Only -or $_.Name -like "*$Only*" })
+# **-Only が空振りしたら止める。**(#576)
+# 0 件のまま進むと「全対象 OK」と表示され、**打ち間違いが緑になる。**
+if ($Only)
+{
+ if ($selected.Count -eq 0)
+ {
+ Write-Host (" **-Only '$Only' に一致する対象がありません。**") -ForegroundColor Red
+ Write-Host (" -List で一覧を出せます。") -ForegroundColor Yellow
+ exit 1
+ }
+
+ Write-Host (" -Only '$Only' : {0} 件に絞りました" -f $selected.Count) -ForegroundColor Yellow
+}
+
if (-not $SkipBuild)
{
# ビルドする単位は「フォルダ + バッチ」。同じバッチ名が CS と VB の
@@ -1108,6 +375,10 @@ foreach ($t in $selected)
}
$sw = [Diagnostics.Stopwatch]::StartNew()
+ # **使う前に消す。**(#571)
+ # 残っていると、起動に失敗しても前回のログが読まれる。
+ $null = Reset-Log $out
+
$proc = Start-WebHost $t $out
if (-not $proc)
diff --git a/root/programs/BUILDING.md b/root/programs/BUILDING.md
index 74c4df47f..5d0c8ce48 100644
--- a/root/programs/BUILDING.md
+++ b/root/programs/BUILDING.md
@@ -738,7 +738,31 @@ VS 18 のある環境では従来どおり `18.0` になるため、**挙動は
### 実測(run 30984111639 : 全ステップ成功)
> **当時の件数での記録。** その後、単体テストは 8 ケース(#520)、
-> 疎通は 23 件(#528、#546)に増えている。**時間の目安として読むこと。**
+> 疎通は 25 件(#528、#546、#566、#570、#571)である。**時間の目安として読むこと。**
+
+### 警告の内訳を見る(`-WarnDetail`、#571)
+
+**件数だけでは何を直せばよいか分からない。**
+
+```powershell
+.\1_BuildAll.ps1 -Only "Framework_Tool" -SkipClean -WarnDetail
+```
+
+```
+Framework_Tool (net48)(81 件)
+ 81 MSB3277 ← **全部これ 1 種類**
+
+Framework_ToolCore(23 件)
+ 13 MSB3277
+ 6 SYSLIB0003 DeployZipPackWithHTTP\Form2.cs(CAS の廃止)
+ 2 SYSLIB0014 Program.cs(WebRequest の廃止)
+ 2 SYSLIB0021 Program.cs(廃止された暗号型)
+```
+
+**同じ種類は、たいてい 1 か所の対処でまとめて消える。**
+`MSB3277` は版の混在なので、12 節と `ComparePackage.ps1` を見る。
+
+既定では出さない。**毎回出るとエラーが埋もれる。**
| ステップ | CI | ローカル |
|---|---|---|
@@ -921,3 +945,122 @@ git diff --numstat
# 4. 検証する
.\0_RunAll.ps1 -Lang Both
```
+
+## 12. パッケージの版を変えるとき(#566 / #568 / #569)
+
+**版は 4 か所に散らばっている。** 片方だけ直しても**ビルドは通る**。
+
+| | 場所 | ずれると | 検査 |
+|---|---|---|---|
+| ① | `packages.config` の `version=` | 復元される版が変わらない | `ComparePackage.ps1`(基準) |
+| ② | csproj の `packages\X.Y\` パス | **復元済みでも「パッケージが無い」** | `ComparePackage.ps1` |
+| ③ | csproj の `` | **参照が落ちて bin に配られない** | `ComparePackage.ps1` |
+| ④ | `*.config` の `bindingRedirect` | 実行時に転送先が無い | `CompareRedirect.ps1`(#556) |
+
+`PackageReference`(net10.0)は①だけで済む。**②③④は net48 の話。**
+
+### ③がずれても、ビルドは成功する
+
+`` に強い名前(`, Version=…`)を書くと、
+**`SpecificVersion` は既定で true** になる。宣言と実体がずれると、
+MSBuild は**警告だけ出して参照を落とす。**
+
+#566 では `Microsoft.Data.SqlClient.dll` が bin に配られず、
+**`1_BuildAll.ps1` は終了コード 0 のまま**、実行時に `FileNotFoundException` になった。
+
+### ③④は「パッケージの版」ではない
+
+**アセンブリの版である。計算で導いてはいけない。**
+
+```
+パッケージ 10.0.5 → アセンブリ 10.0.0.5
+パッケージ 8.17.0 → アセンブリ 8.17.0.0
+パッケージ 13.0.4 → アセンブリ 13.0.0.0 ← 版が変わっても、ここは動かない
+```
+
+**復元してから、`HintPath` が指す DLL を読んで測る。**
+
+測るのは**そのプロジェクトの `HintPath`** だけにする。
+リポジトリ全体から名前で引くと、`Microsoft.Owin` のように
+**同名で版が割れている**もので、どちらが正か決められない。
+
+### ②は `HintPath` だけではない
+
+`Import Project` と `Error Condition` にも版が入る。
+
+```xml
+..\packages\System.ValueTuple.4.6.2\lib\...
+
+
+```
+
+`packages\<フォルダ>\` を**全部拾って**突き合わせること。
+
+### 名前が似ているだけの別物に注意
+
+**前方一致で拾うと巻き込む。** 版番号の系列が違う。
+
+```
+Microsoft.Extensions.PlatformAbstractions 1.1.0 (Extensions 10.x とは無関係)
+Newtonsoft.Json.Bson 1.0.3 (Newtonsoft.Json 13.x とは無関係)
+Microsoft.Data.SqlClient.SNI 6.0.2 (SqlClient 7.0.0 でも据え置き)
+```
+
+**「そのパッケージであること」と「今の版がその系列に居ること」の両方**を条件にする。
+
+### 依存の集合が変わることがある
+
+`packages.config` は**依存を自動解決しない。** 追加・削除は手で書く。
+
+`Microsoft.Data.SqlClient` 6.0.2 → 7.0.0 では、`Azure.Identity` 由来の 10 個が落ち、
+`Extensions.Abstractions` / `Internal.Logging` ほかが要る(#568)。
+
+**`.nuspec` を見て確かめること。**`.nupkg`(ZIP)の中にしか無い。
+
+### `CompareRedirect.ps1` の「判定不能」の読み方
+
+**不一致とは別物である。**(#579)
+
+```
+不一致 宣言した版が、配下の実体と食い違う → **直す**
+判定不能 配下にそのアセンブリの実体が無い → **分類を見る**
+```
+
+判定不能は「問題あり」ではない。**大半は仕組み上そうなるだけである。**
+そのため 4 つに分類して出す。
+
+| 分類 | 意味 | 対応 |
+|---|---|---|
+| 未ビルド | 配下に DLL が 1 つも無い | 建ててから測り直す |
+| ライブラリ | クラス ライブラリの `app.config` | **不要。実行時に読まれない** |
+| 連鎖ごと未配布 | 要求元のアセンブリも配られていない | **不要。読み込まれ得ない** |
+| **要調査** | **要求元は在るのに実体だけが無い** | **参照の落ちを疑う(#566)** |
+
+**見るのは「要調査」だけでよい。** `-Detail` が無くても出る。
+
+> **`bindingRedirect` が効くのは、アプリケーションの構成ファイルだけである。**
+> クラス ライブラリの `app.config` に書いても、実行時に読まれるのは呼ぶ側の設定になる。
+> だからライブラリの宣言は、実体が無くても実害が無い。
+
+**Web アプリは `OutputType` が `Library` になる。**
+分類は `Web.config` かどうかを先に見てから `OutputType` を見ている。
+逆順にすると、Web アプリをライブラリと誤って扱う。
+
+### 手順
+
+```powershell
+# 1. 版を書き換える(①②)。net10.0 → net48 の順に分けると、切り分けやすい
+# 2. 復元する
+.\1_BuildAll.ps1
+
+# 3. ③④を、復元された DLL の実体版に合わせる
+# 4. 検査する(不一致 0 であること)
+.\ComparePackage.ps1 -Check # ②③
+.\CompareRedirect.ps1 -Check # ④
+
+# 5. 検証する。**VB 側にも packages.config がある**ので Both で回す
+.\0_RunAll.ps1 -Lang Both
+```
+
+**ビルドが通っただけでは、実際に読み込めるかは分からない。**
+`3_SmokeTest.ps1` まで見ること。
diff --git a/root/programs/CHEATSHEET.md b/root/programs/CHEATSHEET.md
index f2bdd4578..6299a8225 100644
--- a/root/programs/CHEATSHEET.md
+++ b/root/programs/CHEATSHEET.md
@@ -26,7 +26,7 @@ cd root\programs
|---|---|---|
| ビルド | 全ステップ OK | [`BUILDING.md`](BUILDING.md) |
| 単体テスト | 8/8 OK、差分 0 | [`TESTING.md`](TESTING.md) |
-| 疎通 | 23/23 OK | [`SMOKETEST.md`](SMOKETEST.md) |
+| 疎通 | 25/25 OK | [`SMOKETEST.md`](SMOKETEST.md) |
**既定は C# 側。VB 側は `-Lang` で回す**(3 節)。
@@ -37,6 +37,70 @@ cd root\programs
除外した内容は件数つきで別枠に出るため、そちらは目を通すこと
- `2_RunAllTests.ps1` は `Result*.txt` を書き換える。**差分 0 なら中身は同じ**
- 前提(DB・サービス・IIS Express)は [`RELEASE.md`](RELEASE.md) 2 節
+- **`0_RunAll.ps1` は 1・2・3 の実行時間を出す**(#571)
+
+### 通しは長い。反復では絞り込む(#571)
+
+実測(`-Lang Both`)は **合計 21.7 分**で、内訳はこうなっている。
+
+```
+1_BuildAll.ps1 12.6 分 ← 58%。ここが主因
+2_RunAllTests.ps1 1.7 分
+3_SmokeTest.ps1 7.4 分
+```
+
+**変更した箇所だけを回す。** 通しは最後に 1 回でよい。
+
+```powershell
+.\1_BuildAll.ps1 -Only "WSSrv" -SkipClean # Clean を挟まない分だけ速い
+.\2_RunAllTests.ps1 -Only "TestBatch" # Result*.txt も絞った分だけ書き換わる
+.\3_SmokeTest.ps1 -Only "MVC_Sample" -SkipBuild
+```
+
+**`-Only` に指定できる名前は `-List` で出す。**(#576)
+
+```powershell
+.\3_SmokeTest.ps1 -List # 対象名の一覧。-Lang が効く
+```
+
+**どこまで回すかは依存関係で決まる。**(#576)
+ツールと個別サンプルは末端なので、基盤のビルドも他のサンプルの疎通も要らない。
+**`2_RunAllTests.ps1` の対象はフレームワークのテストだけ**で、
+ツールやサンプルを変えても動かないため、回す理由が無い。
+対応表は [`AGENTS.md`](../../AGENTS.md)「通しで回す前に、依存関係を見る」。
+
+**警告が多いステップの内訳を見る。**
+
+```powershell
+.\1_BuildAll.ps1 -Only "Framework_Tool" -SkipClean -WarnDetail
+```
+
+> **`-Only` + `-SkipClean` は万能ではない。**(#574 で踏んだ)
+> **前段が用意する状態に依存するステップは、飛ばすと落ちる。**
+>
+> ```
+> 通し(Clean あり) エラー 0 件
+> -Only "Business" -SkipClean Business.RichClient_net48 で
+> 「does not reference .NETFramework,Version=v4.8」
+> ```
+>
+> **変更と無関係なエラーに見えるため、切り分けで時間を失う。**
+> 絞り込みで妙なエラーが出たら、**まず通しで再現するかを見ること。**
+> 再現しなければ、それは絞り込みの制約であって不具合ではない。
+
+**パッケージの版を触ったら、次の 2 本も見る**(`0_RunAll.ps1` は自動で回して警告する)。
+
+```powershell
+.\ComparePackage.ps1 -Check # packages.config と csproj の版(#569)
+.\CompareRedirect.ps1 -Check # bindingRedirect と配布物の版(#556)
+```
+
+**どちらもビルドの後に回す。** 復元された DLL を読むため。
+`1_BuildAll.ps1` は bin を消すので、**clean の前に測ると古い残骸を拾う。**(#579)
+
+`CompareRedirect.ps1` の**「判定不能」は不一致ではない。**
+分類のうち**「要調査」だけ**を見ればよい(他は実行時に読まれない)。
+読み方は [`BUILDING.md`](BUILDING.md) 12 節。
---
@@ -188,6 +252,7 @@ powershell.exe -NoProfile -Command "Set-Location 'root\programs'; .\3_SmokeTest.
| `MSB4226`(`Microsoft.WebApplication.targets`) | nuget が別製品の MSBuild を拾った | `nuget.exe restore ... %NUGET_MSBUILD%` |
| ビルドは通るのに `DllNotFoundException`(`...SNI...`) | `nuget restore` を呼んでおらず、ネイティブ DLL が出力に入らない | 該当 sln に restore を足す。[`BUILDING.md`](BUILDING.md) 10 節 |
| `packages.config` の id が `csproj` に無い=不要に見える | サテライト(`.ja`)とコンテンツ パッケージは**出なくて正常**(48 件中 44 件) | 消す前に [`BUILDING.md`](BUILDING.md) 11 節 |
+| ビルドは通るのに実行時 `FileNotFoundException` | **版は 4 か所に散らばる。** `` の `Version=` がずれると、警告だけ出て参照が落ちる | `.\ComparePackage.ps1 -Check`。[`BUILDING.md`](BUILDING.md) 12 節 |
**NuGet パッケージ作成の落とし穴は
[`CS/NuGet/README.md`](CS/NuGet/README.md) 9 節**にまとめてある。
diff --git a/root/programs/CS/6_Build_WSSrvCore_sample.bat b/root/programs/CS/6_Build_WSSrvCore_sample.bat
index b44c0ca7a..dc94e57cc 100644
--- a/root/programs/CS/6_Build_WSSrvCore_sample.bat
+++ b/root/programs/CS/6_Build_WSSrvCore_sample.bat
@@ -30,6 +30,13 @@ dotnet msbuild %COMMANDLINE% "Samples4NetCore\Legacy\WS_sample\WSServer_sample\W
xcopy /E /Y "Samples4NetCore\Legacy\WS_sample\WSServer_sample\bin\%BUILD_CONFIG%\net10.0" "Samples4NetCore\Legacy\WS_sample\Temp\%BUILD_CONFIG%\net10.0\"
xcopy /E /Y "Samples4NetCore\Legacy\WS_sample\Temp\%BUILD_CONFIG%\net10.0" "Samples4NetCore\Legacy\WS_sample\Build\net10.0\"
+@rem --------------------------------------------------
+@rem Batch build of ASPNETWebService(ResourceServer).
+@rem --------------------------------------------------
+@rem Not copied to Build\ : nothing else references it, so just build it.
+dotnet restore "Samples4NetCore\Backend\ASPNETWebService\ASPNETWebService.sln"
+dotnet msbuild %COMMANDLINE% "Samples4NetCore\Backend\ASPNETWebService\ASPNETWebService.sln"
+
pause
rem -------------------------------------------------------
diff --git a/root/programs/CS/6_Build_WSSrv_sample.bat b/root/programs/CS/6_Build_WSSrv_sample.bat
index 8c9f18697..e5bfbfce3 100644
--- a/root/programs/CS/6_Build_WSSrv_sample.bat
+++ b/root/programs/CS/6_Build_WSSrv_sample.bat
@@ -30,6 +30,13 @@ rem --------------------------------------------------
xcopy /E /Y "Samples\WS_sample\WSServer_sample\bin\%BUILD_CONFIG%" "Samples\WS_sample\Temp\%BUILD_CONFIG%\"
xcopy /E /Y "Samples\WS_sample\Temp\%BUILD_CONFIG%" "Samples\WS_sample\Build\"
+@rem --------------------------------------------------
+@rem Batch build of ASPNETWebService(ResourceServer).
+@rem --------------------------------------------------
+@rem Not copied to Build\ : nothing else references it, so just build it.
+..\nuget.exe restore "Samples\WS_sample\ASPNETWebService\ASPNETWebService.sln" %NUGET_MSBUILD%
+%BUILDFILEPATH% %COMMANDLINE% "Samples\WS_sample\ASPNETWebService\ASPNETWebService.sln"
+
pause
rem -------------------------------------------------------
diff --git a/root/programs/CS/7_Build_Framework_WSCore.bat b/root/programs/CS/7_Build_Framework_WSCore.bat
index 292d576a8..8c626ffd5 100644
--- a/root/programs/CS/7_Build_Framework_WSCore.bat
+++ b/root/programs/CS/7_Build_Framework_WSCore.bat
@@ -16,10 +16,15 @@ set CURRENT_DIR="%~dp0"
call %CURRENT_DIR%z_Common.bat
rem --------------------------------------------------
-rem Batch build of ServiceInterface(ASPNETWebServiceCore).
+rem Batch build of ServiceInterface (.NET Core).
rem --------------------------------------------------
-rem dotnet restore "Frameworks\Infrastructure\ServiceInterface\ASPNETWebServiceCore\ASPNETWebServiceCore.sln"
-rem dotnet msbuild %COMMANDLINE% "Frameworks\Infrastructure\ServiceInterface\ASPNETWebServiceCore\ASPNETWebServiceCore.sln"
+rem Nothing to build here.
+rem The .NET Core edition of ServiceInterface (ASPNETWebServiceCore) was dropped
+rem when BinarySerialize was abolished for Core.
+rem ServiceInterface now holds ASPNETWebService (net48) and WCFService only.
+rem
+rem Keep this batch : 1_BuildAll.ps1 runs it as the Framework_WSCore step.
+rem Removing it would make a skipped step look like a failed one.
echo Core系のBinarySerializeの完全廃止対応
diff --git a/root/programs/CS/Frameworks/Infrastructure/Business/Business_netcore100.csproj b/root/programs/CS/Frameworks/Infrastructure/Business/Business_netcore100.csproj
index 9a2dbe718..b0f5b80e6 100644
--- a/root/programs/CS/Frameworks/Infrastructure/Business/Business_netcore100.csproj
+++ b/root/programs/CS/Frameworks/Infrastructure/Business/Business_netcore100.csproj
@@ -1,4 +1,4 @@
-
+
net10.0
@@ -62,8 +62,18 @@
-
-
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Infrastructure/Business/Presentation/MyMVCCoreFilterAttribute.cs b/root/programs/CS/Frameworks/Infrastructure/Business/Presentation/MyMVCCoreFilterAttribute.cs
index 81779e05e..6444ee94f 100644
--- a/root/programs/CS/Frameworks/Infrastructure/Business/Presentation/MyMVCCoreFilterAttribute.cs
+++ b/root/programs/CS/Frameworks/Infrastructure/Business/Presentation/MyMVCCoreFilterAttribute.cs
@@ -28,6 +28,7 @@
//* 日時 更新者 内容
//* ---------- ---------------- -------------------------------------------------
//* 2018/08/08 西野 大介 新規作成
+//* 2026/08/21 玄人 幸道 CS4014対応(GetUserInfoAsyncを待つ。ログのUserInfoが空になっていた)
//**********************************************************************************
using System;
@@ -90,8 +91,10 @@ private void OutputErrorLog(ExceptionContext exceptionContext)
{
this.GetRouteData(exceptionContext.RouteData);
- // 内部で await するが、呼出し元は同期なので、結果として同期実行になる。
- this.GetUserInfoAsync();
+ // **await しないと、この後の UserInfo がまだ設定されていない。**(CS4014)
+ // OutputErrorLog は void で await できないため、ここで待つ。
+ // ASP.NET Core には SynchronizationContext が無く、デッドロックしない。
+ this.GetUserInfoAsync().GetAwaiter().GetResult();
// 非同期ControllerのInnerException対策(底のExceptionを取得する)。
Exception ex = exceptionContext.Exception;
diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTColumns.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTColumns.cs
index f80315521..7d44e8efe 100644
--- a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTColumns.cs
+++ b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTColumns.cs
@@ -30,6 +30,7 @@
//* 2010/03/xx 西野 大介 新規作成
//* 2010/11/11 前川 祐介 Silverlight対応(ジェネリック)
//* 2011/10/09 西野 大介 国際化対応
+//* 2026/08/18 玄人 幸道 変更前の値(Original)の保持に対応(#567)
//**********************************************************************************
using System;
@@ -145,6 +146,16 @@ public int Count
/// 列を保持するList
/// 変更させないため、外部に公開しない。
+ /// 変更前の値(Original)を保持するか
+ /// DTRow から参照する。設定は DTTable.KeepOriginal で行う。
+ internal bool KeepOriginal
+ {
+ get
+ {
+ return this._tblStat.KeepOriginal;
+ }
+ }
+
internal List ColsInfo
{
get
diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRow.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRow.cs
index f8c8027c9..a22c27b15 100644
--- a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRow.cs
+++ b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRow.cs
@@ -35,6 +35,7 @@
//* 2011/09/06 前川 祐介 共通化、同値が設定された場合、RowStateを変更しない
//* 2011/10/09 西野 大介 国際化対応
//* 2026/08/14 玄人 幸道 DataRowStateをDTRowStateに改名(#544)。
+//* 2026/08/18 玄人 幸道 変更前の値(Original)の保持に対応(#567)
//**********************************************************************************
using System;
@@ -57,6 +58,14 @@ public class DTRow : IEnumerable
/// 行ステータス
private DTRowState _rowState;
+
+ /// 変更前のデータ本体
+ ///
+ /// **KeepOriginal が真で、Unchanged から初めて変更したときにだけ作る。**(#567)
+ /// null のままなら「変更前=現在値」であり、Original を尋ねられても現在値を返す。
+ /// 保持しない設定では常に null なので、記憶域は増えない。
+ ///
+ private List
-
- packages\Azure.Core.1.46.2\lib\net472\Azure.Core.dll
+
+ packages\Microsoft.Bcl.AsyncInterfaces.10.0.5\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
-
- packages\Azure.Identity.1.14.0\lib\netstandard2.0\Azure.Identity.dll
+
+ packages\Microsoft.Bcl.Cryptography.10.0.5\lib\net462\Microsoft.Bcl.Cryptography.dll
-
- packages\Microsoft.Bcl.AsyncInterfaces.9.0.6\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
+
+ packages\Microsoft.Bcl.TimeProvider.10.0.5\lib\net462\Microsoft.Bcl.TimeProvider.dll
-
- packages\Microsoft.Bcl.Cryptography.9.0.6\lib\net462\Microsoft.Bcl.Cryptography.dll
+
+
+ packages\Microsoft.Data.SqlClient.7.0.0\lib\net462\Microsoft.Data.SqlClient.dll
-
- packages\Microsoft.Bcl.TimeProvider.9.0.6\lib\net462\Microsoft.Bcl.TimeProvider.dll
+
+ packages\System.Threading.Channels.10.0.5\lib\net462\System.Threading.Channels.dll
-
-
- packages\Microsoft.Data.SqlClient.6.0.2\lib\net462\Microsoft.Data.SqlClient.dll
+
+ packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
-
- packages\Microsoft.Extensions.Caching.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
+
+ packages\Microsoft.Data.SqlClient.Internal.Logging.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Internal.Logging.dll
-
- packages\Microsoft.Extensions.Caching.Memory.9.0.6\lib\net462\Microsoft.Extensions.Caching.Memory.dll
+
+ packages\Microsoft.Data.SqlClient.Extensions.Abstractions.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Extensions.Abstractions.dll
-
- packages\Microsoft.Extensions.DependencyInjection.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+
+ packages\Microsoft.Extensions.Caching.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
-
- packages\Microsoft.Extensions.Logging.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
+
+ packages\Microsoft.Extensions.Caching.Memory.10.0.5\lib\net462\Microsoft.Extensions.Caching.Memory.dll
-
- packages\Microsoft.Extensions.Options.9.0.6\lib\net462\Microsoft.Extensions.Options.dll
+
+ packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
-
- packages\Microsoft.Extensions.Primitives.9.0.6\lib\net462\Microsoft.Extensions.Primitives.dll
+
+ packages\Microsoft.Extensions.Logging.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
-
- packages\Microsoft.Identity.Client.4.72.1\lib\net472\Microsoft.Identity.Client.dll
+
+ packages\Microsoft.Extensions.Options.10.0.5\lib\net462\Microsoft.Extensions.Options.dll
-
- packages\Microsoft.Identity.Client.Extensions.Msal.4.72.1\lib\netstandard2.0\Microsoft.Identity.Client.Extensions.Msal.dll
+
+ packages\Microsoft.Extensions.Primitives.10.0.5\lib\net462\Microsoft.Extensions.Primitives.dll
-
- packages\Microsoft.IdentityModel.Abstractions.8.12.0\lib\net472\Microsoft.IdentityModel.Abstractions.dll
+
+ packages\Microsoft.IdentityModel.Abstractions.8.17.0\lib\net472\Microsoft.IdentityModel.Abstractions.dll
-
- packages\Microsoft.IdentityModel.JsonWebTokens.8.12.0\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
+
+ packages\Microsoft.IdentityModel.JsonWebTokens.8.17.0\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
-
- packages\Microsoft.IdentityModel.Logging.8.12.0\lib\net472\Microsoft.IdentityModel.Logging.dll
+
+ packages\Microsoft.IdentityModel.Logging.8.17.0\lib\net472\Microsoft.IdentityModel.Logging.dll
-
- packages\Microsoft.IdentityModel.Protocols.8.12.0\lib\net472\Microsoft.IdentityModel.Protocols.dll
+
+ packages\Microsoft.IdentityModel.Protocols.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.dll
-
- packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.8.12.0\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
+
+ packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
-
- packages\Microsoft.IdentityModel.Tokens.8.12.0\lib\net472\Microsoft.IdentityModel.Tokens.dll
+
+ packages\Microsoft.IdentityModel.Tokens.8.17.0\lib\net472\Microsoft.IdentityModel.Tokens.dll
packages\Newtonsoft.Json.Bson.1.0.3\lib\net45\Newtonsoft.Json.Bson.dll
@@ -121,33 +121,24 @@
packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll
-
- packages\System.ClientModel.1.4.2\lib\netstandard2.0\System.ClientModel.dll
-
-
- packages\System.Diagnostics.DiagnosticSource.9.0.6\lib\net462\System.Diagnostics.DiagnosticSource.dll
+
+ packages\System.Diagnostics.DiagnosticSource.10.0.5\lib\net462\System.Diagnostics.DiagnosticSource.dll
-
- packages\System.Formats.Asn1.9.0.6\lib\net462\System.Formats.Asn1.dll
+
+ packages\System.Formats.Asn1.10.0.5\lib\net462\System.Formats.Asn1.dll
-
- packages\System.IdentityModel.Tokens.Jwt.8.12.0\lib\net472\System.IdentityModel.Tokens.Jwt.dll
-
-
- packages\System.IO.FileSystem.AccessControl.5.0.0\lib\net461\System.IO.FileSystem.AccessControl.dll
+
+ packages\System.IdentityModel.Tokens.Jwt.8.17.0\lib\net472\System.IdentityModel.Tokens.Jwt.dll
-
- packages\System.IO.Pipelines.9.0.6\lib\net462\System.IO.Pipelines.dll
+
+ packages\System.IO.Pipelines.10.0.5\lib\net462\System.IO.Pipelines.dll
packages\System.Memory.4.6.3\lib\net462\System.Memory.dll
-
- packages\System.Memory.Data.9.0.6\lib\net462\System.Memory.Data.dll
-
packages\Microsoft.AspNet.WebApi.Client.6.0.0\lib\net45\System.Net.Http.Formatting.dll
@@ -159,23 +150,14 @@
packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll
-
- packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll
-
-
- packages\System.Security.Cryptography.Pkcs.9.0.6\lib\net462\System.Security.Cryptography.Pkcs.dll
-
-
- packages\System.Security.Cryptography.ProtectedData.9.0.6\lib\net462\System.Security.Cryptography.ProtectedData.dll
-
-
- packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll
+
+ packages\System.Security.Cryptography.Pkcs.10.0.5\lib\net462\System.Security.Cryptography.Pkcs.dll
-
- packages\System.Text.Encodings.Web.9.0.6\lib\net462\System.Text.Encodings.Web.dll
+
+ packages\System.Text.Encodings.Web.10.0.5\lib\net462\System.Text.Encodings.Web.dll
-
- packages\System.Text.Json.9.0.6\lib\net462\System.Text.Json.dll
+
+ packages\System.Text.Json.10.0.5\lib\net462\System.Text.Json.dll
packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll
@@ -228,7 +210,7 @@
packages\Microsoft.Web.Infrastructure.2.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
- packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll
+ packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll
@@ -316,9 +298,9 @@
このプロジェクトは、このコンピューター上にない NuGet パッケージを参照しています。それらのパッケージをダウンロードするには、[NuGet パッケージの復元] を使用します。詳細については、http://go.microsoft.com/fwlink/?LinkID=322105 を参照してください。見つからないファイルは {0} です。
-
+
-
+
>,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%-
+[2026/08/20 10:55:09,854],[INFO ],[1],,,,----->>,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%-
log4net: configuring repository [log4net-default-repository] using stream
log4net: loading XML configuration
log4net: Configuring Repository [log4net-default-repository]
@@ -144,6 +144,6 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level
log4net: Created Appender [OPERATION2]
log4net: Adding appender named [OPERATION2] to logger [OPERATION].
log4net: Hierarchy Threshold []
-[2026/08/17 17:21:43,471],[INFO ],[1],92,78,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]:
-[2026/08/17 17:21:43,472],[INFO ],[1],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%-,114,94
+[2026/08/20 10:55:09,992],[INFO ],[1],96,78,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]:
+[2026/08/20 10:55:09,993],[INFO ],[1],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%-,126,94
3件のデータがあります
diff --git a/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt b/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt
index 046ea96cc..d5af16dde 100644
--- a/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt
+++ b/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt
@@ -69,7 +69,7 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level
log4net: Created Appender [OPERATION2]
log4net: Adding appender named [OPERATION2] to logger [OPERATION].
log4net: Hierarchy Threshold []
-[2026/08/17 17:21:45,741],[INFO ],[2],,,,----->>,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%-
+[2026/08/20 10:55:12,568],[INFO ],[2],,,,----->>,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%-
log4net: configuring repository [log4net-default-repository] using stream
log4net: loading XML configuration
log4net: Configuring Repository [log4net-default-repository]
@@ -144,6 +144,6 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level
log4net: Created Appender [OPERATION2]
log4net: Adding appender named [OPERATION2] to logger [OPERATION].
log4net: Hierarchy Threshold []
-[2026/08/17 17:21:45,817],[INFO ],[2],50,,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]:
-[2026/08/17 17:21:45,818],[INFO ],[2],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%-,66,
+[2026/08/20 10:55:12,663],[INFO ],[2],68,,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]:
+[2026/08/20 10:55:12,663],[INFO ],[2],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%-,86,
3件のデータがあります
diff --git a/root/programs/CS/Frameworks/Tests/TestBatch/SimpleBatchCore/SimpleBatchCore.csproj b/root/programs/CS/Frameworks/Tests/TestBatch/SimpleBatchCore/SimpleBatchCore.csproj
index 34980605e..69c3e6cd4 100644
--- a/root/programs/CS/Frameworks/Tests/TestBatch/SimpleBatchCore/SimpleBatchCore.csproj
+++ b/root/programs/CS/Frameworks/Tests/TestBatch/SimpleBatchCore/SimpleBatchCore.csproj
@@ -18,12 +18,12 @@
-
-
-
-
+
+
+
+
-
+
diff --git a/root/programs/CS/Frameworks/Tests/TestDataAccess/core100/TestDataAccessCore.csproj b/root/programs/CS/Frameworks/Tests/TestDataAccess/core100/TestDataAccessCore.csproj
index 88f8cb686..6e9a1a8e4 100644
--- a/root/programs/CS/Frameworks/Tests/TestDataAccess/core100/TestDataAccessCore.csproj
+++ b/root/programs/CS/Frameworks/Tests/TestDataAccess/core100/TestDataAccessCore.csproj
@@ -36,12 +36,12 @@
-
-
-
-
+
+
+
+
-
+
diff --git a/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore1.csproj b/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore1.csproj
index a78b72703..0092f0236 100644
--- a/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore1.csproj
+++ b/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore1.csproj
@@ -43,10 +43,10 @@
-
-
-
-
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore2.csproj b/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore2.csproj
index 159cda630..f32af8c5e 100644
--- a/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore2.csproj
+++ b/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore2.csproj
@@ -43,10 +43,10 @@
-
-
-
-
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore3.csproj b/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore3.csproj
index d787cab12..8408ec693 100644
--- a/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore3.csproj
+++ b/root/programs/CS/Frameworks/Tests/TestLog/TestLogCore3.csproj
@@ -42,10 +42,10 @@
-
-
-
-
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tests/TestWebAPIClient/App.config b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/App.config
new file mode 100644
index 000000000..80e333518
--- /dev/null
+++ b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/App.config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tests/TestWebAPIClient/AssemblyInfo.cs b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/AssemblyInfo.cs
new file mode 100644
index 000000000..22e60aacb
--- /dev/null
+++ b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/AssemblyInfo.cs
@@ -0,0 +1,46 @@
+#region Apache License
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+//**********************************************************************************
+//* クラス名 :AssemblyInfo
+//* クラス日本語名 :アセンブリ情報
+//*
+//* 作成者 :玄人 幸道
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2026/08/20 玄人 幸道 新規作成(#570)
+//**********************************************************************************
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("TestWebAPIClientFx")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TestWebAPIClientFx")]
+[assembly: AssemblyCopyright("Copyright © 2026")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+[assembly: Guid("7b2e4c81-9d35-4a62-8f17-2c6d0b570001")]
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/root/programs/CS/Frameworks/Tests/TestWebAPIClient/Program.cs b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/Program.cs
new file mode 100644
index 000000000..70135e67d
--- /dev/null
+++ b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/Program.cs
@@ -0,0 +1,737 @@
+#region Apache License
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+//**********************************************************************************
+//* クラス名 :Program
+//* クラス日本語名 :DTO を使用したバッチ更新(WebAPI Client)の確認
+//*
+//* 作成者 :玄人 幸道
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2026/08/20 玄人 幸道 新規作成(#570)
+//**********************************************************************************
+
+using System;
+using System.Data;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Text.RegularExpressions;
+
+using Touryo.Infrastructure.Public.Dto;
+
+namespace TestWebAPIClient
+{
+ /// 応答
+ ///
+ /// **状態コードを持たせる。**
+ /// 本文の部分一致だけで判定すると、エラー ページに引っかかる。
+ /// 実際、IIS Express の 500.19(構成エラー)が返す HTML に "test" が含まれており、
+ /// **疎通が OK と表示された。**「通ったこと」は正しさの証拠にならない。
+ ///
+ class Res
+ {
+ /// 状態コード(取れないときは -1)
+ public int Status;
+
+ /// 本文
+ public string Body;
+ }
+
+ /// DTO を使用したバッチ更新(WebAPI Client)の確認
+ ///
+ /// **何を確かめるか。**
+ ///
+ /// DataTable を DTTables 経由で JSON にして往復させたとき、
+ /// RowState と Original が保たれ、**バッチ更新に使える**こと(#567 / #570)。
+ ///
+ /// **なぜクライアントを別に建てるか。**
+ ///
+ /// サーバ側だけでは「同一プロセス内の DataTable」を触ってしまい、
+ /// **JSON をまたいだことにならない。**
+ /// HTTP 越しに送って戻すところまでやらないと、往復の検証にならない。
+ ///
+ /// **判定。** 項目ごとに OK / NG を出し、末尾に件数を出す(TestTransmission と同じ)。
+ ///
+ class Program
+ {
+ /// NG の件数
+ private static int NG = 0;
+
+ /// 接続先(サイト ルート)
+ ///
+ /// **ルートを受け取る。** 1 本のクライアントから
+ /// api/json(CRUD 一巡)と api/batchupdate(DTO 往復)の両方を叩くため。
+ ///
+ private static string BaseUrl = "http://localhost:51087";
+
+ /// バッチ更新の口
+ private static string Api { get { return Program.BaseUrl + "/api/batchupdate"; } }
+
+ /// JSON-RPC の口
+ private static string Json { get { return Program.BaseUrl + "/api/json"; } }
+
+ #region エントリ ポイント
+
+ /// エントリ ポイント
+ /// 引数(第 1 引数で接続先を上書きできる)
+ static void Main(string[] args)
+ {
+ if (args != null && args.Length >= 1 && !string.IsNullOrEmpty(args[0]))
+ {
+ Program.BaseUrl = args[0].TrimEnd('/');
+ }
+
+ Console.OutputEncoding = Encoding.UTF8;
+ Console.WriteLine("接続先 : {0}", Program.BaseUrl);
+ Console.WriteLine();
+
+ try
+ {
+ Program.CaseConnect();
+ Program.CaseJsonCrud();
+ Program.CaseRoundTrip();
+ Program.CaseOptimisticLock();
+ }
+ catch (Exception e)
+ {
+ // **ここで握って NG に数える。** 落として終わると件数が出ない。
+ Program.NG++;
+ Console.WriteLine(" [NG] 想定外の例外 : {0} : {1}", e.GetType().Name, e.Message);
+ }
+
+ Console.WriteLine();
+ Console.WriteLine("NG : {0} 件", Program.NG);
+ }
+
+ #endregion
+
+ #region 疎通
+
+ /// 疎通(DB を使わない)
+ ///
+ /// **状態コードと本文の形の両方を見る。**
+ /// 200 でないものは、内容を見るまでもなく NG。
+ ///
+ private static void CaseConnect()
+ {
+ Console.WriteLine("=== 疎通 ===");
+
+ // net48 は JSON なので "test"、net10.0 は素の text なので test で返る。
+ Res res = Program.Get(Program.Api + "/test");
+ Program.Check("GET /test",
+ res.Status == 200 && Program.Unquote(res.Body) == "test", res);
+
+ res = Program.Post(Program.Api + "/SelectCount", "");
+ Program.Check("POST /SelectCount",
+ res.Status == 200 && Program.Match(res.Body, "\"count\"\\s*:\\s*\\d+"), res);
+
+ Console.WriteLine();
+ }
+
+ #endregion
+
+ #region CRUD 一巡(JsonController)
+
+ /// JsonController で CRUD を一巡し、件数が元に戻ること
+ ///
+ /// **元は 3_SmokeTest.ps1 の Flow だった**(#566)。
+ /// 同じ WebAPI が相手なので、こちらへ寄せて対象を 4 → 2 に減らした(#571)。
+ ///
+ /// ここまで通れば、ホスティング・ルーティング・B層・D層・DB が繋がっている。
+ ///
+ private static void CaseJsonCrud()
+ {
+ Console.WriteLine("=== CRUD 一巡(JsonController)===");
+
+ // 疎通(DB を使わない。ここが通らなければ配置か構成の問題)
+ Res res = Program.Get(Program.Json + "/test");
+ Program.Check("GET /api/json/test",
+ res.Status == 200 && Program.Unquote(res.Body) == "test", res);
+ if (res.Status != 200) { Console.WriteLine(); return; }
+
+ // 件数(B層・D層・DB まで到達する最初の要求)
+ int before = Program.SelectCountOfShippers(out res);
+ Program.Check("SelectCount", before >= 0, res);
+ if (before < 0) { Console.WriteLine(); return; }
+
+ // 追加(消し忘れても次回に拾えるよう、名前を毎回変える)
+ string name = "smoke-" + DateTime.Now.ToString("HHmmss");
+ res = Program.Post(Program.Json + "/Insert",
+ Program.ShipperBody(0, name, "000-0000"));
+ Program.Check("Insert", res.Status == 200 && Program.Match(res.Body, "件追加"), res);
+
+ // 一覧から、今入れた行の ShipperID を拾う(Insert は採番結果を返さない)
+ res = Program.PostForm(Program.Json + "/SelectAll_DT", Program.DdlForm());
+ int id = Program.FindShipperId(res.Body, name);
+ Program.Check("SelectAll_DT で採番を拾う", res.Status == 200 && id > 0,
+ res.Status == 200 ? "ShipperID=" + id : Program.Trim(res.Body));
+
+ if (id > 0)
+ {
+ // 取得
+ res = Program.Post(Program.Json + "/Select", Program.ShipperBody(id, null, null));
+ Program.Check("Select",
+ res.Status == 200 && res.Body.IndexOf(name, StringComparison.Ordinal) >= 0, res);
+
+ // 更新
+ res = Program.Post(Program.Json + "/Update",
+ Program.ShipperBody(id, name + "-upd", "111-1111"));
+ Program.Check("Update", res.Status == 200 && Program.Match(res.Body, "件更新"), res);
+
+ // 削除(後片付けを兼ねる)
+ res = Program.Post(Program.Json + "/Delete", Program.ShipperBody(id, null, null));
+ Program.Check("Delete", res.Status == 200 && Program.Match(res.Body, "件削除"), res);
+ }
+
+ // 件数が元に戻ったか(消し残しの検知)
+ int after = Program.SelectCountOfShippers(out res);
+ Program.Check("件数が戻る", after == before,
+ "前 " + before + " → 後 " + after);
+
+ Console.WriteLine();
+ }
+
+ /// Shippers の件数を取る
+ /// 応答
+ /// 件数(取れなければ -1)
+ private static int SelectCountOfShippers(out Res res)
+ {
+ res = Program.PostForm(Program.Json + "/SelectCount", Program.DdlForm());
+ if (res.Status != 200) { return -1; }
+
+ Match m = Regex.Match(res.Body, @"(\d+)件のデータがあります");
+ return m.Success ? int.Parse(m.Groups[1].Value) : -1;
+ }
+
+ /// 一覧から、指定した名前の ShipperID を拾う
+ /// 応答
+ /// CompanyName
+ /// ShipperID(見つからなければ -1)
+ private static int FindShipperId(string body, string name)
+ {
+ if (body == null) { return -1; }
+
+ // {"shipperID":"4","companyName":"smoke-…","phone":"…"}
+ Match m = Regex.Match(body,
+ "\"shipperID\"\\s*:\\s*\"(\\d+)\"[^}]*?" + Regex.Escape(name));
+ return m.Success ? int.Parse(m.Groups[1].Value) : -1;
+ }
+
+ /// WebApiParams のフォーム(データアクセスの指定)
+ /// フォーム
+ private static string DdlForm()
+ {
+ return "ddlDap=SQL&ddlMode1=individual&ddlMode2=static&ddlExRollback=-";
+ }
+
+ /// WebApiParams の JSON(Shipper 付き)
+ /// ShipperID(0 なら採番させる)
+ /// CompanyName(不要なら null)
+ /// Phone(不要なら null)
+ /// JSON
+ private static string ShipperBody(int id, string company, string phone)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("{\"ddlDap\":\"SQL\",\"ddlMode1\":\"individual\"");
+ sb.Append(",\"ddlMode2\":\"static\",\"ddlExRollback\":\"-\"");
+ sb.Append(",\"Shipper\":{\"ShipperID\":").Append(id);
+
+ if (company != null) { sb.Append(",\"CompanyName\":").Append(Program.Quote(company)); }
+ if (phone != null) { sb.Append(",\"Phone\":").Append(Program.Quote(phone)); }
+
+ sb.Append("}}");
+ return sb.ToString();
+ }
+
+ #endregion
+
+ #region 往復(RowState と Original)
+
+ /// DataTable → JSON → DataTable の往復で、CUD を振り分けられること
+ private static void CaseRoundTrip()
+ {
+ Console.WriteLine("=== 往復(RowState と Original)===");
+
+ // ---- 一覧を取る ----
+ DataTable before = Program.SelectAll();
+ if (before == null || before.Rows.Count == 0)
+ {
+ Program.Check("一覧の取得", false, "(取れない)");
+ return;
+ }
+ Program.Check("一覧の取得", true, before.Rows.Count + " 件");
+
+ Program.Check("全列が揃っている", before.Columns.Count == 12,
+ before.Columns.Count + " 列");
+
+ // ---- 編集する(追加と更新を 1 件ずつ)----
+ string tag = "smoke-" + DateTime.Now.ToString("HHmmss");
+
+ DataRow added = before.NewRow();
+ added["CompanyName"] = tag;
+ added["ContactName"] = "tester";
+ added["Country"] = "Japan";
+ before.Rows.Add(added);
+
+ DataRow modified = before.Rows[0];
+ string originalName = Program.Str(modified["CompanyName"]);
+ modified["ContactName"] = tag;
+
+ Program.Check("編集後の RowState",
+ added.RowState == DataRowState.Added && modified.RowState == DataRowState.Modified,
+ "Added=" + added.RowState + " / Modified=" + modified.RowState);
+
+ // ---- JSON にして戻す(ここが往復)----
+ DTTables dtts = new DTTables();
+ dtts.Add(DTTable.FromDataTable(before, true));
+ string json = DTTables.DTTablesToJson(dtts);
+
+ DataTable after = Program.FirstTable(DTTables.JsonToDTTables(json));
+
+ Program.Check("往復後も RowState が残る",
+ Program.CountByState(after, DataRowState.Added) == 1
+ && Program.CountByState(after, DataRowState.Modified) == 1,
+ "Added=" + Program.CountByState(after, DataRowState.Added)
+ + " / Modified=" + Program.CountByState(after, DataRowState.Modified));
+
+ // **Original が残っているか。** これが無いと楽観排他が組めない。
+ // Current が編集後になっていることも同時に見る(両方揃って初めて使える)。
+ DataRow afterModified = Program.FirstByState(after, DataRowState.Modified);
+ Program.Check("往復後も Original が残る",
+ afterModified != null
+ && Program.Str(afterModified["CompanyName", DataRowVersion.Original]) == originalName
+ && Program.Str(afterModified["ContactName"]) == tag,
+ afterModified == null ? "(無し)"
+ : "Original=" + Program.Str(afterModified["CompanyName", DataRowVersion.Original])
+ + " / Current=" + Program.Str(afterModified["ContactName"]));
+
+ Console.WriteLine();
+ }
+
+ #endregion
+
+ #region 楽観排他
+
+ /// Original を WHERE に入れた楽観排他が効くこと
+ ///
+ /// **「他者が先に更新した」状況を作る。**
+ /// ① 一覧を取る(これが古い版になる)
+ /// ② 別の経路で 1 件更新する(他者の更新)
+ /// ③ ①を編集して送る → **更新件数 0 で業務例外**になるはず
+ ///
+ private static void CaseOptimisticLock()
+ {
+ Console.WriteLine("=== 楽観排他(Original を WHERE に入れる)===");
+
+ // ① 古い版
+ DataTable stale = Program.SelectAll();
+ if (stale == null || stale.Rows.Count == 0)
+ {
+ Program.Check("一覧の取得", false, "(取れない)");
+ return;
+ }
+
+ int targetId = Convert.ToInt32(stale.Rows[0]["SupplierID"]);
+ string tag = "lock-" + DateTime.Now.ToString("HHmmss");
+
+ // ② 他者の更新(同じ行の ContactTitle を変える)
+ DataTable other = Program.SelectAll();
+ DataRow otherRow = Program.FindById(other, targetId);
+ string keep = Program.Str(otherRow["ContactTitle"]);
+ otherRow["ContactTitle"] = tag;
+
+ Res res = Program.BatchUpdate(other);
+ Program.Check("他者の更新が通る",
+ res.Status == 200 && Program.Match(res.Body, "\"updateCount\"\\s*:\\s*1"), res);
+
+ // ③ 古い版を編集して送る
+ DataRow staleRow = Program.FindById(stale, targetId);
+ staleRow["ContactName"] = tag;
+
+ res = Program.BatchUpdate(stale);
+ Program.Check("**古い版の更新が弾かれる**",
+ res.Status == 200 && Program.Match(res.Body, "\"errorMessageID\"\\s*:\\s*\"W0002\""), res);
+
+ // ---- 後片付け(他者の更新を戻す)----
+ DataTable restore = Program.SelectAll();
+ DataRow restoreRow = Program.FindById(restore, targetId);
+ if (restoreRow != null)
+ {
+ restoreRow["ContactTitle"] = keep;
+ res = Program.BatchUpdate(restore);
+ Program.Check("後片付け",
+ res.Status == 200 && Program.Match(res.Body, "\"updateCount\"\\s*:\\s*1"), res);
+ }
+
+ Console.WriteLine();
+ }
+
+ #endregion
+
+ #region WebAPI の呼び出し
+
+ /// 一覧を取る
+ /// DataTable(取れなければ null)
+ private static DataTable SelectAll()
+ {
+ Res res = Program.Post(Program.Api + "/SelectAll", "");
+ if (res.Status != 200) { return null; }
+
+ string json = Program.ExtractJsonString(res.Body, "Suppliers");
+ if (json == null) { return null; }
+
+ return Program.FirstTable(DTTables.JsonToDTTables(json));
+ }
+
+ /// バッチ更新する
+ /// 対象
+ /// 応答
+ private static Res BatchUpdate(DataTable dt)
+ {
+ DTTables dtts = new DTTables();
+ dtts.Add(DTTable.FromDataTable(dt, true));
+
+ string json = DTTables.DTTablesToJson(dtts);
+
+ // JSON の中に JSON を入れるので、文字列としてエスケープする。
+ return Program.Post(Program.Api + "/BatchUpdate", "{\"Suppliers\":" + Program.Quote(json) + "}");
+ }
+
+ #endregion
+
+ #region HTTP
+
+ /// GET
+ /// パス
+ /// 応答
+ private static Res Get(string url)
+ {
+ HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
+ req.Method = "GET";
+ return Program.ReadResponse(req);
+ }
+
+ /// POST(application/json)
+ /// パス
+ /// 本文
+ /// 応答
+ private static Res Post(string url, string body)
+ {
+ return Program.Post(url, body, "application/json");
+ }
+
+ /// POST(form)
+ /// URL
+ /// 本文
+ /// 応答
+ ///
+ /// **net10.0 の JsonController は SelectXxx が [FromForm]。**
+ /// net48 はどちらも受け付けるので、net10.0 に合わせる。
+ ///
+ private static Res PostForm(string url, string form)
+ {
+ return Program.Post(url, form, "application/x-www-form-urlencoded");
+ }
+
+ /// POST
+ /// URL
+ /// 本文
+ /// Content-Type
+ /// 応答
+ private static Res Post(string url, string body, string contentType)
+ {
+ HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
+ req.Method = "POST";
+ req.ContentType = contentType;
+
+ byte[] bytes = Encoding.UTF8.GetBytes(body ?? "");
+ req.ContentLength = bytes.Length;
+ using (Stream s = req.GetRequestStream())
+ {
+ s.Write(bytes, 0, bytes.Length);
+ }
+
+ return Program.ReadResponse(req);
+ }
+
+ /// 応答を読む
+ /// 要求
+ /// 応答
+ /// **4xx / 5xx でも本文を読む。** 例外にすると内容が分からない。
+ private static Res ReadResponse(HttpWebRequest req)
+ {
+ try
+ {
+ using (HttpWebResponse res = (HttpWebResponse)req.GetResponse())
+ using (StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8))
+ {
+ return new Res { Status = (int)res.StatusCode, Body = sr.ReadToEnd() };
+ }
+ }
+ catch (WebException we)
+ {
+ HttpWebResponse res = we.Response as HttpWebResponse;
+ if (res == null)
+ {
+ return new Res { Status = -1, Body = "(応答なし) " + we.Message };
+ }
+
+ using (StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8))
+ {
+ return new Res { Status = (int)res.StatusCode, Body = sr.ReadToEnd() };
+ }
+ }
+ }
+
+ #endregion
+
+ #region ユーティリティ
+
+ /// 判定を出す(応答つき)
+ /// 項目
+ /// 合否
+ /// 応答
+ private static void Check(string title, bool ok, Res res)
+ {
+ string detail = (res == null) ? "(応答なし)"
+ : "HTTP " + res.Status + " : " + res.Body;
+
+ Program.Check(title, ok, detail);
+ }
+
+ /// 判定を出す
+ /// 項目
+ /// 合否
+ /// 内容
+ private static void Check(string title, bool ok, string detail)
+ {
+ if (!ok) { Program.NG++; }
+
+ Console.WriteLine(" [{0}] {1,-28} : {2}",
+ ok ? "OK" : "NG", title, Program.Trim(detail));
+ }
+
+ /// 正規表現に一致するか
+ /// 文字列
+ /// パターン
+ /// 一致するか
+ ///
+ /// **部分一致ではなく形で見る。**
+ /// 「その語が含まれるか」だと、エラー ページの HTML にも一致してしまう。
+ ///
+ private static bool Match(string s, string pattern)
+ {
+ if (s == null) { return false; }
+ return Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase);
+ }
+
+ /// 前後の二重引用符を外す
+ /// 文字列
+ /// 文字列
+ private static string Unquote(string s)
+ {
+ if (s == null) { return ""; }
+
+ s = s.Trim();
+ if (s.Length >= 2 && s[0] == '"' && s[s.Length - 1] == '"')
+ {
+ return s.Substring(1, s.Length - 2);
+ }
+ return s;
+ }
+
+ /// 長い応答を切り詰める
+ /// 文字列
+ /// 文字列
+ private static string Trim(string s)
+ {
+ if (s == null) { return "(null)"; }
+
+ s = s.Replace("\r", "").Replace("\n", " ");
+ return s.Length <= 110 ? s : s.Substring(0, 110) + " …";
+ }
+
+ /// DTTables の先頭テーブルを DataTable にする
+ /// DTTables
+ /// DataTable
+ private static DataTable FirstTable(DTTables dtts)
+ {
+ foreach (DTTable dtt in dtts)
+ {
+ return dtt.ToDataTable();
+ }
+ return null;
+ }
+
+ /// RowState ごとの件数
+ /// 対象
+ /// RowState
+ /// 件数
+ private static int CountByState(DataTable dt, DataRowState state)
+ {
+ if (dt == null) { return -1; }
+
+ int n = 0;
+ foreach (DataRow dr in dt.Rows)
+ {
+ if (dr.RowState == state) { n++; }
+ }
+ return n;
+ }
+
+ /// RowState が一致する最初の行
+ /// 対象
+ /// RowState
+ /// DataRow
+ private static DataRow FirstByState(DataTable dt, DataRowState state)
+ {
+ if (dt == null) { return null; }
+
+ foreach (DataRow dr in dt.Rows)
+ {
+ if (dr.RowState == state) { return dr; }
+ }
+ return null;
+ }
+
+ /// 主キーで行を探す
+ /// 対象
+ /// SupplierID
+ /// DataRow
+ private static DataRow FindById(DataTable dt, int id)
+ {
+ if (dt == null) { return null; }
+
+ foreach (DataRow dr in dt.Rows)
+ {
+ if (dr.RowState == DataRowState.Deleted) { continue; }
+ if (Convert.ToInt32(dr["SupplierID"]) == id) { return dr; }
+ }
+ return null;
+ }
+
+ /// JSON から文字列の値を取り出す
+ /// JSON
+ /// 名前
+ /// 値(見つからなければ null)
+ ///
+ /// **素朴に取り出す。** ここで JSON ライブラリに依存させたくない
+ /// (このクライアントは DTO の往復を見るのが目的で、JSON 処理は手段でしかない)。
+ ///
+ private static string ExtractJsonString(string json, string name)
+ {
+ if (json == null) { return null; }
+
+ string key = "\"" + name + "\":\"";
+ int i = json.IndexOf(key, StringComparison.OrdinalIgnoreCase);
+ if (i < 0)
+ {
+ key = "\"" + Program.LowerFirst(name) + "\":\"";
+ i = json.IndexOf(key, StringComparison.OrdinalIgnoreCase);
+ if (i < 0) { return null; }
+ }
+
+ i += key.Length;
+
+ StringBuilder sb = new StringBuilder();
+ while (i < json.Length)
+ {
+ char c = json[i];
+ if (c == '\\' && i + 1 < json.Length)
+ {
+ char n = json[i + 1];
+ switch (n)
+ {
+ case '"': sb.Append('"'); break;
+ case '\\': sb.Append('\\'); break;
+ case '/': sb.Append('/'); break;
+ case 'b': sb.Append('\b'); break;
+ case 'f': sb.Append('\f'); break;
+ case 'n': sb.Append('\n'); break;
+ case 'r': sb.Append('\r'); break;
+ case 't': sb.Append('\t'); break;
+ case 'u':
+ sb.Append((char)Convert.ToInt32(json.Substring(i + 2, 4), 16));
+ i += 4;
+ break;
+ default: sb.Append(n); break;
+ }
+ i += 2;
+ continue;
+ }
+ if (c == '"') { break; }
+
+ sb.Append(c);
+ i++;
+ }
+
+ return sb.ToString();
+ }
+
+ /// JSON の文字列リテラルにする
+ /// 文字列
+ /// 文字列リテラル
+ private static string Quote(string s)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append('"');
+
+ foreach (char c in s)
+ {
+ switch (c)
+ {
+ case '"': sb.Append("\\\""); break;
+ case '\\': sb.Append("\\\\"); break;
+ case '\b': sb.Append("\\b"); break;
+ case '\f': sb.Append("\\f"); break;
+ case '\n': sb.Append("\\n"); break;
+ case '\r': sb.Append("\\r"); break;
+ case '\t': sb.Append("\\t"); break;
+ default:
+ if (c < ' ') { sb.Append("\\u").Append(((int)c).ToString("x4")); }
+ else { sb.Append(c); }
+ break;
+ }
+ }
+
+ sb.Append('"');
+ return sb.ToString();
+ }
+
+ /// 先頭を小文字にする
+ /// 文字列
+ /// 文字列
+ private static string LowerFirst(string s)
+ {
+ if (string.IsNullOrEmpty(s)) { return s; }
+ return char.ToLowerInvariant(s[0]) + s.Substring(1);
+ }
+
+ /// null 安全に文字列化する
+ /// 値
+ /// 文字列
+ private static string Str(object o)
+ {
+ if (o == null || o == DBNull.Value) { return ""; }
+ return o.ToString();
+ }
+
+ #endregion
+ }
+}
diff --git a/root/programs/CS/Frameworks/Tests/TestWebAPIClient/TestWebAPIClientFx48.sln b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/TestWebAPIClientFx48.sln
new file mode 100644
index 000000000..8b0b6b93b
--- /dev/null
+++ b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/TestWebAPIClientFx48.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11205.157 d18.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWebAPIClientFx", "net48\TestWebAPIClientFx.csproj", "{7B2E4C81-9D35-4A62-8F17-2C6D0B570001}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7B2E4C81-9D35-4A62-8F17-2C6D0B570001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7B2E4C81-9D35-4A62-8F17-2C6D0B570001}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7B2E4C81-9D35-4A62-8F17-2C6D0B570001}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7B2E4C81-9D35-4A62-8F17-2C6D0B570001}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {7B2E4C81-9D35-4A62-8F17-2C6D0B570002}
+ EndGlobalSection
+EndGlobal
diff --git a/root/programs/CS/Frameworks/Tests/TestWebAPIClient/net48/TestWebAPIClientFx.csproj b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/net48/TestWebAPIClientFx.csproj
new file mode 100644
index 000000000..4f449484b
--- /dev/null
+++ b/root/programs/CS/Frameworks/Tests/TestWebAPIClient/net48/TestWebAPIClientFx.csproj
@@ -0,0 +1,62 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {7B2E4C81-9D35-4A62-8F17-2C6D0B570001}
+ Exe
+ Properties
+ TestWebAPIClient
+ TestWebAPIClientFx
+ v4.8
+ 512
+ true
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE;NET48
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE;NET48
+ prompt
+ 4
+
+
+ true
+
+
+
+ ..\..\..\Infrastructure\Build_net48\OpenTouryo.Public.dll
+
+
+
+
+
+
+
+
+ Program.cs
+
+
+ AssemblyInfo.cs
+
+
+
+
+ App.config
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tools/DPQuery_Tool/DPQuery_ToolCore.csproj b/root/programs/CS/Frameworks/Tools/DPQuery_Tool/DPQuery_ToolCore.csproj
index fefb5ca5f..2cd41a641 100644
--- a/root/programs/CS/Frameworks/Tools/DPQuery_Tool/DPQuery_ToolCore.csproj
+++ b/root/programs/CS/Frameworks/Tools/DPQuery_Tool/DPQuery_ToolCore.csproj
@@ -27,12 +27,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tools/DaoGen_Tool/DaoGen_ToolCore.csproj b/root/programs/CS/Frameworks/Tools/DaoGen_Tool/DaoGen_ToolCore.csproj
index e0dc773f6..64a44c85c 100644
--- a/root/programs/CS/Frameworks/Tools/DaoGen_Tool/DaoGen_ToolCore.csproj
+++ b/root/programs/CS/Frameworks/Tools/DaoGen_Tool/DaoGen_ToolCore.csproj
@@ -27,12 +27,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/DeployZipPackWithHTTP.csproj b/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/DeployZipPackWithHTTP.csproj
index 7fa54813d..b40cea6a6 100644
--- a/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/DeployZipPackWithHTTP.csproj
+++ b/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/DeployZipPackWithHTTP.csproj
@@ -87,6 +87,9 @@
+
+
@@ -179,6 +182,25 @@
13.0.3
+
+
+ 4.6.3
+
+
+ 6.1.2
+
+
+ 4.6.3
+
net10.0-windows7.0
true
+
+ true
DeployZipPackWithHTTP
@@ -49,11 +56,11 @@
フレームワークは HintPath 参照のため、NuGet の推移的依存が効かない。
Public が使うパッケージは、ここにも並べる必要がある。-->
-
-
-
-
-
+
+
+
+
+
diff --git a/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Form2.cs b/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Form2.cs
index a1ff3d42e..62d3d71e4 100644
--- a/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Form2.cs
+++ b/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Form2.cs
@@ -51,7 +51,9 @@
using System.IO;
using System.Collections.Generic;
using System.Threading;
+#if !NETCOREAPP
using System.Security.Permissions;
+#endif
using System.Resources;
using System.Windows.Forms;
@@ -358,9 +360,14 @@ private void FinalBeginInvoke()
/// http://dobon.net/vb/dotnet/form/disabledclosebutton.html
///
/// Windowメッセージ
+ // **CAS は .NET (Core) 以降で尊重されない**(SYSLIB0003)。
+ // ただし net48 では生きているため、**消さずに条件で外す**(#575)。
+ // 消すと net48 側の挙動が変わる。
+#if !NETCOREAPP
[SecurityPermission(
SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.UnmanagedCode)]
+#endif
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x112;
diff --git a/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Program.cs b/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Program.cs
index 48db3b243..81c42c785 100644
--- a/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Program.cs
+++ b/root/programs/CS/Frameworks/Tools/DeployZipPackWithHTTP/Program.cs
@@ -68,6 +68,7 @@
using System;
using System.IO;
using System.Net;
+using System.Net.Http;
using System.Text;
using System.Collections.Generic;
using System.Threading;
@@ -1761,8 +1762,10 @@ private static void CreateMirror(string src, string dst)
public static string GetMD5Hash(string FilePath)
{
// 暗号化サービスプロバイダ
- // MD5CryptoServiceProviderサービスプロバイダ
- HashAlgorithm ha = new MD5CryptoServiceProvider();
+ // **MD5CryptoServiceProvider は廃止**(SYSLIB0021)。基底の Create を使う。
+ // **用途は配布物の同一性確認**(変更検知)で、暗号ではない。
+ // 実装が変わるだけで、**ハッシュ値は同じ**(#575)。
+ HashAlgorithm ha = MD5.Create();
// ハッシュ値を計算する
return CustomEncode.ToBase64String(
@@ -1785,26 +1788,33 @@ public static string GetMD5Hash(string FilePath)
///
public static bool LastModifiedCheck_ByHead(Entry entry, Entry history, string zipFile)
{
- HttpWebRequest hwReq = null;
- HttpWebResponse hwRes = null;
+ string url = Program.GetRequestUrl(entry, zipFile);
- try
+ using (HttpClient client = Program.GetHttpClient(entry))
+ using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Head, url))
+ using (HttpResponseMessage res =
+ client.SendAsync(req).GetAwaiter().GetResult())
{
- hwReq = Program.GetHttpWebRequest(entry, zipFile);
- hwReq.Timeout = 5000;
- hwReq.Method = "HEAD";
- hwRes = (HttpWebResponse)hwReq.GetResponse();
+ res.EnsureSuccessStatusCode();
// 更新日付のチェック
string httpLastModifiedHis = "";
- string httpLastModifiedWeb = hwRes.LastModified.ToString(
+
+ // **Last-Modified が無いときの既定値が違う。**(#575)
+ // HttpWebResponse.LastModified は**現在時刻**を返していた。
+ // HttpClient は null になるので、**従来の見え方に合わせる。**
+ DateTime lastModified = res.Content.Headers.LastModified.HasValue
+ ? res.Content.Headers.LastModified.Value.LocalDateTime
+ : DateTime.Now;
+
+ string httpLastModifiedWeb = lastModified.ToString(
"yyyy-MM-dd HH:mm:ss:fff"); //.Headers["Last-Modified"];
if (httpLastModifiedWeb == null && httpLastModifiedWeb == "")
{
// 更新日付ヘッダなし
Program.OutPutMessage(string.Format(
- GetMessage.GetMessageDescription("I0006"), hwReq.RequestUri.AbsoluteUri), LogLevel.InfoLog);
+ GetMessage.GetMessageDescription("I0006"), url), LogLevel.InfoLog);
}
else
{
@@ -1855,7 +1865,7 @@ public static bool LastModifiedCheck_ByHead(Entry entry, Entry history, string z
// メッセージ
Program.OutPutMessage(string.Format(
- GetMessage.GetMessageDescription("I0007"), hwReq.RequestUri.AbsoluteUri), LogLevel.InfoLog);
+ GetMessage.GetMessageDescription("I0007"), url), LogLevel.InfoLog);
// → パス
return false;
@@ -1866,7 +1876,7 @@ public static bool LastModifiedCheck_ByHead(Entry entry, Entry history, string z
// メッセージ
Program.OutPutMessage(string.Format(
- GetMessage.GetMessageDescription("I0008"), hwReq.RequestUri.AbsoluteUri), LogLevel.InfoLog);
+ GetMessage.GetMessageDescription("I0008"), url), LogLevel.InfoLog);
}
}
}
@@ -1876,19 +1886,15 @@ public static bool LastModifiedCheck_ByHead(Entry entry, Entry history, string z
// メッセージ
Program.OutPutMessage(string.Format(
- GetMessage.GetMessageDescription("I0009"), hwReq.RequestUri.AbsoluteUri), LogLevel.InfoLog);
+ GetMessage.GetMessageDescription("I0009"), url), LogLevel.InfoLog);
}
}
// → インスコ
return true;
}
- finally
- {
- // 閉じる(これが無いと2回目実行できない。)
- if (hwRes != null) { hwRes.Close(); }
- if (hwReq != null) { hwReq.Abort(); }
- }
+ // **using で破棄する。**(#575)
+ // 元は「閉じる(これが無いと2回目実行できない。)」と書かれていた。
}
#endregion
@@ -1900,17 +1906,20 @@ public static bool LastModifiedCheck_ByHead(Entry entry, Entry history, string z
/// ZIPファイル
public static void GetAndSaveContent(Entry entry, string zipFile)
{
- HttpWebRequest hwReq = null;
- HttpWebResponse hwRes = null;
+ string url = Program.GetRequestUrl(entry, zipFile);
- try
+ using (HttpClient client = Program.GetHttpClient(entry))
+ using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url))
+ using (HttpResponseMessage res =
+ client.SendAsync(req).GetAwaiter().GetResult())
{
- hwReq = Program.GetHttpWebRequest(entry, zipFile);
- hwReq.Timeout = 5000;
- hwReq.Method = "GET";
- hwRes = (HttpWebResponse)hwReq.GetResponse();
+ res.EnsureSuccessStatusCode();
- if (hwRes.ContentLength != -1)
+ // **長さ不明の表し方が違う。**(#575)
+ // HttpWebResponse.ContentLength は long で、**不明は -1** だった。
+ // HttpClient は long? で、**不明は null。**
+ // `!= -1` をそのまま移すと常に真になり、意味が変わる。
+ if (res.Content.Headers.ContentLength.HasValue)
{
Stream sm = null;
FileStream fs = null;
@@ -1918,7 +1927,7 @@ public static void GetAndSaveContent(Entry entry, string zipFile)
try
{
// 応答データを受信するためのStreamを取得
- sm = hwRes.GetResponseStream();
+ sm = res.Content.ReadAsStreamAsync().GetAwaiter().GetResult();
// ファイルに書き込むためのFileStreamを作成
string saveFileName = "";
@@ -1946,7 +1955,7 @@ public static void GetAndSaveContent(Entry entry, string zipFile)
// メッセージ
Program.OutPutMessage(string.Format(
- GetMessage.GetMessageDescription("I0010"), hwReq.RequestUri.AbsoluteUri), LogLevel.InfoLog);
+ GetMessage.GetMessageDescription("I0010"), url), LogLevel.InfoLog);
}
finally
{
@@ -1956,26 +1965,22 @@ public static void GetAndSaveContent(Entry entry, string zipFile)
}
}
}
- finally
- {
- // 閉じる(これが無いと2回目実行できない。)
- if (hwRes != null) { hwRes.Close(); }
- if (hwReq != null) { hwReq.Abort(); }
- }
+ // **using で破棄する。**(#575)
+ // 元は「閉じる(これが無いと2回目実行できない。)」と書かれていた。
+ // 同じことが起きないよう、確実に捨てる。
}
#endregion
- #region new HttpWebRequest
+ #region HTTP 要求の組み立て(#575 で HttpClient へ)
- /// HttpWebRequestを取得する。
+ /// 要求先の URL を組み立てる。
/// エントリ
/// ZIPファイル名
- /// HttpWebRequest
- public static HttpWebRequest GetHttpWebRequest(Entry entry, string zipFile)
+ /// URL
+ public static string GetRequestUrl(Entry entry, string zipFile)
{
string zipURL = entry.WWWURL;
- HttpWebRequest hwReq = null;
if (!string.IsNullOrEmpty(zipFile))
{
@@ -1998,14 +2003,29 @@ public static HttpWebRequest GetHttpWebRequest(Entry entry, string zipFile)
zipURL += "/" + zipFile;
}
- // リクエストを生成する。
- hwReq = (HttpWebRequest)HttpWebRequest.Create(new Uri(zipURL));
+ return zipURL;
+ }
+
+ /// HttpClient を取得する。
+ /// エントリ
+ /// HttpClient
+ ///
+ /// **WebRequest は廃止された**(SYSLIB0014)ため HttpClient にした(#575)。
+ ///
+ /// **エントリごとに作る。**
+ /// 資格情報とプロキシは HttpClient ではなく HttpClientHandler が持つため、
+ /// エントリで設定が変わる以上、使い回せない。
+ /// 呼び出し側で using して破棄すること。
+ ///
+ public static HttpClient GetHttpClient(Entry entry)
+ {
+ HttpClientHandler handler = new HttpClientHandler();
// WWWサーバのNetworkCredential
if (entry.WWWUID == null && entry.WWWUID == "")
{
// NetworkCredentialなし(デフォルト)
- //hwReq.UseDefaultCredentials = true;// ★要・検討
+ //handler.UseDefaultCredentials = true;// ★要・検討
}
else
{
@@ -2013,13 +2033,13 @@ public static HttpWebRequest GetHttpWebRequest(Entry entry, string zipFile)
if (entry.WWWDomain == null || entry.WWWDomain == "")
{
// UID、PWD
- hwReq.Credentials = new NetworkCredential(
+ handler.Credentials = new NetworkCredential(
entry.WWWUID, entry.WWWPWD);
}
else
{
// UID、PWD、Domain
- hwReq.Credentials = new NetworkCredential(
+ handler.Credentials = new NetworkCredential(
entry.WWWUID, entry.WWWPWD, entry.WWWDomain);
}
}
@@ -2030,15 +2050,14 @@ public static HttpWebRequest GetHttpWebRequest(Entry entry, string zipFile)
// Proxyサーバ
if (entry.ProxyURL.ToLower() == "none")
{
- // Proxyサーバなし(null)
- hwReq.Proxy = null;
-
- // ※ GlobalProxySelection.GetEmptyWebProxy()は古い形式
+ // Proxyサーバなし
+ // **HttpClientHandler では Proxy = null が「使わない」にならない。**
+ // UseProxy を落とす必要がある。
+ handler.UseProxy = false;
}
else if (entry.ProxyURL == null || entry.ProxyURL == "")
{
- // proxy = (WebProxy)WebProxy.GetDefaultProxy();// 古い形式
- // 何もしない → DefaultWebProxyプロパティの値を使用する。
+ // 何もしない → 既定のプロキシ設定を使用する。
}
else
{
@@ -2073,10 +2092,16 @@ public static HttpWebRequest GetHttpWebRequest(Entry entry, string zipFile)
}
// プロキシ設定
- hwReq.Proxy = proxy;
+ handler.Proxy = proxy;
+ handler.UseProxy = true;
}
- return hwReq;
+ HttpClient client = new HttpClient(handler);
+
+ // 従来の HttpWebRequest.Timeout = 5000 に合わせる。
+ client.Timeout = TimeSpan.FromMilliseconds(5000);
+
+ return client;
}
#endregion
diff --git a/root/programs/CS/Samples/ANALYSIS.md b/root/programs/CS/Samples/ANALYSIS.md
index 6e8347dac..5b5af62ae 100644
--- a/root/programs/CS/Samples/ANALYSIS.md
+++ b/root/programs/CS/Samples/ANALYSIS.md
@@ -88,7 +88,7 @@ CS/VB 合わせて **42 プロジェクト**がこの `Build\` を参照して
| `WS_sample/WSServer_sample` | Library | **サービス側の B/D 層**。ServiceInterface からレイトバインドされる |
| `WS_sample/WSIFType_sample` | Library | WS の I/F 型(引数・戻り値クラス)を**サーバ/クライアント共有**するためのアセンブリ |
| `WS_sample/WSClient_sample/*` | WinForms/WPF | **3層型クライアント**。`CallController` 経由で B層を呼ぶ(4 種) |
-| `WS_sample/ASPNETWebService` | — | **README のみ**。実体は別リポジトリ `ResourceServerTemplates` へ移動済み |
+| `WS_sample/ASPNETWebService` | ASP.NET WebAPI | **ResourceServer**。`JsonController` が `MVC_Sample` の `Crud1Controller` と同じ CRUD を公開する(#566) |
| `WebApp_sample/WebForms_Sample` | ASP.NET Web Forms | **最大のサンプル(.aspx 38 画面)**。P層機能の網羅テスト |
| `WebApp_sample/MVC_Sample` | ASP.NET MVC5 | MVC5 + WebAPI 版 |
| `CLI_sample/*` | — | **README のみ(3 件)**。net48 版はドロップ済み(後述) |
@@ -98,7 +98,9 @@ CS/VB 合わせて **42 プロジェクト**がこの `Build\` を参照して
- **`CLI_sample` の実体は無い。** `Simple_CLI` は Sharprompt が .NET Framework サポートを終了したため net48 版をドロップ。
`DAG_Login_CLI` / `LIR_Login_CLI` は `System.CommandLine` の beta 解除待ちで移植保留。
→ **実装は `../Samples4NetCore/Legacy/CLI_sample/` にのみ存在する。**
-- **`WS_sample/ASPNETWebService`** は `OpenTouryoProject/ResourceServerTemplates` へ移動。
+- **`WS_sample/ASPNETWebService` は戻ってきた(#566)。**
+ 一度 `OpenTouryoProject/ResourceServerTemplates` へ移したが、共通基盤の上に乗せるため引き戻した。
+ ビルド(`6_Build_WSSrv_sample.bat`)と疎通(`3_SmokeTest.ps1`)に組み込み済み。
---
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService.sln b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService.sln
new file mode 100644
index 000000000..755fd1f79
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 18
+VisualStudioVersion = 18.3.11527.330 d18.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASPNETWebService", "ASPNETWebService\ASPNETWebService.csproj", "{C24BC2FA-D423-4F0F-B2B0-E647B621683D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C24BC2FA-D423-4F0F-B2B0-E647B621683D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C24BC2FA-D423-4F0F-B2B0-E647B621683D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C24BC2FA-D423-4F0F-B2B0-E647B621683D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C24BC2FA-D423-4F0F-B2B0-E647B621683D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E15CBB9C-DAC2-4585-AE19-29431C3C1E25}
+ EndGlobalSection
+EndGlobal
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/ASPNETWebService.csproj b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/ASPNETWebService.csproj
new file mode 100644
index 000000000..57d31a7a0
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/ASPNETWebService.csproj
@@ -0,0 +1,318 @@
+
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {C24BC2FA-D423-4F0F-B2B0-E647B621683D}
+ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ ASPNETWebService
+ ASPNETWebService
+ v4.8
+ true
+ 44335
+ enabled
+ disabled
+ false
+
+
+
+ true
+
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll
+
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.5\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
+
+
+ ..\packages\Microsoft.Bcl.Cryptography.10.0.5\lib\net462\Microsoft.Bcl.Cryptography.dll
+
+
+ ..\packages\Microsoft.Bcl.TimeProvider.10.0.5\lib\net462\Microsoft.Bcl.TimeProvider.dll
+
+
+ ..\packages\Microsoft.Data.SqlClient.7.0.0\lib\net462\Microsoft.Data.SqlClient.dll
+
+
+ ..\packages\Microsoft.Data.SqlClient.Extensions.Abstractions.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Extensions.Abstractions.dll
+
+
+ ..\packages\Microsoft.Data.SqlClient.Internal.Logging.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Internal.Logging.dll
+
+
+ ..\packages\Microsoft.Extensions.Caching.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.Caching.Memory.10.0.5\lib\net462\Microsoft.Extensions.Caching.Memory.dll
+
+
+ ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.Logging.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.Options.10.0.5\lib\net462\Microsoft.Extensions.Options.dll
+
+
+ ..\packages\Microsoft.Extensions.Primitives.10.0.5\lib\net462\Microsoft.Extensions.Primitives.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Abstractions.8.17.0\lib\net472\Microsoft.IdentityModel.Abstractions.dll
+
+
+ ..\packages\Microsoft.IdentityModel.JsonWebTokens.8.17.0\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Logging.8.17.0\lib\net472\Microsoft.IdentityModel.Logging.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Protocols.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Tokens.8.17.0\lib\net472\Microsoft.IdentityModel.Tokens.dll
+
+
+ ..\packages\Microsoft.Owin.4.2.3\lib\net45\Microsoft.Owin.dll
+
+
+
+ ..\packages\Microsoft.Owin.Host.SystemWeb.4.2.3\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
+
+
+ ..\packages\Microsoft.Web.Infrastructure.2.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
+
+
+ ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\Newtonsoft.Json.Bson.1.0.3\lib\net45\Newtonsoft.Json.Bson.dll
+
+
+ ..\..\..\..\Frameworks\Infrastructure\Build\OpenTouryo.Business.dll
+
+
+ ..\..\..\..\Frameworks\Infrastructure\Build\OpenTouryo.Framework.dll
+
+
+ ..\..\..\..\Frameworks\Infrastructure\Build\OpenTouryo.Public.dll
+
+
+ ..\..\..\..\Frameworks\Infrastructure\Build\OpenTouryo.Public.Security.dll
+
+
+ ..\packages\Owin.1.0\lib\net40\Owin.dll
+ True
+
+
+ ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll
+
+
+
+
+
+
+ ..\packages\System.Diagnostics.DiagnosticSource.10.0.5\lib\net462\System.Diagnostics.DiagnosticSource.dll
+
+
+ ..\packages\System.Formats.Asn1.10.0.5\lib\net462\System.Formats.Asn1.dll
+
+
+ ..\packages\System.IdentityModel.Tokens.Jwt.8.17.0\lib\net472\System.IdentityModel.Tokens.Jwt.dll
+
+
+ ..\packages\System.IO.Pipelines.10.0.5\lib\net462\System.IO.Pipelines.dll
+
+
+ ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll
+
+
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Client.6.0.0\lib\net45\System.Net.Http.Formatting.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+ True
+ True
+
+
+
+
+ ..\packages\System.Security.Cryptography.Pkcs.10.0.5\lib\net462\System.Security.Cryptography.Pkcs.dll
+
+
+
+ ..\packages\System.Text.Encodings.Web.10.0.5\lib\net462\System.Text.Encodings.Web.dll
+
+
+ ..\packages\System.Text.Json.10.0.5\lib\net462\System.Text.Json.dll
+
+
+ ..\packages\System.Threading.Channels.10.0.5\lib\net462\System.Threading.Channels.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll
+
+
+
+ ..\packages\Microsoft.AspNet.Cors.5.3.0\lib\net45\System.Web.Cors.dll
+
+
+
+
+
+ ..\packages\Microsoft.AspNet.WebPages.3.3.0\lib\net45\System.Web.Helpers.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Core.5.3.0\lib\net45\System.Web.Http.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Cors.5.3.0\lib\net45\System.Web.Http.Cors.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.WebHost.5.3.0\lib\net45\System.Web.Http.WebHost.dll
+
+
+ ..\packages\Microsoft.AspNet.Mvc.5.3.0\lib\net45\System.Web.Mvc.dll
+
+
+ ..\packages\Microsoft.AspNet.Razor.3.3.0\lib\net45\System.Web.Razor.dll
+
+
+ ..\packages\Microsoft.AspNet.WebPages.3.3.0\lib\net45\System.Web.WebPages.dll
+
+
+ ..\packages\Microsoft.AspNet.WebPages.3.3.0\lib\net45\System.Web.WebPages.Deployment.dll
+
+
+ ..\packages\Microsoft.AspNet.WebPages.3.3.0\lib\net45\System.Web.WebPages.Razor.dll
+
+
+
+
+
+
+
+
+
+
+ ..\packages\Microsoft.AspNet.FriendlyUrls.Core.1.0.2\lib\net45\Microsoft.AspNet.FriendlyUrls.dll
+
+
+ ..\packages\WebGrease.1.6.0\lib\WebGrease.dll
+
+
+ ..\..\Build\WSIFType_sample.dll
+
+
+ ..\..\Build\WSServer_sample.dll
+
+
+
+
+ ..\packages\Microsoft.Web.Infrastructure.2.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
+
+
+
+
+
+
+
+ Web.config
+
+
+ Web.config
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+
+
+
+
+ True
+ True
+ 58497
+ /
+ https://localhost:44335/
+ False
+ False
+
+
+ False
+
+
+
+
+
+
+
+
+ このプロジェクトは、このコンピューター上にない NuGet パッケージを参照しています。それらのパッケージをダウンロードするには、[NuGet パッケージの復元] を使用します。詳細については、http://go.microsoft.com/fwlink/?LinkID=322105 を参照してください。見つからないファイルは {0} です。
+
+
+
+
+
+
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/App_Start/FilterConfig.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/App_Start/FilterConfig.cs
new file mode 100644
index 000000000..7ae70da93
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/App_Start/FilterConfig.cs
@@ -0,0 +1,47 @@
+//**********************************************************************************
+//* テンプレート
+//**********************************************************************************
+
+// サンプル中のテンプレートなので、必要に応じて使用して下さい。
+
+//**********************************************************************************
+//* クラス名 :FilterConfig
+//* クラス日本語名 :グローバルフィルタに関する指定
+//*
+//* 作成日時 :-
+//* 作成者 :-
+//* 更新履歴 :-
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 20xx/xx/xx XX XX XXXX
+//**********************************************************************************
+
+using System.Web.Mvc;
+
+namespace ASPNETWebService
+{
+ ///
+ /// グローバルフィルタに関する指定
+ ///
+ public class FilterConfig
+ {
+ ///
+ /// ASP.NET MVC 3 の新機能、グローバルフィルタは地味だけどイケてる - しばやん雑記
+ /// http://shiba-yan.hatenablog.jp/entry/20110104/1294073715
+ /// ASP.NET MVC 3 ではグローバルフィルタという機能が追加されました。
+ /// Razor や DI に比べてかなり地味ですが、今までコントローラクラスに毎回付ける必要があった
+ /// アクションフィルタを Global.asax で一括指定できるようになりました。
+ ///
+ ///
+ public static void RegisterGlobalFilters(GlobalFilterCollection filters)
+ {
+ // デフォルトで HandleError アクションフィルタを全てのコントローラへ適用するようになっている。
+ filters.Add(new HandleErrorAttribute());
+
+ //// OutputCache アクションフィルタ
+ //// 全てのページを 60 秒間キャッシュする
+ //filters.Add(new OutputCacheAttribute { Duration = 60 });
+ }
+ }
+}
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/App_Start/WebApiConfig.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/App_Start/WebApiConfig.cs
new file mode 100644
index 000000000..0bffcc04c
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/App_Start/WebApiConfig.cs
@@ -0,0 +1,61 @@
+//**********************************************************************************
+//* テンプレート
+//**********************************************************************************
+
+// サンプル中のテンプレートなので、必要に応じて使用して下さい。
+
+//**********************************************************************************
+//* クラス名 :WebApiConfig
+//* クラス日本語名 :ルート定義に関する指定(WebApi用)
+//*
+//* 作成日時 :-
+//* 作成者 :-
+//* 更新履歴 :-
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 20xx/xx/xx XX XX XXXX
+//**********************************************************************************
+
+using System.Web.Http;
+//using Microsoft.Owin.Security.OAuth;
+
+using Newtonsoft.Json.Serialization;
+
+namespace ASPNETWebService
+{
+ public static class WebApiConfig
+ {
+ public static void Register(HttpConfiguration config)
+ {
+ //// Web API configuration and services
+ //// 「Bearer Token」認証のみを使用するように、Web API を設定。
+ //config.SuppressDefaultHostAuthentication();
+ //config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
+
+ // JSON データをDefaultに使用 (JSON.NET)
+ config.Formatters.Remove(config.Formatters.XmlFormatter);
+ config.Formatters.Insert(0, config.Formatters.JsonFormatter);
+ config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
+
+ // CORS (Cross-Origin Resource Sharing)の有効化
+ // 別ドメイン上で動作する Web アプリからアクセス可能に設定。
+ config.EnableCors();
+
+ // Web API routes を設定する。
+
+ // Attribute Routing
+ config.MapHttpAttributeRoutes();
+
+ // MapHttpRoute
+ config.Routes.MapHttpRoute(
+ name: "DefaultApi",
+ routeTemplate: "api/{controller}/{action}/{id}",
+ defaults: new { id = RouteParameter.Optional }
+ );
+
+ //// トレース機能を有効化します。
+ //TraceConfig.Register(config);
+ }
+ }
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/BatchUpdateController.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/BatchUpdateController.cs
new file mode 100644
index 000000000..83da1ecfa
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/BatchUpdateController.cs
@@ -0,0 +1,233 @@
+#region Apache License
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+//**********************************************************************************
+//* クラス名 :BatchUpdateController
+//* クラス日本語名 :DTO を使用したバッチ更新処理の WebAPI
+//*
+//* 作成者 :玄人 幸道
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2026/08/20 玄人 幸道 新規作成(#570)
+//**********************************************************************************
+
+using System;
+using System.Data;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using System.Web.Http;
+using System.Web.Http.Cors;
+
+using ASPNETWebService.Logic.Business;
+using ASPNETWebService.Logic.Common;
+
+using Touryo.Infrastructure.Business.Presentation;
+using Touryo.Infrastructure.Business.Util;
+using Touryo.Infrastructure.Framework.Exceptions;
+using Touryo.Infrastructure.Public.Db;
+using Touryo.Infrastructure.Public.Dto;
+using Touryo.Infrastructure.Public.Security;
+
+namespace ASPNETWebService.Controllers
+{
+ /// DTO を使用したバッチ更新処理の WebAPI
+ ///
+ /// **DataTable を DTTables 経由で JSON にして往復させる。**
+ ///
+ /// 一覧 : DataTable → DTTable.FromDataTable(dt, keepOriginal: true) → DTTables → JSON
+ /// 更新 : JSON → DTTables → ToDataTable() → RowState と Original が戻った DataTable
+ ///
+ /// **keepOriginal を立てないと、楽観排他が組めない。**(#567)
+ /// Modified 行の WHERE には「取得時の値(Original)」が要るが、
+ /// 素の JSON では現在値しか運べない。
+ ///
+ /// B層の呼び出しは MVC_Sample の Crud1Controller と同じく DoBusinessLogicAsync を使う。
+ ///
+ [EnableCors(origins: "*", headers: "*", methods: "*", SupportsCredentials = true)]
+ [MyBaseAsyncApiController(httpAuthHeader:
+ EnumHttpAuthHeader.None // 認証無くても通すので、
+ | EnumHttpAuthHeader.Bearer)] // Bearer認証の結果をGetClaimsで検証。
+ [RoutePrefix("api/batchupdate")]
+ public class BatchUpdateController : ApiController
+ {
+ /// テーブル名
+ /// DTTables の中の識別に使う。
+ private const string TableName = "Suppliers";
+
+ #region 疎通
+
+ /// 疎通確認
+ /// string
+ /// http(s)://hostName:portNum/api/batchupdate/test で疎通テスト可能。
+ [HttpGet]
+ [Route("test")]
+ public string test()
+ {
+ return "test";
+ }
+
+ #endregion
+
+ #region 件数確認
+
+ /// Suppliers の件数を返す
+ /// HttpResponseMessage
+ [HttpPost]
+ [Route("SelectCount")]
+ public async Task SelectCount()
+ {
+ SuppliersReturnValue returnValue = await this.CallLayerB("SelectCount", null);
+
+ if (returnValue.ErrorFlag)
+ {
+ return this.CreateErrorResponse(returnValue);
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, new { Count = returnValue.Count });
+ }
+
+ #endregion
+
+ #region 一覧取得
+
+ /// Suppliers の一覧を DTTables の JSON で返す
+ /// HttpResponseMessage
+ ///
+ /// **keepOriginal: true で作る。**
+ /// このあとクライアント側で編集され、バッチ更新へ戻ってくるため、
+ /// 取得時の値(Original)を保った状態で渡す必要がある。
+ ///
+ [HttpPost]
+ [Route("SelectAll")]
+ public async Task SelectAll()
+ {
+ SuppliersReturnValue returnValue = await this.CallLayerB("SelectAll", null);
+
+ if (returnValue.ErrorFlag)
+ {
+ return this.CreateErrorResponse(returnValue);
+ }
+
+ DTTables dtts = new DTTables();
+ dtts.Add(DTTable.FromDataTable(returnValue.Suppliers, true));
+
+ return Request.CreateResponse(HttpStatusCode.OK,
+ new { Suppliers = DTTables.DTTablesToJson(dtts) });
+ }
+
+ #endregion
+
+ #region バッチ更新
+
+ /// 編集済みの DTTables を受け取り、バッチ更新する
+ /// 引数
+ /// HttpResponseMessage
+ ///
+ /// **RowState と Original が復元されることが肝。**
+ /// ToDataTable() が戻した DataTable は、そのまま
+ /// dr.RowState での振り分けと dr[col, DataRowVersion.Original] に使える。
+ ///
+ [HttpPost]
+ [Route("BatchUpdate")]
+ public async Task BatchUpdate(BatchUpdateParams param)
+ {
+ if (param == null || string.IsNullOrEmpty(param.Suppliers))
+ {
+ return Request.CreateResponse(HttpStatusCode.OK,
+ new { ErrorMSG = "更新対象がありません。" });
+ }
+
+ DTTables dtts = DTTables.JsonToDTTables(param.Suppliers);
+
+ DataTable dt = null;
+ foreach (DTTable dtt in dtts)
+ {
+ if (dtt.TableName == BatchUpdateController.TableName)
+ {
+ dt = dtt.ToDataTable();
+ break;
+ }
+ }
+
+ SuppliersReturnValue returnValue = await this.CallLayerB("BatchUpdate", dt);
+
+ if (returnValue.ErrorFlag)
+ {
+ return this.CreateErrorResponse(returnValue);
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, new
+ {
+ returnValue.InsertCount,
+ returnValue.UpdateCount,
+ returnValue.DeleteCount
+ });
+ }
+
+ #endregion
+
+ #region ユーティリティ
+
+ /// B層を呼ぶ
+ /// メソッド名(UOC_〈methodName〉 が呼ばれる)
+ /// バッチ更新の対象(不要なら null)
+ /// 戻り値クラス
+ /// MVC_Sample の Crud1Controller と同じく DoBusinessLogicAsync を使う。
+ private async Task CallLayerB(string methodName, DataTable dt)
+ {
+ // Claim を取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ SuppliersParameterValue parameterValue = new SuppliersParameterValue(
+ "BatchUpdateController", "-", methodName, methodName,
+ new MyUserInfo(userName, ipAddress));
+
+ parameterValue.Suppliers = dt;
+
+ SuppliersLayerB layerB = new SuppliersLayerB();
+
+ // B層呼出し+都度コミット
+ return (SuppliersReturnValue)await layerB.DoBusinessLogicAsync(
+ parameterValue, DbEnum.IsolationLevelEnum.DefaultTransaction);
+ }
+
+ /// 業務エラーの応答を作る
+ /// 戻り値クラス
+ /// HttpResponseMessage
+ private HttpResponseMessage CreateErrorResponse(SuppliersReturnValue returnValue)
+ {
+ return Request.CreateResponse(HttpStatusCode.OK, new
+ {
+ ErrorMessageID = returnValue.ErrorMessageID,
+ ErrorMessage = returnValue.ErrorMessage,
+ ErrorInfo = returnValue.ErrorInfo
+ });
+ }
+
+ #endregion
+ }
+
+ /// バッチ更新の引数
+ public class BatchUpdateParams
+ {
+ /// DTTables の JSON
+ public string Suppliers { get; set; }
+ }
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/JsonController.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/JsonController.cs
new file mode 100644
index 000000000..ee907da29
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/JsonController.cs
@@ -0,0 +1,904 @@
+//**********************************************************************************
+//* Copyright (C) 2007,2016 Hitachi Solutions,Ltd.
+//**********************************************************************************
+
+#region Apache License
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+//**********************************************************************************
+//* クラス名 :JsonController
+//* クラス日本語名 :ASP.NET WebAPI JSON-RPCの個別Webメソッドを公開するサービス インターフェイス基盤。
+//*
+//* 作成日時 :-
+//* 作成者 :生技
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2017/08/18 西野 大介 新規作成
+//**********************************************************************************
+
+using System;
+using System.Data;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Diagnostics;
+using System.Runtime.ExceptionServices;
+
+using System.Web.Http;
+using System.Web.Http.Cors;
+using System.Net;
+using System.Net.Http;
+
+using Touryo.Infrastructure.Business.Presentation;
+using Touryo.Infrastructure.Business.Util;
+using Touryo.Infrastructure.Framework.Common;
+using Touryo.Infrastructure.Framework.Transmission;
+using Touryo.Infrastructure.Framework.Exceptions;
+using Touryo.Infrastructure.Framework.Util;
+using Touryo.Infrastructure.Public.Db;
+using Touryo.Infrastructure.Public.Dto;
+using Touryo.Infrastructure.Public.Log;
+using Touryo.Infrastructure.Public.Util;
+using Touryo.Infrastructure.Public.Security;
+using Touryo.Infrastructure.Public.Reflection;
+using Touryo.Infrastructure.Public.Diagnostics;
+
+using WSIFType_sample;
+using WSServer_sample.Common;
+
+namespace ASPNETWebService.Controllers
+{
+ ///
+ /// ASP.NET WebAPI JSON-RPCの個別Webメソッドを公開するサービス インターフェイス基盤。
+ ///
+ [EnableCors(
+ // リソースへのアクセスを許可されている発生元
+ origins: "*",
+ // リソースによってサポートされているヘッダー
+ headers: "*",
+ // リソースによってサポートされているメソッド
+ methods: "*",
+ //
+ SupportsCredentials = true)]
+ [MyBaseAsyncApiController(httpAuthHeader:
+ EnumHttpAuthHeader.None // 認証無くても通すので、
+ | EnumHttpAuthHeader.Bearer)] // Bearer認証の結果をGetClaimsで検証。
+ [RoutePrefix("api/json")]
+ public class JsonController : ApiController
+ {
+ #region 疎通テスト用
+
+ ///
+ /// 疎通テスト用
+ /// http(s)://hostName:portNum/api/json/testで疎通テスト可能。
+ ///
+ /// string
+ [HttpGet]
+ public string test()
+ {
+ return "test";
+ }
+
+ #endregion
+
+ #region グローバル変数
+
+ /// インプロセス呼び出しの名前解決シングルトン クラス
+ ///
+ /// 初期化は起動時の1回のみであり、
+ /// 読み取り専用のデータを保持する場合
+ /// のみに適用するデザインパターンとする。
+ ///
+ private static InProcessNameService IPR_NS = new InProcessNameService();
+
+ #endregion
+
+ /// 非同期化のため
+ public class AsyncRetVal
+ {
+ /// 返すべきエラーの情報
+ public Dictionary WsErrorInfo = null;
+ /// 戻り値
+ public BaseReturnValue ReturnValue = null;
+ }
+
+ #region ASP.NET WebAPI JSON-RPCのWebメソッド
+
+ #region 個別部
+
+ ///
+ /// POST JsonController/SelectCount
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("SelectCount")]
+ public async Task SelectCount(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "SelectCount", "SelectCount",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ string message = testReturnValue.Obj.ToString() + "件のデータがあります";
+ ret = new { Message = message };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/SelectAll_DT
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("SelectAll_DT")]
+ public async Task SelectAll_DT(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "SelectAll_DT", "SelectAll_DT",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ DataTable dt = (DataTable)testReturnValue.Obj;
+
+ // 一部、DataToDictionaryのテストコード
+ DataToDictionary d2d = null;
+ List> list = null;
+
+ d2d = new DataToDictionary(
+ new Dictionary()
+ {
+ { "ShipperID", "_ShipperID"},
+ { "CompanyName", "_CompanyName"},
+ { "Phone", "_Phone"}
+ },
+ null, null);
+ list = d2d.DataTableToDictionaryList(dt);
+ Debug.WriteLine(ObjectInspector.Inspect(list));
+
+ d2d = new DataToDictionary(null, null, null);
+ list = d2d.DataTableToDictionaryList(dt);
+ Debug.WriteLine(ObjectInspector.Inspect(list));
+
+ ret = new { Message = "", Result = list };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/SelectAll_DS
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("SelectAll_DS")]
+ public async Task SelectAll_DS(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "SelectAll_DS", "SelectAll_DS",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ DataTable dt = ((DataSet)testReturnValue.Obj).Tables[0];
+ DataToDictionary d2d = new DataToDictionary(null, null, null);
+ ret = new { Message = "", Result = d2d.DataTableToDictionaryList(dt) };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/SelectAll_DR
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("SelectAll_DR")]
+ public async Task SelectAll_DR(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "SelectAll_DR", "SelectAll_DR",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ DataTable dt = (DataTable)testReturnValue.Obj;
+ DataToDictionary d2d = new DataToDictionary(
+ new Dictionary
+ {
+ { "c1", "ShipperID" },
+ { "c2", "CompanyName" },
+ { "c3", "Phone" }
+ }, "", "");
+ ret = new { Message = "", Result = d2d.DataTableToDictionaryList(dt) };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/SelectAll_DSQL
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("SelectAll_DSQL")]
+ public async Task SelectAll_DSQL(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "SelectAll_DSQL", "SelectAll_DSQL",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ testParameterValue.OrderColumn = param.OrderColumn;
+ testParameterValue.OrderSequence = param.OrderSequence;
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ DataTable dt = (DataTable)testReturnValue.Obj;
+ DataToDictionary d2d = new DataToDictionary(null, null, null);
+ ret = new { Message = "", Result = d2d.DataTableToDictionaryList(dt) };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/Select
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("Select")]
+ public async Task Select(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "Select", "Select",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ testParameterValue.ShipperID = param.Shipper.ShipperID;
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ Dictionary dic = new Dictionary()
+ {
+ { "ShipperID", testReturnValue.ShipperID.ToString()},
+ { "CompanyName", testReturnValue.CompanyName},
+ { "Phone", testReturnValue.Phone}
+ };
+ ret = new { Message = "", Result = dic };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/Insert
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("Insert")]
+ public async Task Insert(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "Insert", "Insert",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ testParameterValue.CompanyName = param.Shipper.CompanyName;
+ testParameterValue.Phone = param.Shipper.Phone;
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ string message = testReturnValue.Obj.ToString() + "件追加";
+
+ ret = new { Message = message };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/Update
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("Update")]
+ public async Task Update(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "Update", "Update",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ testParameterValue.ShipperID = param.Shipper.ShipperID;
+ testParameterValue.CompanyName = param.Shipper.CompanyName;
+ testParameterValue.Phone = param.Shipper.Phone;
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ string message = testReturnValue.Obj.ToString() + "件更新";
+
+ ret = new { Message = message };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ ///
+ /// POST JsonController/Delete
+ ///
+ /// 引数
+ /// 戻り値
+ [HttpPost]
+ [Route("Delete")]
+ public async Task Delete(WebApiParams param)
+ {
+ // Claimを取得する。
+ string userName, roles, scopes, ipAddress;
+ MyBaseAsyncApiController.GetClaims(out userName, out roles, out scopes, out ipAddress);
+
+ // 引数クラスを生成
+ // 下位(B・D層)は、テスト クラスを流用する
+ TestParameterValue testParameterValue
+ = new TestParameterValue(
+ "JsonController", "Delete", "Delete",
+ param.ddlDap + "%" + param.ddlMode1 + "%" + param.ddlMode2 + "%" + param.ddlExRollback,
+ new MyUserInfo(userName, ipAddress));
+
+ testParameterValue.ShipperID = param.Shipper.ShipperID;
+
+ // 非同期呼び出し
+ AsyncRetVal asyncRetVal = await this.Call("testInProcess", testParameterValue);
+
+ object ret = null;
+
+ if (asyncRetVal.WsErrorInfo != null)
+ {
+ // ランタイムエラー
+ ret = new { ExceptionMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ TestReturnValue testReturnValue = (TestReturnValue)asyncRetVal.ReturnValue;
+
+ if (testReturnValue.ErrorFlag == true)
+ {
+ // 結果(業務続行可能なエラー)
+ asyncRetVal.WsErrorInfo = new Dictionary();
+ asyncRetVal.WsErrorInfo["ErrorMessageID"] = testReturnValue.ErrorMessageID;
+ asyncRetVal.WsErrorInfo["ErrorMessage"] = testReturnValue.ErrorMessage;
+ asyncRetVal.WsErrorInfo["ErrorInfo"] = testReturnValue.ErrorInfo;
+
+ ret = new { ErrorMSG = asyncRetVal.WsErrorInfo };
+ }
+ else
+ {
+ // 結果(正常系)
+ string message = testReturnValue.Obj.ToString() + "件削除";
+
+ ret = new { Message = message };
+ }
+ }
+
+ return Request.CreateResponse(HttpStatusCode.OK, ret);
+ }
+
+ #endregion
+
+ #region 共通部
+
+ /// ASP.NET WebAPI JSON-RPCの個別Webメソッドの共通部
+ /// サービス名
+ /// 引数
+ ///
+ /// AsyncRetVal(非同期化のため)
+ /// ・WsErrorInfo:返すべきエラーの情報
+ /// ・ReturnValue:戻り値
+ ///
+ private async Task Call(
+ string serviceName,
+ BaseParameterValue parameterValue)
+ {
+ // ステータス
+ string status = "-";
+
+ #region 呼出し制御関係の変数
+
+ // アセンブリ名
+ string assemblyName = "";
+
+ // クラス名
+ string className = "";
+
+ #endregion
+
+ #region 引数・戻り値関係の変数
+
+ BaseReturnValue returnValue = null;
+
+ // エラー情報(XMLフォーマット)
+ Dictionary wsErrorInfo = new Dictionary();
+
+ // エラー情報(ログ出力用)
+ string errorType = ""; // 2009/09/15-この行
+ string errorMessageID = "";
+ string errorMessage = "";
+ string errorToString = "";
+
+ #endregion
+
+ try
+ {
+ // 開始ログの出力
+ LogIF.InfoLog("SERVICE-IF", FxLiteral.SIF_STATUS_START);
+
+ #region 名前解決
+
+ // ★
+ status = FxLiteral.SIF_STATUS_NAME_SERVICE;
+
+ // 名前解決(インプロセス)
+ JsonController.IPR_NS.NameResolution(serviceName, out assemblyName, out className);
+
+ #endregion
+
+ #region 引数の.NETオブジェクト化(UOC)
+
+ // ★
+ status = FxLiteral.SIF_STATUS_DESERIALIZE;
+
+ // 引数クラスをパラメタ セットに格納
+ object[] paramSet = new object[] { parameterValue, DbEnum.IsolationLevelEnum.User };
+
+ #endregion
+
+ #region 認証処理(UOC)
+
+ // MyBaseApiControllerに実装する。
+
+ #endregion
+
+ #region B層・D層呼出し
+
+ // ★
+ status = FxLiteral.SIF_STATUS_INVOKE;
+
+ try
+ {
+ // B層・D層呼出し
+
+ try
+ {
+ // B層・D層呼出し
+ Task result = (Task)Latebind.InvokeMethod(
+ assemblyName, className,
+ FxLiteral.TRANSMISSION_INPROCESS_ASYNC_METHOD_NAME, paramSet);
+ returnValue = await result;
+ }
+ catch (System.Reflection.TargetInvocationException rtEx)
+ {
+ //// InnerExceptionを投げなおす。
+ //throw rtEx.InnerException;
+
+ // スタックトレースを保って InnerException を throw
+ ExceptionDispatchInfo.Capture(rtEx.InnerException).Throw();
+ }
+ }
+ catch (System.Reflection.TargetInvocationException rtEx)
+ {
+ // InnerExceptionを投げなおす。
+ throw rtEx.InnerException;
+ }
+
+ #endregion
+
+ // ★
+ status = "";
+
+ // 戻り値を返す。
+ return new AsyncRetVal
+ {
+ WsErrorInfo = null,
+ ReturnValue = returnValue
+ };
+ }
+ //catch (BusinessApplicationException baEx)
+ //{
+ // ここには来ない↑
+ //}
+ catch (BusinessSystemException bsEx)
+ {
+ // エラー情報を設定する。
+ // システム例外
+ wsErrorInfo["ErrorType"] = FxEnum.ErrorType.BusinessSystemException.ToString();
+ wsErrorInfo["MessageID"] = bsEx.messageID;
+ wsErrorInfo["Message"] = bsEx.Message;
+
+ // ログ出力用の情報を保存
+ errorType = FxEnum.ErrorType.BusinessSystemException.ToString(); // 2009/09/15-この行
+ errorMessageID = bsEx.messageID;
+ errorMessage = bsEx.Message;
+
+ errorToString = bsEx.ToString();
+
+ // エラー情報を戻す。
+ return new AsyncRetVal
+ {
+ WsErrorInfo = wsErrorInfo,
+ ReturnValue = returnValue
+ };
+ }
+ catch (FrameworkException fxEx)
+ {
+ // エラー情報を設定する。
+ // フレームワーク例外
+ // ★ インナーエクセプション情報は消失
+ wsErrorInfo["ErrorType"] = FxEnum.ErrorType.FrameworkException.ToString();
+ wsErrorInfo["MessageID"] = fxEx.messageID;
+ wsErrorInfo["Message"] = fxEx.Message;
+
+ // ログ出力用の情報を保存
+ errorType = FxEnum.ErrorType.FrameworkException.ToString(); // 2009/09/15-この行
+ errorMessageID = fxEx.messageID;
+ errorMessage = fxEx.Message;
+
+ errorToString = fxEx.ToString();
+
+ // エラー情報を戻す。
+ return new AsyncRetVal
+ {
+ WsErrorInfo = wsErrorInfo,
+ ReturnValue = returnValue
+ };
+ }
+ catch (Exception ex)
+ {
+ // エラー情報を設定する。
+ // フレームワーク例外
+ // ★ インナーエクセプション情報は消失
+ wsErrorInfo["ErrorType"] = FxEnum.ErrorType.ElseException.ToString();
+ wsErrorInfo["MessageID"] = "-";
+ wsErrorInfo["Message"] = ex.ToString();
+
+ // ログ出力用の情報を保存
+ errorType = FxEnum.ErrorType.ElseException.ToString(); // 2009/09/15-この行
+ errorMessageID = "-";
+ errorMessage = ex.Message;
+
+ // どちらを戻すべきか?
+ // Muの場合は、Messageがデフォ
+ errorToString = ex.Message;
+ //errorToString = ex.ToString();
+
+ // エラー情報を戻す。
+ return new AsyncRetVal
+ {
+ WsErrorInfo = wsErrorInfo,
+ ReturnValue = returnValue
+ };
+ }
+ finally
+ {
+ // 用途によってSessionを解放するかどうかを検討。
+
+ //// Sessionステートレス
+ //Session.Clear();
+ //Session.Abandon();
+
+ // 終了ログの出力
+ if (status == "")
+ {
+ // 終了ログ出力
+ LogIF.InfoLog("SERVICE-IF", "正常終了");
+ }
+ else
+ {
+ // 終了ログ出力
+ LogIF.ErrorLog("SERVICE-IF",
+ "異常終了"
+ + ":" + status + "\r\n"
+ + "エラー タイプ:" + errorType + "\r\n" // 2009/09/15-この行
+ + "エラー メッセージID:" + errorMessageID + "\r\n"
+ + "エラー メッセージ:" + errorMessage + "\r\n"
+ + errorToString + "\r\n");
+ }
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/SampleDataController.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/SampleDataController.cs
new file mode 100644
index 000000000..5f73d292c
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/SampleDataController.cs
@@ -0,0 +1,78 @@
+//**********************************************************************************
+//* テスト・コントローラー
+//**********************************************************************************
+
+// テスト・コントローラーなので、必要に応じて流用 or 削除して下さい。
+
+//**********************************************************************************
+//* クラス名 :SampleDataController
+//* クラス日本語名 :疎通確認用
+//*
+//* 作成日時 :-
+//* 作成者 :生技
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2026/04/02 西野 大介 復元
+//**********************************************************************************
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web.Http;
+using System.Web.Http.Cors;
+
+namespace ASPNETWebService.Controllers
+{
+ [RoutePrefix("api/values")]
+ public class SampleDataController : ApiController
+ {
+ ///
+ /// GET api/sampledata/weatherforecasts
+ ///
+ ///
+ /// IEnumerable(string)
+ ///
+ [HttpGet]
+ [EnableCors(
+ // リソースへのアクセスを許可されている発生元
+ origins: "*",
+ // リソースによってサポートされているヘッダー
+ headers: "*",
+ // リソースによってサポートされているメソッド
+ methods: "*",
+ //
+ SupportsCredentials = true)]
+ public IEnumerable WeatherForecasts(int startDateIndex)
+ {
+ var rng = new Random();
+ return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+ {
+ DateFormatted = DateTime.Now.AddDays(index + startDateIndex).ToString("d"),
+ TemperatureC = rng.Next(-20, 55),
+ Summary = Summaries[rng.Next(Summaries.Length)]
+ });
+ }
+
+ private static string[] Summaries = new[]
+ {
+ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ };
+
+ public class WeatherForecast
+ {
+ public string DateFormatted { get; set; }
+ public int TemperatureC { get; set; }
+ public string Summary { get; set; }
+
+ public int TemperatureF
+ {
+ get
+ {
+ return 32 + (int)(TemperatureC / 0.5556);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/ValuesController.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/ValuesController.cs
new file mode 100644
index 000000000..cbb9067a7
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Controllers/ValuesController.cs
@@ -0,0 +1,40 @@
+//**********************************************************************************
+//* テスト・コントローラー
+//**********************************************************************************
+
+// テスト・コントローラーなので、必要に応じて流用 or 削除して下さい。
+
+//**********************************************************************************
+//* クラス名 :ValuesController
+//* クラス日本語名 :疎通確認用
+//*
+//* 作成日時 :-
+//* 作成者 :生技
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2018/09/07 西野 大介 新規作成
+//**********************************************************************************
+
+using System.Collections.Generic;
+using System.Web.Http;
+
+namespace ASPNETWebService.Controllers
+{
+ [RoutePrefix("api/values")]
+ public class ValuesController : ApiController
+ {
+ ///
+ /// GET api/values/get
+ ///
+ ///
+ /// IEnumerable(string)
+ ///
+ [HttpGet]
+ public IEnumerable Get()
+ {
+ return new string[] { "value1", "value2" };
+ }
+ }
+}
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Business/SuppliersLayerB.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Business/SuppliersLayerB.cs
new file mode 100644
index 000000000..7a643ce96
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Business/SuppliersLayerB.cs
@@ -0,0 +1,370 @@
+#region Apache License
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+//**********************************************************************************
+//* クラス名 :SuppliersLayerB
+//* クラス日本語名 :Suppliers の業務処理
+//*
+//* 作成者 :玄人 幸道
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2026/08/20 玄人 幸道 新規作成(#570)
+//**********************************************************************************
+
+using System;
+using System.Data;
+
+using ASPNETWebService.Logic.Common;
+
+using Touryo.Infrastructure.Business.Business;
+using Touryo.Infrastructure.Framework.Exceptions;
+
+namespace ASPNETWebService.Logic.Business
+{
+ /// Suppliers の業務処理
+ ///
+ /// UOC_〈methodName〉 はレイトバインドで呼ばれる(引数 1 つ・戻り値 void)。
+ /// 戻り値は this.ReturnValue で返す(メソッド冒頭で設定する=例外時にも戻るようにするため)。
+ /// トランザクションのコミット/ロールバックはフレームワークが行うので、ここには書かない。
+ ///
+ public class SuppliersLayerB : MyFcBaseLogic
+ {
+ /// 楽観排他の対象にする列
+ ///
+ /// **HomePage を含めない。**
+ /// HomePage は ntext で、SQL Server では "=" で比較できない
+ /// (Msg 402: データ型 ntext と nvarchar は equal to 演算子では互換性がありません)。
+ ///
+ /// D3_Update の WHERE は列ごとの <IF> で組まれており、
+ /// **パラメタを設定しなければ、その <IF> ごと消える**(BaseDam の仕様)。
+ /// つまり、ここに挙げた列だけが WHERE に載る。
+ ///
+ /// **HomePage だけを他者が更新した場合は検知できない。** ntext の制約による割り切り。
+ ///
+ private static readonly string[] OptimisticLockColumns = new string[]
+ {
+ "SupplierID", "CompanyName", "ContactName", "ContactTitle", "Address",
+ "City", "Region", "PostalCode", "Country", "Phone", "Fax"
+ };
+
+ /// 更新する列(SET 句)
+ /// HomePage は "=" で比較できないだけで、**更新はできる。**
+ private static readonly string[] UpdateColumns = new string[]
+ {
+ "CompanyName", "ContactName", "ContactTitle", "Address",
+ "City", "Region", "PostalCode", "Country", "Phone", "Fax", "HomePage"
+ };
+
+ #region 件数確認
+
+ /// Suppliers のデータ件数を取得する
+ /// 引数クラス
+ private void UOC_SelectCount(SuppliersParameterValue parameterValue)
+ {
+ // 戻り値クラスは業務処理の前に設定する(例外時にも戻り値を返すため)
+ SuppliersReturnValue returnValue = new SuppliersReturnValue();
+ this.ReturnValue = returnValue;
+
+ // ↓業務処理-----------------------------------------------------
+
+ DaoSuppliers dao = new DaoSuppliers(this.GetDam());
+
+ // 条件を設定しなければ、WHERE 句ごと消える(全件が対象になる)。
+ returnValue.Count = Convert.ToInt32(dao.D5_SelCnt());
+
+ // ↑業務処理-----------------------------------------------------
+ }
+
+ #endregion
+
+ #region 一覧取得
+
+ /// Suppliers の一覧を取得する
+ /// 引数クラス
+ ///
+ /// **全列を取得する。** 楽観排他で Original と突き合わせるため、
+ /// 画面に出さない列も持ち帰る必要がある。
+ ///
+ private void UOC_SelectAll(SuppliersParameterValue parameterValue)
+ {
+ SuppliersReturnValue returnValue = new SuppliersReturnValue();
+ this.ReturnValue = returnValue;
+
+ // ↓業務処理-----------------------------------------------------
+
+ DaoSuppliers dao = new DaoSuppliers(this.GetDam());
+
+ DataTable dt = new DataTable("Suppliers");
+ dao.D2_Select(dt);
+
+ // **追加行(Added)を作れるようにする。**
+ // Fill はスキーマ(NOT NULL)も取り込むため、そのままだと dt.NewRow() の追加が
+ // NoNullAllowedException(列 'SupplierID' に nulls を使用することはできません)になる。
+ // SupplierID は IDENTITY = 実際の採番は DB 側なので、DataTable 上は
+ // 実データと衝突しない負値で仮採番しておく(INSERT には渡さない)。
+ DataColumn pk = dt.Columns["SupplierID"];
+ pk.AutoIncrement = true;
+ pk.AutoIncrementSeed = -1;
+ pk.AutoIncrementStep = -1;
+
+ // 主キーを持たせておく(行の特定・バッチ更新の前提)
+ dt.PrimaryKey = new DataColumn[] { pk };
+
+ returnValue.Suppliers = dt;
+
+ // ↑業務処理-----------------------------------------------------
+ }
+
+ #endregion
+
+ #region バッチ更新
+
+ /// Suppliers の明細をバッチ更新する(DataRowState で CUD を振り分ける)
+ /// 引数クラス
+ ///
+ /// **RowState と Original が復元されていることが前提。**
+ /// WebAPI 越しに来た場合は、DTTable.FromDataTable(dt, keepOriginal: true) で
+ /// 作られた DTTables を経由している必要がある(#567)。
+ ///
+ private void UOC_BatchUpdate(SuppliersParameterValue parameterValue)
+ {
+ SuppliersReturnValue returnValue = new SuppliersReturnValue();
+ this.ReturnValue = returnValue;
+
+ // ↓業務処理-----------------------------------------------------
+
+ DataTable dt = parameterValue.Suppliers;
+ if (dt == null)
+ {
+ throw new BusinessApplicationException(
+ "W0001", "更新対象がありません。先に一覧を取得して下さい。", "-");
+ }
+
+ DaoSuppliers dao = new DaoSuppliers(this.GetDam());
+
+ // **Deleted → Added の順に流す。**
+ // Added を先に流すと、まだ消えていない旧行と主キーが衝突しうる。
+ this.DeleteRows(dt, dao, returnValue);
+ this.InsertAndUpdateRows(dt, dao, returnValue);
+
+ // ↑業務処理-----------------------------------------------------
+ }
+
+ /// 削除行を流す
+ /// 対象
+ /// Dao
+ /// 戻り値クラス
+ ///
+ /// **削除は主キーのみで特定する(S4_Delete)。**
+ /// 削除は「消えていればよい」ので、Original の突き合わせは要らない。
+ ///
+ private void DeleteRows(DataTable dt, DaoSuppliers dao, SuppliersReturnValue returnValue)
+ {
+ foreach (DataRow dr in dt.Rows)
+ {
+ if (dr.RowState != DataRowState.Deleted) { continue; }
+
+ dao.ClearParametersFromHt();
+
+ // **削除行は現在値を持たない。** Original から読む。
+ dao.PK_SupplierID = dr["SupplierID", DataRowVersion.Original];
+
+ int deleted = dao.S4_Delete();
+ if (deleted == 0)
+ {
+ // 対象行が既に無い(他者が先に削除した)= 再取得すれば続行できるので業務例外
+ throw new BusinessApplicationException(
+ "W0002", "他のユーザによって削除されています。再取得してやり直して下さい。",
+ "SupplierID=" + dr["SupplierID", DataRowVersion.Original]);
+ }
+
+ returnValue.DeleteCount += deleted;
+ }
+ }
+
+ /// 追加行・更新行を流す
+ /// 対象
+ /// Dao
+ /// 戻り値クラス
+ private void InsertAndUpdateRows(DataTable dt, DaoSuppliers dao, SuppliersReturnValue returnValue)
+ {
+ foreach (DataRow dr in dt.Rows)
+ {
+ switch (dr.RowState)
+ {
+ case DataRowState.Added:
+
+ dao.ClearParametersFromHt();
+
+ // **SupplierID は設定しない。** IDENTITY 列なので DB 側が採番する。
+ // D1_Insert は「設定した列だけ」を INSERT する(動的 SQL)。
+ foreach (string col in SuppliersLayerB.UpdateColumns)
+ {
+ SuppliersLayerB.SetInsertValue(dao, col, dr[col]);
+ }
+
+ returnValue.InsertCount += dao.D1_Insert();
+
+ break;
+
+ case DataRowState.Modified:
+
+ dao.ClearParametersFromHt();
+
+ // **WHERE = 取得時の値(Original)。** これが楽観排他になる。
+ // HomePage は設定しない(ntext。設定しなければ ごと消える)。
+ foreach (string col in SuppliersLayerB.OptimisticLockColumns)
+ {
+ SuppliersLayerB.SetWhereValue(dao, col, dr[col, DataRowVersion.Original]);
+ }
+
+ // SET = 変更後の値(Current)
+ foreach (string col in SuppliersLayerB.UpdateColumns)
+ {
+ SuppliersLayerB.SetUpdateValue(dao, col, dr[col]);
+ }
+
+ int updated = dao.D3_Update();
+ if (updated == 0)
+ {
+ // **更新件数 0 = 取得時から変わっている**(他者が先に更新/削除した)
+ throw new BusinessApplicationException(
+ "W0002", "他のユーザによって更新されています。再取得してやり直して下さい。",
+ "SupplierID=" + dr["SupplierID", DataRowVersion.Original]);
+ }
+
+ returnValue.UpdateCount += updated;
+
+ break;
+ }
+ }
+ }
+
+ #endregion
+
+ #region ユーティリティ
+
+ /// WHERE 句のパラメタを設定する
+ /// Dao
+ /// 列名
+ /// 値
+ ///
+ /// **null(DBNull)でも設定する。**
+ /// 設定しなければ <IF> ごと消えて比較されなくなるが、
+ /// 設定して null なら <ELSE> の IS NULL に落ちる。
+ /// Region / PostalCode / Fax は NULL を含むため、ここが効く。
+ ///
+ private static void SetWhereValue(DaoSuppliers dao, string column, object value)
+ {
+ // **NULL は null で渡す。DBNull ではない。**
+ // BaseDam の判定は obj == null で、DBNull.Value は null ではないため、
+ // DBNull を渡すと 側(= @X)が採られ、SQL 上 `= NULL` になって
+ // **決して一致しない**(Region / Fax が NULL の行が必ず弾かれる)。
+ object v = SuppliersLayerB.ToWhereValue(value);
+
+ switch (column)
+ {
+ case "SupplierID": dao.PK_SupplierID = v; break;
+ case "CompanyName": dao.CompanyName = v; break;
+ case "ContactName": dao.ContactName = v; break;
+ case "ContactTitle": dao.ContactTitle = v; break;
+ case "Address": dao.Address = v; break;
+ case "City": dao.City = v; break;
+ case "Region": dao.Region = v; break;
+ case "PostalCode": dao.PostalCode = v; break;
+ case "Country": dao.Country = v; break;
+ case "Phone": dao.Phone = v; break;
+ case "Fax": dao.Fax = v; break;
+ }
+ }
+
+ /// SET 句のパラメタを設定する
+ /// Dao
+ /// 列名
+ /// 値
+ private static void SetUpdateValue(DaoSuppliers dao, string column, object value)
+ {
+ object v = SuppliersLayerB.ToDbValue(value);
+
+ switch (column)
+ {
+ case "CompanyName": dao.Set_CompanyName_forUPD = v; break;
+ case "ContactName": dao.Set_ContactName_forUPD = v; break;
+ case "ContactTitle": dao.Set_ContactTitle_forUPD = v; break;
+ case "Address": dao.Set_Address_forUPD = v; break;
+ case "City": dao.Set_City_forUPD = v; break;
+ case "Region": dao.Set_Region_forUPD = v; break;
+ case "PostalCode": dao.Set_PostalCode_forUPD = v; break;
+ case "Country": dao.Set_Country_forUPD = v; break;
+ case "Phone": dao.Set_Phone_forUPD = v; break;
+ case "Fax": dao.Set_Fax_forUPD = v; break;
+ case "HomePage": dao.Set_HomePage_forUPD = v; break;
+ }
+ }
+
+ /// INSERT のパラメタを設定する
+ /// Dao
+ /// 列名
+ /// 値
+ private static void SetInsertValue(DaoSuppliers dao, string column, object value)
+ {
+ object v = SuppliersLayerB.ToDbValue(value);
+
+ switch (column)
+ {
+ case "CompanyName": dao.CompanyName = v; break;
+ case "ContactName": dao.ContactName = v; break;
+ case "ContactTitle": dao.ContactTitle = v; break;
+ case "Address": dao.Address = v; break;
+ case "City": dao.City = v; break;
+ case "Region": dao.Region = v; break;
+ case "PostalCode": dao.PostalCode = v; break;
+ case "Country": dao.Country = v; break;
+ case "Phone": dao.Phone = v; break;
+ case "Fax": dao.Fax = v; break;
+ case "HomePage": dao.HomePage = v; break;
+ }
+ }
+
+ /// WHERE 用に、NULL 相当を null にする
+ /// 列の値
+ /// WHERE へ渡す値
+ ///
+ /// **null を渡すと <ELSE>(IS NULL)に落ちる。**
+ /// DBNull を渡すと <IF>(= @X)が採られ、SQL 上 `= NULL` になって一致しない。
+ ///
+ private static object ToWhereValue(object value)
+ {
+ if (value == null || value == DBNull.Value) { return null; }
+ if (value is string && ((string)value).Length == 0) { return null; }
+ return value;
+ }
+
+ /// 空文字は NULL 相当(DBNull)として扱う
+ /// 列の値
+ /// DB へ渡す値
+ private static object ToDbValue(object value)
+ {
+ if (value == null || value == DBNull.Value) { return DBNull.Value; }
+ if (value is string && ((string)value).Length == 0) { return DBNull.Value; }
+ return value;
+ }
+
+ #endregion
+ }
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Common/SuppliersParameterValue.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Common/SuppliersParameterValue.cs
new file mode 100644
index 000000000..002620e51
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Common/SuppliersParameterValue.cs
@@ -0,0 +1,64 @@
+#region Apache License
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+//**********************************************************************************
+//* クラス名 :SuppliersParameterValue
+//* クラス日本語名 :Suppliers の引数クラス
+//*
+//* 作成者 :玄人 幸道
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2026/08/20 玄人 幸道 新規作成(#570)
+//**********************************************************************************
+
+using System.Data;
+
+using Touryo.Infrastructure.Business.Common;
+using Touryo.Infrastructure.Business.Util;
+
+namespace ASPNETWebService.Logic.Common
+{
+ /// Suppliers の引数クラス
+ public class SuppliersParameterValue : MyParameterValue
+ {
+ /// バッチ更新の対象
+ ///
+ /// **RowState と Original を持ったまま渡ってくる。**
+ /// WebAPI 越しでは DTTables を経由するため、
+ /// DTTable.FromDataTable(dt, keepOriginal: true) で作られている必要がある(#567)。
+ ///
+ public DataTable Suppliers { get; set; }
+
+ #region コンストラクタ
+
+ /// コンストラクタ
+ /// 画面ID
+ /// コントロールID
+ /// メソッド名
+ /// アクションタイプ
+ /// ユーザ情報
+ public SuppliersParameterValue(
+ string screenId, string controlId, string methodName, string actionType, MyUserInfo user)
+ : base(screenId, controlId, methodName, actionType, user)
+ {
+ // Baseのコンストラクタに引数を渡すために必要。
+ }
+
+ #endregion
+ }
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Common/SuppliersReturnValue.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Common/SuppliersReturnValue.cs
new file mode 100644
index 000000000..c6baacedf
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Common/SuppliersReturnValue.cs
@@ -0,0 +1,53 @@
+#region Apache License
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#endregion
+
+//**********************************************************************************
+//* クラス名 :SuppliersReturnValue
+//* クラス日本語名 :Suppliers の戻り値クラス
+//*
+//* 作成者 :玄人 幸道
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 2026/08/20 玄人 幸道 新規作成(#570)
+//**********************************************************************************
+
+using System.Data;
+
+using Touryo.Infrastructure.Business.Common;
+
+namespace ASPNETWebService.Logic.Common
+{
+ /// Suppliers の戻り値クラス
+ public class SuppliersReturnValue : MyReturnValue
+ {
+ /// 件数
+ public int Count;
+
+ /// 一覧
+ public DataTable Suppliers;
+
+ /// 追加した件数
+ public int InsertCount;
+
+ /// 更新した件数
+ public int UpdateCount;
+
+ /// 削除した件数
+ public int DeleteCount;
+ }
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Dao/DaoSuppliers.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Dao/DaoSuppliers.cs
new file mode 100644
index 000000000..f0e4770f8
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Logic/Dao/DaoSuppliers.cs
@@ -0,0 +1,802 @@
+//**********************************************************************************
+//* フレームワーク・テストクラス(D層)
+//**********************************************************************************
+
+// テスト用サンプルなので、必要に応じて流用 or 削除して下さい。
+
+//**********************************************************************************
+//* クラス名 :DaoSuppliers
+//* クラス日本語名 :自動生成Daoクラス
+//*
+//* 作成日時 :2014/2/9
+//* 作成者 :棟梁 D層自動生成ツール(墨壺), 日立 太郎
+//* 更新履歴 :
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 20xx/xx/xx XX XX XXXX
+//* 2012/06/14 西野 大介 ResourceLoaderに加え、EmbeddedResourceLoaderに対応
+//* 2013/09/09 西野 大介 ExecGenerateSQLメソッドを追加した(バッチ更新用)。
+//**********************************************************************************
+
+using System.Data;
+using System.Collections;
+
+using Touryo.Infrastructure.Business.Dao;
+using Touryo.Infrastructure.Public.Db;
+
+/// 自動生成Daoクラス
+public class DaoSuppliers : MyBaseDao
+{
+ #region インスタンス変数
+
+ /// ユーザ パラメタ(文字列置換)用ハッシュ テーブル
+ protected Hashtable HtUserParameter = new Hashtable();
+ /// パラメタ ライズド クエリのパラメタ用ハッシュ テーブル
+ protected Hashtable HtParameter = new Hashtable();
+
+ #endregion
+
+ #region コンストラクタ
+
+ /// コンストラクタ
+ public DaoSuppliers(BaseDam dam) : base(dam) { }
+
+ #endregion
+
+ #region 共通関数(パラメタの制御)
+
+ /// ユーザ パラメタ(文字列置換)をハッシュ テーブルに設定する。
+ /// ユーザ パラメタ名
+ /// ユーザ パラメタ値
+ public void SetUserParameteToHt(string userParamName, string userParamValue)
+ {
+ // ユーザ パラメタをハッシュ テーブルに設定
+ this.HtUserParameter[userParamName] = userParamValue;
+ }
+
+ /// パラメタ ライズド クエリのパラメタをハッシュ テーブルに設定する。
+ /// パラメタ名
+ /// パラメタ値
+ public void SetParameteToHt(string paramName, object paramValue)
+ {
+ // ユーザ パラメタをハッシュ テーブルに設定
+ this.HtParameter[paramName] = paramValue;
+ }
+
+ ///
+ /// ・ユーザ パラメタ(文字列置換)
+ /// ・パラメタ ライズド クエリのパラメタ
+ /// を格納するハッシュ テーブルをクリアする。
+ ///
+ public void ClearParametersFromHt()
+ {
+ // ユーザ パラメタ(文字列置換)用ハッシュ テーブルを初期化
+ this.HtUserParameter = new Hashtable();
+ // パラメタ ライズド クエリのパラメタ用ハッシュ テーブルを初期化
+ this.HtParameter = new Hashtable();
+ }
+
+ /// パラメタの設定(内部用)
+ protected void SetParametersFromHt()
+ {
+ // ユーザ パラメタ(文字列置換)を設定する。
+ foreach (string userParamName in this.HtUserParameter.Keys)
+ {
+ this.SetUserParameter(userParamName, this.HtUserParameter[userParamName].ToString());
+ }
+
+ // パラメタ ライズド クエリのパラメタを設定する。
+ foreach (string paramName in this.HtParameter.Keys)
+ {
+ this.SetParameter(paramName, this.HtParameter[paramName]);
+ }
+ }
+
+ #endregion
+
+ #region プロパティ プロシージャ(setter、getter)
+
+
+ /// SupplierID列(主キー列)に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object PK_SupplierID
+ {
+ set
+ {
+ this.HtParameter["SupplierID"] = value;
+ }
+ get
+ {
+ return this.HtParameter["SupplierID"];
+ }
+ }
+
+
+
+ /// CompanyName列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object CompanyName
+ {
+ set
+ {
+ this.HtParameter["CompanyName"] = value;
+ }
+ get
+ {
+ return this.HtParameter["CompanyName"];
+ }
+ }
+
+ /// ContactName列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object ContactName
+ {
+ set
+ {
+ this.HtParameter["ContactName"] = value;
+ }
+ get
+ {
+ return this.HtParameter["ContactName"];
+ }
+ }
+
+ /// ContactTitle列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object ContactTitle
+ {
+ set
+ {
+ this.HtParameter["ContactTitle"] = value;
+ }
+ get
+ {
+ return this.HtParameter["ContactTitle"];
+ }
+ }
+
+ /// Address列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object Address
+ {
+ set
+ {
+ this.HtParameter["Address"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Address"];
+ }
+ }
+
+ /// City列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object City
+ {
+ set
+ {
+ this.HtParameter["City"] = value;
+ }
+ get
+ {
+ return this.HtParameter["City"];
+ }
+ }
+
+ /// Region列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object Region
+ {
+ set
+ {
+ this.HtParameter["Region"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Region"];
+ }
+ }
+
+ /// PostalCode列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object PostalCode
+ {
+ set
+ {
+ this.HtParameter["PostalCode"] = value;
+ }
+ get
+ {
+ return this.HtParameter["PostalCode"];
+ }
+ }
+
+ /// Country列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object Country
+ {
+ set
+ {
+ this.HtParameter["Country"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Country"];
+ }
+ }
+
+ /// Phone列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object Phone
+ {
+ set
+ {
+ this.HtParameter["Phone"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Phone"];
+ }
+ }
+
+ /// Fax列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object Fax
+ {
+ set
+ {
+ this.HtParameter["Fax"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Fax"];
+ }
+ }
+
+ /// HomePage列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタを除く
+ public object HomePage
+ {
+ set
+ {
+ this.HtParameter["HomePage"] = value;
+ }
+ get
+ {
+ return this.HtParameter["HomePage"];
+ }
+ }
+
+
+ /// Set_SupplierID_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_SupplierID_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_SupplierID_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_SupplierID_forUPD"];
+ }
+ }
+
+
+ /// Set_CompanyName_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_CompanyName_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_CompanyName_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_CompanyName_forUPD"];
+ }
+ }
+
+
+ /// Set_ContactName_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_ContactName_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_ContactName_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_ContactName_forUPD"];
+ }
+ }
+
+
+ /// Set_ContactTitle_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_ContactTitle_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_ContactTitle_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_ContactTitle_forUPD"];
+ }
+ }
+
+
+ /// Set_Address_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_Address_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_Address_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_Address_forUPD"];
+ }
+ }
+
+
+ /// Set_City_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_City_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_City_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_City_forUPD"];
+ }
+ }
+
+
+ /// Set_Region_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_Region_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_Region_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_Region_forUPD"];
+ }
+ }
+
+
+ /// Set_PostalCode_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_PostalCode_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_PostalCode_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_PostalCode_forUPD"];
+ }
+ }
+
+
+ /// Set_Country_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_Country_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_Country_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_Country_forUPD"];
+ }
+ }
+
+
+ /// Set_Phone_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_Phone_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_Phone_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_Phone_forUPD"];
+ }
+ }
+
+
+ /// Set_Fax_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_Fax_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_Fax_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_Fax_forUPD"];
+ }
+ }
+
+
+ /// Set_HomePage_forUPD列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 更新処理時のSET句で使用するパラメタ専用
+ public object Set_HomePage_forUPD
+ {
+ set
+ {
+ this.HtParameter["Set_HomePage_forUPD"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Set_HomePage_forUPD"];
+ }
+ }
+
+
+
+ /// SupplierID_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object SupplierID_Like
+ {
+ set
+ {
+ this.HtParameter["SupplierID_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["SupplierID_Like"];
+ }
+ }
+
+
+ /// CompanyName_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object CompanyName_Like
+ {
+ set
+ {
+ this.HtParameter["CompanyName_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["CompanyName_Like"];
+ }
+ }
+
+
+ /// ContactName_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object ContactName_Like
+ {
+ set
+ {
+ this.HtParameter["ContactName_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["ContactName_Like"];
+ }
+ }
+
+
+ /// ContactTitle_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object ContactTitle_Like
+ {
+ set
+ {
+ this.HtParameter["ContactTitle_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["ContactTitle_Like"];
+ }
+ }
+
+
+ /// Address_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object Address_Like
+ {
+ set
+ {
+ this.HtParameter["Address_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Address_Like"];
+ }
+ }
+
+
+ /// City_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object City_Like
+ {
+ set
+ {
+ this.HtParameter["City_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["City_Like"];
+ }
+ }
+
+
+ /// Region_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object Region_Like
+ {
+ set
+ {
+ this.HtParameter["Region_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Region_Like"];
+ }
+ }
+
+
+ /// PostalCode_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object PostalCode_Like
+ {
+ set
+ {
+ this.HtParameter["PostalCode_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["PostalCode_Like"];
+ }
+ }
+
+
+ /// Country_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object Country_Like
+ {
+ set
+ {
+ this.HtParameter["Country_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Country_Like"];
+ }
+ }
+
+
+ /// Phone_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object Phone_Like
+ {
+ set
+ {
+ this.HtParameter["Phone_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Phone_Like"];
+ }
+ }
+
+
+ /// Fax_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object Fax_Like
+ {
+ set
+ {
+ this.HtParameter["Fax_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["Fax_Like"];
+ }
+ }
+
+
+ /// HomePage_Like列に対するパラメタ ライズド クエリのパラメタを設定する。
+ /// 動的参照処理時のLIKE検索で使用するパラメタ専用
+ public object HomePage_Like
+ {
+ set
+ {
+ this.HtParameter["HomePage_Like"] = value;
+ }
+ get
+ {
+ return this.HtParameter["HomePage_Like"];
+ }
+ }
+
+
+ #endregion
+
+ #region クエリ メソッド
+
+ #region Insert
+
+ /// 1レコード挿入する。
+ /// 挿入された行の数
+ public int S1_Insert()
+ {
+ // ファイルからSQL(Insert)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_S1_Insert.sql");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(Insert)を実行し、戻り値を戻す。
+ return this.ExecInsUpDel_NonQuery();
+ }
+
+ /// 1レコード挿入する。
+ /// 挿入された行の数
+ /// パラメタで指定した列のみ挿入値が有効になる。
+ public int D1_Insert()
+ {
+ // ファイルからSQL(DynIns)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_D1_Insert.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(DynIns)を実行し、戻り値を戻す。
+ return this.ExecInsUpDel_NonQuery();
+ }
+
+ #endregion
+
+ #region Select
+
+ /// 主キーを指定し、1レコード参照する。
+ /// 結果を格納するDataTable
+ public void S2_Select(DataTable dt)
+ {
+ // ファイルからSQL(Select)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_S2_Select.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(Select)を実行し、戻り値を戻す。
+ this.ExecSelectFill_DT(dt);
+ }
+
+ /// 検索条件を指定し、結果セットを参照する。
+ /// 結果を格納するDataTable
+ public void D2_Select(DataTable dt)
+ {
+ // ファイルからSQL(DynSel)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_D2_Select.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(DynSel)を実行し、戻り値を戻す。
+ this.ExecSelectFill_DT(dt);
+ }
+
+ #endregion
+
+ #region Update
+
+ /// 主キーを指定し、1レコード更新する。
+ /// 更新された行の数
+ /// パラメタで指定した列のみ更新値が有効になる。
+ public int S3_Update()
+ {
+ // ファイルからSQL(Update)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_S3_Update.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(Update)を実行し、戻り値を戻す。
+ return this.ExecInsUpDel_NonQuery();
+ }
+
+ /// 任意の検索条件でデータを更新する。
+ /// 更新された行の数
+ /// パラメタで指定した列のみ更新値が有効になる。
+ public int D3_Update()
+ {
+ // ファイルからSQL(DynUpd)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_D3_Update.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(DynUpd)を実行し、戻り値を戻す。
+ return this.ExecInsUpDel_NonQuery();
+ }
+
+ #endregion
+
+ #region Delete
+
+ /// 主キーを指定し、1レコード削除する。
+ /// 削除された行の数
+ public int S4_Delete()
+ {
+ // ファイルからSQL(Delete)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_S4_Delete.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(Delete)を実行し、戻り値を戻す。
+ return this.ExecInsUpDel_NonQuery();
+ }
+
+ /// 任意の検索条件でデータを削除する。
+ /// 削除された行の数
+ public int D4_Delete()
+ {
+ // ファイルからSQL(DynDel)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_D4_Delete.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(DynDel)を実行し、戻り値を戻す。
+ return this.ExecInsUpDel_NonQuery();
+ }
+
+ #endregion
+
+ #region 拡張メソッド
+
+ /// テーブルのレコード件数を取得する
+ /// テーブルのレコード件数
+ public object D5_SelCnt()
+ {
+ // ファイルからSQL(DynSelCnt)を設定する。
+ this.SetSqlByFile2("DaoSuppliers_D5_SelCnt.xml");
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ // SQL(SELECT COUNT)を実行し、戻り値を戻す。
+ return this.ExecSelectScalar();
+ }
+
+ /// 静的SQLを生成する。
+ /// ファイル名
+ /// SQLユーティリティ
+ /// 生成した静的SQL
+ public string ExecGenerateSQL(string fileName, SQLUtility sqlUtil)
+ {
+ // ファイルからSQLを設定する。
+ this.SetSqlByFile2(fileName);
+
+ // パラメタの設定
+ this.SetParametersFromHt();
+
+ return base.ExecGenerateSQL(sqlUtil);
+ }
+
+ #endregion
+
+ #endregion
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Properties/AssemblyInfo.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..bcec23bad
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// アセンブリに関する一般情報は、以下の属性セットによって
+// 制御されます。アセンブリに関連付けられている情報を変更するには、
+// これらの属性値を変更します。
+[assembly: AssemblyTitle("ASPNETWebService")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ASPNETWebService")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible を false に設定すると、
+// COM コンポーネントがこのアセンブリ内のその型を認識できなくなります。
+// COM からこのアセンブリ内の型にアクセスする必要がある場合は、その型の ComVisible 属性を true に設定してください。
+[assembly: ComVisible(false)]
+
+// このプロジェクトが COM に公開される場合、次の GUID がタイプ ライブラリの ID になります。
+[assembly: Guid("c24bc2fa-d423-4f0f-b2b0-e647b621683d")]
+
+// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// すべての値を指定するか、下のように "*" を使ってリビジョンおよびビルド番号を
+// 既定値にすることができます:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Startup.cs b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Startup.cs
new file mode 100644
index 000000000..404a2e2a3
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Startup.cs
@@ -0,0 +1,65 @@
+//**********************************************************************************
+//* テンプレート
+//**********************************************************************************
+
+// サンプル中のテンプレートなので、必要に応じて使用して下さい。
+
+//**********************************************************************************
+//* クラス名 :OwinStartup
+//* クラス日本語名 :OwinStartup
+//*
+//* 作成日時 :-
+//* 作成者 :-
+//* 更新履歴 :-
+//*
+//* 日時 更新者 内容
+//* ---------- ---------------- -------------------------------------------------
+//* 20xx/xx/xx XX XX XXXX
+//**********************************************************************************
+
+using System.Web.Mvc;
+using System.Web.Http;
+using System.Net.Http;
+
+using Owin;
+using Microsoft.Owin;
+
+using Touryo.Infrastructure.Framework.Authentication;
+
+[assembly: OwinStartup(typeof(ASPNETWebService.Startup))]
+
+namespace ASPNETWebService
+{
+ public class Startup
+ {
+ /// Configuration
+ ///
+ public void Configuration(IAppBuilder app)
+ {
+ // アプリケーションの設定方法の詳細については、http://go.microsoft.com/fwlink/?LinkID=316888 を参照してください
+
+ // アプリケーションのスタートアップで実行するコードです
+
+ //
+ AreaRegistration.RegisterAllAreas();
+
+ //
+ WebApiConfig.Register(GlobalConfiguration.Configuration);
+
+ // グローバルフィルタの登録
+ FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
+
+ //// URLルーティングの登録
+ //RouteConfig.RegisterRoutes(RouteTable.Routes);
+
+ //// バンドル&ミニフィケーションの登録
+ //BundleConfig.RegisterBundles(BundleTable.Bundles);
+
+ //// 認証に関するOWINミドルウェアの設定を行う。
+ //StartupAuth.Configure(app);
+ OAuth2AndOIDCClient.HttpClient = new HttpClient(); // JwkSet取得用
+
+ GlobalConfiguration.Configuration.Initializer(GlobalConfiguration.Configuration);
+ }
+ }
+}
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.Debug.config b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.Debug.config
new file mode 100644
index 000000000..33653fb72
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.Debug.config
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.Release.config b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.Release.config
new file mode 100644
index 000000000..8818ed478
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.Release.config
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.config b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.config
new file mode 100644
index 000000000..584de81b2
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/Web.config
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/app.config b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/app.config
new file mode 100644
index 000000000..14c7895a0
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/app.config
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/packages.config b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/packages.config
new file mode 100644
index 000000000..2ffac8030
--- /dev/null
+++ b/root/programs/CS/Samples/WS_sample/ASPNETWebService/ASPNETWebService/packages.config
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/ASPNETWebService/README.md b/root/programs/CS/Samples/WS_sample/ASPNETWebService/README.md
deleted file mode 100644
index 00c5ea578..000000000
--- a/root/programs/CS/Samples/WS_sample/ASPNETWebService/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Moved to the following repository.
-
-- OpenTouryoProject/ResourceServerTemplates
-https://github.com/OpenTouryoProject/ResourceServerTemplates/tree/master/root/programs/ASPNETWebService
\ No newline at end of file
diff --git a/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWPF_sample/app.config b/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWPF_sample/app.config
index 22f09fbd2..ff988bbd1 100644
--- a/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWPF_sample/app.config
+++ b/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWPF_sample/app.config
@@ -110,10 +110,6 @@
-
-
-
-
diff --git a/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWinCone_sample/app.config b/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWinCone_sample/app.config
index 7e5d7bcfe..99258bb52 100644
--- a/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWinCone_sample/app.config
+++ b/root/programs/CS/Samples/WS_sample/WSClient_sample/WSClientWinCone_sample/app.config
@@ -94,7 +94,7 @@
-
+
diff --git a/root/programs/CS/Samples/WebApp_sample/MVC_Sample/MVC_Sample/MVC_Sample.csproj b/root/programs/CS/Samples/WebApp_sample/MVC_Sample/MVC_Sample/MVC_Sample.csproj
index a69202e32..f143ed0fa 100644
--- a/root/programs/CS/Samples/WebApp_sample/MVC_Sample/MVC_Sample/MVC_Sample.csproj
+++ b/root/programs/CS/Samples/WebApp_sample/MVC_Sample/MVC_Sample/MVC_Sample.csproj
@@ -44,66 +44,66 @@
4
-
- ..\packages\Azure.Core.1.46.2\lib\net472\Azure.Core.dll
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.5\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
-
- ..\packages\Azure.Identity.1.14.0\lib\netstandard2.0\Azure.Identity.dll
+
+ ..\packages\Microsoft.Bcl.Cryptography.10.0.5\lib\net462\Microsoft.Bcl.Cryptography.dll
-
- ..\packages\Microsoft.Bcl.AsyncInterfaces.9.0.6\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
+
+ ..\packages\Microsoft.Bcl.TimeProvider.10.0.5\lib\net462\Microsoft.Bcl.TimeProvider.dll
-
- ..\packages\Microsoft.Bcl.Cryptography.9.0.6\lib\net462\Microsoft.Bcl.Cryptography.dll
+
+
+ ..\packages\Microsoft.Data.SqlClient.7.0.0\lib\net462\Microsoft.Data.SqlClient.dll
-
- ..\packages\Microsoft.Bcl.TimeProvider.9.0.6\lib\net462\Microsoft.Bcl.TimeProvider.dll
+
+ ..\packages\System.Threading.Channels.10.0.5\lib\net462\System.Threading.Channels.dll
-
-
- ..\packages\Microsoft.Data.SqlClient.6.0.2\lib\net462\Microsoft.Data.SqlClient.dll
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
-
- ..\packages\Microsoft.Extensions.Caching.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
+
+ ..\packages\Microsoft.Data.SqlClient.Internal.Logging.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Internal.Logging.dll
-
- ..\packages\Microsoft.Extensions.Caching.Memory.9.0.6\lib\net462\Microsoft.Extensions.Caching.Memory.dll
+
+ ..\packages\Microsoft.Data.SqlClient.Extensions.Abstractions.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Extensions.Abstractions.dll
-
- ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+
+ ..\packages\Microsoft.Extensions.Caching.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
-
- ..\packages\Microsoft.Extensions.Logging.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
+
+ ..\packages\Microsoft.Extensions.Caching.Memory.10.0.5\lib\net462\Microsoft.Extensions.Caching.Memory.dll
-
- ..\packages\Microsoft.Extensions.Options.9.0.6\lib\net462\Microsoft.Extensions.Options.dll
+
+ ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
-
- ..\packages\Microsoft.Extensions.Primitives.9.0.6\lib\net462\Microsoft.Extensions.Primitives.dll
+
+ ..\packages\Microsoft.Extensions.Logging.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
-
- ..\packages\Microsoft.Identity.Client.4.72.1\lib\net472\Microsoft.Identity.Client.dll
+
+ ..\packages\Microsoft.Extensions.Options.10.0.5\lib\net462\Microsoft.Extensions.Options.dll
-
- ..\packages\Microsoft.Identity.Client.Extensions.Msal.4.72.1\lib\netstandard2.0\Microsoft.Identity.Client.Extensions.Msal.dll
+
+ ..\packages\Microsoft.Extensions.Primitives.10.0.5\lib\net462\Microsoft.Extensions.Primitives.dll
-
- ..\packages\Microsoft.IdentityModel.Abstractions.8.12.0\lib\net472\Microsoft.IdentityModel.Abstractions.dll
+
+ ..\packages\Microsoft.IdentityModel.Abstractions.8.17.0\lib\net472\Microsoft.IdentityModel.Abstractions.dll
-
- ..\packages\Microsoft.IdentityModel.JsonWebTokens.8.12.0\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
+
+ ..\packages\Microsoft.IdentityModel.JsonWebTokens.8.17.0\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
-
- ..\packages\Microsoft.IdentityModel.Logging.8.12.0\lib\net472\Microsoft.IdentityModel.Logging.dll
+
+ ..\packages\Microsoft.IdentityModel.Logging.8.17.0\lib\net472\Microsoft.IdentityModel.Logging.dll
-
- ..\packages\Microsoft.IdentityModel.Protocols.8.12.0\lib\net472\Microsoft.IdentityModel.Protocols.dll
+
+ ..\packages\Microsoft.IdentityModel.Protocols.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.dll
-
- ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.8.12.0\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
+
+ ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
-
- ..\packages\Microsoft.IdentityModel.Tokens.8.12.0\lib\net472\Microsoft.IdentityModel.Tokens.dll
+
+ ..\packages\Microsoft.IdentityModel.Tokens.8.17.0\lib\net472\Microsoft.IdentityModel.Tokens.dll
..\packages\Microsoft.Owin.4.2.2\lib\net45\Microsoft.Owin.dll
@@ -145,33 +145,24 @@
..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll
-
- ..\packages\System.ClientModel.1.4.2\lib\netstandard2.0\System.ClientModel.dll
-
-
- ..\packages\System.Diagnostics.DiagnosticSource.9.0.6\lib\net462\System.Diagnostics.DiagnosticSource.dll
+
+ ..\packages\System.Diagnostics.DiagnosticSource.10.0.5\lib\net462\System.Diagnostics.DiagnosticSource.dll
-
- ..\packages\System.Formats.Asn1.9.0.6\lib\net462\System.Formats.Asn1.dll
+
+ ..\packages\System.Formats.Asn1.10.0.5\lib\net462\System.Formats.Asn1.dll
-
- ..\packages\System.IdentityModel.Tokens.Jwt.8.12.0\lib\net472\System.IdentityModel.Tokens.Jwt.dll
-
-
- ..\packages\System.IO.FileSystem.AccessControl.5.0.0\lib\net461\System.IO.FileSystem.AccessControl.dll
+
+ ..\packages\System.IdentityModel.Tokens.Jwt.8.17.0\lib\net472\System.IdentityModel.Tokens.Jwt.dll
-
- ..\packages\System.IO.Pipelines.9.0.6\lib\net462\System.IO.Pipelines.dll
+
+ ..\packages\System.IO.Pipelines.10.0.5\lib\net462\System.IO.Pipelines.dll
..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll
-
- ..\packages\System.Memory.Data.9.0.6\lib\net462\System.Memory.Data.dll
-
..\packages\Microsoft.AspNet.WebApi.Client.6.0.0\lib\net45\System.Net.Http.Formatting.dll
@@ -183,23 +174,14 @@
..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll
-
- ..\packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll
-
-
- ..\packages\System.Security.Cryptography.Pkcs.9.0.6\lib\net462\System.Security.Cryptography.Pkcs.dll
-
-
- ..\packages\System.Security.Cryptography.ProtectedData.9.0.6\lib\net462\System.Security.Cryptography.ProtectedData.dll
-
-
- ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll
+
+ ..\packages\System.Security.Cryptography.Pkcs.10.0.5\lib\net462\System.Security.Cryptography.Pkcs.dll
-
- ..\packages\System.Text.Encodings.Web.9.0.6\lib\net462\System.Text.Encodings.Web.dll
+
+ ..\packages\System.Text.Encodings.Web.10.0.5\lib\net462\System.Text.Encodings.Web.dll
-
- ..\packages\System.Text.Json.9.0.6\lib\net462\System.Text.Json.dll
+
+ ..\packages\System.Text.Json.10.0.5\lib\net462\System.Text.Json.dll
..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll
@@ -256,7 +238,7 @@
..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll
- ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll
+ ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll
True
@@ -432,9 +414,9 @@
このプロジェクトは、このコンピューター上にない NuGet パッケージを参照しています。それらのパッケージをダウンロードするには、[NuGet パッケージの復元] を使用します。詳細については、http://go.microsoft.com/fwlink/?LinkID=322105 を参照してください。見つからないファイルは {0} です。
-
+
-
+
",
+ "FxSqlTraceLog": "on",
+ // D層のSQL文キャッシュ機能のon・off
+ // 開発フェーズのことを考慮して、デフォルトoffに設定",
+ "FxSqlCacheSwitch": "off",
+ // D層のSQLロード時のエンコーディングを指定(shift_jis、utf-8.etc)
+ "FxSqlEncoding": "utf-8",
+ // D層のコマンド タイムアウト値を指定(秒)
+ "FxSqlCommandTimeout": "30",
+
+ // 共通部品の使用するパラメータ - end
+
+ // アプリケーションの使用するパラメータ - start
+
+ // OAuth2, OIDC認証
+ "JwkSetUri": "https://localhost:44300/MultiPurposeAuthSite/jwkcerts/",
+ "SpRp_RsaCerFilePath": "C:/root/files/resource/X509/SHA256RSA_Server.cer",
+ "SpRp_Isser": "https://ssoauth.opentouryo.com",
+ "OAuth2AndOidcClientIDs": [
+ "67d328bfe8604aae83fb15fa44780d8b",
+ "f53469c17c5a432f86ce563b7805ab89",
+ "b6b393fe861b430eb4ee061006826b03",
+ "f374a155909d486a9234693c34e94479"
+ ],
+
+ // SQLファイルファイル(フォルダ)へのパス
+ "SqlTextFilePath": "C:/root/files/resource/Sql"
+
+ // アプリケーションの使用するパラメータ - end
+ }
+}
diff --git a/root/programs/CS/Samples4NetCore/Backend/ASPNETWebService/README.md b/root/programs/CS/Samples4NetCore/Backend/ASPNETWebService/README.md
deleted file mode 100644
index 4667ebe8c..000000000
--- a/root/programs/CS/Samples4NetCore/Backend/ASPNETWebService/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Moved to the following repository.
-
-- OpenTouryoProject/ResourceServerTemplates
-https://github.com/OpenTouryoProject/ResourceServerTemplates/tree/master/root/programs/ASPNETWebServiceCore
\ No newline at end of file
diff --git a/root/programs/CS/Samples4NetCore/Backend/MVC_Sample/MVC_Sample/MVC_Sample.csproj b/root/programs/CS/Samples4NetCore/Backend/MVC_Sample/MVC_Sample/MVC_Sample.csproj
index fe2f7d83d..cf1dbe92b 100644
--- a/root/programs/CS/Samples4NetCore/Backend/MVC_Sample/MVC_Sample/MVC_Sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Backend/MVC_Sample/MVC_Sample/MVC_Sample.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWPF_sample/2CSClientWPF_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWPF_sample/2CSClientWPF_sample.csproj
index ea085d287..fc2aae711 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWPF_sample/2CSClientWPF_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWPF_sample/2CSClientWPF_sample.csproj
@@ -18,11 +18,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWin_sample/2CSClientWin_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWin_sample/2CSClientWin_sample.csproj
index 3a06822d3..7aa65a850 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWin_sample/2CSClientWin_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/2CSClientWin_sample/2CSClientWin_sample.csproj
@@ -20,11 +20,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/CustCtrl_sample/CustCtrl_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/CustCtrl_sample/CustCtrl_sample.csproj
index 6ea183d51..ddd2132db 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/CustCtrl_sample/CustCtrl_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/CustCtrl_sample/CustCtrl_sample.csproj
@@ -12,10 +12,10 @@
-
-
-
-
+
+
+
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/GenDaoAndBatUpd_sample/GenDaoAndBatUpd_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/GenDaoAndBatUpd_sample/GenDaoAndBatUpd_sample.csproj
index 56353541b..e34243be1 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/GenDaoAndBatUpd_sample/GenDaoAndBatUpd_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/GenDaoAndBatUpd_sample/GenDaoAndBatUpd_sample.csproj
@@ -131,11 +131,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/TimeStamp_sample/TimeStamp_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/TimeStamp_sample/TimeStamp_sample.csproj
index b42243277..bb2155c92 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/TimeStamp_sample/TimeStamp_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/2CS_sample/TimeStamp_sample/TimeStamp_sample.csproj
@@ -234,11 +234,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample/RerunnableBatch_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample/RerunnableBatch_sample.csproj
index ea6eee18b..9bb6d7c4d 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample/RerunnableBatch_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample/RerunnableBatch_sample.csproj
@@ -17,15 +17,15 @@
-
-
-
-
-
+
+
+
+
+
-
+
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample2/RerunnableBatch_sample2.csproj b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample2/RerunnableBatch_sample2.csproj
index ea6eee18b..9bb6d7c4d 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample2/RerunnableBatch_sample2.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample2/RerunnableBatch_sample2.csproj
@@ -17,15 +17,15 @@
-
-
-
-
-
+
+
+
+
+
-
+
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample3/RerunnableBatch_sample3.csproj b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample3/RerunnableBatch_sample3.csproj
index ea6eee18b..9bb6d7c4d 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample3/RerunnableBatch_sample3.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/RerunnableBatch_sample3/RerunnableBatch_sample3.csproj
@@ -17,15 +17,15 @@
-
-
-
-
-
+
+
+
+
+
-
+
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/SimpleBatch_sample/SimpleBatch_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/SimpleBatch_sample/SimpleBatch_sample.csproj
index 942929f1d..f28fb3247 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/SimpleBatch_sample/SimpleBatch_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/Bat_sample/SimpleBatch_sample/SimpleBatch_sample.csproj
@@ -16,15 +16,15 @@
-
-
-
-
-
+
+
+
+
+
-
+
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/DAG_Login_CLI/DAG_Login_CLI/DAG_Login_CLI.csproj b/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/DAG_Login_CLI/DAG_Login_CLI/DAG_Login_CLI.csproj
index 30195c712..6ccf0ea77 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/DAG_Login_CLI/DAG_Login_CLI/DAG_Login_CLI.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/DAG_Login_CLI/DAG_Login_CLI/DAG_Login_CLI.csproj
@@ -6,10 +6,10 @@
-
-
-
-
+
+
+
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/LIR_Login_CLI/LIR_Login_CLI/LIR_Login_CLI.csproj b/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/LIR_Login_CLI/LIR_Login_CLI/LIR_Login_CLI.csproj
index ce653d485..bf7a1d1a6 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/LIR_Login_CLI/LIR_Login_CLI/LIR_Login_CLI.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/LIR_Login_CLI/LIR_Login_CLI/LIR_Login_CLI.csproj
@@ -6,11 +6,11 @@
-
-
-
+
+
+
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/Simple_CLI/Simple_CLI/Simple_CLI.csproj b/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/Simple_CLI/Simple_CLI/Simple_CLI.csproj
index 019f7135a..fb259ba14 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/Simple_CLI/Simple_CLI/Simple_CLI.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/CLI_sample/Simple_CLI/Simple_CLI/Simple_CLI.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWPF_sample/WSClientWPF_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWPF_sample/WSClientWPF_sample.csproj
index c0d117164..9d2f7b05c 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWPF_sample/WSClientWPF_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWPF_sample/WSClientWPF_sample.csproj
@@ -41,14 +41,14 @@
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin2_sample/WSClientWin2_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin2_sample/WSClientWin2_sample.csproj
index ab35da905..fbae484f8 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin2_sample/WSClientWin2_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin2_sample/WSClientWin2_sample.csproj
@@ -22,10 +22,10 @@
-
-
-
-
+
+
+
+
diff --git a/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.csproj b/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.csproj
index fffe74349..0590c77ce 100644
--- a/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.csproj
+++ b/root/programs/CS/Samples4NetCore/Legacy/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.csproj
@@ -43,14 +43,14 @@
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/root/programs/CS/y_Build_TestWebAPIClient.bat b/root/programs/CS/y_Build_TestWebAPIClient.bat
new file mode 100644
index 000000000..e18580d00
--- /dev/null
+++ b/root/programs/CS/y_Build_TestWebAPIClient.bat
@@ -0,0 +1,27 @@
+setlocal
+
+@rem --------------------------------------------------
+@rem Turn off the echo function.
+@rem --------------------------------------------------
+@echo off
+
+@rem --------------------------------------------------
+@rem Get the path to the executable file.
+@rem --------------------------------------------------
+set CURRENT_DIR="%~dp0"
+
+@rem --------------------------------------------------
+@rem Execution of the common processing.
+@rem --------------------------------------------------
+call %CURRENT_DIR%z_Common.bat
+
+@rem --------------------------------------------------
+@rem Batch build of TestTransmission.
+@rem --------------------------------------------------
+..\nuget.exe restore "Frameworks\Tests\TestWebAPIClient\TestWebAPIClientFx48.sln" %NUGET_MSBUILD%
+%BUILDFILEPATH% %COMMANDLINE% "Frameworks\Tests\TestWebAPIClient\TestWebAPIClientFx48.sln"
+
+pause
+
+rem -------------------------------------------------------
+endlocal
diff --git a/root/programs/ComparePackage.ps1 b/root/programs/ComparePackage.ps1
new file mode 100644
index 000000000..633d789cc
--- /dev/null
+++ b/root/programs/ComparePackage.ps1
@@ -0,0 +1,355 @@
+<#
+.SYNOPSIS
+ packages.config の版と、csproj / vbproj に書かれた版が揃っているかを突き合わせる。
+
+.DESCRIPTION
+ **パッケージの版は 4 か所に散らばっている。**(#566 / #568 で全部踏んだ)
+
+ | | 場所 | 検査 |
+ |---|-----------------------------------------------|-------------------|
+ | ① | packages.config の version= | 本スクリプト(基準)|
+ | ② | csproj の packages\X.Y\ というパス表記 | **本スクリプト** |
+ | ③ | csproj の | **本スクリプト** |
+ | ④ | *.config の bindingRedirect | CompareRedirect.ps1(#556)|
+
+ ①を基準に、②③がそこから外れていないかを見る。
+
+ <②がずれると>
+
+ **復元済みでも「パッケージが無い」と言われる。**
+
+ error : このプロジェクトは、このコンピューター上にない NuGet パッケージを参照しています。
+ 見つからないファイルは ..\packages\System.ValueTuple.4.6.2\build\net471\
+ System.ValueTuple.targets です。
+
+ **HintPath だけを見ても足りない。**
+ Import Project と Error Condition にも版が入るため、
+ `packages\<フォルダ>\` を**全部拾って**突き合わせる。
+
+ <③がずれると>
+
+ **参照が落ちて、bin に配られない。**
+
+ に強い名前(, Version=…)を書くと、
+ SpecificVersion は既定で true になる。宣言と実体がずれると、
+ MSBuild は**警告だけ出して参照を落とす。ビルドは成功する。**
+
+ #566 では Microsoft.Data.SqlClient.dll が bin に配られず、
+ 実行時に FileNotFoundException になった。**0_RunAll.ps1 は通っていた。**
+
+ <③は「パッケージの版」ではない>
+
+ **アセンブリの版である。計算で導いてはいけない。**
+
+ パッケージ 10.0.5 → アセンブリ 10.0.0.5
+ パッケージ 8.17.0 → アセンブリ 8.17.0.0
+ パッケージ 13.0.4 → アセンブリ 13.0.0.0 ← 版が変わっても、ここは動かない
+
+ HintPath が指す DLL を読んで測る。
+
+ <そのプロジェクトの HintPath だけを見る>
+
+ **リポジトリ全体から名前で引いてはいけない。**(#556 と同じ理由)
+ Microsoft.Owin が 4.2.2.0 と 4.2.3.0 に割れているため、
+ 全体から引くと、どちらが正かを決められない。
+
+ <復元してから実行すること>
+
+ ②は復元前でも判定できる(テキストどうしの突き合わせ)。
+ **③は DLL を読むので、復元していないと「判定不能」になる。**
+
+ 0_RunAll.ps1 に組み込むなら 1_BuildAll.ps1 の後。
+
+ **「判定不能」は「問題なし」ではない。** 材料が無いだけである。
+ 不一致には数えないが、件数は必ず出す。
+
+ <対象外>
+
+ **CS/NuGet/proj 配下は見ない。** NuGet パッケージの検証用で、
+ 1_BuildAll.ps1 が建てないため、必ず「判定不能」になる。
+ CompareRedirect.ps1 と同じ扱い(#557)。
+
+ **商用パッケージは復元されない。** DamDB2 の IBM.Data.DB2 系は
+ nuget.org に無いため、③は常に「判定不能」になる。これは正しい。
+
+.PARAMETER Detail
+ 「判定不能」の内訳も表示する。
+
+.PARAMETER Only
+ 対象を相対パスの部分一致で絞る(例: -Only "ASPNETWebService")。
+
+.PARAMETER Check
+ **合否を返す。** 不一致が 1 件でもあれば終了コード 1 を返す。
+ 「判定不能」では落とさない。
+
+.EXAMPLE
+ .\ComparePackage.ps1
+
+.EXAMPLE
+ .\ComparePackage.ps1 -Detail -Only "VB"
+
+.EXAMPLE
+ .\ComparePackage.ps1 -Check
+#>
+[CmdletBinding()]
+param(
+ [switch]$Detail,
+ [string]$Only,
+ [switch]$Check
+)
+
+$ErrorActionPreference = "Continue"
+
+$root = $PSScriptRoot
+. (Join-Path $root "SummaryTable.ps1")
+
+if ([Console]::OutputEncoding.CodePage -ne 65001)
+{
+ [Console]::OutputEncoding = New-Object Text.UTF8Encoding $false
+}
+
+# ------------------------------------------------------------------
+# packages.config から、宣言された版を読む
+# ------------------------------------------------------------------
+function Get-DeclaredPackages([string]$path)
+{
+ $map = @{}
+ try { [xml]$x = Get-Content $path -Raw -EA Stop } catch { return $map }
+
+ foreach ($p in $x.SelectNodes("//*[local-name()='package']"))
+ {
+ $id = $p.GetAttribute("id")
+ $ver = $p.GetAttribute("version")
+ if ($id -and $ver) { $map[$id] = $ver }
+ }
+ return $map
+}
+
+# ------------------------------------------------------------------
+# packages\<フォルダ>\ から、パッケージ ID を割り出す
+# ------------------------------------------------------------------
+# フォルダ名は「.」で、どちらもドットを含むため、
+# **右から削りながら、宣言に在る ID を探す。**
+function Resolve-PackageId([string]$folder, [hashtable]$declared)
+{
+ $id = $folder
+ while ($id -and -not $declared.ContainsKey($id) -and $id.Contains("."))
+ {
+ $id = $id.Substring(0, $id.LastIndexOf("."))
+ }
+ if ($declared.ContainsKey($id)) { return $id }
+ return $null
+}
+
+# ------------------------------------------------------------------
+# 対象(追跡下の packages.config と、同じフォルダの csproj / vbproj)
+# ------------------------------------------------------------------
+$tracked = & git -C $root ls-files
+if ($LASTEXITCODE -ne 0 -or -not $tracked)
+{
+ Write-Host " git ls-files が使えないため、中止する。" -ForegroundColor Red
+ exit 0
+}
+
+$rows = @()
+$mismatch = @()
+$unknown = @()
+$total = 0
+$okCount = 0
+
+foreach ($t in $tracked)
+{
+ if ($t -notmatch '/packages\.config$') { continue }
+ if ($t -match '/packages/') { continue }
+
+ # 通しビルドの対象外は見ない(#557。CompareRedirect.ps1 と同じ)
+ if ($t -match '/NuGet/proj/') { continue }
+
+ if ($Only -and ($t -notlike "*$Only*")) { continue }
+
+ $full = Join-Path $root ($t -replace '/', '\')
+ if (-not (Test-Path $full)) { continue }
+
+ $declared = Get-DeclaredPackages $full
+ if ($declared.Count -eq 0) { continue }
+
+ $rel = $t -replace '^root/programs/', ''
+ $dir = Split-Path $full -Parent
+
+ $projs = @(Get-ChildItem $dir -File -EA SilentlyContinue |
+ Where-Object { $_.Extension -in @(".csproj", ".vbproj") })
+ if ($projs.Count -eq 0) { continue }
+
+ $ok = 0; $ng = 0; $unk = 0
+
+ foreach ($proj in $projs)
+ {
+ $text = Get-Content $proj.FullName -Raw -EA SilentlyContinue
+ if (-not $text) { continue }
+
+ # ---------- ② packages\<フォルダ>\ ----------
+ # HintPath / Import Project / Error Condition の別を問わず、全部拾う。
+ $folders = [regex]::Matches($text, 'packages\\([^\\]+)\\') |
+ ForEach-Object { $_.Groups[1].Value } |
+ Sort-Object -Unique
+
+ foreach ($folder in $folders)
+ {
+ $id = Resolve-PackageId $folder $declared
+ if (-not $id) { continue }
+
+ $total++
+ $want = "$id.$($declared[$id])"
+
+ if ($folder -eq $want)
+ {
+ $ok++
+ }
+ else
+ {
+ $ng++
+ $mismatch += [PSCustomObject]@{
+ 対象 = $rel
+ 種類 = "パス表記"
+ 名前 = $id
+ 宣言 = $want
+ 記述 = $folder
+ }
+ }
+ }
+
+ # ---------- ③ ----------
+ $refs = [regex]::Matches($text,
+ '(.*?)',
+ [Text.RegularExpressions.RegexOptions]::Singleline)
+
+ foreach ($m in $refs)
+ {
+ $name = $m.Groups[1].Value
+ $rest = $m.Groups[2].Value
+ $body = $m.Groups[3].Value
+
+ $vm = [regex]::Match($rest, 'Version=([\d\.]+)')
+ if (-not $vm.Success) { continue }
+
+ $hm = [regex]::Match($body, '(.*?)',
+ [Text.RegularExpressions.RegexOptions]::Singleline)
+ if (-not $hm.Success) { continue }
+
+ $total++
+
+ # **そのプロジェクトの HintPath が指す DLL だけを測る。**
+ $dll = Join-Path $proj.DirectoryName ($hm.Groups[1].Value.Trim())
+ try { $dll = [IO.Path]::GetFullPath($dll) } catch { }
+
+ if (-not (Test-Path $dll))
+ {
+ $unk++
+ $unknown += [PSCustomObject]@{
+ 対象 = $rel; 名前 = $name; 宣言 = $vm.Groups[1].Value
+ }
+ continue
+ }
+
+ try { $actual = [Reflection.AssemblyName]::GetAssemblyName($dll).Version.ToString() }
+ catch
+ {
+ $unk++
+ $unknown += [PSCustomObject]@{
+ 対象 = $rel; 名前 = $name; 宣言 = $vm.Groups[1].Value
+ }
+ continue
+ }
+
+ if ($vm.Groups[1].Value -eq $actual)
+ {
+ $ok++
+ }
+ else
+ {
+ $ng++
+ $mismatch += [PSCustomObject]@{
+ 対象 = $rel
+ 種類 = "Reference"
+ 名前 = $name
+ 宣言 = $vm.Groups[1].Value
+ 記述 = "実体 $actual"
+ }
+ }
+ }
+ }
+
+ $okCount += $ok
+
+ $note = "一致 $ok"
+ if ($ng -gt 0) { $note += " / **不一致 $ng**" }
+ if ($unk -gt 0) { $note += " / 判定不能 $unk" }
+
+ $rows += [PSCustomObject]@{
+ 対象 = $rel
+ 結果 = $(if ($ng -gt 0) { "不一致" } elseif ($ok -eq 0) { "不明" } else { "OK" })
+ 内容 = $note
+ }
+}
+
+# ------------------------------------------------------------------
+# 出力
+# ------------------------------------------------------------------
+Write-Host ""
+Write-Host "================ パッケージの版 ================"
+Write-SummaryTable -Rows $rows -Columns @("対象", "結果", "内容")
+
+Write-Host ""
+Write-Host (" 照合 {0} 件 : 一致 {1} / **不一致 {2}** / 判定不能 {3}" -f `
+ $total, $okCount, $mismatch.Count, $unknown.Count)
+
+if ($unknown.Count -gt 0)
+{
+ Write-Host " **判定不能は「問題なし」ではない。** HintPath の先に DLL が無い、という意味である。"
+ Write-Host " 復元していないなら、復元してから測り直す(-Detail で内訳)。"
+ Write-Host " **nuget.org に無い商用パッケージ(IBM.Data.DB2 系)は、常にここに出る。**"
+}
+
+if ($mismatch.Count -gt 0)
+{
+ Write-Host ""
+ Write-Host "=== 不一致 ==="
+ foreach ($m in ($mismatch | Sort-Object 対象, 種類, 名前))
+ {
+ Write-Host (" " + $m.対象)
+ Write-Host (" [{0}] {1} : 宣言 {2} / 記述 {3}" -f $m.種類, $m.名前, $m.宣言, $m.記述)
+ }
+}
+
+if ($Detail -and $unknown.Count -gt 0)
+{
+ Write-Host ""
+ Write-Host "=== 判定不能(HintPath の先に DLL が無い。未復元、または商用パッケージ)==="
+ foreach ($u in ($unknown | Sort-Object 対象, 名前))
+ {
+ Write-Host (" {0} : {1} → {2}" -f $u.対象, $u.名前, $u.宣言)
+ }
+}
+
+if ($Check)
+{
+ Write-Host ""
+ Write-Host "================ 判定 ================"
+
+ if ($mismatch.Count -eq 0)
+ {
+ Write-Host (" 不一致なし。(判定不能 {0} 件)" -f $unknown.Count) -ForegroundColor Green
+ }
+ else
+ {
+ Write-Host (" **不一致 {0} 件**" -f $mismatch.Count) -ForegroundColor Red
+ Write-Host ""
+ Write-Host " packages.config の版に合わせること。**パス表記は HintPath だけではない。**"
+ Write-Host " Import Project と Error Condition にも版が入る(#566)。"
+ }
+}
+
+Write-Host ""
+
+if ($Check -and $mismatch.Count -gt 0) { exit 1 }
+exit 0
diff --git a/root/programs/CompareRedirect.ps1 b/root/programs/CompareRedirect.ps1
index 130023c79..41e38ffe9 100644
--- a/root/programs/CompareRedirect.ps1
+++ b/root/programs/CompareRedirect.ps1
@@ -35,9 +35,25 @@
配布物を見るため、**ビルドしていないと「判定不能」ばかりになる。**
0_RunAll.ps1 に組み込むなら 1_BuildAll.ps1 の後。
- **「判定不能」は「問題なし」ではない。** 材料が無いだけである。
+ **「判定不能」は「問題なし」ではない。**
不一致には数えないが、件数は必ず出す。
+ <「判定不能」の原因は 1 つではない>
+
+ **ビルドしていない、とは限らない。**(#566 で踏んだ)
+ ビルド済みでも、**参照そのものが落ちていれば bin に配られない。**
+
+ csproj の `` に強い名前を書くと、
+ SpecificVersion は既定で true になる。宣言と実体の版がずれると、
+ MSBuild は**警告だけ出して参照を落とす**(ビルドは成功する)。
+
+ このとき本スクリプトは「判定不能」と答えるが、実態は
+ **「宣言した版が配布されていない」そのもの**であり、不一致より悪い。
+
+ **建てたはずのものが「判定不能」なら、まず bin を見ること。**
+ DLL が無ければ、packages.config・csproj のパス表記・Reference の
+ Version の 3 つが揃っているかを疑う。
+
<対象外>
**CS/NuGet/proj 配下は見ない。** NuGet パッケージの検証用で、
@@ -101,9 +117,28 @@ if ([Console]::OutputEncoding.CodePage -ne 65001)
# ------------------------------------------------------------------
# config から bindingRedirect を読む
# ------------------------------------------------------------------
+$script:unreadable = @()
+
function Get-Redirects([string]$path)
{
- try { [xml]$x = Get-Content $path -Raw -EA Stop } catch { return @() }
+ # **Get-Content でテキストとして読まない。**(#579)
+ # Windows PowerShell 5.1 の Get-Content は、BOM が無いと既定の文字コード(CP932)で
+ # 読む。UTF-8 の日本語が壊れて XML として解析できなくなり、
+ # **その config は宣言ごと数えられずに消えていた。**
+ # 実測で 5 ファイル・38 宣言が 5.1 でだけ測られていなかった。
+ #
+ # XmlDocument.Load は BOM と XML 宣言を見て復号するため、5.1 と 7 で揃う。
+ $x = New-Object System.Xml.XmlDocument
+
+ try { $x.Load($path) }
+ catch
+ {
+ # **黙って捨てない。** 捨てると「宣言が無い」と区別がつかない。
+ $script:unreadable += [PSCustomObject]@{
+ Config = $path; 理由 = $_.Exception.Message
+ }
+ return @()
+ }
$list = @()
foreach ($d in $x.SelectNodes("//*[local-name()='dependentAssembly']"))
@@ -166,16 +201,22 @@ foreach ($t in $tracked)
foreach ($r in $redirects) { $wanted[$r.Name] = $true }
$found = @{}
- Get-ChildItem $dir -Recurse -File -Filter *.dll -EA SilentlyContinue | ForEach-Object {
- if (-not $wanted.ContainsKey($_.BaseName)) { return }
- try { $v = [System.Reflection.AssemblyName]::GetAssemblyName($_.FullName).Version.ToString() }
- catch { return }
- if (-not $found.ContainsKey($_.BaseName))
+ # **配下の DLL の総数も数える。**(#579)
+ # 0 なら「建てていない」と分かり、判定不能の理由を切り分けられる。
+ $dlls = @(Get-ChildItem $dir -Recurse -File -Filter *.dll -EA SilentlyContinue)
+
+ foreach ($f in $dlls)
+ {
+ if (-not $wanted.ContainsKey($f.BaseName)) { continue }
+ try { $v = [System.Reflection.AssemblyName]::GetAssemblyName($f.FullName).Version.ToString() }
+ catch { continue }
+
+ if (-not $found.ContainsKey($f.BaseName))
{
- $found[$_.BaseName] = New-Object System.Collections.Generic.HashSet[string]
+ $found[$f.BaseName] = New-Object System.Collections.Generic.HashSet[string]
}
- $null = $found[$_.BaseName].Add($v)
+ $null = $found[$f.BaseName].Add($v)
}
$ok = 0; $ng = 0; $unk = 0
@@ -188,7 +229,10 @@ foreach ($t in $tracked)
if ($null -eq $vers)
{
$unk++
- $unknown += [PSCustomObject]@{ Config = $rel; Name = $r.Name; New = $r.New }
+ $unknown += [PSCustomObject]@{
+ Config = $rel; Name = $r.Name; New = $r.New
+ Dir = $dir; DllCount = $dlls.Count
+ }
}
elseif ($vers.Contains($r.New))
{
@@ -217,6 +261,112 @@ foreach ($t in $tracked)
}
}
+# ------------------------------------------------------------------
+# 判定不能の分類(#579)
+# ------------------------------------------------------------------
+# **判定不能を一律に「怪しい」と出すと、見るべきものが埋もれる。**
+# 実測すると、大半は仕組み上そうなるだけで実害が無い。
+# 残る「要調査」だけが、参照の落ち(#566)を疑う対象である。
+
+$KindNote = [ordered]@{
+ "未ビルド" = "配下に DLL が無い。建ててから測り直す"
+ "ライブラリ" = "**実行時に読まれない。**効くのはアプリの構成ファイルだけ"
+ "連鎖ごと未配布" = "要求元も配られていない。読み込まれ得ない"
+ "要調査" = "**要求元は在るのに実体が無い。参照が落ちている疑い**"
+}
+
+function Get-ProjectKind
+{
+ <#
+ .SYNOPSIS
+ その構成ファイルが、実行時に読まれる側のものかを判定する。
+ .DESCRIPTION
+ **bindingRedirect が効くのはアプリケーションの構成ファイルだけ**である。
+ クラス ライブラリの app.config は、そのままでは実行時に読まれない。
+
+ Web アプリは OutputType が Library になるため、**先に Web.config で判定する。**
+ OutputType だけで見ると、Web アプリをライブラリと誤って扱う。
+ #>
+ param([string]$Dir, [string]$Config)
+
+ if ($Config -match '(?i)[\\/]web\.config$') { return "アプリ" }
+
+ $proj = @(Get-ChildItem $Dir -File -EA SilentlyContinue |
+ Where-Object { $_.Extension -eq ".csproj" -or $_.Extension -eq ".vbproj" })
+ if ($proj.Count -eq 0) { return "アプリ" }
+
+ $txt = Get-Content $proj[0].FullName -Raw -EA SilentlyContinue
+ if ($txt -match '\s*([^<]+?)\s*')
+ {
+ if ($Matches[1] -match '(?i)^library$') { return "ライブラリ" }
+ return "アプリ"
+ }
+
+ return "ライブラリ"
+}
+
+function Test-Requester
+{
+ <#
+ .SYNOPSIS
+ そのアセンブリを要求している側が、配下に在るかを調べる。
+ .DESCRIPTION
+ アセンブリ参照は metadata に名前がそのまま入るため、バイト列を見れば分かる。
+ GetReferencedAssemblies は読み込みを伴い、**遅い上に失敗しやすい。**
+ #>
+ param([string]$Dir, [string[]]$Names)
+
+ $hit = @{}
+ foreach ($n in $Names) { $hit[$n] = $false }
+
+ foreach ($f in (Get-ChildItem $Dir -Recurse -File -Filter *.dll -EA SilentlyContinue))
+ {
+ $rest = @($Names | Where-Object { -not $hit[$_] })
+ if ($rest.Count -eq 0) { break }
+ if ($Names -contains $f.BaseName) { continue }
+
+ try { $txt = [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($f.FullName)) }
+ catch { continue }
+
+ foreach ($n in $rest) { if ($txt.Contains($n)) { $hit[$n] = $true } }
+ }
+
+ return $hit
+}
+
+$kinds = @{}
+
+foreach ($g in ($unknown | Group-Object Dir))
+{
+ $names = @($g.Group | ForEach-Object { $_.Name } | Sort-Object -Unique)
+ $req = $null
+
+ foreach ($u in $g.Group)
+ {
+ $key = $u.Config + "|" + $u.Name
+
+ if ($u.DllCount -eq 0)
+ {
+ $kinds[$key] = "未ビルド"
+ continue
+ }
+
+ if ((Get-ProjectKind -Dir $u.Dir -Config $u.Config) -eq "ライブラリ")
+ {
+ $kinds[$key] = "ライブラリ"
+ continue
+ }
+
+ # **要求元を見るのは、ここまで絞ってから。**(配下の DLL を全部読むため)
+ if ($null -eq $req) { $req = Test-Requester -Dir $u.Dir -Names $names }
+
+ if ($req[$u.Name]) { $kinds[$key] = "要調査" }
+ else { $kinds[$key] = "連鎖ごと未配布" }
+ }
+}
+
+$review = @($unknown | Where-Object { $kinds[($_.Config + "|" + $_.Name)] -eq "要調査" })
+
# ------------------------------------------------------------------
# 出力
# ------------------------------------------------------------------
@@ -230,8 +380,27 @@ Write-Host (" 宣言 {0} 件 : 一致 {1} / **不一致 {2}** / 判定不能 {3
if ($unknown.Count -gt 0)
{
- Write-Host " **判定不能は「問題なし」ではない。** そのプロジェクトの配下に実体が無いだけで、"
- Write-Host " ビルドしてから測り直せば判定できる(-Detail で内訳)。"
+ $byKind = @{}
+ foreach ($k in $kinds.Values)
+ {
+ if ($byKind.ContainsKey($k)) { $byKind[$k] = $byKind[$k] + 1 } else { $byKind[$k] = 1 }
+ }
+
+ Write-Host ""
+ Write-Host " 判定不能の内訳 :"
+
+ foreach ($k in $KindNote.Keys)
+ {
+ if (-not $byKind.ContainsKey($k)) { continue }
+ # **-f の桁指定は文字数で数える。** 日本語は全角なので崩れる(SummaryTable.ps1 1 節)。
+ Write-Host (" {0} {1} 件 {2}" -f `
+ (Add-Padding $k 16), (Add-LeftPadding ([string]$byKind[$k]) 3), $KindNote[$k])
+ }
+
+ if ($review.Count -eq 0)
+ {
+ Write-Host " **要調査は 0 件。** 残りは仕組み上そうなるだけで、実行時に読まれない。"
+ }
}
if ($mismatch.Count -gt 0)
@@ -245,16 +414,42 @@ if ($mismatch.Count -gt 0)
}
}
+# **要調査は -Detail が無くても出す。** ここだけが実害を疑う対象である。
+if ($review.Count -gt 0)
+{
+ Write-Host ""
+ Write-Host "=== 要調査(要求元は配られているのに、実体だけが無い)==="
+ foreach ($u in ($review | Sort-Object Config, Name))
+ {
+ Write-Host (" {0} : {1} → {2}" -f $u.Config, $u.Name, $u.New)
+ }
+ Write-Host " **参照が落ちている可能性がある。**(#566)"
+ Write-Host " bin を見て、csproj の Reference / PackageReference が在るかを確かめること。"
+}
+
if ($Detail -and $unknown.Count -gt 0)
{
Write-Host ""
- Write-Host "=== 判定不能(配下に実体が無い。ビルドしていない可能性)==="
+ Write-Host "=== 判定不能の一覧(分類つき)==="
foreach ($u in ($unknown | Sort-Object Config, Name))
{
- Write-Host (" {0} : {1} → {2}" -f $u.Config, $u.Name, $u.New)
+ Write-Host (" [{0,-14}] {1} : {2} → {3}" -f `
+ $kinds[($u.Config + "|" + $u.Name)], $u.Config, $u.Name, $u.New)
}
}
+if ($script:unreadable.Count -gt 0)
+{
+ Write-Host ""
+ Write-Host "=== 読めなかった config(解析に失敗)==="
+ foreach ($u in $script:unreadable)
+ {
+ Write-Host (" " + $u.Config)
+ Write-Host (" " + $u.理由)
+ }
+ Write-Host " **宣言が無いのと区別がつかないため、件数に表れない。**"
+}
+
if ($Check)
{
Write-Host ""
@@ -262,7 +457,15 @@ if ($Check)
if ($mismatch.Count -eq 0)
{
- Write-Host (" 不一致なし。(判定不能 {0} 件)" -f $unknown.Count) -ForegroundColor Green
+ if ($review.Count -eq 0)
+ {
+ Write-Host (" 不一致なし。(判定不能 {0} 件 / **要調査 0**)" -f $unknown.Count) -ForegroundColor Green
+ }
+ else
+ {
+ Write-Host (" 不一致なし。ただし**要調査 {0} 件**(判定不能 {1} 件)" -f `
+ $review.Count, $unknown.Count) -ForegroundColor Yellow
+ }
}
else
{
diff --git a/root/programs/GetAgentSkills.ps1 b/root/programs/GetAgentSkills.ps1
new file mode 100644
index 000000000..6771f441b
--- /dev/null
+++ b/root/programs/GetAgentSkills.ps1
@@ -0,0 +1,216 @@
+#Requires -Version 5.1
+<#
+.SYNOPSIS
+ スキル リポジトリの main から src/skills を取得し、.claude/skills へ配置する。
+
+.DESCRIPTION
+ スキルの本体は OpenTouryoCodingAgentAssets にある。**こちらは複製である。**
+
+ https://github.com/OpenTouryoProject/OpenTouryoCodingAgentAssets
+
+ 向こうの install.ps1 は**フレームワークの利用者(アプリ開発)向け**で、
+ AGENTS.md や CLAUDE.md も書き換える。
+ こちらはフレームワーク本体のリポジトリで、**独自の AGENTS.md を持つ**ため、
+ スキルだけを取りに行く。
+
+ **配置先は .gitignore の対象である。** 複製をコミットすると、
+ 向こうが更新されたときに古くなり、どちらが正か分からなくなる。
+ 使う前にこのスクリプトを実行すること。
+
+ 既定で除外するスキル(フレームワーク本体の開発には合わない)。
+
+ opentouryo-project-setup* アプリの新規構築手順
+ opentouryo-project-policy プロジェクト方針。**本体は AGENTS.md が正**
+ opentouryo-project-transform 既存資産の移行。本体側では別の話
+ opentouryo-comment-convention コメント規約。**本体は CODING.md が正**
+ opentouryo-base2-customize 利用者による基底クラスの改造
+
+.PARAMETER Ref
+ 取得するブランチまたはタグ。既定は main。
+
+.PARAMETER Skill
+ 取得するスキル名。省略時は除外分を引いた全件。
+
+.PARAMETER Exclude
+ 除外するスキル名。ワイルドカード可。既定は上記の 3 種。
+
+.PARAMETER Destination
+ 配置先。既定はリポジトリ直下の .claude/skills。
+
+.PARAMETER List
+ 取得せず、対象になるスキル名を一覧表示して終わる。
+
+.EXAMPLE
+ .\GetAgentSkills.ps1
+
+.EXAMPLE
+ .\GetAgentSkills.ps1 -List
+
+.EXAMPLE
+ .\GetAgentSkills.ps1 -Skill opentouryo-layer-d,opentouryo-layer-b
+
+.NOTES
+ 作成者 :玄人 幸道
+ 更新履歴 :
+ 日時 更新者 内容
+ ---------- ---------------- -------------------------------------------------
+ 2026/08/21 玄人 幸道 新規作成(#577)
+#>
+[CmdletBinding(SupportsShouldProcess)]
+param(
+ [string]$Ref = "main",
+ [string[]]$Skill,
+ [string[]]$Exclude = @(
+ "opentouryo-project-setup*",
+ "opentouryo-project-policy",
+ "opentouryo-project-transform",
+ "opentouryo-comment-convention",
+ "opentouryo-base2-customize"
+ ),
+ [string]$Destination,
+ [switch]$List
+)
+
+$ErrorActionPreference = "Stop"
+
+$Repo = "OpenTouryoProject/OpenTouryoCodingAgentAssets"
+$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
+
+if (-not $Destination) { $Destination = Join-Path $repoRoot ".claude\skills" }
+
+# Windows PowerShell 5.1 は既定で TLS 1.0 を使うことがあり、GitHub に繋がらない。
+[Net.ServicePointManager]::SecurityProtocol =
+ [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
+
+$work = Join-Path ([IO.Path]::GetTempPath()) ("OpenTouryoSkills_" + [Guid]::NewGuid().ToString("N"))
+$zip = Join-Path $work "skills.zip"
+
+try
+{
+ New-Item -ItemType Directory -Path $work -Force | Out-Null
+
+ $url = "https://codeload.github.com/$Repo/zip/refs/heads/$Ref"
+ Write-Host ("=== 取得 : {0} ({1}) ===" -f $Repo, $Ref) -ForegroundColor Cyan
+
+ # -UseBasicParsing は 5.1 で必要(IE エンジンに依存しない)。
+ Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
+
+ Expand-Archive -Path $zip -DestinationPath $work -Force
+
+ $src = Get-ChildItem -Path $work -Directory |
+ ForEach-Object { Join-Path $_.FullName "src\skills" } |
+ Where-Object { Test-Path $_ } |
+ Select-Object -First 1
+
+ if (-not $src) { throw "src/skills が見つかりません($url)" }
+
+ # SKILL.md を持つものだけがスキルである。
+ $all = @(Get-ChildItem -Path $src -Directory |
+ Where-Object { Test-Path (Join-Path $_.FullName "SKILL.md") })
+
+ if ($all.Count -eq 0) { throw "スキルが 1 件も見つかりません : $src" }
+
+ $targets = @($all)
+
+ if ($Skill)
+ {
+ $unknown = @($Skill | Where-Object { $n = $_; -not ($all | Where-Object { $_.Name -eq $n }) })
+ if ($unknown.Count -gt 0)
+ {
+ Write-Host (" **不明なスキル : {0}**" -f ($unknown -join ", ")) -ForegroundColor Red
+ Write-Host " -List で一覧を出せます。" -ForegroundColor Yellow
+ exit 1
+ }
+ $targets = @($targets | Where-Object { $Skill -contains $_.Name })
+ }
+ else
+ {
+ foreach ($pat in $Exclude)
+ {
+ $targets = @($targets | Where-Object { $_.Name -notlike $pat })
+ }
+ }
+
+ if ($targets.Count -eq 0)
+ {
+ Write-Host " **対象が 0 件です。**" -ForegroundColor Red
+ exit 1
+ }
+
+ if ($List)
+ {
+ Write-Host ("=== 対象のスキル(全 {0} 件中 {1} 件)===" -f $all.Count, $targets.Count) -ForegroundColor Cyan
+ foreach ($t in ($targets | Sort-Object Name)) { Write-Host (" " + $t.Name) }
+
+ $skipped = @($all | Where-Object { $n = $_.Name; -not ($targets | Where-Object { $_.Name -eq $n }) })
+ if ($skipped.Count -gt 0)
+ {
+ Write-Host ("=== 除外 {0} 件 ===" -f $skipped.Count) -ForegroundColor Yellow
+ foreach ($s in ($skipped | Sort-Object Name)) { Write-Host (" " + $s.Name) }
+ }
+ exit 0
+ }
+
+ New-Item -ItemType Directory -Path $Destination -Force | Out-Null
+
+ $n = 0
+ foreach ($t in ($targets | Sort-Object Name))
+ {
+ $dest = Join-Path $Destination $t.Name
+
+ if ($PSCmdlet.ShouldProcess($dest, "配置"))
+ {
+ # **毎回入れ替える。** 差分を追うより、向こうの状態に揃えるほうが確実。
+ if (Test-Path $dest) { Remove-Item -Path $dest -Recurse -Force }
+ Copy-Item -Path $t.FullName -Destination $dest -Recurse -Force
+ $n++
+ }
+ }
+
+ # **除外に回ったものが残っていたら消す。**
+ # 除外を増やしたときに、前回配置したものが取り残される。
+ # 消すのは「向こうに在るスキル名」だけにする(無関係な物は触らない)。
+ $pruned = 0
+
+ if (-not $Skill)
+ {
+ $keep = @($targets | ForEach-Object { $_.Name })
+
+ foreach ($known in $all)
+ {
+ if ($keep -contains $known.Name) { continue }
+
+ $stale = Join-Path $Destination $known.Name
+ if (-not (Test-Path $stale)) { continue }
+
+ if ($PSCmdlet.ShouldProcess($stale, "除外分を削除"))
+ {
+ Remove-Item -Path $stale -Recurse -Force
+ $pruned++
+ }
+ }
+ }
+
+ Write-Host ""
+ Write-Host (" 配置 : {0} 件 → {1}" -f $n, $Destination) -ForegroundColor Green
+ if ($Skill)
+ {
+ Write-Host (" ※ -Skill 指定のため、他の {0} 件は取得していない" -f ($all.Count - $targets.Count))
+ }
+ else
+ {
+ Write-Host (" 除外 : {0} 件" -f ($all.Count - $targets.Count))
+ }
+ if ($pruned -gt 0)
+ {
+ Write-Host (" 削除 : {0} 件(除外に回ったもの)" -f $pruned) -ForegroundColor Yellow
+ }
+
+ Write-Host " **ここは .gitignore の対象。コミットしないこと。**"
+}
+finally
+{
+ if (Test-Path $work) { Remove-Item -Path $work -Recurse -Force -ErrorAction SilentlyContinue }
+}
+
+exit 0
diff --git a/root/programs/RELEASE.md b/root/programs/RELEASE.md
index d29217a18..e84126bef 100644
--- a/root/programs/RELEASE.md
+++ b/root/programs/RELEASE.md
@@ -185,7 +185,7 @@ cd root\programs
- [ ] **`1_BuildAll.ps1` のエラーが「既知の 1 件」だけである**
… `-SkipClean` は**使わない**。前回の成果物が残っていると通ったように見える
- [ ] **`2_RunAllTests.ps1` が終了コード 0**(8 ケース)
-- [ ] **`3_SmokeTest.ps1` が終了コード 0**(23 件)
+- [ ] **`3_SmokeTest.ps1` が終了コード 0**(25 件)
> **`1_BuildAll.ps1` は現状ここで終了コード 1 になる。**
> `WSClnt_sample (net48)` の ClickOnce 署名エラー(`MSB3482`)が残るため。
@@ -209,7 +209,7 @@ cd root\programs
|---|---|
| `1_BuildAll.ps1`(31 ステップ) | 5.8 分 |
| `2_RunAllTests.ps1`(8 ケース) | 1.3 分 |
-| `3_SmokeTest.ps1`(23 件) | 4.0 分 |
+| `3_SmokeTest.ps1`(25 件) | 7.4 分 |
### VB 側は、この 3 本に含めない
@@ -280,10 +280,23 @@ cd root\programs
- [ ] 2 層 C/S 系(net48 6 本 / net10.0 5 本)が起動し、CRUD 画面が操作できる
- [ ] WS クライアント系(net48 4 本 / net10.0 3 本)が起動する
-> **WS クライアントの疎通には別リポジトリが要る。**
-> 呼び先の Web サービスは
-> [`OpenTouryoProject/ResourceServerTemplates`](https://github.com/OpenTouryoProject/ResourceServerTemplates)
-> へ移設済みで、本リポジトリだけでは接続先が無い。
+> **WS クライアントの呼び先は、本リポジトリにある。**
+> `TMProtocolDefinition.xml` で生きているのは次の 3 つで、
+> **ASMX(`protocol="2"`)と WCF-HTTP(`protocol="3"`)はコメントアウト済み**(呼ばれない)。
+>
+> | 定義 | 接続先 | 用意するもの |
+> |---|---|---|
+> | `testInProcess`(1) | インプロセス | **不要** |
+> | `testWebService3`(4) | `net.tcp://localhost:7777/WCFService/WCFTCPSvcForFx/` | `ServiceInterface\WCFService`(自己ホストの exe)を起動 |
+> | `testWebService4`(5) | `https://localhost/WebAPIControllerForFx` | `ServiceInterface\ASPNETWebService` を `https://localhost/` で公開 |
+>
+> **どちらも `1_BuildAll.ps1` の `Framework_WS` 段が建てている**(`7_Build_Framework_WS.bat`)。
+> `WCFService\App.config` の待ち受けは、クライアントの定義と一致している。
+>
+> **要るのは別リポジトリではなく、上記のホスティングである。**
+> `3_SmokeTest.ps1` が WS クライアントを対象外にしているのは、
+> **WinForms / WPF で UI Automation が要るため**であって、接続先が無いからではない
+> (プロトコル自体は `TestTransmission` が自前のスタブで確認している。#546 / #561)。
### フレームワーク付属ツール
@@ -375,7 +388,7 @@ cd root\programs
| Web アプリ(3 件) | 済 | ログインまで通せば認証・セッションまで確認できる |
| **UI 系サンプル(18 本)** | **見送り** | UI Automation が必要。画面定義の変更で壊れやすく維持費が高い。
通す B 層/D 層は Web 系・バッチ系と重複し、回帰検出力の増分が小さい |
| **GUI ツール(4 本)** | **見送り** | 同上。`DaoGen_Tool` は生成ロジックを CUI 側で確認済みのため、
手作業で見るのは GUI が起動することだけでよい。
`DeployZipPackWithHTTP` は ZIP の圧縮・解凍まで見る(CUI が無いため) |
-| **Web サービス** | **不可** | 本リポジトリにホストが無い(別リポジトリへ移設済み) |
+| Web サービス(WebAPI Client 2 件) | 済 | #566 で引き戻し、#571 で統合。**CRUD 一巡 + DTO の往復 + 楽観排他**を 1 本で見る |
**自動化した対象が「起動する」ことは、手作業側の確認範囲を狭める。**
`DaoGen_Tool` は CUI で生成ロジックまで確認できるようになったため、
diff --git a/root/programs/SMOKETEST.md b/root/programs/SMOKETEST.md
index e7793be61..461dd118c 100644
--- a/root/programs/SMOKETEST.md
+++ b/root/programs/SMOKETEST.md
@@ -81,57 +81,15 @@ cd root\programs
---
-## 3. 対象(23 件)
+## 3. 対象(25 件)
-### バッチ(8 件)
-
-| 対象 | 判定 |
-|---|---|
-| `SimpleBatch_sample` (net48 / net10.0) | `〇件のデータがあります` が出力される |
-| `RerunnableBatch_sample` (net48 / net10.0) | `Orders2` の件数が `Orders` と一致する |
-| `RerunnableBatch_sample2` (net48 / net10.0) | 同上 |
-| `RerunnableBatch_sample3` (net48 / net10.0) | 同上 |
-
-`RerunnableBatch` 系は `Orders`(830 件) を読み `Orders2` へ INSERT する。3 本の違いは
-INSERT の方法(1 件ずつ/SQL 連結/INSERT 文組み立て)で、いずれも結果は同じになる。
-
-**実行前に `Orders2` を空にする必要がある。** `OrderID` が主キーのため、
-残っていると重複で落ちる。スクリプトが `DELETE FROM [Orders2]` を行ってから実行する。
-実行後は 830 件=初期状態に戻るため、後始末は不要。
-
-#### `Orders2` はスクリプトが作る
-
-**`Orders2` は Northwind 標準の表ではない。** `instnwnd.sql` に含まれないため、
-**DB を作り直すたびに消える**。そのたびに手で作るのは現実的でないので、
-事前準備の中で存在を確認し、無ければ作る。
+**基盤系を先に置く。**(#571)
+環境やツールが壊れていれば、アプリケーションの検証を待たずに分かる。
```
-Orders2 がありません。作成します(...\RerunnableBatch_sample\CREATE ORDERS2.sql)。
+DaoGen_Tool → DeployZip → TestTransmission → バッチ → CLI → WebAPI Client → Web アプリ
```
-DDL はサンプル同梱の `CREATE ORDERS2.sql` をそのまま流す。**スクリプトに書き写さない**
-(同じ DDL がサンプル配下に 9 つ重複しており、増やす意味がない)。
-
-流すときの注意が 2 つある。
-
-- **`GO` は自前で分割する。** `SqlClient` は `GO` を解釈できない(`sqlcmd` の
- バッチ区切りであって T-SQL ではない)。このファイルは 3 バッチに分かれる。
-- **`USE [Northwind]` は流さない。** 接続先は接続文字列に従うべきで、
- 流すと接続文字列が別 DB を指していた場合にそちらへ表を作ってしまう。
-
-> CI では `Set up Northwind` ステップが `instnwnd.sql` の直後に同じ DDL を流している。
-> そちらは `sqlcmd` なので `GO` をそのまま解釈できる。
-
-### CLI(1 件)
-
-| 対象 | 判定 |
-|---|---|
-| `Simple_CLI` (net10.0) | `cmd1 --an-int 123` が `Sub command cmd1: 123` を出力する |
-
-net48 版は `System.CommandLine` / `Sharprompt` の .NET Framework サポート終了により
-ドロップされている(`5_Build_CLI_sample.bat` 参照)。
-`interactive` サブコマンドは対話プロンプトを使うため対象外。
-
### DaoGen_Tool(墨壺)の CUI モード(6 件)
#508 で追加された CUI。net48 / net10.0 それぞれ 3 件。
@@ -354,6 +312,97 @@ ASP.NET WebAPI の経路(`protocol="5"`)は **.NET Framework 限定**であ
.NET Core 版では `FxEnum.TmProtocol` ごと落とされている
([`CS/Frameworks/ANALYSIS.md`](CS/Frameworks/ANALYSIS.md) と #543)。
+### バッチ(8 件)
+
+| 対象 | 判定 |
+|---|---|
+| `SimpleBatch_sample` (net48 / net10.0) | `〇件のデータがあります` が出力される |
+| `RerunnableBatch_sample` (net48 / net10.0) | `Orders2` の件数が `Orders` と一致する |
+| `RerunnableBatch_sample2` (net48 / net10.0) | 同上 |
+| `RerunnableBatch_sample3` (net48 / net10.0) | 同上 |
+
+`RerunnableBatch` 系は `Orders`(830 件) を読み `Orders2` へ INSERT する。3 本の違いは
+INSERT の方法(1 件ずつ/SQL 連結/INSERT 文組み立て)で、いずれも結果は同じになる。
+
+**実行前に `Orders2` を空にする必要がある。** `OrderID` が主キーのため、
+残っていると重複で落ちる。スクリプトが `DELETE FROM [Orders2]` を行ってから実行する。
+実行後は 830 件=初期状態に戻るため、後始末は不要。
+
+#### `Orders2` はスクリプトが作る
+
+**`Orders2` は Northwind 標準の表ではない。** `instnwnd.sql` に含まれないため、
+**DB を作り直すたびに消える**。そのたびに手で作るのは現実的でないので、
+事前準備の中で存在を確認し、無ければ作る。
+
+```
+Orders2 がありません。作成します(...\RerunnableBatch_sample\CREATE ORDERS2.sql)。
+```
+
+DDL はサンプル同梱の `CREATE ORDERS2.sql` をそのまま流す。**スクリプトに書き写さない**
+(同じ DDL がサンプル配下に 9 つ重複しており、増やす意味がない)。
+
+流すときの注意が 2 つある。
+
+- **`GO` は自前で分割する。** `SqlClient` は `GO` を解釈できない(`sqlcmd` の
+ バッチ区切りであって T-SQL ではない)。このファイルは 3 バッチに分かれる。
+- **`USE [Northwind]` は流さない。** 接続先は接続文字列に従うべきで、
+ 流すと接続文字列が別 DB を指していた場合にそちらへ表を作ってしまう。
+
+> CI では `Set up Northwind` ステップが `instnwnd.sql` の直後に同じ DDL を流している。
+> そちらは `sqlcmd` なので `GO` をそのまま解釈できる。
+
+### CLI(1 件)
+
+| 対象 | 判定 |
+|---|---|
+| `Simple_CLI` (net10.0) | `cmd1 --an-int 123` が `Sub command cmd1: 123` を出力する |
+
+net48 版は `System.CommandLine` / `Sharprompt` の .NET Framework サポート終了により
+ドロップされている(`5_Build_CLI_sample.bat` 参照)。
+`interactive` サブコマンドは対話プロンプトを使うため対象外。
+
+### DTO を使用したバッチ更新(WebAPI Client)(2 件)
+
+**対象は EXE だが、相手に WebAPI が要る。**(#570)
+`Kind = "Web"` は対象自身が Web アプリのときの仕組みなので使えない。
+`Pre` で起動し、`Verify` の最後で止める(`DeployZipPackWithHTTP` と同じ形)。
+
+| 対象 | 相手 | ポート |
+|---|---|---|
+| `TestWebAPIClient (net48)` | `Samples/WS_sample/ASPNETWebService` | 51087 |
+| `TestWebAPIClient (net10.0)` | `Samples4NetCore/Backend/ASPNETWebService` | 51088 |
+
+**クライアントは 1 本。** 接続先を引数で切り替えるだけで両方に使える。
+
+<何を確かめるか>
+
+ **1 本で 3 つを見る。**(#571 で ResourceServer の 2 件を取り込んだ)
+
+ | 観点 | 口 | 内容 |
+ |---|---|---|
+ | CRUD 一巡 | `api/json` | `JsonController` で追加→取得→更新→削除し、**件数が戻る** |
+ | DTO の往復 | `api/batchupdate` | **`RowState` と `Original` が保たれる**(#567 / #570) |
+ | 楽観排他 | 〃 | `Original` を WHERE に入れ、**古い版の更新を弾く** |
+
+ `JsonController` は `MVC_Sample` の `Crud1Controller` と同じ処理を公開している
+ (B層は同じ `WSServer_sample.Business.LayerB`)。
+
+ **同じ WebAPI を相手にしながらホストを 2 回起動していた**ため、1 つにまとめた。
+
+```
+往復後も RowState が残る Added=1 / Modified=1
+往復後も Original が残る Original=Exotic Liquids / Current=smoke-103919
+他者の更新が通る {"updateCount":1}
+古い版の更新が弾かれる {"errorMessageID":"W0002"}
+```
+
+**サーバ側だけでは検証にならない。** 同一プロセス内の `DataTable` を触ってしまい、
+JSON をまたいだことにならないため、HTTP 越しに送って戻すところまでやる。
+
+> **判定は状態コードと JSON の形で行う。**
+> 部分一致にしていたところ、**IIS Express の 500.19 が返す HTML に "test" が含まれ、
+> 疎通が OK と表示された。**「通ったこと」は正しさの証拠にならない。
+
### Web アプリ(3 件)
| 対象 | ホスト | 認証の実装 | 判定 |
@@ -386,19 +435,16 @@ ASP.NET WebAPI の経路(`protocol="5"`)は **.NET Framework 限定**であ
ポストバックには画面が発行した `__VIEWSTATE` / `__VIEWSTATEGENERATOR` / `__EVENTVALIDATION` を
そのまま返す必要があり、コントロール名はマスタ ページ配下のため
`ctl00$ContentPlaceHolder_A$` が付く。
-
### 対象外
| 対象 | 理由 |
|---|---|
| `2CS_sample` 系(11 本) | WinForms / WPF。UI Automation が必要で、画面変更に弱く維持費が高い |
| `WSClient_sample` 系(7 本) | 同上 |
-| Web サービス(`ASPNETWebService`) | **別リポジトリへ移設済み**。本リポジトリにホストが無い |
`CS/Samples/WS_sample/WSServer_sample` はクラス ライブラリ(B層・D層)で、
-これを載せる Web サービスは
-[`OpenTouryoProject/ResourceServerTemplates`](https://github.com/OpenTouryoProject/ResourceServerTemplates)
-へ移設されている。このため本リポジトリだけでは HTTP 疎通ができない。
+**これを載せる Web サービスは `ASPNETWebService`**(上記 ResourceServer)である。
+一度 `OpenTouryoProject/ResourceServerTemplates` へ移設したが、**#566 で引き戻した。**
WinForms / WPF 系は、リリース チェックリスト(段階 4)の**手作業項目**として残す。
@@ -595,7 +641,10 @@ MVC_Sample (net10.0) OK ログイン後 /Crud1/Index = 200
全対象 OK
```
-全 23 件(ビルド 8 バッチ + 疎通 23 件)で **約 4 分**。
+全 25 件(ビルド 10 バッチ + 疎通 25 件)で **約 7.4 分**。
+
+**#571 の統合で、13.9 分から半減した。**
+同じ WebAPI を相手にしながらホストを 2 回起動していたのをやめたため。
### リダイレクトの扱い
@@ -609,6 +658,34 @@ MVC_Sample (net10.0) OK ログイン後 /Crud1/Index = 200
---
+## 8.5 ファイルの構成(#571 で分割)
+
+**1,477 行あったので分けた。** 本体には引数・環境の準備・実行ループ・サマリだけを残す。
+
+| ファイル | 役割 |
+|---|---|
+| `3_SmokeTest.ps1` | 引数・環境の準備・実行ループ・サマリ |
+| `st_Utility.ps1` | 環境・基盤系(DB / ログ / DaoGen / DeployZip) |
+| `st_Server.ps1` | サーバー系(Web ホストの起動と停止、HTTP 要求) |
+| `st_Flow.ps1` | アプリケーション検証フロー(WebForms / MVC) |
+| `st_Targets.ps1` | テスト対象定義(CS / VB) |
+
+**ドット ソースで読む。** `& file` では関数と変数が呼び出し元のスコープに入らない。
+
+```powershell
+. (Join-Path $PSScriptRoot "st_Utility.ps1")
+. (Join-Path $PSScriptRoot "st_Server.ps1")
+. (Join-Path $PSScriptRoot "st_Flow.ps1")
+. (Join-Path $PSScriptRoot "st_Targets.ps1")
+```
+
+**順序が要る。** `st_Targets.ps1` は他の 3 つが定義した関数と変数
+(`$batchArgs` / `$mvcLoginFlow` / `Start-ApiWeb` 等)を参照するため、**最後に読む。**
+
+**`st_*.ps1` は単体では動かない。** 個別に実行しても意味がない。
+
+---
+
## 9. 対象を追加するとき
`3_SmokeTest.ps1` の `$targets` に定義を足す。
@@ -685,7 +762,6 @@ $client.Connect("localhost", $port)
| `-SkipHttpErrorCheck` が 5.1 に無く、HTTP が常に失敗 | `3_SmokeTest.ps1` の `Invoke-Http` |
| `chcp` による画面クリアと、ログの文字化け | 3 本すべて(冒頭でコード ページを切り替え) |
-
### PowerShell から `.bat` を呼ぶときの注意
`TESTING.md` と同じく、`NoDefaultCurrentDirectoryInExePath` を解除している。
@@ -697,7 +773,7 @@ $client.Connect("localhost", $port)
```powershell
.\3_SmokeTest.ps1 -Lang VB # VB のみ(6 件)
-.\3_SmokeTest.ps1 -Lang Both # C# 23 件 + VB 6 件
+.\3_SmokeTest.ps1 -Lang Both # C# 25 件 + VB 6 件
```
**既定に VB を含めない。** リリース時の検証([`RELEASE.md`](RELEASE.md) 3 節)は
diff --git a/root/programs/TESTING.md b/root/programs/TESTING.md
index e864dfb47..dfaa61532 100644
--- a/root/programs/TESTING.md
+++ b/root/programs/TESTING.md
@@ -70,8 +70,8 @@ cd root\programs
| TestCode (net10.0) | `TestCode/ResultCore100.txt` | 同上 | 同上 |
| TestDataAccess (net48) | `TestDataAccess/Result48.txt` | `y_Build_TestCode_DataAccess.bat` | データ アクセス(#520)。**実行モードで対象 DBMS が変わる** |
| TestDataAccess (net10.0) | `TestDataAccess/ResultCore100.txt` | 同上 | 同上 |
-| SimpleBatch (net48) | `TestBatch/ResultSimpleBatch48.txt` | `y_Build_TestCode_Batch.bat` | **DB 接続あり**(Northwind) |
-| SimpleBatch (net10.0) | `TestBatch/ResultSimpleBatchCore100.txt` | 同上 | 同上 |
+| TestBatch (net48) | `TestBatch/ResultSimpleBatch48.txt` | `y_Build_TestCode_Batch.bat` | **DB 接続あり**(Northwind) |
+| TestBatch (net10.0) | `TestBatch/ResultSimpleBatchCore100.txt` | 同上 | 同上 |
| EncAndDecUtilCUI (net48) | `EncAndDecUtilCUI/Result48.txt` | `y_Build_TestCode_SecCUI.bat` | 暗号・JWT・XML 署名 |
| EncAndDecUtilCUI (net10.0) | `EncAndDecUtilCUI/ResultCore100.txt` | 同上 | 同上 |
@@ -256,7 +256,7 @@ $old | Where-Object { $new -notcontains $_ }
`3` の実例(2026-08-01 に発生。復旧済み):
```
-SimpleBatch (net48 / net10.0)
+TestBatch (net48 / net10.0)
[実測のみ] 4件のデータがあります
[期待のみ] 3件のデータがあります
```
diff --git a/root/programs/VB/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.vbproj b/root/programs/VB/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.vbproj
index 60a372dba..158af9d5f 100644
--- a/root/programs/VB/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.vbproj
+++ b/root/programs/VB/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.vbproj
@@ -49,72 +49,72 @@
packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll
-
- packages\Azure.Core.1.46.2\lib\net472\Azure.Core.dll
+
+ packages\Microsoft.Bcl.AsyncInterfaces.10.0.5\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
-
- packages\Azure.Identity.1.14.0\lib\netstandard2.0\Azure.Identity.dll
+
+ packages\Microsoft.Bcl.Cryptography.10.0.5\lib\net462\Microsoft.Bcl.Cryptography.dll
-
- packages\Microsoft.Bcl.AsyncInterfaces.9.0.6\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
+
+ packages\Microsoft.Bcl.TimeProvider.10.0.5\lib\net462\Microsoft.Bcl.TimeProvider.dll
-
- packages\Microsoft.Bcl.Cryptography.9.0.6\lib\net462\Microsoft.Bcl.Cryptography.dll
+
+
+ packages\Microsoft.Data.SqlClient.7.0.0\lib\net462\Microsoft.Data.SqlClient.dll
-
- packages\Microsoft.Bcl.TimeProvider.9.0.6\lib\net462\Microsoft.Bcl.TimeProvider.dll
+
+ packages\System.Threading.Channels.10.0.5\lib\net462\System.Threading.Channels.dll
-
-
- packages\Microsoft.Data.SqlClient.6.0.2\lib\net462\Microsoft.Data.SqlClient.dll
+
+ packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
-
- packages\Microsoft.Extensions.Caching.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
+
+ packages\Microsoft.Data.SqlClient.Internal.Logging.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Internal.Logging.dll
-
- packages\Microsoft.Extensions.Caching.Memory.9.0.6\lib\net462\Microsoft.Extensions.Caching.Memory.dll
+
+ packages\Microsoft.Data.SqlClient.Extensions.Abstractions.1.0.0\lib\netstandard2.0\Microsoft.Data.SqlClient.Extensions.Abstractions.dll
-
- packages\Microsoft.Extensions.DependencyInjection.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+
+ packages\Microsoft.Extensions.Caching.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Caching.Abstractions.dll
-
- packages\Microsoft.Extensions.Logging.Abstractions.9.0.6\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
+
+ packages\Microsoft.Extensions.Caching.Memory.10.0.5\lib\net462\Microsoft.Extensions.Caching.Memory.dll
-
- packages\Microsoft.Extensions.Options.9.0.6\lib\net462\Microsoft.Extensions.Options.dll
+
+ packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll
-
- packages\Microsoft.Extensions.Primitives.9.0.6\lib\net462\Microsoft.Extensions.Primitives.dll
+
+ packages\Microsoft.Extensions.Logging.Abstractions.10.0.5\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll
-
- packages\Microsoft.Identity.Client.4.72.1\lib\net472\Microsoft.Identity.Client.dll
+
+ packages\Microsoft.Extensions.Options.10.0.5\lib\net462\Microsoft.Extensions.Options.dll
-
- packages\Microsoft.Identity.Client.Extensions.Msal.4.72.1\lib\netstandard2.0\Microsoft.Identity.Client.Extensions.Msal.dll
+
+ packages\Microsoft.Extensions.Primitives.10.0.5\lib\net462\Microsoft.Extensions.Primitives.dll
-
- packages\Microsoft.IdentityModel.Abstractions.8.12.0\lib\net472\Microsoft.IdentityModel.Abstractions.dll
+
+ packages\Microsoft.IdentityModel.Abstractions.8.17.0\lib\net472\Microsoft.IdentityModel.Abstractions.dll
-
- packages\Microsoft.IdentityModel.JsonWebTokens.8.12.0\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
+
+ packages\Microsoft.IdentityModel.JsonWebTokens.8.17.0\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
-
- packages\Microsoft.IdentityModel.Logging.8.12.0\lib\net472\Microsoft.IdentityModel.Logging.dll
+
+ packages\Microsoft.IdentityModel.Logging.8.17.0\lib\net472\Microsoft.IdentityModel.Logging.dll
-
- packages\Microsoft.IdentityModel.Protocols.8.12.0\lib\net472\Microsoft.IdentityModel.Protocols.dll
+
+ packages\Microsoft.IdentityModel.Protocols.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.dll
-
- packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.8.12.0\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
+
+ packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.8.17.0\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
-
- packages\Microsoft.IdentityModel.Tokens.8.12.0\lib\net472\Microsoft.IdentityModel.Tokens.dll
+
+ packages\Microsoft.IdentityModel.Tokens.8.17.0\lib\net472\Microsoft.IdentityModel.Tokens.dll
packages\Microsoft.Web.Infrastructure.2.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
- packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll
+ packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll
packages\Newtonsoft.Json.Bson.1.0.3\lib\net45\Newtonsoft.Json.Bson.dll
@@ -132,35 +132,26 @@
packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll
-
- packages\System.ClientModel.1.4.2\lib\netstandard2.0\System.ClientModel.dll
-
-
- packages\System.Diagnostics.DiagnosticSource.9.0.6\lib\net462\System.Diagnostics.DiagnosticSource.dll
+
+ packages\System.Diagnostics.DiagnosticSource.10.0.5\lib\net462\System.Diagnostics.DiagnosticSource.dll
-
- packages\System.Formats.Asn1.9.0.6\lib\net462\System.Formats.Asn1.dll
+
+ packages\System.Formats.Asn1.10.0.5\lib\net462\System.Formats.Asn1.dll
-
- packages\System.IdentityModel.Tokens.Jwt.8.12.0\lib\net472\System.IdentityModel.Tokens.Jwt.dll
+
+ packages\System.IdentityModel.Tokens.Jwt.8.17.0\lib\net472\System.IdentityModel.Tokens.Jwt.dll
-
- packages\System.IO.FileSystem.AccessControl.5.0.0\lib\net461\System.IO.FileSystem.AccessControl.dll
-
-
- packages\System.IO.Pipelines.9.0.6\lib\net462\System.IO.Pipelines.dll
+
+ packages\System.IO.Pipelines.10.0.5\lib\net462\System.IO.Pipelines.dll
packages\System.Memory.4.6.3\lib\net462\System.Memory.dll
-
- packages\System.Memory.Data.9.0.6\lib\net462\System.Memory.Data.dll
-
packages\Microsoft.AspNet.WebApi.Client.6.0.0\lib\net45\System.Net.Http.Formatting.dll
@@ -174,24 +165,15 @@
-
- packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll
-
-
- packages\System.Security.Cryptography.Pkcs.9.0.6\lib\net462\System.Security.Cryptography.Pkcs.dll
-
-
- packages\System.Security.Cryptography.ProtectedData.9.0.6\lib\net462\System.Security.Cryptography.ProtectedData.dll
-
-
- packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll
+
+ packages\System.Security.Cryptography.Pkcs.10.0.5\lib\net462\System.Security.Cryptography.Pkcs.dll
-
- packages\System.Text.Encodings.Web.9.0.6\lib\net462\System.Text.Encodings.Web.dll
+
+ packages\System.Text.Encodings.Web.10.0.5\lib\net462\System.Text.Encodings.Web.dll
-
- packages\System.Text.Json.9.0.6\lib\net462\System.Text.Json.dll
+
+ packages\System.Text.Json.10.0.5\lib\net462\System.Text.Json.dll
packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll
@@ -251,9 +233,6 @@
-
- packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
-
@@ -385,9 +364,9 @@
このプロジェクトは、このコンピューター上にない NuGet パッケージを参照しています。それらのパッケージをダウンロードするには、[NuGet パッケージの復元] を使用します。詳細については、http://go.microsoft.com/fwlink/?LinkID=322105 を参照してください。見つからないファイルは {0} です。
-
+
-
+