Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions .github/workflows/build-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ on:
- "csharp-language/high-performance-memory-management/**"
- "csharp-language/modern-patterns-result-pipeline/**"
- "dotnet-8-essentials/background-jobs-hostedservice-queues/**"
- "dotnet-8-essentials/configuration-secrets-environments/**"
- ".github/workflows/build-samples.yml"

pull_request:
Expand All @@ -48,6 +49,7 @@ on:
- "csharp-language/high-performance-memory-management/**"
- "csharp-language/modern-patterns-result-pipeline/**"
- "dotnet-8-essentials/background-jobs-hostedservice-queues/**"
- "dotnet-8-essentials/configuration-secrets-environments/**"
- ".github/workflows/build-samples.yml"

workflow_dispatch:
Expand Down Expand Up @@ -1100,3 +1102,133 @@ jobs:
)"

test "${status}" = "400"

test-config-precedence:
name: Test typed configuration precedence sample
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Install .NET 10 SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"

- name: Restore
run: >
dotnet restore
dotnet-8-essentials/configuration-secrets-environments/ConfigPrecedenceMinimal.slnx

- name: Build
run: >
dotnet build
dotnet-8-essentials/configuration-secrets-environments/ConfigPrecedenceMinimal.slnx
--configuration Release
--no-restore

- name: Test
run: >
dotnet test
dotnet-8-essentials/configuration-secrets-environments/ConfigPrecedenceMinimal.slnx
--configuration Release
--no-build

- name: Verify application has no direct packages
shell: bash
run: |
output="$(
dotnet list \
dotnet-8-essentials/configuration-secrets-environments/src/ConfigPrecedenceMinimal/ConfigPrecedenceMinimal.csproj \
package
)"

printf '%s\n' "${output}"

printf '%s\n' "${output}" |
grep --fixed-strings \
"No packages were found"

- name: Verify configuration precedence and redaction
shell: bash
env:
DOTNET_ENVIRONMENT: Development
ASPNETCORE_URLS: http://127.0.0.1:5097
CFGPLAY_App__ApiKey: ci-demo-key-1234
CFGPLAY_App__TimeoutSeconds: "30"
run: |
app_log="${RUNNER_TEMP}/config-precedence.log"

dotnet run \
--project dotnet-8-essentials/configuration-secrets-environments/src/ConfigPrecedenceMinimal/ConfigPrecedenceMinimal.csproj \
--configuration Release \
--no-build \
-- \
--App:TimeoutSeconds=40 \
--Features:AdvancedSearch=false \
>"${app_log}" 2>&1 &

app_pid="$!"

cleanup() {
kill "${app_pid}" 2>/dev/null || true
wait "${app_pid}" 2>/dev/null || true
}

trap cleanup EXIT

for attempt in $(seq 1 40); do
if curl \
--fail \
--silent \
http://127.0.0.1:5097/ \
>"${RUNNER_TEMP}/config-root.json"; then
break
fi

if ! kill -0 "${app_pid}" 2>/dev/null; then
cat "${app_log}"
exit 1
fi

sleep 0.25
done

root="$(
cat "${RUNNER_TEMP}/config-root.json"
)"

expected_root='{"sample":"typed-config-precedence","environment":"Development","serviceBaseUrl":"https://api.example.test","timeoutSeconds":40,"advancedSearch":false,"apiKeyConfigured":true}'

if [[ "${root}" != "${expected_root}" ]]; then
printf 'Unexpected root response:\n%s\n' "${root}"
exit 1
fi

diagnostics="$(
curl \
--fail \
--silent \
http://127.0.0.1:5097/dev/config
)"

expected_diagnostics='{"environment":"Development","serviceBaseUrl":"https://api.example.test","timeoutSeconds":40,"advancedSearch":false,"apiKey":"[REDACTED]","apiKeyConfigured":true}'

if [[ "${diagnostics}" != "${expected_diagnostics}" ]]; then
printf 'Unexpected diagnostics response:\n%s\n' "${diagnostics}"
exit 1
fi

