Whenever a project needs an internal wallet, SaaS credit system, or user balance, doing a naive wallet.Balance -= amount in EF Core is a recipe for race conditions and negative balances under load.
I put together a minimal reference architecture in .NET + EF Core + PostgreSQL to solve the four main headaches of balance management without introducing heavy distributed locks (like Redis Redlock).
1. Preventing Double-Spending (Pessimistic + Optimistic Locking)
Instead of relying solely on optimistic concurrency (which throws DbUpdateConcurrencyException and forces clients to retry under high contention), we lock the wallet row inside the transaction using PostgreSQL's FOR UPDATE :
var lockedWallets = await db . Wallets . FromSqlInterpolated ( $"SELECT *, xmin FROM \"Wallets\" WHERE \"Id\" = { walletId } FOR UPDATE" ) .…