Part 3 - Production-Ready C#: Resource Management, Testing & Telemetry, Security & Reliability
Published: (January 7, 2026 at 06:03 AM EST)
2 min read
Source: Dev.to
Source: Dev.to
Resource Management: IDisposable, IAsyncDisposable, Timeouts & Cancellation
Deterministic Disposal
using var stream = File.OpenRead(path); // disposed at scope end
await using var connection = new SomeAsyncDisposable(); // async cleanup
Timeouts + Cancellation
public async Task FetchAsync(HttpClient http, string url, CancellationToken ct)
{
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromSeconds(3));
return await http.GetStringAsync(url, cts.Token).ConfigureAwait(false);
}
Guidelines
- Do not rely on finalizers; they’re non‑deterministic.
- Dispose streams, DB connections, sockets promptly.
- Propagate
CancellationTokenfrom controller → service → repository.
Unit & Integration Tests
// xUnit unit test
public class MathTests
{
[Fact]
public void Sum_Works() => Assert.Equal(7, 3 + 4);
}
// ASP.NET Core integration test with WebApplicationFactory
public class ApiTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public ApiTests(WebApplicationFactory<Program> factory) => _client = factory.CreateClient();
[Fact]
public async Task GetUsers_ReturnsOk()
{
var res = await _client.GetAsync("/api/users");
Assert.True(res.IsSuccessStatusCode);
}
}
Benchmarking (Evidence‑Based Decisions)
[MemoryDiagnoser]
public class ConcatBench
{
[Benchmark]
public string PlusConcat()
{
var s = "";
for (int i = 0; i // (truncated example)
}
}
Structured Logging
public class UsersController : ControllerBase
{
private readonly ILogger<UsersController> _logger;
public UsersController(ILogger<UsersController> logger) => _logger = logger;
[HttpGet]
public IActionResult Get()
{
using (_logger.BeginScope(new Dictionary<string, object> { ["CorrelationId"] = Guid.NewGuid() }))
{
_logger.LogInformation("Fetching users");
return Ok(new[] { "alice", "bob" });
}
}
}
Tracing with OpenTelemetry
// Program.cs
builder.Services.AddOpenTelemetry()
.WithTracing(t => t
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSource("MyCompany.MyService"))
.StartWithHost();
// Use ActivitySource in code
var activitySource = new ActivitySource("MyCompany.MyService");
using var activity = activitySource.StartActivity("ProcessOrder");
// add tags/events...
Observability Pipeline (Simplified)
flowchart LR
A[App code] --> B[ILogger]
A --> C[ActivitySource]
B --> D[Log sink]
C --> E[OTel exporter]
D --> F[Central log store]
E --> G[Tracing backend]
Validation
public class CreateUserRequest
{
[Required, EmailAddress] public string Email { get; set; } = default!;
[Required, MinLength(8)] public string Password { get; set; } = default!;
}
Prevent Injection
- Use parameterized queries (ORMs handle this; use
FromSqlInterpolatedcarefully). - Encode/escape output in HTML contexts.
Idempotency & Retries
- Make external calls idempotent; use idempotency keys or
PUTsemantics where possible. - Apply timeouts, circuit‑breakers, and safe retries.
Timeout + Retry Sketch
public async Task SafeFetchAsync(HttpClient http, string url, CancellationToken ct)
{
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromSeconds(2));
for (int attempt = 0; attempt // (truncated example)
{
try
{
return await http.GetStringAsync(url, cts.Token);
}
catch when (attempt < 3)
{
// retry with backoff
await Task.Delay(TimeSpan.FromMilliseconds(100 * (attempt + 1)), ct);
}
}
throw new TimeoutException("Exceeded retry attempts");
}