ISO 4217 Currency Reference for .NET — Strongly Typed and Production-Ready
Source: Dev.to
Overview
Working with currencies in .NET often looks simple—until it becomes a mess. String‑based codes, inconsistent casing, withdrawn currencies, special units, and edge cases can silently break your logic. Common pitfalls include:
if (currencyCode == "usd" || currencyCode == "Usd" || currencyCode == "USD")
{
// ...
}
or the “improved” version:
if (string.Equals(currencyCode, "usd", StringComparison.OrdinalIgnoreCase))
{
// ...
}
and even worse:
if (currency == "XXX") { /* ??? */ }
if (currency == "BYR") { /* replaced by BYN */ }
All of this happens because currencies are treated as plain strings. ISO 4217 is a standard, so we should be able to consume it in a strongly‑typed way.
The Idea
Generate ISO currency types at compile time with a Source Generator. The result is the HawkN.Currency.Reference.Iso4217 library.
- NuGet:
- GitHub:
A lightweight, zero‑dependency, source‑generated, production‑ready ISO 4217 reference library for .NET.
Installation
CLI
dotnet add package HawkN.Currency.Reference.Iso4217 --version 8.0.1
Package Manager
Install-Package HawkN.Currency.Reference.Iso4217 -Version 8.0.1
Registration
services.AddCurrencyService();
Basic Usage
Console Application
using var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddCurrencyService();
})
.Build();
var currencyService = host.Services.GetRequiredService<ICurrencyService>();
Minimal API Injection
app.MapGet("/currencies", ([FromServices] ICurrencyService svc) => svc.Query().Build());
Querying Currencies
Include Specific Types
var currencies = currencyService.Query()
.Includes
.Type(CurrencyType.Fiat)
.Type(CurrencyType.SpecialUnit)
.Type(CurrencyType.SpecialReserve)
.Type(CurrencyType.PreciousMetal)
.Build();
Currency Types
- Fiat – e.g.,
USD,EUR,JPY - SpecialUnit – e.g.,
SDR, IMF units - SpecialReserve
- PreciousMetal – e.g.,
XAU,XAG,XPT,XPD
Filter Only Fiat Currencies
var fiat = currencyService.Query()
.Includes.Type(CurrencyType.Fiat)
.Build();
Exclude Specific Codes
var filtered = currencyService.Query()
.Includes.Type(CurrencyType.Fiat)
.Without(w => w.Codes(nameof(CurrencyCode.EUR), nameof(CurrencyCode.USD)))
.Build();
Conditional Selection
var selected = currencyService.Query()
.Includes.Type(CurrencyType.Fiat)
.Where(x => x.Code is "EUR" or "USD")
.Build();
Validation
var ok = currencyService.TryValidate("AFN", out var result);
Or using the strongly‑typed enum:
var ok = currencyService.TryValidate(CurrencyCode.AFN, out var result);
Retrieval
By String
var afn = currencyService.Get("AFN");
By Enum
var afn = currencyService.Get(CurrencyCode.AFN);
Historical Currencies
var historical = currencyService.GetAllHistorical();
foreach (var c in historical)
{
Console.WriteLine($"{c.Code} - {c.Name} (Withdrawn: {c.WithdrawnOn})");
}
Benefits
- Compile‑time safety instead of raw string literals.
- ISO‑compliant data without manual maintenance.
- Extremely fast lookups.
- Great developer experience (IntelliSense everywhere).
- AOT‑ready and reflection‑free.
- Static, deterministic data.
- Automatic updates via CI workflows.
Project Background
This project started as a small experiment: “Can I generate all ISO 4217 currencies with a Source Generator?”
- GitHub:
- NuGet:
Happy coding!