مسیر C#/.NET و آداپتورهای پشته · فصل ۱ — Modern C#، ASP.NET Core، EF Core و Testing · 328

.NET Testing, Diagnostics and Hosting

Build a bounded .NET service readiness pack that separates unit from integration evidence, executes critical requests through WebApplicationFactory/TestServer, declares host/environment/configuration and dependency lifetimes, instruments logs/metrics/traces through .NET observability primitives, captures failure diagnostics without leaking sensitive data, verifies startup/shutdown and health behavior in an isolated fixture, and gates hosting readiness on reproducible tests plus operational evidence rather than on local startup alone. می‌تواند «.NET Testing, Diagnostics و Hosting» را در یک سرویس .NET محدود اجرا و با تست/diagnostics مناسب verify کند.

.NET Testing, Diagnostics و Hosting — fa-IR

Lesson summary

Build a bounded .NET service readiness pack that separates unit from integration evidence, executes critical requests through WebApplicationFactory/TestServer, declares host/environment/configuration and dependency lifetimes, instruments logs/metrics/traces through .NET observability primitives, captures failure diagnostics without leaking sensitive data, verifies startup/shutdown and health behavior in an isolated fixture, and gates hosting readiness on reproducible tests plus operational evidence rather than on local startup alone.

می‌تواند «.NET Testing, Diagnostics و Hosting» را در یک سرویس .NET محدود اجرا و با تست/diagnostics مناسب verify کند.

This lesson produces

می‌تواند «.NET Testing, Diagnostics و Hosting» را در یک سرویس .NET محدود اجرا و با تست/diagnostics مناسب verify کند.

Who it is for

Foundation

Use a field map, one worked scenario and explicit state vocabulary before independent construction.

Repair

Identify the failed evidence dimension, use the smallest mutated fixture and rerun only the affected gate.

Experienced

Compress definitions; require trade-off defense, counterexample analysis and changed-context transfer.

Prerequisites

Green tests, failed startup, unexplained timeouts

A small service returns a dependency's response. Its unit tests pass, and it runs on your machine. In a new environment, the service name is missing from configuration and startup fails. After you repair that setting, a slow dependency produces an error response. The log says “timeout,” but you cannot connect the message to the request or the operation that failed.

Your deliverable is the .NET Service Verification, Observability and Hosting Readiness Pack v1. It answers a specific question: for this revision, configuration and environment, which behaviors have evidence, and which remain unverified?

Plan for 60 active minutes: 10 on the model and test boundary, 15 on the example and code, 15 on guided practice, 15 on independent transfer, and 5 defending your decision. SDK installation and the supplementary .NET execution take additional time.

The core exercise runs in Python against synthetic records. Those records are not captured .NET executions. A C# project and integration tests are included, but their execution by the publisher is UNVERIFIED in this version. Completing the portable exercise demonstrates evidence analysis. Verifying the service requires results from a real execution.

This lesson is for readers who know C# and the basic structure of an ASP.NET Core API and need a defensible hosting decision beyond local startup.

Database migrations, Kubernetes or Kafka installation, cloud-provider selection and live deployment are outside this exercise.

Choose your entry point

The .NET foundation lesson is required. Familiarity with data access and migrations helps, but this exercise uses no database.

Before continuing, explain how an HTTP request reaches a handler, what dependency injection supplies to that handler, and whether calling a method directly traverses the same path.

If you need a bridge: the host brings application resources together; the request pipeline connects HTTP input to an operation; dependency injection creates the objects that operation needs. Calling a method can test its logic without proving that a route exists, configuration binds correctly, or a dependency can be constructed. Name one possible defect in each of those three areas before proceeding.

For a foundation route, work through each column of the example. For a repair route, identify the first invalid evidence dimension and repair that dimension. Experienced readers may start with independent practice; the evidence requirements and transfer task remain the same.

Readiness depends on connected evidence

A unit test examines a bounded piece of code in isolation. An integration test examines components working together. In ASP.NET Core, WebApplicationFactory<Program> can bootstrap the system under test, or SUT, and send HTTP requests through its application pipeline. Integration testing guidance

