Publishing Bi-Weekly · ASP.NET Core · Design Patterns · Architecture · 20 yrs C#/.NET · cleancsharp.com
Professional Development Patterns

Write .NET code that scales.

20 years of C#/.NET in production — world-class healthcare companies, national-scale real estate platforms, high-volume microservices. The patterns here are the ones that actually held up. No toy examples. Just what works at scale.

Michael O'Hara
Michael O'Hara · writes here
20 YRS · C# / .NET / ASP.NET
OrderController.cs WebApi Controllers main · 18 lines · UTF-8
1// ❌ The wrong way — you wrote your own dependency.
2public class OrderController : ControllerBase
3{
4    private readonly OrderService _service = new OrderService();
5}
6
7// ✓ The clean way — ask for what you need.
8public class OrderController(IOrderService service) : ControllerBase
9{
10    private readonly IOrderService _service = service;
11
12    [HttpPost]
13    public IActionResult Create(OrderDto dto) =>
14        Ok(_service.Create(dto));
15}
16
17// Program.cs — register once, inject everywhere.
18builder.Services.AddScoped<IOrderService, OrderService>();
● main C# .NET 9 Ln 8, Col 24 UTF-8 LF
$
// LATEST The two most recent posts
POST 01 · PUBLISHED
Live Async

Async/Await — Don't Block Your Threads

A waiter who stands at the kitchen window waiting for one dish starves the whole dining room. Your API does the same thing every time async code turns back into blocking code — and it only shows up when production traffic hits.

OrderController.cs
// ❌ Blocks the thread — exhausts the pool under load.
public IActionResult GetOrder(int id) =>
    Ok(_repo.GetAsync(id).Result);

// ✓ Awaits properly — frees the thread for other work.
public async Task<IActionResult> GetOrder(int id) =>
    Ok(await _repo.GetAsync(id));

// And the call site:
await using var db = await _factory.CreateAsync();
var customer = await db.Customers
    .Where(c => c.Id == id)
    .SingleAsync(ct);
POST 02 · PUBLISHED
Live Dependency Injection

Dependency Injection in ASP.NET Core — Stop Using new

If your controller creates its own dependencies, your tests must too — including real database connections. A test that should take 50 ms now takes 2 seconds and fails if the database is offline.

OrderController.cs
// Constructor injection — clean & testable.
public class OrderController(IOrderService service) : ControllerBase
{
    [HttpPost]
    public IActionResult Create(OrderDto dto) =>
        Ok(service.Create(dto));
}

// Program.cs — register once, inject everywhere.
builder.Services.AddScoped<IOrderService, OrderService>();
// MOST READ Top performers all time
POST 03 · PUBLISHED
Live Error Handling

Exception Handling That Survives Production

At 1,500 fake "errors" an hour, your error log is 97% noise — and the one SqlException that matters is buried under exceptions that aren't exceptional. Expected failures are results, not throws.

ClaimService.cs
// ❌ Exceptions as control flow — your log becomes noise.
throw new ClaimNotFoundException(claimId);

// ✓ Expected outcomes are results, not exceptions.
public async Task<Result<Claim>> GetClaimAsync(string id)
{
    var claim = await _repo.FindAsync(id);
    return claim is null
        ? Result<Claim>.Failure($"Claim {id} not found")
        : Result<Claim>.Success(claim);
}
POST 04 · PUBLISHED
Live AI & Workflow

Be the Architect, Not the Typist

Letting AI write your code one method at a time is a step sideways. Direct it at the system, hand it the constraints, and review the design — that's how twenty years of experience compounds with the tools.

PaymentReconciler.cs
// Don't type code. Direct the architect.
//
// Instead of:
//   "Write a method that adds two numbers"
//
// Try:
//   "Add an idempotent reconcile step to the
//    payment pipeline that handles partial refunds
//    without double-crediting the customer."

public sealed class PaymentReconciler
{
    public async Task<ReconcileResult> ReconcileAsync(
        PaymentBatch batch,
        CancellationToken ct)
    {
        // implementation guided by your specification
    }
}
// 02 Everything ALL POSTS · NEWEST FIRST
// UPCOMING What's on the publishing schedule
05 Configuration Management That Won't Get You Fired Configuration Jun 24 8 min 06 Building Professional WebAPI Controllers WebAPI Jul 8 10 min 07 When the CRUD Hits the Fan — Building Resilient WebAPI Controllers Resilience Jul 22 14 min 08 Unit Testing with NSubstitute — Tests That Survive Refactoring Testing Aug 5 14 min 09 LINQ Pitfalls — Multiple Enumeration and Deferred Execution Data Aug 2026 ~10 min 10 EF Core Best Practices — Don't Let Your ORM Bite You Data Sep 2026 ~12 min 11 API Versioning Without Breaking Clients Backend Sep 2026 ~9 min