Newsletter Archive
Browse through our collection of past newsletters. Each edition is packed with C# and .NET insights.
Page 11 of 19 (56 editions)
Stop Mocking the Clock: TimeProvider Makes Time Testable
We’ve all been there. You write a unit test that depends on DateTime.UtcNow, and suddenly you’re building a custom IClock interface, or wrapping things in a static ambient context, or just… hoping the test doesn’t run at midnight on a leap year. TimeProvider puts all of that to rest. It’s a first-class abstraction for time, and it lives right in the base class library.
The concept is straightforward. Instead of calling DateTime.UtcNow or DateTimeOffset.UtcNow directly, you inject a TimeProvider and call GetUtcNow(). In production, you register TimeProvider.System, which just returns the real clock. In tests, you swap in a FakeTimeProvider that lets you set, freeze, and advance time whenever you want.
Let’s look at a practical example. Say you have a service that checks whether a coupon code has expired:
public class CouponService(TimeProvider timeProvider){ public bool IsValid(Coupon coupon) { var now = timeProvider.GetUtcNow(); return now < coupon.ExpiresAt; }
public async Task WaitAndExpire(Coupon coupon, CancellationToken ct) { var remaining = coupon.ExpiresAt - timeProvider.GetUtcNow(); if (remaining > TimeSpan.Zero) { await Task.Delay(remaining, timeProvider, ct); } coupon.IsActive = false; }}Notice that Task.Delay overload. It accepts a TimeProvider, so even delays become fully controllable in tests. Registering it in your DI container works the same way as any other service:
builder.Services.AddSingleton(TimeProvider.System);Now for the fun part. Install the testing companion package:
dotnet add package Microsoft.Extensions.TimeProvider.TestingAnd write tests that are completely deterministic. No flaky timing, no Thread.Sleep, no crossing your fingers:
using Microsoft.Extensions.Time.Testing;
public class CouponServiceTests{ [Fact] public void IsValid_ReturnsFalse_AfterExpiration() { // Start at a known point in time var fake = new FakeTimeProvider( new DateTimeOffset(2025, 6, 1, 12, 0, 0, TimeSpan.Zero));
var coupon = new Coupon { ExpiresAt = fake.GetUtcNow().AddHours(2) };
var service = new CouponService(fake);
Assert.True(service.IsValid(coupon));
// Jump forward 3 hours, instantly fake.Advance(TimeSpan.FromHours(3));
Assert.False(service.IsValid(coupon)); }
[Fact] public async Task WaitAndExpire_DeactivatesCoupon_WhenTimeAdvances() { var fake = new FakeTimeProvider( new DateTimeOffset(2025, 6, 1, 12, 0, 0, TimeSpan.Zero));
var coupon = new Coupon { ExpiresAt = fake.GetUtcNow().AddMinutes(30), IsActive = true };
var service = new CouponService(fake); var task = service.WaitAndExpire(coupon, CancellationToken.None);
// Time hasn't moved, coupon is still active Assert.True(coupon.IsActive);
// Advance past expiration fake.Advance(TimeSpan.FromMinutes(31));
await task; Assert.False(coupon.IsActive); }}
public class Coupon{ public DateTimeOffset ExpiresAt { get; set; } public bool IsActive { get; set; }}The FakeTimeProvider.Advance() call is the secret sauce. It moves the clock forward instantly, and any pending Task.Delay calls waiting on that TimeProvider will resolve right away. No real waiting, no race conditions, no flaky CI runs.
If you’re still maintaining a homegrown IClock or IDateTimeProvider interface, it might be time to retire it. TimeProvider is baked into the framework, works with Task.Delay, PeriodicTimer, and CancellationTokenSource.CancelAfter, and has a battle-tested fake ready to go. That’s one less abstraction you have to carry around.
Don't miss the next tip 💧
Get a .NET tip and curated links delivered to your inbox every week.
Freeze Your Lookups: FrozenDictionary & FrozenSet for Hot-Path Reads
If you’ve ever built a Dictionary or HashSet at startup, say for config values, feature flags, or permission sets, and then hammered it with reads for the lifetime of your app, .NET has a gift for you: System.Collections.Frozen.
FrozenDictionary<TKey, TValue> and FrozenSet<T> are immutable, read-optimized collections. You build them once from an existing collection, and in return the runtime produces an internal layout that’s specifically tuned for the keys you gave it. The trade-off is simple: construction is slower than a normal dictionary (the runtime analyzes your keys to pick an optimal strategy), but every subsequent lookup is faster, often dramatically on hot paths.
The API couldn’t be easier. You already have the extension methods you need:
using System.Collections.Frozen;
// Built once at startup from config, a database, etc.var configMap = new Dictionary<string, string>{ ["FeatureX:Enabled"] = "true", ["FeatureX:MaxRetries"] = "3", ["Cache:SlidingExpiration"] = "00:05:00", ["Logging:MinLevel"] = "Warning"};
var enabledFeatures = new HashSet<string>{ "dark-mode", "beta-search", "new-checkout-flow"};
// One call. Every read after this is fasterFrozenDictionary<string, string> frozenConfig = configMap.ToFrozenDictionary();FrozenSet<string> frozenFeatures = enabledFeatures.ToFrozenSet();Once frozen, the read API is exactly what you’re used to with ContainsKey, TryGetValue, indexer access, and Contains. So, it’s a drop-in replacement anywhere you have a build-once, read-many collection:
string retries = frozenConfig["FeatureX:MaxRetries"];
if (frozenConfig.TryGetValue("Cache:SlidingExpiration", out var expiration)) Console.WriteLine($"Sliding expiration: {expiration}");
bool betaSearchOn = frozenFeatures.Contains("beta-search");How much faster? In a quick micro-benchmark doing 10 million lookups against a small collection, the frozen variants were roughly 2× faster than their mutable counterparts:
| Collection | 10M Lookups |
|---|---|
Dictionary<string, string> | ~90 ms |
FrozenDictionary<string, string> | ~41 ms |
HashSet<string> | ~72 ms |
FrozenSet<string> | ~30 ms |
The key thing to remember: these collections are immutable after creation. There’s no Add, Remove, or Clear. If you need to mutate, you’re back to a regular Dictionary or HashSet. But for the very common pattern of “load once, read forever”, FrozenDictionary and FrozenSet are a one-line upgrade that turns a hot path cold.
Don't miss the next tip 💧
Get a .NET tip and curated links delivered to your inbox every week.
How to Implement Type Safe Unions in C# With OneOf
Grant Riordan shows how OneOf brings F# like discriminated unions to C#, so your methods can return precise outcomes with compile-time checks instead of sprawling hierarchies or surprise exceptions. In this post you get practical patterns for explicit success or failure, polymorphic return types, and stateful workflows using .Match for exhaustive handling that keeps APIs honest and maintainable.
Implementing Distributed Caching in Web API with Azure Managed Redis
Sai Nitesh Palamakula walks through adding distributed caching to an ASP.NET Core Web API using Azure Managed Redis and the IDistributedCache abstraction to speed up read-heavy endpoints across instances. This post focuses on when Redis is the right tool, how to implement the cache-aside pattern with absolute and sliding expiration, and the practical knobs like LRU eviction and AddStackExchangeRedisCache that move the performance needle.
Should my Services be Transient, Scoped, or Singleton?
The basics of Transient, Scoped, and Singleton .NET dependency injection lifetime scopes. This video covers the basics of each scope, including when it's best to use each, when not to use each, and an important reminder of something you should never do when registering your services.
Blazor Without the Bloat: Building “Power Inputs” with Plain HTML
David Gallivan makes the case for building Blazor forms without third-party component bloat by leaning on the power already built into plain HTML inputs. This post shows how to bind native inputs and constraints to Blazor with a little C#, delivering smaller bundles, built-in validation, and a modern UX without extra dependencies.
Create and Run Durable AI Agents in .NET with Microsoft Agent Framework
Kavathiyakhushali shows how to build durable AI agents in .NET with the Microsoft Agent Framework on Azure Durable Functions so your C# workflows can pause, persist state, and resume. This post highlights the orchestration pattern and minimal setup, and why this approach fits real world automation and multi step processes.
Building a Gated Multi-Step Wizard in .NET MAUI with Tab View
Sneha Kumar shows how to build a gated multi-step registration wizard in .NET MAUI with Syncfusion Tab View, turning tabs into a state-driven workflow that only unlocks after validation. In this post you will pick up patterns that reduce form fatigue like state-controlled tab visibility, real-time validation, and modular views for each step, plus a GitHub sample.
Don't miss the next tip 💧
Get a .NET tip and curated links delivered to your inbox every week.
How to Improve Performance with Threading in .NET
Saurav Kumar explains how threading and parallel programming in .NET can boost responsiveness and throughput, and why modern apps lean on Tasks, the thread pool, and Parallel APIs. This post focuses on choosing the right tool for I/O vs CPU workloads and avoiding gotchas like oversubscription, blocking, and unsafe shared state, so your code stays fast and sane.
New in .NET 10 and C# 14: Multi-Tenant Rate Limiting
Ali Hamza Ansari walks through building a multi-tenant rate limiter in ASP.NET Core 10 using the native throttling middleware in .NET 10 and C# 14, with a tenant resolver and token bucket partitions keyed by X-Tenant-ID. This post shows how the updated AddRateLimiter and UseRateLimiter simplify fair-use controls, reduce third-party dependencies, and keep noisy neighbor problems in check for real-world SaaS APIs.
How to Classify Documents using AI in C#/.NET
Cloudmersive shows how to plug AI driven document classification into C# and .NET, turning single type upload portals into smart intakes that handle resumes, cover letters, and more. In this post you install the Cloudmersive Document AI SDK, send an InputFile with category definitions, and read back a classification plus confidence to drive routing in your workflows.
Builder Design Pattern in C#: Complete Guide with Examples
Nick Cosentino walks through the Builder pattern in C#, showing how step by step construction tames telescoping constructors, complex initialization, and immutable models while keeping APIs fluent and readable. This post uses relatable examples from custom pizzas to query builders, calls out pitfalls and best practices, and maps the pattern to real .NET tooling like StringBuilder, EF Core, and configuration so you know when to reach for it and when to keep things simple.
Make Unity Feel Instant: Background Jobs With Thread Manager
WitShells shows how to keep Unity scenes smooth by offloading heavy work to background threads with ThreadManager, a lightweight C# package for jobs and progress reporting. This post focuses on a reusable pattern using a small worker pool, typed ThreadJob
Scalable .NET MAUI: Implementing Clean Architecture for Enterprise-Grade Apps
The C# Alchemist walks through applying Clean Architecture to .NET MAUI so you can dodge the Massive ViewModel trap and keep business rules clear of UI concerns. This post outlines the domain, application, infrastructure, and presentation layers with DI glue, highlighting how decoupling boosts testability, maintainability, and even makes platform or data store swaps far less painful.