Our example replaces the external dependency with a synthetic implementation. Routes, DI, configuration and application responses are within the test boundary. The real dependency's network, TLS, reverse proxy and target host are outside it. A test name containing “integration” does not enlarge that boundary.

The accompanying flow has three exits: complete evidence supports bounded readiness; negative evidence requires repair and another execution; absent evidence requires HOLD. Its text equivalent is: bind revision and environment, examine critical tests, check configuration and dependency lifetimes, verify startup and shutdown, then inspect the injected failure. A green unit test provides no shortcut to the final decision.

This readiness gate is a MAKNOW exercise design, not a Microsoft certification. HOSTING_READY on a synthetic input means only that this fixture satisfies the declared rules.

Turn each question into an inspectable record

QuestionRequired evidenceInsufficient substitute
What was tested?Test ID, level, SUT, input state, expectation and observationTotal number of green tests
Where did the setting come from?Environment, key and configuration sourceA screenshot from a previous run
When is a dependency created?Declared lifetime and scope validationA type registration alone
Does the lifecycle behave correctly?Startup, health and shutdown capturesA startup message alone
Can this failure be explained?Related log and trace, metric and diagnostic captureA generic error message
What does the decision apply to?Revision, environment, context ID, input digest and reviewerA copied local decision

UNKNOWN means a value is not known. UNVERIFIED means the required check has not been executed or recorded. STALE means evidence no longer belongs to the relevant time or context. FAILED means an observation conflicts with a valid expectation. REVIEW_REQUIRED records the need for review. Each blocks the aggregate decision with HOLD. SECRET_EXPOSURE_RISK is a specific blocking cause that other successful tests cannot cancel.

Configuration provenance matters. ASP.NET Core's environment-variable mapping connects Service__Name to hierarchical key Service:Name. This fixture requires Service:Name and Dependency:TimeoutMs. The 100, 20 and 250 millisecond values used below are exercise choices, not platform recommendations. ASP.NET Core configuration

From valid configuration to a traceable failure

The Generic Host brings configuration, DI, logging and hosted-service lifetime management together. Its presence does not prove that your settings are valid. Generic Host

The supplied project binds related settings to options classes. Startup validation exposes a missing service name or invalid timeout:

builder.Services.AddOptions<ServiceOptions>()
    .BindConfiguration("Service")
    .Validate(o => !string.IsNullOrWhiteSpace(o.Name), "Service name required")
    .ValidateOnStart();

ValidateOnStart moves validation to application startup. Report the violated rule without printing its value or dumping configuration. Supply test settings to the host before the application consumes the options. Options pattern

A scoped dependency typically has one instance per request scope; a singleton is shared. Here the synthetic dependency is scoped and telemetry instruments are shared. Capturing a scoped dependency directly inside a singleton breaks that lifetime boundary, so scope validation is enabled in the test host. This is a sample-specific choice, not a universal service design. Dependency lifetimes

Keep the diagnostic questions separate. A log describes an event. A metric aggregates counts or measurements. A trace connects related operations for a request. .NET provides APIs including ILogger, Meter and ActivitySource/Activity. Instrumentation in source code does not prove that data was collected or exported. Diagnostics, OpenTelemetry observability

For our injected timeout, log event 1002 must contain the operation's trace ID. Metric lesson.dependency.timeouts increases by one with only the bounded tag operation=dependency. Do not create a metric label for every request ID; request correlation belongs in logs and traces. No cloud collector is configured here. Tests receive data through in-memory listeners and a test logger.

Our health contract asks two questions: can the process respond, and is its required dependency ready? Normal readiness returns 200, unavailable-dependency readiness returns 503, and liveness during that dependency failure returns 200. These endpoint meanings and codes belong to this sample's contract. They do not prove a real load balancer's behavior.

Record shutdown separately: request it, observe the stopping signal and record host completion. A shutdown callback does not establish that every in-flight request drained. That stronger claim needs a long-running request, a deadline and a test of the target host's behavior.

