diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8e0afa9..1fbd0de 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1 +1,148 @@
-慮敭›䥃ਊ湥㩶 䘠剏䕃䩟噁十剃偉彔䍁䥔乏当佔也䑏㉅㨴琠畲漊㩮 瀠獵㩨 †戠慲据敨㩳嬠慭湩†異汬牟煥敵瑳††牢湡档獥›浛楡嵮 眠牯晫潬彷楤灳瑡档瀊牥業獳潩獮†潣瑮湥獴›敲摡ਊ潣据牵敲据㩹 朠潲灵›楣筻朠瑩畨潷歲汦睯素笤⁻楧桴扵爮晥素†慣据汥椭牰杯敲獳›牴敵ਊ潪獢†畢汩ⵤ整瑳††畲獮漭㩮甠畢瑮慬整瑳 †猠整獰†††獵獥›捡楴湯⽳档捥潫瑵癀 ††ⴠ渠浡㩥匠瑥灵⸠䕎⁔䑓ੋ††††獵獥›捡楴湯⽳敳畴⵰潤湴瑥癀††††楷桴†††††潤湴瑥瘭牥楳湯›〱〮砮ਊ†††慮敭›慃档畎敇⁴慰正条獥 †††甠敳㩳愠瑣潩獮振捡敨癀††††楷桴†††††慰桴›⽾渮杵瑥瀯捡慫敧ੳ†††††敫㩹␠筻爠湵敮獯素畮敧笤⁻慨桳楆敬⡳⨧⼪⸪獣牰橯Ⱗ✠⨪术潬慢獪湯Ⱗ✠⨪⨯献湬❸
絽 ††††爠獥潴敲欭祥㩳簠 †††††␠筻爠湵敮獯素畮敧ਊ†††慮敭›畒䥃瘠慩丠歵††††畲㩮搠瑯敮⁴畲ⴭ牰橯捥⁴畢汩⽤扟極摬挮灳潲ⴭⴠ爭潯⁴ⴭ慴杲瑥䌠ⴭ潣普杩牵瑡潩敒敬獡 ††ⴠ渠浡㩥唠汰慯整瑳爠獥汵獴 †††椠㩦愠睬祡⡳††††獵獥›捡楴湯⽳灵潬摡愭瑲晩捡䁴㑶 †††眠瑩㩨 ††††渠浡㩥琠獥敲畳瑬ੳ†††††慰桴›敔瑳敒畳瑬⽳ ††††椠ⵦ潮昭汩獥昭畯摮›慷湲ਊ†††慮敭›灕潬摡挠摯潣敶慲敧 †††椠㩦愠睬祡⡳††††獵獥›捡楴湯⽳灵潬摡愭瑲晩捡䁴㑶 †††眠瑩㩨 ††††渠浡㩥挠摯ⵥ潣敶慲敧 ††††瀠瑡㩨吠獥剴獥汵獴⨯⼪潣敶慲敧挮扯牥畴慲砮汭 ††††椠ⵦ潮昭汩獥昭畯摮›慷湲ਊ†慳灭敬㩳 †爠湵湯›扵湵畴氭瑡獥ੴ††瑳灥㩳 ††ⴠ渠浡㩥䌠敨正畯⁴敄楳湧慐瑴牥獮 †††甠敳㩳愠瑣潩獮振敨正畯䁴㑶 †††眠瑩㩨 ††††瀠瑡㩨䐠獥杩偮瑡整湲ੳ ††ⴠ渠浡㩥䌠敨正畯⁴敄楳湧慐瑴牥獮匮浡汰獥 †††甠敳㩳愠瑣潩獮振敨正畯䁴㑶 †††眠瑩㩨 ††††爠灥獯瑩牯㩹匠祫汭⽹敄楳湧慐瑴牥獮匮浡汰獥 ††††瀠瑡㩨䐠獥杩偮瑡整湲慓灭敬ੳ ††ⴠ渠浡㩥匠瑥灵⸠䕎⁔䑓ੋ††††獵獥›捡楴湯⽳敳畴⵰潤湴瑥癀††††楷桴†††††潤湴瑥瘭牥楳湯›〱〮砮ਊ†††慮敭›慃档畎敇⁴慰正条獥 †††甠敳㩳愠瑣潩獮振捡敨癀††††楷桴†††††慰桴›⽾渮杵瑥瀯捡慫敧ੳ†††††敫㩹␠筻爠湵敮獯素畮敧灤猭浡汰獥筻栠獡䙨汩獥✨敄楳湧慐瑴牥獮匮浡汰獥⨯⼪⸪獣牰橯Ⱗ✠敄楳湧慐瑴牥獮匮浡汰獥术潬慢獪湯Ⱗ✠敄楳湧慐瑴牥獮⨯⼪⸪獣牰橯⤧素†††††敲瑳牯ⵥ敫獹›††††††笤⁻畲湮牥漮絽渭杵瑥搭⵰慳灭敬 †††††␠筻爠湵敮獯素畮敧ਊ†††慮敭›畒楳汢湩慳灭敬楶畎敫 †††眠牯楫杮搭物捥潴祲›敄楳湧慐瑴牥獮匮浡汰獥 †††爠湵›潤湴瑥爠湵ⴠ瀭潲敪瑣戠極摬弯畢汩獣牰橯ⴠⴭ潲瑯⸠ⴠ琭牡敧⁴楃ⴠ挭湯楦畧慲楴湯删汥慥敳ਊ†畢汩ⵤ整瑳渭瑥㠴††畲獮漭㩮眠湩潤獷氭瑡獥ੴ††瑳灥㩳 ††ⴠ甠敳㩳愠瑣潩獮振敨正畯䁴㑶ਊ†††慮敭›敓畴⁰丮呅匠䭄 †††甠敳㩳愠瑣潩獮猯瑥灵搭瑯敮䁴㑶 †††眠瑩㩨 ††††搠瑯敮敶獲潩㩮ㄠ⸰⸰ ††ⴠ渠浡㩥䌠捡敨丠䝵瑥瀠捡慫敧ੳ††††獵獥›捡楴湯⽳慣档䁥㑶 †††眠瑩㩨 ††††瀠瑡㩨縠ⸯ畮敧⽴慰正条獥 ††††欠祥›笤⁻畲湮牥漮絽渭杵瑥筻栠獡䙨汩獥✨⨪⨯挮灳潲❪⨧⼪汧扯污樮潳❮⨧⼪⸪汳确⤧素†††††敲瑳牯ⵥ敫獹›††††††笤⁻畲湮牥漮絽渭杵瑥ਭ ††ⴠ渠浡㩥删湵渠瑥㠴琠獥獴瘠慩丠歵††††畲㩮搠瑯敮⁴畲ⴭ牰橯捥⁴畢汩⽤扟極摬挮灳潲ⴭⴠ爭潯⁴ⴭ慴杲瑥䌠乩瑥㠴ⴠ挭湯楦畧慲楴湯删汥慥敳ਊ†††慮敭›灕潬摡渠瑥㠴琠獥⁴敲畳瑬ੳ††††晩›污慷獹⤨ †††甠敳㩳愠瑣潩獮甯汰慯ⵤ牡楴慦瑣癀††††楷桴†††††慮敭›整瑳爭獥汵獴渭瑥㠴 ††††瀠瑡㩨吠獥剴獥汵獴ਯ†††††晩渭ⵯ楦敬潦湵㩤眠牡੮ 瀠捡㩫 †爠湵湯›扵湵畴氭瑡獥ੴ††敮摥㩳嬠畢汩ⵤ整瑳畢汩ⵤ整瑳渭瑥㠴慳灭敬嵳 †猠整獰†††獵獥›捡楴湯⽳档捥潫瑵癀 ††ⴠ渠浡㩥匠瑥灵⸠䕎⁔䑓ੋ††††獵獥›捡楴湯⽳敳畴⵰潤湴瑥癀††††楷桴†††††潤湴瑥瘭牥楳湯›〱〮砮ਊ†††慮敭›慃档畎敇⁴慰正条獥 †††甠敳㩳愠瑣潩獮振捡敨癀††††楷桴†††††慰桴›⽾渮杵瑥瀯捡慫敧ੳ†††††敫㩹␠筻爠湵敮獯素畮敧笤⁻慨桳楆敬⡳⨧⼪⸪獣牰橯Ⱗ✠⨪术潬慢獪湯Ⱗ✠⨪⨯献湬❸
絽 ††††爠獥潴敲欭祥㩳簠 †††††␠筻爠湵敮獯素畮敧ਊ†††慮敭›慐正愠摮瘠牥晩⁹楶畎敫 †††爠湵›潤湴瑥爠湵ⴠ瀭潲敪瑣戠極摬弯畢汩獣牰橯ⴠⴭ潲瑯⸠ⴠ琭牡敧⁴楃慐正ⴠ挭湯楦畧慲楴湯删汥慥敳ਊ†††慮敭›灕潬摡瀠捡慫敧ੳ††††獵獥›捡楴湯⽳灵潬摡愭瑲晩捡䁴㑶 †††眠瑩㩨 ††††渠浡㩥渠杵瑥瀭捡慫敧ੳ†††††慰桴›牡楴慦瑣⽳慰正条⽥⸪畮歰੧†††††晩渭ⵯ楦敬潦湵㩤攠牲牯�
\ No newline at end of file
+name: CI
+
+env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: ci-${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build-test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/global.json', '**/*.slnx') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ - name: Run CI via Nuke
+ run: dotnet run --project build/_build.csproj -- --root . --target Ci --configuration Release
+
+ - name: Upload test results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-results
+ path: TestResults/
+ if-no-files-found: warn
+
+ - name: Upload code coverage
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: code-coverage
+ path: TestResults/**/coverage.cobertura.xml
+ if-no-files-found: warn
+
+ samples:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout DesignPatterns
+ uses: actions/checkout@v4
+ with:
+ path: DesignPatterns
+
+ - name: Checkout DesignPatterns.Samples
+ uses: actions/checkout@v4
+ with:
+ repository: Skymly/DesignPatterns.Samples
+ # Paired PR branches share a name; main pushes use main.
+ ref: ${{ github.head_ref || github.ref_name }}
+ path: DesignPatterns.Samples
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-dp-samples-${{ hashFiles('DesignPatterns.Samples/**/*.csproj', 'DesignPatterns.Samples/global.json', 'DesignPatterns/**/*.csproj') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-dp-samples-
+ ${{ runner.os }}-nuget-
+
+ - name: Run sibling samples via Nuke
+ working-directory: DesignPatterns.Samples
+ run: dotnet run --project build/_build.csproj -- --root . --target Ci --configuration Release
+
+ build-test-net48:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/global.json', '**/*.slnx') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ - name: Run net48 tests via Nuke
+ run: dotnet run --project build/_build.csproj -- --root . --target CiNet48 --configuration Release
+
+ - name: Upload net48 test results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-results-net48
+ path: TestResults/
+ if-no-files-found: warn
+
+ pack:
+ runs-on: ubuntu-latest
+ needs: [build-test, build-test-net48, samples]
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/global.json', '**/*.slnx') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ - name: Pack and verify via Nuke
+ run: dotnet run --project build/_build.csproj -- --root . --target CiPack --configuration Release
+
+ - name: Upload packages
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget-packages
+ path: artifacts/package/*.nupkg
+ if-no-files-found: error
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ba9fab1..c98fe74 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1 +1,90 @@
-慮敭›畐汢獩畎敇ੴ攊癮†但䍒彅䅊䅖䍓䥒呐䅟呃佉华呟彏低䕄㐲›牴敵ਊ湯†異桳††慴獧†††瘢∪ 眠牯晫潬彷楤灳瑡档††湩異獴†††敶獲潩㩮 †††搠獥牣灩楴湯›倢捡慫敧瘠牥楳湯⠠〠ㄮ〮瀭敲楶睥⤱ਢ††††敲畱物摥›牴敵ਊ数浲獩楳湯㩳 挠湯整瑮㩳爠慥†慰正条獥›牷瑩樊扯㩳 瀠扵楬桳††晩›潣瑮楡獮昨潲䩭潳⡮嬧匢祫汭≹∬祷びㄶ∰❝Ⱙ朠瑩畨捡潴⥲ †爠湵湯›扵湵畴氭瑡獥ੴ †猠整獰†††獵獥›捡楴湯⽳档捥潫瑵癀 ††ⴠ渠浡㩥匠瑥灵⸠䕎⁔䑓ੋ††††獵獥›捡楴湯⽳敳畴⵰潤湴瑥癀††††楷桴†††††潤湴瑥瘭牥楳湯›〱〮砮ਊ†††慮敭›敒潳癬敶獲潩੮††††桳汥㩬戠獡੨††††畲㩮簠 ††††椠孛∠笤䥇䡔䉕䕟䕖呎也䵁絅•㴽∠潷歲汦睯摟獩慰捴≨崠㭝琠敨੮††††††䕖卒佉㵎␢筻朠瑩畨癥湥湩異獴瘮牥楳湯素≽ ††††攠楬孛∠笤䥇䡔䉕剟䙅也䵁絅•㴽瘠嵝※桴湥 †††††嘠剅䥓乏∽笤䥇䡔䉕剟䙅也䵁⍅絶ਢ†††††汥敳 †††††攠档䔢牲牯›慴畭瑳猠慴瑲眠瑩瘧⸧䌠牵敲瑮爠晥›笤䥇䡔䉕剟䙅也䵁絅ਢ††††††硥瑩ㄠ ††††映੩ ††††椠孛ⴠ⁺␢噻剅䥓乏≽崠㭝琠敨੮††††††捥潨∠牅潲㩲嘠剅䥓乏椠浥瑰≹ †††††攠楸⁴†††††楦ਊ†††††捥潨∠畐汢獩楨杮瘠牥楳湯›笤䕖卒佉絎ਢ†††††捥潨∠䕖卒佉㵎笤䕖卒佉絎•㸾∠䜤呉啈彂久≖ਊ†††慮敭›慐正敶楲祦湡異汢獩楶畎敫 †††攠癮†††††䕖卒佉㩎␠筻攠癮嘮剅䥓乏素†††††啎䕇彔偁彉䕋㩙␠筻猠捥敲獴丮䝕呅䅟䥐䭟奅素†††††䥇䡔䉕呟䭏久›笤⁻敳牣瑥䥇䡔䉕呟䭏久素††††畲㩮簠 ††††猠瑥ⴠ略楰数慦汩 ††††搠瑯敮⁴畲ⴭ牰橯捥⁴畢汩⽤扟極摬挮灳潲ⴭⴠ爭潯⁴ⴭ慴杲瑥倠扵楬桳ⴠ挭湯楦畧慲楴湯删汥慥敳㈠☾‱⁼整異汢獩潬੧ ††ⴠ渠浡㩥唠汰慯異汢獩潬੧††††晩›污慷獹⤨ †††甠敳㩳愠瑣潩獮甯汰慯ⵤ牡楴慦瑣癀††††楷桴†††††慮敭›畮敧異汢獩潬੧†††††慰桴›異汢獩潬੧†††††晩渭ⵯ楦敬潦湵㩤眠牡੮ ††ⴠ渠浡㩥唠汰慯慰正条牡楴慦瑣ੳ††††晩›污慷獹⤨ †††甠敳㩳愠瑣潩獮甯汰慯ⵤ牡楴慦瑣癀††††楷桴†††††慮敭›畮敧慰正条獥 ††††瀠瑡㩨愠瑲晩捡獴瀯捡慫敧⨯渮灵杫 ††††椠ⵦ潮昭汩獥昭畯摮›慷湲ਊ†††慮敭›牗瑩異汢獩畳浭牡††††晩›污慷獹⤨ †††猠敨汬›慢桳 †††爠湵›†††††††††††捥潨∠⌣丠䝵瑥倠扵楬桳匠浵慭祲ਢ††††††捥潨∠ਢ††††††捥潨∠敖獲潩㩮尠①噻剅䥓乏ⴺ湵湫睯絮恜ਢ††††††捥潨∠牔杩敧㩲尠①䝻呉啈彂噅久彔䅎䕍屽≠ †††††攠档ⴢ删晥›恜笤䥇䡔䉕剟䙅屽≠ †††††攠档ⴢ䄠瑣牯›恜笤䥇䡔䉕䅟呃剏屽≠ †††††攠档ⴢ䨠扯猠慴畴㩳尠①筻樠扯献慴畴絽恜ਢ†††††⁽㸾∠䜤呉啈彂呓偅卟䵕䅍奒ਢ
\ No newline at end of file
+name: Publish NuGet
+
+env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
+on:
+ push:
+ tags:
+ - "v*"
+ workflow_dispatch:
+ inputs:
+ version:
+ description: "Package version (e.g. 0.1.0-preview1)"
+ required: true
+
+permissions:
+ contents: read
+ packages: write
+
+jobs:
+ publish:
+ if: contains(fromJson('["Skymly","wys0610"]'), github.actor)
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Resolve version
+ shell: bash
+ run: |
+ if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
+ VERSION="${{ github.event.inputs.version }}"
+ elif [[ "${GITHUB_REF_NAME}" == v* ]]; then
+ VERSION="${GITHUB_REF_NAME#v}"
+ else
+ echo "Error: tag must start with 'v'. Current ref: ${GITHUB_REF_NAME}"
+ exit 1
+ fi
+
+ if [[ -z "${VERSION}" ]]; then
+ echo "Error: VERSION is empty"
+ exit 1
+ fi
+
+ echo "Publishing version: ${VERSION}"
+ echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
+
+ - name: Pack, verify, and publish via Nuke
+ env:
+ VERSION: ${{ env.VERSION }}
+ NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ set -euo pipefail
+ dotnet run --project build/_build.csproj -- --root . --target Publish --configuration Release 2>&1 | tee publish.log
+
+ - name: Upload publish log
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget-publish-log
+ path: publish.log
+ if-no-files-found: warn
+
+ - name: Upload package artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget-packages
+ path: artifacts/package/*.nupkg
+ if-no-files-found: warn
+
+ - name: Write publish summary
+ if: always()
+ shell: bash
+ run: |
+ {
+ echo "## NuGet Publish Summary"
+ echo ""
+ echo "- Version: \`${VERSION:-unknown}\`"
+ echo "- Trigger: \`${GITHUB_EVENT_NAME}\`"
+ echo "- Ref: \`${GITHUB_REF}\`"
+ echo "- Actor: \`${GITHUB_ACTOR}\`"
+ echo "- Job status: \`${{ job.status }}\`"
+ } >> "$GITHUB_STEP_SUMMARY"
diff --git a/AGENTS.md b/AGENTS.md
index 88665c3..b86871e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -10,7 +10,7 @@
| **远端** | https://github.com/Skymly/DesignPatterns(**公开**);文件夹名 `DesignPatterns` = 仓库名 |
| **许可证** | [MIT](LICENSE) |
| **阶段** | **早期预览**:公共 API、生成器产出与 `DP###` 诊断**尚未稳定**(见 [README.md](README.md)) |
-| **NuGet** | 元包 **`Skymly.DesignPatterns`**;当前 **`0.2.3-preview1`**;`release.yml` + Nuke `Publish` → nuget.org + GitHub Packages |
+| **NuGet** | 元包 **`Skymly.DesignPatterns`**;当前 **`0.2.3-preview2`**;`release.yml` + Nuke `Publish` → nuget.org + GitHub Packages |
| **Sibling 仓库** | [DesignPatterns.Samples](https://github.com/Skymly/DesignPatterns.Samples)、[DesignPatterns.Docs](https://github.com/Skymly/DesignPatterns.Docs) — 工作区路径 `Skymly/DesignPatterns/DesignPatterns.Samples/`、`Skymly/DesignPatterns/DesignPatterns.Docs/` |
## 项目是什么
@@ -258,7 +258,7 @@ git push origin v0.1.0-preview3
版本与变更记录:
-- 版本号:`VersionPrefix` + `VersionSuffix`(当前 `0.2.3-preview1`),单一真相源为 [`Directory.Build.props`](Directory.Build.props);发版时可通过环境变量 `VERSION` 覆盖。
+- 版本号:`VersionPrefix` + `VersionSuffix`(当前 `0.2.3-preview2`),单一真相源为 [`Directory.Build.props`](Directory.Build.props);发版时可通过环境变量 `VERSION` 覆盖。
- 变更记录:[`CHANGELOG.md`](CHANGELOG.md)(Keep a Changelog 风格)。
---
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5a10d3..e8bffab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.2.3-preview2] - 2026-07-20
+
+### Changed
+
+- **Removed AppSettings extension**: deleted the non-packable `DesignPatterns.Extensions.AppSettings` project. Use `Skymly.DesignPatterns.Extensions.Configuration` with an `IConfiguration` adapter for legacy `App.config` hosts.
+- **Configuration extension packable**: `DesignPatterns.Extensions.Configuration` now ships as `Skymly.DesignPatterns.Extensions.Configuration`.
+- **Encoding fix**: restored UTF-8 content for files previously corrupted by the Nuke upgrade API push / bad re-encode (`Directory.Packages.props`, `global.json`, build project, CI workflows).
+
## [0.2.3-preview1] - 2026-07-18
### Added
@@ -130,6 +138,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `WithTrackingName` to generator pipelines and added cache-hit regression tests.
- Migrated analyzer tests to Verify snapshots for consistency with generator tests.
+[0.2.3-preview2]: https://github.com/Skymly/DesignPatterns/releases/tag/v0.2.3-preview2
[0.2.3-preview1]: https://github.com/Skymly/DesignPatterns/releases/tag/v0.2.3-preview1
[0.2.0-preview1]: https://github.com/Skymly/DesignPatterns/releases/tag/v0.2.0-preview1
[0.1.0-preview7]: https://github.com/Skymly/DesignPatterns/releases/tag/v0.1.0-preview7
diff --git a/DesignPatterns.Extensions.AppSettings/DesignPatterns.Extensions.AppSettings.csproj b/DesignPatterns.Extensions.AppSettings/DesignPatterns.Extensions.AppSettings.csproj
deleted file mode 100644
index bbbd366..0000000
--- a/DesignPatterns.Extensions.AppSettings/DesignPatterns.Extensions.AppSettings.csproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- netstandard2.0;net8.0
- DesignPatterns.Extensions.AppSettings
- DesignPatterns.Extensions.AppSettings
- latest
- enable
- true
- false
-
-
-
-
-
-
-
-
-
-
-
diff --git a/DesignPatterns.Extensions.AppSettings/Properties/AssemblyInfo.cs b/DesignPatterns.Extensions.AppSettings/Properties/AssemblyInfo.cs
deleted file mode 100644
index edcdd09..0000000
--- a/DesignPatterns.Extensions.AppSettings/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("DesignPatterns.Extensions.AppSettings.Tests")]
-[assembly: InternalsVisibleTo("DesignPatterns.Extensions.AppSettings.Tests.Net48")]
diff --git a/DesignPatterns.Extensions.AppSettings/RegistryConfiguration.cs b/DesignPatterns.Extensions.AppSettings/RegistryConfiguration.cs
deleted file mode 100644
index 47484cc..0000000
--- a/DesignPatterns.Extensions.AppSettings/RegistryConfiguration.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using DesignPatterns.Behavioral;
-
-namespace DesignPatterns.Extensions.AppSettings;
-
-///
-/// Bridges to lookups.
-///
-public static class RegistryConfiguration
-{
- ///
- /// Reads an AppSettings value and resolves the matching strategy implementation.
- ///
- /// Strategy contract type.
- /// Registry to query.
- /// AppSettings key name.
- /// Fallback registry key when the AppSettings entry is missing or whitespace.
- /// The resolved strategy implementation.
- /// is null.
- /// is null or empty.
- /// The configured or default key cannot be resolved.
- public static TContract ResolveConfigured(
- IStrategyRegistry registry,
- string appSettingsKey,
- string? defaultKey = null)
- {
- if (TryResolveConfigured(registry, appSettingsKey, out var implementation, defaultKey))
- {
- return implementation!;
- }
-
- throw CreateException(registry, appSettingsKey, defaultKey);
- }
-
- ///
- /// Reads an AppSettings value and resolves the matching strategy implementation when possible.
- ///
- /// Strategy contract type.
- /// Registry to query.
- /// AppSettings key name.
- /// Resolved implementation when this method returns .
- /// Fallback registry key when the AppSettings entry is missing or whitespace.
- /// when a strategy was resolved; otherwise .
- /// is null.
- /// is null or empty.
- public static bool TryResolveConfigured(
- IStrategyRegistry registry,
- string appSettingsKey,
- out TContract? implementation,
- string? defaultKey = null)
- {
- if (registry is null)
- {
- throw new ArgumentNullException(nameof(registry));
- }
-
- if (string.IsNullOrEmpty(appSettingsKey))
- {
- throw new ArgumentException("AppSettings key must not be null or empty.", nameof(appSettingsKey));
- }
-
- var configuredValue = RegistryConfigurationAppSettings.GetValue(appSettingsKey);
- var strategyKey = string.IsNullOrWhiteSpace(configuredValue) ? defaultKey : configuredValue;
-
- if (string.IsNullOrWhiteSpace(strategyKey))
- {
- implementation = default;
- return false;
- }
-
- var resolvedKey = strategyKey!;
- if (registry.TryGet(resolvedKey, out implementation))
- {
- return true;
- }
-
- implementation = default;
- return false;
- }
-
- private static RegistryConfigurationException CreateException(
- IStrategyRegistry registry,
- string appSettingsKey,
- string? defaultKey)
- {
- var configuredValue = RegistryConfigurationAppSettings.GetValue(appSettingsKey);
- var registeredKeys = FormatKeys(registry.Keys);
-
- if (string.IsNullOrWhiteSpace(configuredValue))
- {
- if (string.IsNullOrWhiteSpace(defaultKey))
- {
- return new RegistryConfigurationException(
- $"AppSettings key '{appSettingsKey}' is missing or empty. Registered keys: {registeredKeys}.");
- }
-
- return new RegistryConfigurationException(
- $"AppSettings key '{appSettingsKey}' is missing or empty; default key '{defaultKey}' is not registered. Registered keys: {registeredKeys}.");
- }
-
- return new RegistryConfigurationException(
- $"AppSettings key '{appSettingsKey}' has value '{configuredValue}' which is not registered. Registered keys: {registeredKeys}.");
- }
-
- private static string FormatKeys(IReadOnlyCollection keys) =>
- keys.Count == 0
- ? "(none)"
- : string.Join(", ", keys.OrderBy(key => key, StringComparer.Ordinal));
-}
diff --git a/DesignPatterns.Extensions.AppSettings/RegistryConfigurationAppSettings.cs b/DesignPatterns.Extensions.AppSettings/RegistryConfigurationAppSettings.cs
deleted file mode 100644
index 7898aef..0000000
--- a/DesignPatterns.Extensions.AppSettings/RegistryConfigurationAppSettings.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-using System.Configuration;
-
-namespace DesignPatterns.Extensions.AppSettings;
-
-internal static class RegistryConfigurationAppSettings
-{
- internal static Func GetValue { get; set; } = key => ConfigurationManager.AppSettings[key];
-}
diff --git a/DesignPatterns.Extensions.AppSettings/RegistryConfigurationException.cs b/DesignPatterns.Extensions.AppSettings/RegistryConfigurationException.cs
deleted file mode 100644
index 5167fb5..0000000
--- a/DesignPatterns.Extensions.AppSettings/RegistryConfigurationException.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-
-namespace DesignPatterns.Extensions.AppSettings;
-
-///
-/// Thrown when an AppSettings-backed strategy key cannot be resolved from a registry.
-///
-public sealed class RegistryConfigurationException : Exception
-{
- ///
- /// Initializes a new instance of the class.
- ///
- public RegistryConfigurationException()
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The error message.
- public RegistryConfigurationException(string message)
- : base(message)
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The error message.
- /// The inner exception.
- public RegistryConfigurationException(string message, Exception innerException)
- : base(message, innerException)
- {
- }
-}
diff --git a/DesignPatterns.Extensions.Configuration/DesignPatterns.Extensions.Configuration.csproj b/DesignPatterns.Extensions.Configuration/DesignPatterns.Extensions.Configuration.csproj
index a72d414..0df4e5f 100644
--- a/DesignPatterns.Extensions.Configuration/DesignPatterns.Extensions.Configuration.csproj
+++ b/DesignPatterns.Extensions.Configuration/DesignPatterns.Extensions.Configuration.csproj
@@ -7,15 +7,25 @@
latest
enable
true
- false
+ true
+ Skymly.DesignPatterns.Extensions.Configuration
+ Skymly DesignPatterns Configuration Extensions
+ IConfiguration bridge to strategy registries for Skymly.DesignPatterns.
+ design-patterns;configuration;strategy
+ README.md
+
+
+
+
-
+
+
diff --git a/DesignPatterns.slnx b/DesignPatterns.slnx
index 021422a..b487bc1 100644
--- a/DesignPatterns.slnx
+++ b/DesignPatterns.slnx
@@ -5,8 +5,6 @@
-
-
@@ -14,7 +12,6 @@
-
diff --git a/Directory.Build.props b/Directory.Build.props
index 1d657dc..0d78073 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,7 +7,7 @@
0.2.3
- preview1
+ preview2
$(VersionPrefix)-$(VersionSuffix)
$(Version)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f32326a..7bc8518 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1 +1,39 @@
-值潲敪瑣ਾ†值潲数瑲䝹潲灵ਾ††䴼湡条健捡慫敧敖獲潩獮敃瑮慲汬㹹牴敵⼼慍慮敧慐正条噥牥楳湯䍳湥牴污祬ਾ†⼼牐灯牥祴片畯㹰ਊ†䤼整䝭潲灵ਾ††值捡慫敧敖獲潩湉汣摵㵥挢癯牥敬潣汬捥潴≲嘠牥楳湯∽⸶⸰∰⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥䴢捩潲潳瑦䌮摯䅥慮祬楳湁污穹牥≳嘠牥楳湯∽⸳⸳∴⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥䴢捩潲潳瑦䌮摯䅥慮祬楳千慨灲•敖獲潩㵮㐢㠮〮•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽楍牣獯景潃敤湁污獹獩䌮桓牡潗歲灳捡獥•敖獲潩㵮㐢㠮〮•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽楍牣獯景䕎⹔敔瑳匮此•敖獲潩㵮ㄢ⸷⸸∰⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥丢䝵瑥倮捡慫楧杮•敖獲潩㵮㘢ㄮ⸴∳⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥丢歵潃浭湯•敖獲潩㵮ㄢ⸰⸱∰⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥倢汯晹汩≬嘠牥楳湯∽〱ㄮ⸰∰⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥匢祫汭敄楳湧慐瑴牥獮•敖獲潩㵮␢倨捡慫敧敖獲潩⥮•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽祓瑳浥匮捥牵瑩牃灹潴牧灡票堮汭•敖獲潩㵮ㄢ⸰⸰〱•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽祓瑳浥吮牨慥楤杮吮獡獫䔮瑸湥楳湯≳嘠牥楳湯∽⸴⸵∴⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥嘢牥晩畘楮≴嘠牥楳湯∽㠲㌮㈮•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽畸楮≴嘠牥楳湯∽⸲⸹∲⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥砢湵瑩爮湵敮楶畳污瑳摵潩•敖獲潩㵮㈢㠮㈮•㸯 㰠䤯整䝭潲灵ਾ 㰠瑉浥片畯⁰潃摮瑩潩㵮✢⠤慔杲瑥牆浡睥牯⥫‧㴽✠敮獴慴摮牡㉤〮∧ਾ††值捡慫敧敖獲潩湉汣摵㵥䴢捩潲潳瑦䔮瑸湥楳湯敄数摮湥祣湉敪瑣潩≮嘠牥楳湯∽⸶⸰∰⼠ਾ†⼼瑉浥片畯㹰ਊ†䤼整䝭潲灵䌠湯楤楴湯∽吨牡敧䙴慲敭潷歲✩℠‽渧瑥瑳湡慤摲⸲✰㸢 †㰠慐正条噥牥楳湯䤠据畬敤∽楍牣獯景硅整獮潩獮䐮灥湥敤据䥹橮捥楴湯•敖獲潩㵮㠢〮〮•㸯 㰠䤯整䝭潲灵ਾ 㰠瑉浥片畯㹰 †㰠慐正条噥牥楳湯䤠据畬敤∽畁潴慦≣嘠牥楳湯∽⸸⸲∰⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥匢獹整潃普杩牵瑡潩潃普杩牵瑡潩䵮湡条牥•敖獲潩㵮㠢〮〮•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽楍牣獯景硅整獮潩獮䌮湯楦畧慲楴湯•敖獲潩㵮㠢〮〮•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽楍牣獯景硅整獮潩獮䌮湯楦畧慲楴湯䄮獢牴捡楴湯≳嘠牥楳湯∽⸸⸰∰⼠ਾ††值捡慫敧敖獲潩湉汣摵㵥䴢捩潲潳瑦䔮瑸湥楳湯楄条潮瑳捩效污桴桃捥獫•敖獲潩㵮㠢〮〮•㸯 †㰠慐正条噥牥楳湯䤠据畬敤∽楍牣獯景硅整獮潩獮䰮杯楧杮•敖獲潩㵮㠢〮〮•㸯 㰠䤯整䝭潲灵ਾ⼼牐橯捥㹴�
\ No newline at end of file
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 887cde9..4bcbc73 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
> **本仓库已开源([MIT](LICENSE)),但处于早期开发阶段。**
>
> - **公共 API 尚未稳定**:类型、特性、源生成器产出、诊断 ID(`DP###`)及命名规则可能在任意版本变更,**不保证**语义化版本下的向后兼容。
-> - **NuGet 发布**:元包 **`Skymly.DesignPatterns`** **`0.2.3-preview1`** 发布至 [nuget.org](https://www.nuget.org/packages/Skymly.DesignPatterns) 与 GitHub Packages;亦支持本地 `./build.ps1 --target CiPack`。C# 命名空间仍为 `DesignPatterns.*`;旧 GitHub 包 ID `DesignPatterns`(preview1/2)已弃用。
+> - **NuGet 发布**:元包 **`Skymly.DesignPatterns`** **`0.2.3-preview2`** 发布至 [nuget.org](https://www.nuget.org/packages/Skymly.DesignPatterns) 与 GitHub Packages;亦支持本地 `./build.ps1 --target CiPack`。C# 命名空间仍为 `DesignPatterns.*`;旧 GitHub 包 ID `DesignPatterns`(preview1/2)已弃用。
> - **生产环境**:建议仅在可接受 API 变动的实验、内部工具或学习场景使用;在 API 稳定前请固定 commit 或 fork,并关注 [ROADMAP](docs/ROADMAP.md) 与 breaking changes。
**English:** This project is open source under the MIT license and in an **early preview**. Public APIs, generated code shapes, and diagnostics are **not stable** yet. Do not assume SemVer compatibility until a stability announcement.
@@ -96,10 +96,10 @@ DesignPatterns.slnx
## 快速开始
-### NuGet(`Skymly.DesignPatterns` `0.2.3-preview1`)
+### NuGet(`Skymly.DesignPatterns` `0.2.3-preview2`)
```xml
-
+
```
默认从 [nuget.org](https://api.nuget.org/v3/index.json) 还原。GitHub Packages 镜像(可选):
diff --git a/build/Program.cs b/build/Program.cs
index 777feb7..80d8252 100644
--- a/build/Program.cs
+++ b/build/Program.cs
@@ -42,6 +42,7 @@ sealed class Build : NukeBuild
AbsolutePath PackProject => Root / "DesignPatterns.Package" / "DesignPatterns.Package.csproj";
AbsolutePath DependencyInjectionPackProject => Root / "DesignPatterns.Extensions.DependencyInjection" / "DesignPatterns.Extensions.DependencyInjection.csproj";
AbsolutePath AutofacPackProject => Root / "DesignPatterns.Extensions.Autofac" / "DesignPatterns.Extensions.Autofac.csproj";
+ AbsolutePath ConfigurationPackProject => Root / "DesignPatterns.Extensions.Configuration" / "DesignPatterns.Extensions.Configuration.csproj";
static readonly string[] TestProjectRelativePaths =
[
@@ -50,13 +51,11 @@ sealed class Build : NukeBuild
"tests/DesignPatterns.Analyzers.Tests/DesignPatterns.Analyzers.Tests.csproj",
"tests/DesignPatterns.Extensions.DependencyInjection.Tests/DesignPatterns.Extensions.DependencyInjection.Tests.csproj",
"tests/DesignPatterns.Extensions.Autofac.Tests/DesignPatterns.Extensions.Autofac.Tests.csproj",
- "tests/DesignPatterns.Extensions.AppSettings.Tests/DesignPatterns.Extensions.AppSettings.Tests.csproj",
"tests/DesignPatterns.Extensions.Configuration.Tests/DesignPatterns.Extensions.Configuration.Tests.csproj",
];
static readonly string[] Net48TestProjectRelativePaths =
[
- "tests/DesignPatterns.Extensions.AppSettings.Tests.Net48/DesignPatterns.Extensions.AppSettings.Tests.Net48.csproj",
"tests/DesignPatterns.Extensions.Configuration.Tests.Net48/DesignPatterns.Extensions.Configuration.Tests.Net48.csproj",
];
@@ -65,6 +64,7 @@ sealed class Build : NukeBuild
[
"Skymly.DesignPatterns.Extensions.DependencyInjection",
"Skymly.DesignPatterns.Extensions.Autofac",
+ "Skymly.DesignPatterns.Extensions.Configuration",
];
public static int Main() => Execute(x => x.Ci);
@@ -142,7 +142,7 @@ sealed class Build : NukeBuild
{
PackageOutputDirectory.CreateOrCleanDirectory();
- foreach (AbsolutePath project in new[] { PackProject, DependencyInjectionPackProject, AutofacPackProject })
+ foreach (AbsolutePath project in new[] { PackProject, DependencyInjectionPackProject, AutofacPackProject, ConfigurationPackProject })
{
DotNetPack(s =>
{
@@ -337,7 +337,13 @@ void VerifyExtensionPackage(string packageId)
string assemblyName = packageId.Replace("Skymly.", string.Empty);
Assert.True(entries.Contains($"lib/netstandard2.0/{assemblyName}.dll"), $"{packageId}: missing netstandard2.0 assembly");
Assert.True(entries.Contains($"lib/net8.0/{assemblyName}.dll"), $"{packageId}: missing net8.0 assembly");
- Assert.True(entries.Contains($"build/{packageId}.targets"), $"{packageId}: missing automatic integration targets");
+ if (packageId is
+ "Skymly.DesignPatterns.Extensions.DependencyInjection" or
+ "Skymly.DesignPatterns.Extensions.Autofac")
+ {
+ Assert.True(entries.Contains($"build/{packageId}.targets"), $"{packageId}: missing automatic integration targets");
+ }
+
Assert.True(entries.Contains("README.md"), $"{packageId}: missing package README.md");
}
diff --git a/build/_build.csproj b/build/_build.csproj
index 4d81962..4dbf123 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -1 +1,19 @@
-值潲敪瑣匠此∽楍牣獯景䕎⹔摓≫ാഊ 㰠牐灯牥祴片畯㹰††似瑵異呴灹㹥硅㱥伯瑵異呴灹㹥††吼牡敧䙴慲敭潷歲渾瑥〱〮⼼慔杲瑥牆浡睥牯㹫††䤼灭楬楣啴楳杮㹳湥扡敬⼼浉汰捩瑩獕湩獧ാ †㰠畎汬扡敬放慮汢㱥丯汵慬汢㹥††丼歵呥汥浥瑥祲敖獲潩㹮㰱丯歵呥汥浥瑥祲敖獲潩㹮††䤼偳捡慫汢㹥慦獬㱥䤯偳捡慫汢㹥†⼼牐灯牥祴片畯㹰†䤼整䝭潲灵ാ †㰠慐正条剥晥牥湥散䤠据畬敤∽畎敫䌮浯潭≮⼠ാ †㰠ⴡ敋灥丠歵❥牴湡楳楴敶戠極摬搠灥湥敤据敩湯瀠瑡档摥瘠牥楳湯ⴠ㸭††值捡慫敧敒敦敲据湉汣摵㵥丢䝵瑥倮捡慫楧杮•牐癩瑡䅥獳瑥㵳愢汬•㸯††值捡慫敧敒敦敲据湉汣摵㵥匢獹整敓畣楲祴䌮祲瑰杯慲桰浘≬倠楲慶整獁敳獴∽污≬⼠ാ 㰠䤯整䝭潲灵ാഊ㰊倯潲敪瑣ാ�
\ No newline at end of file
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ 1
+ false
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/AppSettings.md b/docs/AppSettings.md
deleted file mode 100644
index babada5..0000000
--- a/docs/AppSettings.md
+++ /dev/null
@@ -1,119 +0,0 @@
-# AppSettings bridge — ConfigurationManager to strategy registries
-
-`DesignPatterns.Extensions.AppSettings` maps **`ConfigurationManager.AppSettings`** strings to **`IStrategyRegistry`** lookups, replacing hand-written `switch` blocks when selecting a provider by `App.config` key.
-
-Works with generated `{Contract}Registry.Instance` / `{Contract}Keys` from `[RegisterStrategy]` (see [Strategy.md](Strategy.md)).
-
-Related: [Configuration.md](Configuration.md) (`IConfiguration` bridge), [PluginAssemblies.md](PluginAssemblies.md) (multi-assembly hosts), [Autofac.md](Autofac.md) (container registration).
-
----
-
-## Package
-
-Independent extension assembly (not included in the `Skymly.DesignPatterns` meta-package), same policy as MSDI and Autofac extensions.
-
-```xml
-
-```
-
-Targets: `netstandard2.0` and `net8.0`. Depends on `System.Configuration.ConfigurationManager`.
-
----
-
-## API
-
-```csharp
-using DesignPatterns.Extensions.AppSettings;
-
-// Throws RegistryConfigurationException when the configured key cannot be resolved.
-var card = RegistryConfiguration.ResolveConfigured(
- CardMotionRegistry.Instance,
- appSettingsKey: "Card",
- defaultKey: CardMotionKeys.Alpha);
-
-// Non-throwing variant.
-if (RegistryConfiguration.TryResolveConfigured(
- CardMotionRegistry.Instance,
- "Card",
- out var motion,
- defaultKey: CardMotionKeys.Alpha))
-{
- // use motion
-}
-```
-
-### Resolution order
-
-1. Read `ConfigurationManager.AppSettings[appSettingsKey]`.
-2. When the value is missing or whitespace, use `defaultKey` when provided.
-3. Call `registry.TryGet(strategyKey, out implementation)`.
-
-### Failure messages
-
-`RegistryConfigurationException` includes:
-
-- the **AppSettings key name**
-- the **configured value** (or default key when config is empty)
-- the registry **`Keys`** list
-
-Example:
-
-```text
-AppSettings key 'Card' has value 'beta' which is not registered. Registered keys: alpha, gamma.
-```
-
----
-
-## Host example (plugin assemblies)
-
-After Autofac modules register provider registries ([PluginAssemblies.md](PluginAssemblies.md)):
-
-```xml
-
-
-
-
-
-```
-
-```csharp
-using Autofac;
-using DesignPatterns.Extensions.AppSettings;
-using Plugin.Contracts;
-using Plugin.Providers.Alpha;
-using Plugin.Providers.Gamma;
-
-var builder = new ContainerBuilder();
-builder.RegisterModule();
-builder.RegisterModule();
-using var container = builder.Build();
-
-var card = RegistryConfiguration.ResolveConfigured(
- CardMotionRegistry.Create(container),
- "Card");
-
-var fc = RegistryConfiguration.ResolveConfigured(
- FCControlRegistry.Create(container),
- "FC");
-```
-
-Prefer `{Contract}Keys` constants for `defaultKey` to stay DP025-safe at call sites.
-
----
-
-## Tests
-
-| Project | TFM | Role |
-|---------|-----|------|
-| `tests/DesignPatterns.Extensions.AppSettings.Tests` | net8.0 | Unit tests (valid key, missing entry, unknown key, default fallback) |
-| `tests/DesignPatterns.Extensions.AppSettings.Tests.Net48` | net48 | Same tests linked; compiled on CI, runnable on Windows with `dotnet test` |
-
-Unit tests inject AppSettings via an internal test seam; production code reads `ConfigurationManager.AppSettings` from the host `App.config`.
-
----
-
-## Non-goals
-
-- MSBuild / App.config static analysis (**DP032** — future)
-- Factory registry bridge (strategy-only v1)
-- Replacing composition-root module order
diff --git a/docs/Configuration.md b/docs/Configuration.md
index a3fbf7c..87f6ebe 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -4,13 +4,19 @@
Works with generated `{Contract}Registry.Instance` / `{Contract}Keys` from `[RegisterStrategy]` (see [Strategy.md](Strategy.md)).
-Related: [AppSettings.md](AppSettings.md) (`ConfigurationManager.AppSettings` bridge), [PluginAssemblies.md](PluginAssemblies.md) (multi-assembly hosts), [Autofac.md](Autofac.md) (container registration).
+Related: [PluginAssemblies.md](PluginAssemblies.md) (multi-assembly hosts), [Autofac.md](Autofac.md) (container registration).
---
## Package
-Independent extension assembly (not included in the `Skymly.DesignPatterns` meta-package), same policy as MSDI and Autofac extensions.
+Independent extension package `Skymly.DesignPatterns.Extensions.Configuration` (not included in the `Skymly.DesignPatterns` meta-package), same policy as MSDI and Autofac extensions.
+
+```xml
+
+```
+
+Or sibling project reference:
```xml
@@ -89,8 +95,9 @@ var card = RegistryConfiguration.ResolveConfigured(
Prefer `{Contract}Keys` constants for `defaultKey` to stay DP025-safe at call sites.
-For `App.config` / `ConfigurationManager.AppSettings` hosts, use [AppSettings.md](AppSettings.md) instead.
+### Legacy App.config hosts
+There is no dedicated `ConfigurationManager.AppSettings` extension. Map AppSettings into `IConfiguration` in the host (indexer is enough for `RegistryConfiguration`), then call the same API. See the PluginAssemblies sample host for a minimal adapter.
---
## Tests
@@ -107,3 +114,4 @@ For `App.config` / `ConfigurationManager.AppSettings` hosts, use [AppSettings.md
- MSBuild / App.config static analysis (**DP032** — future)
- Factory registry bridge (strategy-only v1)
- Replacing composition-root module order
+- Bundling `System.Configuration.ConfigurationManager` into this package
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
index 64a053f..17110fb 100644
--- a/docs/DEVELOPMENT.md
+++ b/docs/DEVELOPMENT.md
@@ -64,7 +64,6 @@ dotnet build DesignPatterns/DesignPatterns.csproj
| `DesignPatterns.Analyzers/` | `DiagnosticAnalyzer`(DP006 / DP023 / DP024 / DP025) |
| `DesignPatterns.CodeFixes/` | `CodeFixProvider` |
| `DesignPatterns.Extensions.DependencyInjection/` | MSDI 扩展 + DI 生成器 targets |
-| `DesignPatterns.Extensions.AppSettings/` | AppSettings → strategy registry 桥接 |
| `DesignPatterns.Extensions.Configuration/` | `IConfiguration` → strategy registry 桥接 |
| `DesignPatterns.Package/` | NuGet 元包(`PackageId=Skymly.DesignPatterns`) |
| `tests/DesignPatterns.Tests/` | 运行时 API 单元测试 |
@@ -159,7 +158,6 @@ Nuke `UnitTest` 会依次运行 `tests/` 下四个测试项目,并将 TRX 写
| 快照 | `tests/DesignPatterns.SourceGenerators.Tests` | 生成源码与 DP 诊断(Verify) |
| Analyzer | `tests/DesignPatterns.Analyzers.Tests` | DP006、DP023、DP024、DP025、DP033 与 CodeFix(Strategy/Factory/Handler/Registry key) |
| DI 扩展 | `tests/DesignPatterns.Extensions.DependencyInjection.Tests` | DI 注册与解析 |
-| AppSettings 扩展 | `tests/DesignPatterns.Extensions.AppSettings.Tests`(net8.0)、`tests/DesignPatterns.Extensions.AppSettings.Tests.Net48`(net48 编译) | AppSettings 桥接 |
| Configuration 扩展 | `tests/DesignPatterns.Extensions.Configuration.Tests`(net8.0)、`tests/DesignPatterns.Extensions.Configuration.Tests.Net48`(net48 编译) | `IConfiguration` 桥接 |
```bash
diff --git a/docs/PluginAssemblies.md b/docs/PluginAssemblies.md
index 8a9f181..18af31d 100644
--- a/docs/PluginAssemblies.md
+++ b/docs/PluginAssemblies.md
@@ -125,9 +125,11 @@ v1 **不**提供 `[RegisterStrategyBundle]`;若样板过多再评估(见 #10
## 6. 配置选型
-`App.config` / `ConfigurationManager` 桥接由 `DesignPatterns.Extensions.AppSettings` 提供(`RegistryConfiguration.ResolveConfigured` / `TryResolveConfigured`,见 [AppSettings.md](AppSettings.md))。
+宿主通过 `Skymly.DesignPatterns.Extensions.Configuration` 的 `RegistryConfiguration.ResolveConfigured` / `TryResolveConfigured`,把 `IConfiguration` 字符串映射到 strategy registry(见 [Configuration.md](Configuration.md))。
-在桥接落地前,宿主可:
+`App.config` / `ConfigurationManager.AppSettings` 宿主可先把 AppSettings 适配为 `IConfiguration`(索引器即可),再调用同一 API;库不提供单独的 AppSettings 扩展包。
+
+也可不经过桥接:
- 读取配置字符串,用 `{Contract}Keys` 常量比较;或
- 对 `*Registry.Instance.TryGet(key, out var impl)` 做失败处理。
diff --git a/docs/README.md b/docs/README.md
index cd7d27d..4555fc0 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -45,7 +45,6 @@
| 文档 | 说明 |
|------|------|
| [Autofac.md](Autofac.md) | Autofac `RegisterAutofac` 源生成器集成 |
-| [AppSettings.md](AppSettings.md) | AppSettings → `IStrategyRegistry` 桥接 |
| [Configuration.md](Configuration.md) | `IConfiguration` → `IStrategyRegistry` 桥接 |
| [PluginAssemblies.md](PluginAssemblies.md) | 卫星程序集 + 多项目 Registry 约定 |
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index e9e8271..73eeacb 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -138,7 +138,7 @@ State 转换表 v1 已于 0.1.0-preview4 发布;v2(guard 委托、DI 集成
- **API 冻结**:公共类型 / 特性 / 生成产出 / `DP###` 命名稳定声明(先行条件)。
- **打包修复**:元包多目标(`lib/net8.0`)已完成;「DI 扩展打包归属」仍待确定。
-- **NuGet 首发 / SemVer**:`Skymly.DesignPatterns` `0.2.3-preview1`(`Directory.Build.props` + `release.yml`);稳定版待 API 冻结。
+- **NuGet 首发 / SemVer**:`Skymly.DesignPatterns` `0.2.3-preview2`(`Directory.Build.props` + `release.yml`);稳定版待 API 冻结。
- ~~**release.yml**~~:tag 触发 Publish — 已完成。
开关条件:F1/F2/F2+ 主要项落地且无未决破坏性设计时,再评估进入本支线。
diff --git a/docs/design/ChainOfResponsibility.md b/docs/design/ChainOfResponsibility.md
index bb65aca..7ca9173 100644
--- a/docs/design/ChainOfResponsibility.md
+++ b/docs/design/ChainOfResponsibility.md
@@ -1,6 +1,6 @@
# Design Doc: Chain of Responsibility
-> **版本**:v0.2.3-preview1(与 NuGet 包版本对齐)
+> **版本**:v0.2.3-preview2(与 NuGet 包版本对齐)
> **关联 ADR**:ADR-XXX(如有)
## 概述
diff --git a/docs/design/Composite.md b/docs/design/Composite.md
index 07b2c0a..75b920a 100644
--- a/docs/design/Composite.md
+++ b/docs/design/Composite.md
@@ -1,6 +1,6 @@
# Design Doc: Composite
-> **版本**:v0.2.3-preview1(与 NuGet 包版本对齐)
+> **版本**:v0.2.3-preview2(与 NuGet 包版本对齐)
> **关联 ADR**:[ADR-006](../adr/ADR-006-composite-parallel-traversal.md)、[ADR-007](../adr/ADR-007-composite-tree-schema-validation.md)
## 概述
diff --git a/docs/design/Decorator.md b/docs/design/Decorator.md
index fcb7bca..2276e95 100644
--- a/docs/design/Decorator.md
+++ b/docs/design/Decorator.md
@@ -1,6 +1,6 @@
# Design Doc: Decorator
-> **版本**:v0.2.3-preview1(与 NuGet 包版本对齐)
+> **版本**:v0.2.3-preview2(与 NuGet 包版本对齐)
> **关联 ADR**:ADR-XXX(如有)
## 概述
diff --git a/docs/design/EventAggregator.md b/docs/design/EventAggregator.md
index 34dfcaf..9dd4be5 100644
--- a/docs/design/EventAggregator.md
+++ b/docs/design/EventAggregator.md
@@ -1,6 +1,6 @@
# Design Doc: Event Aggregator
-> **版本**:v0.2.3-preview1
+> **版本**:v0.2.3-preview2
> **关联 ADR**:ADR-XXX(如有)
## 概述
diff --git a/docs/design/FactoryRegistry.md b/docs/design/FactoryRegistry.md
index 48264b0..d0167e4 100644
--- a/docs/design/FactoryRegistry.md
+++ b/docs/design/FactoryRegistry.md
@@ -1,6 +1,6 @@
# Design Doc: Factory Registry
-> **版本**:v0.2.3-preview1(与 NuGet 包版本对齐)
+> **版本**:v0.2.3-preview2(与 NuGet 包版本对齐)
> **关联 ADR**:ADR-XXX(如有)
## 概述
diff --git a/docs/design/StateTransitionTable.md b/docs/design/StateTransitionTable.md
index 2a9b337..7cf5142 100644
--- a/docs/design/StateTransitionTable.md
+++ b/docs/design/StateTransitionTable.md
@@ -1,6 +1,6 @@
# Design Doc: State 转换表
-> **版本**:v0.2.3-preview1(与 NuGet 包版本对齐)
+> **版本**:v0.2.3-preview2(与 NuGet 包版本对齐)
> **关联 ADR**:[ADR-005](../adr/ADR-005-state-transition-table.md)
## 概述
diff --git a/docs/design/Strategy.md b/docs/design/Strategy.md
index 804c72f..894e7f0 100644
--- a/docs/design/Strategy.md
+++ b/docs/design/Strategy.md
@@ -1,6 +1,6 @@
# Design Doc: Strategy
-> **版本**:v0.2.3-preview1(与 NuGet 包版本对齐)
+> **版本**:v0.2.3-preview2(与 NuGet 包版本对齐)
## 概述
diff --git a/global.json b/global.json
index 4648027..44fbb79 100644
--- a/global.json
+++ b/global.json
@@ -1 +1,7 @@
-†猢此㨢笠 †∠敶獲潩≮›ㄢ⸰⸰〳∱ਬ††爢汯䙬牯慷摲㨢∠慬整瑳慍潪≲ਬ††愢汬睯牐牥汥慥敳㨢映污敳 素紊�
\ No newline at end of file
+{
+ "sdk": {
+ "version": "10.0.301",
+ "rollForward": "latestMajor",
+ "allowPrerelease": false
+ }
+}
diff --git a/tests/DesignPatterns.Extensions.AppSettings.Tests.Net48/DesignPatterns.Extensions.AppSettings.Tests.Net48.csproj b/tests/DesignPatterns.Extensions.AppSettings.Tests.Net48/DesignPatterns.Extensions.AppSettings.Tests.Net48.csproj
deleted file mode 100644
index 9ae7ec8..0000000
--- a/tests/DesignPatterns.Extensions.AppSettings.Tests.Net48/DesignPatterns.Extensions.AppSettings.Tests.Net48.csproj
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
- net48
- latest
- enable
- false
- true
- DesignPatterns.Extensions.AppSettings.Tests
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/DesignPatterns.Extensions.AppSettings.Tests/AppSettingsFixture.cs b/tests/DesignPatterns.Extensions.AppSettings.Tests/AppSettingsFixture.cs
deleted file mode 100644
index 63bb056..0000000
--- a/tests/DesignPatterns.Extensions.AppSettings.Tests/AppSettingsFixture.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Specialized;
-using DesignPatterns.Extensions.AppSettings;
-
-namespace DesignPatterns.Extensions.AppSettings.Tests;
-
-public sealed class AppSettingsFixture : IDisposable
-{
- private readonly Func _previous;
-
- public AppSettingsFixture()
- {
- _previous = RegistryConfigurationAppSettings.GetValue;
-
- var settings = new NameValueCollection
- {
- ["ValidProvider"] = "alpha",
- ["UnknownProvider"] = "not-registered",
- ["EmptyProvider"] = string.Empty,
- };
-
- RegistryConfigurationAppSettings.GetValue = key => settings[key];
- }
-
- public void Dispose()
- {
- RegistryConfigurationAppSettings.GetValue = _previous;
- }
-}
diff --git a/tests/DesignPatterns.Extensions.AppSettings.Tests/DesignPatterns.Extensions.AppSettings.Tests.csproj b/tests/DesignPatterns.Extensions.AppSettings.Tests/DesignPatterns.Extensions.AppSettings.Tests.csproj
deleted file mode 100644
index 3ca46c9..0000000
--- a/tests/DesignPatterns.Extensions.AppSettings.Tests/DesignPatterns.Extensions.AppSettings.Tests.csproj
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
- net8.0
- enable
- enable
- false
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/DesignPatterns.Extensions.AppSettings.Tests/RegistryConfigurationTests.cs b/tests/DesignPatterns.Extensions.AppSettings.Tests/RegistryConfigurationTests.cs
deleted file mode 100644
index dadb663..0000000
--- a/tests/DesignPatterns.Extensions.AppSettings.Tests/RegistryConfigurationTests.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System.Collections.Generic;
-using DesignPatterns.Behavioral;
-using DesignPatterns.Extensions.AppSettings;
-
-namespace DesignPatterns.Extensions.AppSettings.Tests;
-
-public sealed class RegistryConfigurationTests : IClassFixture
-{
- public RegistryConfigurationTests(AppSettingsFixture _)
- {
- }
-
- private static IStrategyRegistry CreateRegistry() =>
- new StrategyRegistry(new Dictionary
- {
- ["alpha"] = new AlphaProvider(),
- ["beta"] = new BetaProvider(),
- });
-
- [Fact]
- public void ResolveConfigured_ReturnsImplementation_ForValidAppSettingsKey()
- {
- var registry = CreateRegistry();
-
- var provider = RegistryConfiguration.ResolveConfigured(registry, "ValidProvider");
-
- Assert.Equal("alpha", provider.Name);
- }
-
- [Fact]
- public void TryResolveConfigured_ReturnsFalse_WhenAppSettingsEntryIsMissing()
- {
- var registry = CreateRegistry();
-
- var resolved = RegistryConfiguration.TryResolveConfigured(
- registry,
- "MissingProvider",
- out var provider);
-
- Assert.False(resolved);
- Assert.Null(provider);
- }
-
- [Fact]
- public void ResolveConfigured_Throws_WhenAppSettingsEntryIsMissing()
- {
- var registry = CreateRegistry();
-
- var exception = Assert.Throws(() =>
- RegistryConfiguration.ResolveConfigured(registry, "MissingProvider"));
-
- Assert.Contains("MissingProvider", exception.Message, StringComparison.Ordinal);
- Assert.Contains("alpha", exception.Message, StringComparison.Ordinal);
- Assert.Contains("beta", exception.Message, StringComparison.Ordinal);
- }
-
- [Fact]
- public void ResolveConfigured_Throws_WhenConfiguredKeyIsUnknown()
- {
- var registry = CreateRegistry();
-
- var exception = Assert.Throws(() =>
- RegistryConfiguration.ResolveConfigured(registry, "UnknownProvider"));
-
- Assert.Contains("UnknownProvider", exception.Message, StringComparison.Ordinal);
- Assert.Contains("not-registered", exception.Message, StringComparison.Ordinal);
- Assert.Contains("alpha", exception.Message, StringComparison.Ordinal);
- Assert.Contains("beta", exception.Message, StringComparison.Ordinal);
- }
-
- [Fact]
- public void TryResolveConfigured_UsesDefaultKey_WhenAppSettingsEntryIsMissing()
- {
- var registry = CreateRegistry();
-
- var resolved = RegistryConfiguration.TryResolveConfigured(
- registry,
- "MissingProvider",
- out var provider,
- defaultKey: "beta");
-
- Assert.True(resolved);
- Assert.Equal("beta", provider!.Name);
- }
-
- [Fact]
- public void ResolveConfigured_UsesDefaultKey_WhenAppSettingsValueIsEmpty()
- {
- var registry = CreateRegistry();
-
- var provider = RegistryConfiguration.ResolveConfigured(registry, "EmptyProvider", defaultKey: "alpha");
-
- Assert.Equal("alpha", provider.Name);
- }
-}
diff --git a/tests/DesignPatterns.Extensions.AppSettings.Tests/TestContracts.cs b/tests/DesignPatterns.Extensions.AppSettings.Tests/TestContracts.cs
deleted file mode 100644
index 255b21a..0000000
--- a/tests/DesignPatterns.Extensions.AppSettings.Tests/TestContracts.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace DesignPatterns.Extensions.AppSettings.Tests;
-
-internal interface IProviderStrategy
-{
- string Name { get; }
-}
-
-internal sealed class AlphaProvider : IProviderStrategy
-{
- public string Name => "alpha";
-}
-
-internal sealed class BetaProvider : IProviderStrategy
-{
- public string Name => "beta";
-}