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 _original = null; #endregion @@ -143,6 +152,14 @@ public object this[int index] { //object temp = null; + // **値を書き換える前に、変更前の値を退避する。**(#567) + // 書き換えたあとでは、退避されるのが「変更後の値」になってしまう。 + // Added / Deleted の行は退避しない(変更前という概念が無い)。 + if (this.RowState != DTRowState.Added && this.RowState != DTRowState.Deleted) + { + this.KeepOriginalIfNeeded(); + } + // 列を確認し DTColumn dtCol = (DTColumn)this._cols[index]; @@ -268,6 +285,108 @@ public object this[int index] #endregion + #region 変更前の値(Original) + + /// 変更前の値を取得する + /// 列名 + /// セルのバージョン + /// セルの値 + /// + /// **Original が意味を持つのは、DTTable.KeepOriginal が真で、かつ Modified の行だけ**です。 + /// それ以外は現在の値が返ります(System.Data.DataRow のように例外にはなりません)。 + /// + public object this[string colName, DTRowVersion version] + { + get + { + // 列名チェック + if (this._cols.ColNameIndexMap.ContainsKey(colName)) + { + int index = (int)this._cols.ColNameIndexMap[colName]; + return this[index, version]; + } + else + { + // 列名が不正 + throw new Exception("A column name is inaccurate. "); + } + } + } + + /// 変更前の値を取得する + /// インデックス + /// セルのバージョン + /// セルの値 + public object this[int index, DTRowVersion version] + { + get + { + if (version == DTRowVersion.Original && this._original != null) + { + return this._original[index]; + } + + // 保持していない場合は現在の値 + return this._row[index]; + } + } + + /// 変更前の値を保持しているか + public bool HasOriginal + { + get + { + return (this._original != null); + } + } + + /// 変更前の値を退避する(初回のみ) + /// + /// **DTTable.KeepOriginal が真のときだけ退避する。** + /// 既に退避済みなら何もしない(2 回目以降に上書きすると、 + /// 「1 回目の変更後の値」が変更前の値になってしまう)。 + /// + private void KeepOriginalIfNeeded() + { + if (this._original != null) { return; } + if (!this._cols.KeepOriginal) { return; } + + this._original = new List(this._row); + } + + /// 変更前の値を、外部から設定する + /// 変更前の値 + /// + /// **復元のためだけに使う。**(FromDataTable、FromJsonObject) + /// 通常の編集では KeepOriginalIfNeeded が自動で退避する。 + /// + internal void SetOriginal(List original) + { + this._original = original; + } + + /// 変更前の値を破棄する + /// AcceptChanges から呼ばれる(現在値が確定値になるため)。 + internal void ClearOriginal() + { + this._original = null; + } + + /// 変更前の値へ戻す + /// RejectChanges から呼ばれる。 + internal void RejectChanges() + { + if (this._original != null) + { + this._row = this._original; + this._original = null; + } + + this.RowState = DTRowState.Unchanged; + } + + #endregion + #region サポート情報 /// 列情報の取得 diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRowVersion.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRowVersion.cs new file mode 100644 index 000000000..7bb5edb12 --- /dev/null +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRowVersion.cs @@ -0,0 +1,54 @@ +#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 + +//********************************************************************************** +//* クラス名 :DTRowVersion +//* クラス日本語名 :マーシャリング機能付き汎用DTO(セルのバージョン列挙型) +//* +//* 作成者 :玄人 幸道 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2026/08/18 玄人 幸道 新規作成(#567) +//********************************************************************************** + +using System; + +namespace Touryo.Infrastructure.Public.Dto +{ + /// セルのバージョン + /// + /// System.Data.DataRowVersion に相当する。 + /// DTTable.KeepOriginal が真のときだけ Original が意味を持つ。 + /// + public enum DTRowVersion : int + { + /// + /// 現在の値 + /// + Current = 1, + + /// + /// 変更前の値 + /// + /// + /// 変更していない行(Unchanged / Added / Deleted)では、現在の値と同じです。 + /// DTTable.KeepOriginal が偽の場合も、現在の値が返ります。 + /// + Original = 2 + } +} diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRows.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRows.cs index 919ca8f86..df1a71ddf 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRows.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTRows.cs @@ -35,6 +35,7 @@ //* 2010/11/11 前川 祐介 Silverlight対応(ジェネリック) //* 2011/10/09 西野 大介 国際化対応 //* 2026/08/14 玄人 幸道 DataRowStateをDTRowStateに改名(#544)。 +//* 2026/08/18 玄人 幸道 変更前の値(Original)の保持に対応(#567) //********************************************************************************** using System; diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTable.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTable.cs index d2732d048..b2b0123b1 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTable.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTable.cs @@ -37,6 +37,7 @@ //* 2011/10/09 西野 大介 国際化対応 //* 2026/08/14 玄人 幸道 ToDataTableで行ステータスを保つようにした(#544)。 //* 2026/08/14 玄人 幸道 DataRowStateをDTRowStateに改名(#544)。 +//* 2026/08/18 玄人 幸道 KeepOriginalを追加し、変更前の値を保持できるようにした(#567)。 //********************************************************************************** using System; @@ -60,6 +61,37 @@ public class DTTable #region 行列コレクション + /// 変更前の値(Original)を保持するか + /// + /// **既定は false(保持しない)。**(#567) + /// + /// <有効にする場面> + /// 全列の変更前の値を WHERE 条件に使う楽観排他を、 + /// DTTables 経由(WebAPI 転送・Session 往復)でも効かせたい場合。 + /// 保持しないと、往復後の Original に現在値が入り、 + /// 条件が必ず一致して他者の更新を上書きする。 + /// + /// <有効にしなくてよい場面> + /// timestamp(rowversion)列で排他する構成。 + /// その列 1 つで衝突が分かるため、全列を持ち回るのは負担になるだけ。 + /// + /// <設定するタイミング> + /// **編集を始める前に設定すること。** + /// 変更のたびに退避するかどうかを見るため、 + /// 途中で有効にしても、それ以前の変更は退避されない。 + /// + public bool KeepOriginal + { + set + { + this._tblStat.KeepOriginal = value; + } + get + { + return this._tblStat.KeepOriginal; + } + } + /// 列コレクション private DTColumns _cols; @@ -153,10 +185,13 @@ public DTTable(string tblName) /// 戻すと落ちる、という非対称な状態だった。 /// これでは受け取った側で Added / Modified / Deleted の振り分けができない。 /// - /// <Modified の元の値は復元されない> - /// DTRow は現在値と行ステータスだけを持ち、変更前の値を持たない。 - /// このため DataRowVersion.Original には現在値が入る。 - /// 区分(Added / Modified / Deleted)の判別を目的とした変換である。 + /// <Modified の元の値> + /// **KeepOriginal が真なら復元される。**(#567) + /// 偽の場合は DTRow が変更前の値を持たないため、 + /// DataRowVersion.Original には現在値が入る(従来どおり)。 + /// + /// 全列の Original を WHERE 条件に使う楽観排他を DTTables 経由で行うなら、 + /// KeepOriginal を有効にすること。timestamp 列で排他する構成では不要。 /// public DataTable ToDataTable() { @@ -184,10 +219,17 @@ public DataTable ToDataTable() // 行を新規作成 DataRow dr = dt.NewRow(); + // **Original を持つ行は、まず変更前の値で入れる。**(#567) + // このあと AcceptChanges で確定させると、それが Original になる。 + // 現在値は、行ステータスを復元するときに上書きする。 + bool useOriginal = (row.RowState == DTRowState.Modified && row.HasOriginal); + // 各列ごとに値を追加 foreach (DTColumn col in this.Cols) { - object value = row[col.ColName]; + object value = useOriginal + ? row[col.ColName, DTRowVersion.Original] + : row[col.ColName]; // **null は DBNull に置き換える。**(#544) // DTRow は値なしを null で持つが、DataRow は null を受け付けず @@ -216,7 +258,25 @@ public DataTable ToDataTable() break; case DTRowState.Modified: - dt.Rows[i].SetModified(); + // **確定済みの値が Original になっている。**(#567) + // ここで現在値を当てると、Original ≠ Current の行になる。 + if (added[i].HasOriginal) + { + foreach (DTColumn col in this.Cols) + { + object value = added[i][col.ColName]; + dt.Rows[i][col.ColName] = (value == null) ? DBNull.Value : value; + } + } + + // **SetModified は Unchanged の行にしか使えない。** + // 上で値を当てた場合、その時点で Modified になっているため、 + // ここで呼ぶと InvalidOperationException になる。 + // 値がすべて同じで Unchanged のままの場合もあるので、状態で判断する。 + if (dt.Rows[i].RowState == System.Data.DataRowState.Unchanged) + { + dt.Rows[i].SetModified(); + } break; case DTRowState.Deleted: @@ -239,11 +299,20 @@ public DataTable ToDataTable() /// System.Data.DataTableをDTTableに変換する /// /// 変換元のSystem.Data.DataTable + /// + /// 変更前の値(Original)を保持するか(既定は false) + /// /// 変換後のDTTable - public static DTTable FromDataTable(DataTable table) + /// + /// **keepOriginal は、ここで指定しないと取り込めない。**(#567) + /// 生成したあとに DTTable.KeepOriginal を立てても、 + /// 変換は済んでおり、変更前の値は既に失われている。 + /// + public static DTTable FromDataTable(DataTable table, bool keepOriginal = false) { // テーブル定義 DTTable dt = new DTTable(table.TableName); + dt.KeepOriginal = keepOriginal; // 列定義 foreach (DataColumn col in table.Columns) @@ -273,6 +342,22 @@ public static DTTable FromDataTable(DataTable table) } } + // **変更前の値を取り込む。**(#567) + // Modified の行にだけ Original がある。 + // 行ステータスを戻す前に入れる(戻したあとだと値の設定で退避が走り得る)。 + if (dt.KeepOriginal && row.RowState == System.Data.DataRowState.Modified) + { + List original = new List(); + + foreach (DataColumn col in table.Columns) + { + object o = row[col.ColumnName, DataRowVersion.Original]; + original.Add((o is System.DBNull) ? null : o); + } + + dr.SetOriginal(original); + } + // 行ステータスを復元 if (row.RowState == System.Data.DataRowState.Detached) { @@ -448,6 +533,41 @@ public void AcceptChanges() { // 行ステータスをUnchangedに変え、行の値を確定させる this.Rows[i].RowState = DTRowState.Unchanged; + + // **確定したので、変更前の値は捨てる。**(#567) + // 現在値が確定値になるため、持ち続けると次の変更で + // 「もっと前の値」が Original として残ってしまう。 + this.Rows[i].ClearOriginal(); + + i++; + } + } + } + + /// + /// 行の変更を取り消す + /// + /// + /// **KeepOriginal が真のときだけ、値が戻る。**(#567) + /// 保持していない場合、行ステータスだけが Unchanged に戻る。 + /// + /// Added の行は行リストから取り除き、Deleted の行は復帰させる。 + /// + public void RejectChanges() + { + int i = 0; // カウンタ用 + + while (i < this.Rows.Count) + { + if (this.Rows[i].RowState == DTRowState.Added) + { + // 追加された行は、無かったことにする + this.Rows.DeleteFromList(i); + } + else + { + // Modified は値を戻し、Deleted は復帰させる(どちらも Unchanged になる) + this.Rows[i].RejectChanges(); i++; } } diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTableStatus.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTableStatus.cs index 5d68154b6..46ff3b0c4 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTableStatus.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTableStatus.cs @@ -28,6 +28,7 @@ //* 日時 更新者 内容 //* ---------- ---------------- ------------------------------------------------- //* 2010/03/xx 西野 大介 新規作成 +//* 2026/08/18 玄人 幸道 変更前の値(Original)の保持に対応(#567) //********************************************************************************** namespace Touryo.Infrastructure.Public.Dto @@ -40,5 +41,13 @@ internal class DTTableStatus /// 行数 /// 外部からは使用できないようにする internal int RowsCount = 0; + + /// 変更前の値(Original)を保持するか + /// + /// 既定は false(保持しない)。(#567) + /// timestamp 列で排他する構成では不要なため、必要な表だけ有効にする。 + /// DTColumns と DTRows の両方から見えるよう、ここに置く。 + /// + internal bool KeepOriginal = false; } } diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTables.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTables.cs index 825bd75bb..e950b9182 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTables.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Dto/DTTables.cs @@ -36,6 +36,7 @@ //* 2026/08/14 玄人 幸道 値と文字列の相互変換の呼び先をDTColumnに変更(#544)。 //* 2026/08/14 玄人 幸道 DataSetとの相互変換を追加(#544)。 //* 2026/08/14 玄人 幸道 DataRowStateをDTRowStateに改名(#544)。 +//* 2026/08/18 玄人 幸道 変更前の値(Original)の保持に対応(#567) //********************************************************************************** using System; @@ -427,6 +428,13 @@ public class JsonTable /// 表名 public string tbl { get; set; } + /// 変更前の値(Original)を保持するか + /// + /// 受け取った側が同じ方針で編集を続けられるように運ぶ(#567)。 + /// 古い形式には無いため、既定(false)で読める。 + /// + public bool korg { get; set; } + /// 列(順序に意味があるため配列) public List cols { get; set; } @@ -450,6 +458,13 @@ public class JsonRow /// セル(列と同じ順序。nullはnullのまま) public List cels { get; set; } + /// 変更前のセル(列と同じ順序) + /// + /// **KeepOriginal が真で、かつ Modified の行にだけ入る。**(#567) + /// それ以外は null なので、転送量は増えない。 + /// + public List ocels { get; set; } + /// 行ステータス(DTRowStateの数値) public int state { get; set; } } @@ -469,6 +484,7 @@ public JsonTables ToJsonObject() { JsonTable jTbl = new JsonTable(); jTbl.tbl = dt.TableName; + jTbl.korg = dt.KeepOriginal; // 列情報 jTbl.cols = new List(); @@ -495,6 +511,21 @@ public JsonTables ToJsonObject() } jRow.state = (int)dr.RowState; + + // **変更前の値は、持っている行にだけ付ける。**(#567) + // 持っていない行に現在値を入れて運ぶと、 + // 受け取った側で「変更前=現在値」と区別が付かなくなる。 + if (dr.HasOriginal) + { + jRow.ocels = new List(); + + for (int i = 0; i < dt.Cols.Count; i++) + { + jRow.ocels.Add( + DTColumn.StringFromPrimitivetype(dr[i, DTRowVersion.Original], false)); + } + } + jTbl.rows.Add(jRow); } @@ -520,6 +551,10 @@ public void FromJsonObject(JsonTables jTbls) DTTable tbl = new DTTable(jTbl.tbl); this.Add(tbl); + // **方針も引き継ぐ。**(#567) + // 受け取った側が編集を続けるとき、同じように退避されるようにする。 + tbl.KeepOriginal = jTbl.korg; + // 列情報 if (jTbl.cols != null) { @@ -551,6 +586,32 @@ public void FromJsonObject(JsonTables jTbls) } } + // **変更前の値を復元する。**(#567) + // 編集による退避(KeepOriginalIfNeeded)は Unchanged からの初回だけなので、 + // ここでは通らない(AddNew 直後は Added、値の設定でも Added のまま)。 + // そのため、外部から明示的に入れる。 + if (jRow.ocels != null) + { + List original = new List(); + + for (int i = 0; i < jRow.ocels.Count; i++) + { + string cel = jRow.ocels[i]; + + if (cel == null) + { + original.Add(null); + } + else + { + DTColumn col = (DTColumn)tbl.Cols.ColsInfo[i]; + original.Add(DTColumn.PrimitivetypeFromString(col.ColType, cel)); + } + } + + row.SetOriginal(original); + } + // 行ステータス(値を設定すると Modified になるため、最後に戻す) row.RowState = (DTRowState)jRow.state; } diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Public_net48.csproj b/root/programs/CS/Frameworks/Infrastructure/Public/Public_net48.csproj index e8d1b70a7..4c23ee4d6 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Public_net48.csproj +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Public_net48.csproj @@ -86,6 +86,7 @@ + diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Public_netcore100.csproj b/root/programs/CS/Frameworks/Infrastructure/Public/Public_netcore100.csproj index 70bed1f85..497a54e92 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Public_netcore100.csproj +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Public_netcore100.csproj @@ -89,12 +89,12 @@ - - - - - - + + + + + + diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Security/Jwt/EccPublicKeyConverter.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Security/Jwt/EccPublicKeyConverter.cs index 49bcf7034..9d920ceb1 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Security/Jwt/EccPublicKeyConverter.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Security/Jwt/EccPublicKeyConverter.cs @@ -34,10 +34,14 @@ //* 2019/06/25 西野 大介 インスタンス・メソッド化(ES256, 384, 512対応) //* 2026/08/01 玄人 幸道 jose-jwtへの依存を解消(JwkToCngをBCLのみで実装) //* ※ 楕円曲線の決定にcrvを使用するため、jose-jwt非互換。 +//* 2026/08/21 玄人 幸道 CA1416対応(JwkToCngにSupportedOSPlatformを付与) //********************************************************************************** using System; using System.Collections.Generic; +#if NETCOREAPP +using System.Runtime.Versioning; +#endif using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -244,6 +248,11 @@ public string CngToJwk( /// JwkToCng /// string /// CngKey(公開鍵) + // **CngKey / ECDsaCng は Windows 専用である。**(CA1416) + // 隠さず、要求する OS を宣言する。net48 には属性が無いため囲む。 +#if NETCOREAPP + [SupportedOSPlatform("windows")] +#endif public CngKey JwkToCng(string jwkString) { return this.JwkToCng( @@ -265,6 +274,11 @@ public CngKey JwkToCng(string jwkString) /// なお crv は RFC 7517/RFC 7518 において EC 鍵の必須メンバであり、 /// 姉妹メソッドの JwkToParam も従来から crv を必須としている。 /// + // **CngKey / ECDsaCng は Windows 専用である。**(CA1416) + // 隠さず、要求する OS を宣言する。net48 には属性が無いため囲む。 +#if NETCOREAPP + [SupportedOSPlatform("windows")] +#endif public CngKey JwkToCng(Dictionary jwk) { ECParameters ecParams = new ECParameters(); diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Security/Public.Security_netcore100.csproj b/root/programs/CS/Frameworks/Infrastructure/Public/Security/Public.Security_netcore100.csproj index 2d7b67290..18c6ee9c3 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Security/Public.Security_netcore100.csproj +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Security/Public.Security_netcore100.csproj @@ -37,7 +37,7 @@ - + diff --git a/root/programs/CS/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.csproj b/root/programs/CS/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.csproj index 0e953d13e..67c3131bc 100644 --- a/root/programs/CS/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.csproj +++ b/root/programs/CS/Frameworks/Infrastructure/ServiceInterface/ASPNETWebService/ASPNETWebService.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\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} です。 - + - +