Read a test as preparation, action and assertion

This excerpt comes from the normal-request test in ServiceTests.cs. ServiceFactory is the accompanying class that configures the test host:

using var factory = new ServiceFactory();
using var client = factory.CreateClient();
using var response = await client.GetAsync("/sample");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
using var payload = JsonDocument.Parse(
    await response.Content.ReadAsStringAsync());
Assert.Equal("synthetic-ok",
    payload.RootElement.GetProperty("value").GetString());

Preparation supplies the Testing environment and in-memory settings. The action sends GET /sample through the application; it does not call ReadAsync directly. The assertions examine both the status code and a meaningful body value. A mistyped route or an unrelated body returned with HTTP 200 should be exposed by those checks. These are expectations encoded in the test source; this version has no actual execution receipt.

The invalid-options test asks a different question. It does not expect an HTTP error response: creating the client starts the host, and options validation should reject startup there. Any exception is insufficient. The test checks for OptionsValidationException, so a missing file or broken SDK cannot count as a successful configuration test. This is why test identity, input state and expected failure type are separate evidence fields.

Follow the timeout through the handler

The handler creates a deadline for the dependency operation and links it to request cancellation. In the injected example, a 250 ms delay exceeds the 100 ms deadline. The deadline-cancellation path marks the span as an error, increments the counter, records event 1002 and returns HTTP 504. The catch filter excludes an already cancelled request; that event must not automatically be described as a dependency timeout.

Read the evidence in order: failure-producing input, response, span and log from that operation, then metric change. An aggregate count cannot identify the failed request on its own. Conversely, matching identifiers without captured evidence cannot establish that a failure ran. The exercise records are deliberately synthetic and test the decision procedure only.

The accompanying /ready endpoint reads the synthetic Dependency:Available flag. It sends no request to a real dependency. It teaches the distinction between the two health contracts. Establishing a real dependency's health requires an appropriate probe and observations from the target context.

Worked example: an expected timeout can pass a test

fixtures/baseline.json contains invented evidence for environment Testing, context local-r1. The timeout is 100 ms, normal delay is 20 ms and injected delay is 250 ms. Read expectations before deciding:

CheckExpectationSynthetic observationInterpretation
Normal requestHTTP 200HTTP 200This path met its expectation
Injected timeoutHTTP 504HTTP 504Error handling matched the contract
Required setting removedStartup rejectedStartup rejectedInvalid startup was prevented
Normal lifecycleRecorded startup and shutdownBoth PASSOnly this lifecycle was covered
Timeout visibilityMatching log/trace IDs, metric +1PresentThis synthetic failure is traceable

First, match all capture identities to local-r1. Next check settings, the three critical tests, health and shutdown. Finally examine diagnostics for forbidden content. No declared blocker remains, so the example produces HOSTING_READY with scope SYNTHETIC_ONLY. It makes no claim about an executed service or actual deployment.

Why did HTTP 504 not fail the test? The failure-injection test expected that controlled response. HTTP 504 on the normal path would mean something different. A timeout log alone does not determine test success: input state, expectation and observation must stay together.

Now remove only Service:Name. Even if startup_state=PASS remains in the record, the new input lacks a required startup condition. The previous decision no longer applies. Record CONFIG_MISSING with dimension state FAILED and aggregate HOLD. Repair the configuration source and recapture startup. Editing a PASS label does not generate evidence. In the full CSV, lifecycle and telemetry columns are shared pack-level evidence repeated beside test rows. Normal startup and shutdown belong to the NORMAL scenario; missing_config tests a separate invalid-startup attempt. Repeated columns do not imply successful startup of that invalid scenario or a separately captured trace for every test.

Turn a raw observation into a defensible issue

Inspect the two decisive values before choosing a result. From learner, this command prints observations without making a decision:

python -c 'import json; x=json.load(open("fixtures/missing_env.json")); print(x["configuration"]["values"].get("Service:Name")); print(x["lifecycle"]["startup_state"])'