if printf '%s\n%s\n' \
"${root}" \
"${diagnostics}" |
grep --quiet \
--fixed-strings \
"ci-demo-key-1234"; then
echo "Secret-like CI value leaked into an HTTP response."
exit 1
fi
55 changes: 38 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Each sample folder contains a focused implementation of one tutorial topic. The
| [`csharp-language/high-performance-memory-management`](csharp-language/high-performance-memory-management/) | Focused .NET 10 UTF-8 ingestion sample demonstrating PipeReader framing, segmented ReadOnlySequence handling, span-based numeric parsing, bounded MemoryPool ownership, strict input validation, deterministic output, and tests | [High-Performance C#: Span<T>, Memory<T>, SIMD & Pipelines](https://www.dotnet-guide.com/tutorials/csharp-language/high-performance-memory-management/) |
| [`csharp-language/modern-patterns-result-pipeline`](csharp-language/modern-patterns-result-pipeline/) | Focused .NET 10 console companion locked to C# 12.0, demonstrating a custom Result type, safe and diagnostic errors, Map/Bind/BindAsync composition, invariant text-import validation, short-circuit persistence, cancellation, deterministic output, and tests | [C# 12 Functional Patterns: Result Type, Error Handling & Composable Pipeline Testing](https://www.dotnet-guide.com/tutorials/csharp-language/modern-patterns-result-pipeline/) |
| [`dotnet-8-essentials/background-jobs-hostedservice-queues`](dotnet-8-essentials/background-jobs-hostedservice-queues/) | Focused ASP.NET Core Minimal API demonstrating a bounded Channel queue, asynchronous backpressure, a BackgroundService consumer, fresh scoped handlers, safe in-memory job status, exception isolation, cancellation, and integration tests | [.NET 8 Background Jobs: IBackgroundTaskQueue, BackgroundService & Production-Ready Patterns](https://www.dotnet-guide.com/tutorials/dotnet-8-essentials/background-jobs-hostedservice-queues/) |
| [`dotnet-8-essentials/configuration-secrets-environments`](dotnet-8-essentials/configuration-secrets-environments/) | Focused .NET 10 Minimal API demonstrating layered configuration precedence, prefixed environment variables, command-line overrides, strongly typed options, startup validation, User Secrets metadata, a lightweight feature flag, and safe Development-only diagnostics | [.NET 8 Configuration & Secrets Management: Typed Options, User Secrets & Feature Flags](https://www.dotnet-guide.com/tutorials/dotnet-8-essentials/configuration-secrets-environments/) |

## Companion articles
- [Common Microsoft.Extensions.AI mistakes](https://www.dotnet-guide.com/articles/dotnet-ai/microsoft-extensions-ai-common-mistakes/)
Expand Down Expand Up @@ -133,27 +134,47 @@ tutorials/
| |-- TransactionalOutboxMinimal.Tests.csproj
| `-- OutboxFlowTests.cs
|-- dotnet-8-essentials/
| `-- background-jobs-hostedservice-queues/
| |-- BackgroundJobQueueMinimal.slnx
| |-- background-jobs-hostedservice-queues/
| | |-- BackgroundJobQueueMinimal.slnx
| | |-- README.md
| | |-- src/
| | | `-- BackgroundJobQueueMinimal/
| | | |-- BackgroundJobQueueMinimal.csproj
| | | |-- Program.cs
| | | |-- Jobs/
| | | | |-- BackgroundJobModels.cs
| | | | |-- IBackgroundJobQueue.cs
| | | | |-- BoundedBackgroundJobQueue.cs
| | | | |-- IJobTracker.cs
| | | | |-- InMemoryJobTracker.cs
| | | | `-- QueuedEmailWorker.cs
| | | `-- Services/
| | | |-- IEmailJobHandler.cs
| | | `-- FakeEmailJobHandler.cs
| | `-- tests/
| | `-- BackgroundJobQueueMinimal.Tests/
| | |-- BackgroundJobQueueMinimal.Tests.csproj
| | `-- BackgroundJobQueueTests.cs
| `-- configuration-secrets-environments/
| |-- ConfigPrecedenceMinimal.slnx
| |-- README.md
| |-- src/
| | `-- BackgroundJobQueueMinimal/
| | |-- BackgroundJobQueueMinimal.csproj
| | `-- ConfigPrecedenceMinimal/
| | |-- ConfigPrecedenceMinimal.csproj
| | |-- Program.cs
| | |-- Jobs/
| | | |-- BackgroundJobModels.cs
| | | |-- IBackgroundJobQueue.cs
| | | |-- BoundedBackgroundJobQueue.cs
| | | |-- IJobTracker.cs
| | | |-- InMemoryJobTracker.cs
| | | `-- QueuedEmailWorker.cs
| | `-- Services/
| | |-- IEmailJobHandler.cs
| | `-- FakeEmailJobHandler.cs
| | |-- appsettings.json
| | |-- appsettings.Development.json
| | |-- Diagnostics/
| | | `-- SafeConfigSnapshot.cs
| | |-- Options/
| | | |-- AppOptions.cs
| | | `-- FeatureFlagOptions.cs
| | `-- Validation/
| | `-- AppOptionsValidator.cs
| `-- tests/
| `-- BackgroundJobQueueMinimal.Tests/
| |-- BackgroundJobQueueMinimal.Tests.csproj
| `-- BackgroundJobQueueTests.cs
| `-- ConfigPrecedenceMinimal.Tests/
| |-- ConfigPrecedenceMinimal.Tests.csproj
| `-- ConfigurationTests.cs
|-- blazor/
| |-- create-interactive-ui-csharp-12/
| | |-- BlazorTodoMinimal.slnx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/ConfigPrecedenceMinimal/ConfigPrecedenceMinimal.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/ConfigPrecedenceMinimal.Tests/ConfigPrecedenceMinimal.Tests.csproj" />
</Folder>
</Solution>
Loading
Loading