OpenRates.NET is an open-source .NET library and data service providing daily and historical currency exchange rates from trusted sources like the European Central Bank. Free, cacheable, and CDN-ready with built-in resilience patterns.
- π High Performance: Built-in hybrid caching with configurable expiration
- π‘οΈ Enterprise Ready: Comprehensive error handling, logging, and cancellation support
- π¦ Dependency Injection: First-class support for Microsoft.Extensions.DependencyInjection
- π Daily Updates: Automated data refresh via GitHub Actions
- π CDN Distribution: Fast global access via jsDelivr CDN
- β Well Tested: Comprehensive test coverage with xUnit
This solution contains four projects:
- OpenRates.Core: Class library containing providers, models, and services
Providers/EcbProvider.cs: European Central Bank data provider with HttpClient injectionServices/RateMerger.cs: Merge rates from multiple sources with conflict resolutionModels/ExchangeRates.cs: Strongly-typed rate models
- OpenRates.Publisher: Console application for updating exchange rate data
- Runs daily via GitHub Actions
- Uses Microsoft.Extensions.Hosting for DI and logging
- Includes graceful shutdown and error handling
- OpenRates.Client: NuGet SDK for accessing exchange rates
HybridCacheintegration for optimal performance- Automatic retry and error handling
- Input validation and null safety
- OpenRates.Tests: xUnit test project with 8+ comprehensive tests
Install the OpenRates.Net client SDK via NuGet:
dotnet add package OpenRates.NetOr via Package Manager Console:
Install-Package OpenRates.NetPackage Details:
- Package ID:
OpenRates.Net - Version: 1.0.0
- Target Framework: .NET 9.0
- Dependencies:
- Microsoft.Extensions.Caching.Hybrid (9.10.0)
- OpenRates.Core (included)
What's Included:
IOpenRatesClientinterface for dependency injectionOpenRatesClientimplementation with built-in caching- Full async/await support with cancellation tokens
- Comprehensive error handling and input validation
If you want to build from source:
git clone https://github.com/henrikroschmann/OpenRates.NET.git
cd OpenRates.NET
dotnet restore
dotnet buildTo create a NuGet package locally:
cd src/OpenRates.Client
dotnet pack -c Release -o ./nupkgusing Microsoft.Extensions.DependencyInjection;
using OpenRates.Client;
var services = new ServiceCollection();
// Register OpenRates client with HttpClient factory and caching
services.AddHttpClient<IOpenRatesClient, OpenRatesClient>();
services.AddHybridCache();
var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<IOpenRatesClient>();
// Get current EUR to USD rate
var rate = await client.GetRateAsync("EUR", "USD");
Console.WriteLine($"EUR/USD: {rate}");
// Get historical rate
var historicalRate = await client.GetRateAsync(
"EUR",
"GBP",
at: new DateTime(2024, 1, 15)
);
Console.WriteLine($"EUR/GBP on 2024-01-15: {historicalRate}");using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenRates.Core.Providers;
var services = new ServiceCollection();
services.AddHttpClient<EcbProvider>();
services.AddLogging(builder => builder.AddConsole());
var provider = services.BuildServiceProvider();
var ecbProvider = provider.GetRequiredService<EcbProvider>();
var rates = await ecbProvider.FetchAsync(CancellationToken.None);
Console.WriteLine($"Fetched {rates.Rates.Count} base currencies");
// Get specific rate
var eurToUsd = rates.TryGet("EUR", "USD");
if (eurToUsd.HasValue)
{
Console.WriteLine($"EUR/USD: {eurToUsd.Value}");
}cd src/OpenRates.Publisher
dotnet run
# Output:
# info: OpenRates.Core.Providers.EcbProvider[0]
# Fetching ECB daily exchange rates from https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml
# info: OpenRates.Core.Providers.EcbProvider[0]
# Successfully fetched 31 ECB exchange rates
# info: Program[0]
# β
Rates updated: 2025-10-28
# - Latest: data\latest.json
# - Dated: data\2025-10-28.jsonExchange rate data is automatically updated daily and stored in the /data directory. The data is published to GitHub and can be accessed via jsDelivr CDN:
Latest rates:
https://cdn.jsdelivr.net/gh/henrikroschmann/OpenRates.NET@main/data/latest.json
Historical rates (by date):
https://cdn.jsdelivr.net/gh/henrikroschmann/OpenRates.NET@main/data/2025-10-28.json
{
"date": "2025-10-28T00:00:00",
"rates": {
"eur": {
"usd": 1.0842,
"gbp": 0.8567,
"jpy": 162.45
},
"usd": {
"eur": 0.9223
}
}
}- SOLID: Single responsibility, dependency inversion throughout
- Async/Await: Proper async patterns with cancellation token support
- Null Safety: Nullable reference types enabled across all projects
- Error Handling: Comprehensive try-catch with specific exception types
- Resource Management: HttpClient injection, no resource leaks
- Logging: Structured logging with Microsoft.Extensions.Logging
- Caching: Hybrid cache strategy for optimal performance
- No Resource Leaks: HttpClient injected via IHttpClientFactory
- Comprehensive Error Handling: All HTTP and parsing operations wrapped with specific exceptions
- Caching Restored: HybridCache with 12-hour expiration prevents CDN hammering
- Input Validation: ArgumentNullException and ArgumentException for all public APIs
- Cancellation Support: CancellationToken throughout async operations
- Dependency Injection: Full DI support with Microsoft.Extensions.Hosting
- Enhanced Testing: 8 comprehensive tests with edge case coverage
dotnet restore
dotnet builddotnet testdotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcovservices.AddHttpClient<IOpenRatesClient, OpenRatesClient>(client =>
{
client.Timeout = TimeSpan.FromSeconds(30);
});
services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromHours(12),
LocalCacheExpiration = TimeSpan.FromHours(6)
};
});The publisher uses appsettings.json for configuration:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
}
}- European Central Bank (ECB): Daily reference rates for 30+ currencies against EUR
- Future: CoinGecko (cryptocurrency rates) - planned
- Future: Additional providers - planned
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow existing code style and patterns
- Add tests for new functionality
- Ensure all tests pass (
dotnet test) - Update documentation as needed
- Enable nullable reference types in new files
This project is licensed under the MIT License - see the LICENSE file for details.
- Exchange rate data provided by the European Central Bank
- CDN hosting by jsDelivr
- Built with .NET 9.0
- π Issues: GitHub Issues
- π¬ Discussions: GitHub Discussions
Made with β€οΈ by the OpenRates.NET