This file prints None, then PASS. Explain the conflict: the current required setting is absent, while the startup label still comes from the previous record. The visible rule is CONFIG_MISSING. The decision portion of your response is therefore:

{
  "readiness_state": "HOLD",
  "issues": [{"code": "CONFIG_MISSING", "state": "FAILED"}],
  "evidence_refs": ["/configuration/values", "/lifecycle/startup_state"]
}

This is only the decision portion. The complete submission also needs identity, input and policy digests, evaluation date and three explanations. In rationale, describe the conflict. In minimal_repair, name the configuration source to repair and the startup evidence to recapture. In review_trigger, identify a configuration or environment change. “Fixed” is insufficient because it identifies neither the action nor the new observation. Use this guided example to learn the procedure; the transfer problem contains several different dimensions.

Time also belongs to the evidence. Evaluating the same baseline after October 18, 2026 makes its version review overdue. Unchanged test records do not refresh it. Record September 12 explicitly when reproducing this historical example; use the actual evaluation date for a present-day decision.

Five shortcuts that manufacture confidence

ShortcutDecisive evidenceRequired action
Missing setting, old green startup labelCurrent required-key valueRepair configuration and capture startup again
Timeout log with no matching traceIDs from the same executionRepair correlation and inject the failure again
Replace a route test with a mocked method callTest level and test-host referencesRestore a request through the application
Print a token to improve diagnosticsActual diagnostic contentRemove forbidden data and recheck
Declare readiness without a shutdown recordEnd-of-lifecycle capturePreserve UNVERIFIED until evidence exists

Do not resolve conflicting IDs by manually assigning a log to a trace. The cause might be context propagation, listener configuration or a capture from another run. Isolate the cause and reproduce the failure after repair.

The exposure fixture contains a synthetic canary, not a real credential. Failing to find that one string does not prove comprehensive redaction. Forbidden-field inspection and a review of the captured data boundary are also required. In an actual service, a memory dump or full exception can contain sensitive data; define the capture boundary beforehand. Removing a required test from the list, or changing both expectation and observation from 200 to 504, does not repair a failure. The visible fixed contract in policy.json identifies the three critical tests and their expectations. Malformed decisive input or a changed fixed exercise contract produces INPUT_INVALID, dimension state UNKNOWN and aggregate HOLD. Obtain a complete, inspectable record first.

Defend the boundary, not the test count

Evaluate this proposal: “Replace the real dependency with a fake in every test so the suite runs faster.” That fixture can help test routing and controlled failures. It cannot establish the real dependency's contract, network latency or target environment. A defensible answer names both the gained coverage and the remaining gap.

Then identify what invalidates acceptance: a changed SUT revision, configuration source, response expectation, telemetry export path or hosting profile requires new evidence for the affected dimension. A larger test count cannot repair a context mismatch.

Put the check at the right boundary

MethodUseful coverage hereLimitation
Unit testBounded logic and many input variantsDoes not execute route and host
WebApplicationFactory with a fakeRequest pipeline, settings, DI, controlled failureDoes not prove the real dependency
Local process testProcess lifecycle and environment variablesTarget hosting may differ
Disposable target-profile executionThat hosting profile's contractMore time and setup; context must be recorded
Logs, metrics and tracesExplain observed behaviorDo not replace explicit assertions

For a missing setting, run the smallest startup test first. For a timeout, begin with an HTTP request against a controlled dependency. Increasing log volume indiscriminately can add cost and captured data. Collect enough related evidence to answer the diagnostic question.

Build your own decision

Use the lab package. Python 3.10+ and its standard library are sufficient; no provider account is needed. From the learner directory:

python run_checks.py --inventory
python run_checks.py --template fixtures/missing_env.json submission.json
python run_checks.py --check fixtures/missing_env.json submission.json

The second command supplies input identity and its digest while leaving the decision empty. Read policy.json, then fill in the decision, issues with code and state, JSON Pointer evidence references, rationale, minimal repair and review trigger. /configuration/values is one example of a reference. Do not copy sensitive values into your explanation.

