September 16 2025
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddDbContextFactory<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
public sealed class ReportService(IDbContextFactory<AppDbContext> factory)
{
public async Task<IReadOnlyList<OrderDto>> GetAsync(CancellationToken ct)
{
await using var db = await factory.CreateDbContextAsync(ct);
return await db.Orders
.Where(o => o.Status == OrderStatus.Completed)
.Select(o => new OrderDto(o.Id, o.Total))
.ToListAsync(ct);
}
}
builder.Services.AddDbContextPool<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
app.MapGet("/orders/{id:int}", async (int id, AppDbContext db, CancellationToken ct) =>
{
var order = await db.Orders.FindAsync([id], ct);
return order is null ? Results.NotFound() : Results.Ok(order);
});
public sealed class CleanupService(IServiceScopeFactory scopes, ILogger<CleanupService> log) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using var scope = scopes.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// One short-lived unit of work
var cutoff = DateTime.UtcNow.AddDays(-30);
db.RemoveRange(db.AuditLogs.Where(x => x.CreatedAt < cutoff));
await db.SaveChangesAsync(stoppingToken);
await Task.Delay(TimeSpan.FromHours(6), stoppingToken);
}
}
}
public sealed class CleanupService(IDbContextFactory<AppDbContext> factory, ILogger<CleanupService> log)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await using var db = await factory.CreateDbContextAsync(ct);
// same cleanup logic …
await Task.Delay(TimeSpan.FromHours(6), ct);
}
}
}
public sealed class PricingCache(IDbContextFactory<AppDbContext> factory, IMemoryCache cache)
{
public async Task<decimal> GetPriceAsync(int productId, CancellationToken ct)
{
if (cache.TryGetValue(productId, out decimal price)) return price;
await using var db = await factory.CreateDbContextAsync(ct);
price = await db.Products
.Where(p => p.Id == productId)
.Select(p => p.Price)
.FirstAsync(ct);
cache.Set(productId, price, TimeSpan.FromMinutes(10));
return price;
}
}
builder.Services.AddDbContextPool<AppDbContext>(o =>
{
o.UseNpgsql(builder.Configuration.GetConnectionString("Default"));
// Keep options stateless; avoid per-request mutable state
});
1. Design Patterns that Deliver
This isn’t just another design patterns book. Dive into real-world examples and practical solutions to real problems in real applications.Check out it here.
Go-to resource for understanding the core concepts of design patterns without the overwhelming complexity. In this concise and affordable ebook, I've distilled the essence of design patterns into an easy-to-digest format. It is a Beginner level. Check out it here.
Every Monday morning, I share 1 actionable tip on C#, .NET & Arcitecture topic, that you can use right away.
Join 17,150+ subscribers to improve your .NET Knowledge.
Subscribe to the TheCodeMan.net and be among the 17,150+ subscribers gaining practical tips and resources to enhance your .NET expertise.