Solve missing_env using the worked example. Then analyze uncorrelated_timeout, mocked_unit, secret_exposure and missing_shutdown without supplied decisions. The public checker examines structure, input binding and basic response consistency. Its success message alone does not establish semantic correctness; a separate semantic review is required.

For independent work, open transfer_changed.json. The environment changes to Staging, the profile becomes a disposable container, normal delay rises to 180 ms, the service name is absent from the new source, and prior captures still identify local-r1. Find the complete set of blocking causes without copying the baseline decision. Adding the service name alone cannot repair latency or stale captures.

For an optional execution extension, follow dotnet/README.en.md. It is separate from completion of the core Python exercise; any claim about actually running the service needs its own execution receipt. Its tests target the normal response, timeout, invalid startup, telemetry, health and shutdown. After a real run, record the SDK version, TRX results and fresh captures. Do not rename synthetic fixtures as real execution reports. Add --as-of 2026-09-12 to the template command to reproduce the fixed historical example. Without that option, the template records today's date in evaluated_on. It also records policy_sha256; a response made under old rules must not silently be graded under changed rules.

Use evidence_field_map.csv to locate all 21 evidence dimensions. Examine the corresponding row for each critical test, then the shared lifecycle and telemetry records. The decision belongs in your submission and is deliberately absent from starter fixtures.

For transfer, separate a proposed repair from an observed repair. You may propose a correct configuration source or a different deadline, but extending the deadline can also change the response expectation and injected-failure behavior. The affected tests need new observations bound to the new context. Editing an old capture's context_id is not a rerun. If you simulate an instructional assumption, work on a copy and preserve its synthetic status.

Make the result reproducible

Each decision must bind the fixture ID and digest, execution context, SYNTHETIC_ONLY scope, complete issue set and decisive field references. A hold also needs a minimal repair and next review condition. The digest identifies input bytes; it does not establish their truth.

The accompanying CSV provides the complete evidence table: test and level, SUT and test host, fixture state, environment and configuration source, secret boundary, dependency lifetime, startup/shutdown/health, log/metric/trace and correlation, failure and capture, hosting profile, decision and reviewer. Preserve unknown values explicitly.

Keep an actual execution record separately: timestamp, code revision, SDK/runtime version, exact command, exit code, test names and counts, expectations and observations, and sanitized diagnostic captures. A missing SDK or failed restore means UNVERIFIED, not PASS. Passing an in-memory test is still not live-deployment authorization. In the learner bundle, open resources: NET-004-T1.csv is the complete evidence table, rubric.md contains the assessment criteria, and source_links.md provides teaching and dependency-version sources.

Assessment and transfer

  1. Explain the unit-test versus HTTP-pipeline boundary for this service and name one behavior outside the in-memory test.
  2. Build the baseline readiness pack and explain why HTTP 504 can be the correct failure-injection expectation.
  3. Which current field invalidates a successful-startup claim when configuration is removed? Record consequence, state and minimal repair.
  4. Rebuild the decision for transfer_changed. Identify the configuration, response expectation and captures that need rechecking. A copied baseline result is insufficient.

Educational acceptance requires five dimensions: correct scope, exact evidence binding, treatment of negative evidence, a bounded decision and independently recomputed transfer. A strong average score cannot compensate for secret exposure or unsupported success. The supplied rubric expands these criteria.

AI may organize and compare records. It may not invent test results, captures, owner approval or host execution. You must be able to explain the decision without a tool's hidden reasoning. The next curriculum lesson, Kubernetes and Kafka, examines the separate question of technology fit.

Sources and recheck date

The API baseline is .NET/ASP.NET Core 10. The primary Microsoft references for integration testing, diagnostics, OpenTelemetry and Generic Host were rechecked on September 12, 2026. Options, DI and configuration details use the official pages linked in the relevant sections.

The next planned review is October 18, 2026. Material changes to APIs, target versions or environment rules trigger an earlier review. Timeout thresholds, decision codes, forbidden fields and acceptance rules are local exercise design. An explanation of a platform mechanism is not evidence that this service is ready.

Assessment

There is no answer key in this lesson pack; answer it yourself and mark honestly. "Needs review" sends the lesson to your review queue.

Assessment rubric

هر ردیف باید کافی باشد؛ نمرهٔ میانگین جای شرط بحرانی را نمی‌گیرد. / Every dimension must be sufficient; an average cannot override a critical failure.

معیار / Dimensionکافی / Sufficientناکافی / Insufficient
مرز / Scopeتفکیک unit، pipeline، host و وابستگی واقعی / Distinguishes unit, pipeline, host and real dependencyتست در حافظه را deployment می‌نامد / Claims deployment from an in-memory test
اتصال شواهد / Bindingشناسه، هش ورودی و قواعد، تاریخ ارزیابی، محیط و ارجاع دقیق / Exact identity, input and policy digests, evaluation date, environment and field referencesنتیجهٔ قدیمی یا ارجاع نامعتبر / Copied result or invalid reference
شاهد منفی / Negative evidenceتمام علت‌های توقف و نوع وضعیت / Complete blockers and typed statesافشا یا فقدان داده را نادیده می‌گیرد / Ignores exposure or missing data
تصمیم / Decisionدامنهٔ مصنوعی، تعمیر و محرک بازبینی / Synthetic scope, repair and review triggerموفقیت بی‌شاهد / Unsupported success
انتقال / Transferمحاسبهٔ دوبارهٔ تنظیم، تأخیر و زمینه / Rechecks settings, latency and contextکپی baseline یا تعمیر فقط یک علت / Copies baseline or repairs only one cause

قبولی بخش Python اثبات اجرای C# نیست. / Passing Python does not prove C# execution. بازبین باید بتواند استدلال را از فایل‌های قابل‌دیدن بازسازی کند. / A reviewer must reconstruct the reasoning from visible files.

References & freshness

· TOOL_VOLATILE
Microsoft ASP.NET Core 10 integration-testing guidance, including WebApplicationFactory and test host behavior.
· PRACTICE_EVOLVING
.NET diagnostics overview for logs, metrics, traces and diagnostic tooling.
· PRACTICE_EVOLVING
.NET observability guidance using ILogger, Meter, Activity/ActivitySource and OpenTelemetry collection/export.
· TOOL_VOLATILE
Microsoft guidance for the .NET Generic Host used by ASP.NET Core templates and application lifecycle/configuration.
·
Startup options validation
·
Dependency lifetime boundary
·
Environment key mapping
·
Existence of explicit sample dependency version only; not latest-version or compatibility execution proof.
·
Existence of explicit sample dependency version only; not latest-version or compatibility execution proof.
·
Existence of explicit sample dependency version only; not latest-version or compatibility execution proof.
·
Existence of explicit sample dependency version only; not latest-version or compatibility execution proof.

Case studies

Lab files

Requirements: Python 3.10+, standard library only. All fixture records are synthetic.

  1. Run python run_checks.py --inventory.
  2. Read policy.json and the English lesson.
  3. Run python run_checks.py --template fixtures/missing_env.json submission.json.
  4. Supply your decision, issues, evidence pointers and reasoning.
  5. Run python run_checks.py --check fixtures/missing_env.json submission.json.

STRUCTURE_PASS_SEMANTICS_UNREVIEWED proves structural checks only. Semantic evaluation and an evidence-based explanation are still required. No final decision is prefilled. Existing output files are not overwritten.

After the guided case, analyze the negative cases and transfer_changed. stale_review and failed_request provide additional practice. Preserve shipped inputs and explain repairs in your answer. Use a separate copy for exploratory mutations; never treat edited records as a real execution.

See dotnet/README.en.md for the supplementary C# run. Python success proves neither C# tests nor real hosting.

The template records evaluation date and policy digest. Add --as-of 2026-09-12 to --template for the historical example; the default is today. evidence_field_map.csv locates all 21 dimensions. Do not erase the fixed policy contract; malformed evidence remains UNKNOWN/HOLD.

Accompanying resources: CSV, rubric, source links.