Table of Contents

AggregatingRateProvider Class

Definition

Namespace
Bodu.Financial.ExchangeRates.Caching
Assembly
Bodu.Financial.ExchangeRates.Caching.dll
Package
Bodu.Financial.ExchangeRates.Caching 1.0.0
Source
AggregatingRateProvider.cs

Groups several named IDatedRateProvider children behind a single entry point, combining them through a configurable IRateAggregationStrategy with optional per-currency-pair routing.

public sealed class AggregatingRateProvider : IDatedRateProvider, IRateProvider, IHistoricalRateProvider
Inheritance
AggregatingRateProvider
Implements
Inherited Members
Extension Methods

Examples

// Wrap each source in its own cache, then group them with per-FX-pair routing.
var rba = new CachingRateProvider(rbaSource, new TomlFileRateCache(
    new FileRateCacheOptions { Provider = "RBA", CacheDirectory = "/var/cache/fx" }), options);
var ecb = new CachingRateProvider(ecbSource, new InMemoryRateCache("ECB"), options);

var aggregation = new RateAggregationOptions();
aggregation.Routes[new CurrencyPair(CurrencyCode.AUD, CurrencyCode.USD)] = new CurrencyPairRoute(new[] { "RBA", "ECB" });
aggregation.Routes[new CurrencyPair(CurrencyCode.USD, CurrencyCode.GBP)] = new CurrencyPairRoute(new[] { "ECB", "RBA" });
aggregation.Routes[new CurrencyPair(CurrencyCode.EUR, CurrencyCode.USD)] = new CurrencyPairRoute(new[] { "ECB", "RBA" }, new AverageStrategy());

IDatedRateProvider provider = new AggregatingRateProvider(
    new[]
    {
        new NamedDatedRateProvider("RBA", rba),
        new NamedDatedRateProvider("ECB", ecb),
    },
    aggregation);

// AUD/USD is routed RBA-then-ECB; EUR/USD is averaged across both.
RateLookupResult aud = provider.GetRate("AUD", "USD", new DateOnly(2024, 1, 3), RateLookupOptions.Exact);

// Reach a specific source directly when the routed result is not what you want.
if (((AggregatingRateProvider)provider).TryGetProvider("ECB", out IDatedRateProvider ecbOnly))
{
    RateLookupResult ecbRate = ecbOnly.GetRate("AUD", "USD", new DateOnly(2024, 1, 3), RateLookupOptions.Exact);
}

Remarks

The aggregator is itself a provider on both the dated IDatedRateProvider and timeless IRateProvider surfaces, so it composes anywhere a provider is expected - including wrapping each child in its own CachingRateProvider so the grouping sits above the per-source cache. Same-currency identity is handled here before any strategy is consulted.

Aggregation combines distinct sources - children that differ in who published the rate - for resilience and coverage, and is orthogonal to the tiered read-through that a CachingRateProvider forms by stacking caches over a single source. The two nest: an aggregator child can itself be a stacked cache over a source. Reach for aggregation for fallback, an averaged rate, or per-pair routing across providers; reach for stacking to cut latency and survive restarts on one provider.

To target a specific source rather than the routed result, resolve it by name through TryGetProvider(string, out IDatedRateProvider) (or, under dependency injection, a keyed service); the lookup methods always apply the configured strategy and routing.

When RespectHistoryAvailability is enabled (the default), children that advertise their history depth through IHistoricalRateProvider and have declared they cannot serve any part of the requested date or window are dropped from the candidate set before the strategy runs, so a priority fallback does not waste a call on a source that cannot answer. A non-aware child is treated as unbounded and always kept, and the group's own HistoryAvailability composes the most generous declaration across the children.

Routing is frozen at construction: the per-pair routes and default order are resolved to their candidate sets once, after the constructor validates every referenced name. Mutating the supplied Routes or DefaultProviderOrder after construction has no effect on this instance; construct a new provider to change routing.

Constructors

AggregatingRateProvider(IEnumerable<NamedDatedRateProvider>, RateAggregationOptions?, TimeProvider?, ILogger?)

Initializes a new instance of the AggregatingRateProvider class.

public AggregatingRateProvider(IEnumerable<NamedDatedRateProvider> children, RateAggregationOptions? options = null, TimeProvider? timeProvider = null, ILogger? logger = null)

Parameters

children IEnumerable<NamedDatedRateProvider>

The named children to group, in default priority order.

options RateAggregationOptions

The aggregation options, or null for priority-fallback defaults.

timeProvider TimeProvider

The time source used by the timeless surface. null selects System .

logger ILogger

The logger that records routing and aggregation outcomes. null selects Instance.

Exceptions

ArgumentNullException

Thrown when children is null, or when options has a null DefaultStrategy.

ArgumentException

Thrown when children is empty, contains a blank name, a null provider, or a duplicate name, or when options references a child name that was not supplied.

Properties

HistoryAvailability

Gets the history depth this group advertises: the most generous availability across the grouped children, because a date any single child can serve is a date the group can serve.

public RateHistoryAvailability HistoryAvailability { get; }

Property Value

RateHistoryAvailability

Unbounded when any child is Unbounded or does not implement IHistoricalRateProvider (a non-aware child declares no floor); otherwise the child availability whose earliest available date, evaluated against the current date, reaches furthest back.

ProviderNames

Gets the names of the grouped children.

public IReadOnlyCollection<string> ProviderNames { get; }

Property Value

IReadOnlyCollection<string>

The child names, in no particular order.

Methods

GetRate(string, string, RateLookupOptions?)

Resolves the most recent available exchange rate from fromIsoCode to toIsoCode under options, throwing if no rate is available.

public RateLookupResult GetRate(string fromIsoCode, string toIsoCode, RateLookupOptions? options = null)

Parameters

fromIsoCode string

The source-currency ISO-style code.

toIsoCode string

The destination-currency ISO-style code.

options RateLookupOptions

The lookup rules to apply. null selects the implementation's default most-recent policy.

Returns

RateLookupResult

The resolved RateLookupResult.

Exceptions

ArgumentNullException

Thrown if fromIsoCode or toIsoCode is null.

ArgumentException

Thrown if either ISO code is not a three-character uppercase ASCII code, or if options is invalid.

KeyNotFoundException

Thrown if no rate is available for the request.

GetRate(string, string, DateOnly, RateLookupOptions?)

Resolves the exchange rate from fromIsoCode to toIsoCode on date under options, throwing if no rate is available.

public RateLookupResult GetRate(string fromIsoCode, string toIsoCode, DateOnly date, RateLookupOptions? options = null)

Parameters

fromIsoCode string

The source-currency ISO-style code.

toIsoCode string

The destination-currency ISO-style code.

date DateOnly

The calendar date for which a rate is required.

options RateLookupOptions

The lookup rules to apply, including date-resolution policy and tolerance. null is treated as Exact.

Returns

RateLookupResult

The resolved RateLookupResult.

Exceptions

ArgumentNullException

Thrown if fromIsoCode or toIsoCode is null.

ArgumentException

Thrown if either ISO code is not a three-character uppercase ASCII code, or if options is invalid (for example, Exact with non-zero tolerance).

ArgumentOutOfRangeException

Thrown if options contains a negative tolerance or an undefined enum value.

KeyNotFoundException

Thrown if no rate is available for the request under the supplied options.

GetRateAsync(string, string, RateLookupOptions?, CancellationToken)

Asynchronously resolves the most recent available exchange rate from fromIsoCode to toIsoCode under options, throwing if no rate is available.

public ValueTask<RateLookupResult> GetRateAsync(string fromIsoCode, string toIsoCode, RateLookupOptions? options = null, CancellationToken cancellationToken = default)

Parameters

fromIsoCode string

The source-currency ISO-style code.

toIsoCode string

The destination-currency ISO-style code.

options RateLookupOptions

The lookup rules to apply. null selects the implementation's default most-recent policy.

cancellationToken CancellationToken

A token to observe while awaiting the operation.

Returns

ValueTask<RateLookupResult>

A task that yields the resolved RateLookupResult.

Exceptions

ArgumentNullException

Thrown if fromIsoCode or toIsoCode is null.

ArgumentException

Thrown if either ISO code is not a three-character uppercase ASCII code, or if options is invalid.

KeyNotFoundException

Thrown if no rate is available for the request.

GetRateAsync(string, string, DateOnly, RateLookupOptions?, CancellationToken)

Asynchronously resolves the exchange rate from fromIsoCode to toIsoCode on date under options, throwing if no rate is available.

public ValueTask<RateLookupResult> GetRateAsync(string fromIsoCode, string toIsoCode, DateOnly date, RateLookupOptions? options = null, CancellationToken cancellationToken = default)

Parameters

fromIsoCode string

The source-currency ISO-style code.

toIsoCode string

The destination-currency ISO-style code.

date DateOnly

The calendar date for which a rate is required.

options RateLookupOptions

The lookup rules to apply, including date-resolution policy and tolerance. null is treated as Exact.

cancellationToken CancellationToken

A token to observe while awaiting the operation.

Returns

ValueTask<RateLookupResult>

A task that yields the resolved RateLookupResult.

Exceptions

ArgumentNullException

Thrown if fromIsoCode or toIsoCode is null.

ArgumentException

Thrown if either ISO code is not a three-character uppercase ASCII code, or if options is invalid.

ArgumentOutOfRangeException

Thrown if options contains a negative tolerance or an undefined enum value.

KeyNotFoundException

Thrown if no rate is available for the request under the supplied options.

GetRates(string, string, DateOnly, DateOnly)

Returns every available rate from fromIsoCode to toIsoCode whose observation date falls within the inclusive range startDate to endDate.

public RateRangeResult GetRates(string fromIsoCode, string toIsoCode, DateOnly startDate, DateOnly endDate)

Parameters

fromIsoCode string

The source-currency ISO-style code.

toIsoCode string

The destination-currency ISO-style code.

startDate DateOnly

The inclusive start of the range.

endDate DateOnly

The inclusive end of the range.

Returns

RateRangeResult

An RateRangeResult carrying the rates in the range ordered by date, the requested window, and the observed span; the result is empty when no rates are available.

Remarks

The lookup is range-based and does not apply a date-resolution policy: it returns the observations that exist within the window rather than resolving a single date. An implementation backed by a remote feed may block to fetch on demand, or serve only already-loaded data; use GetRatesAsync(string, string, DateOnly, DateOnly, CancellationToken) to fetch without blocking.

Exceptions

ArgumentNullException

Thrown if fromIsoCode or toIsoCode is null.

ArgumentException

Thrown if either ISO code is not a three-character uppercase ASCII code, or if endDate precedes startDate.

GetRatesAsync(string, string, DateOnly, DateOnly, CancellationToken)

Asynchronously returns every available rate from fromIsoCode to toIsoCode whose observation date falls within the inclusive range startDate to endDate.

public ValueTask<RateRangeResult> GetRatesAsync(string fromIsoCode, string toIsoCode, DateOnly startDate, DateOnly endDate, CancellationToken cancellationToken = default)

Parameters

fromIsoCode string

The source-currency ISO-style code.

toIsoCode string

The destination-currency ISO-style code.

startDate DateOnly

The inclusive start of the range.

endDate DateOnly

The inclusive end of the range.

cancellationToken CancellationToken

A token to observe while awaiting the operation.

Returns

ValueTask<RateRangeResult>

A task that yields an RateRangeResult carrying the rates in the range ordered by date, the requested window, and the observed span; the result is empty when no rates are available.

Remarks

The lookup is range-based and does not apply a date-resolution policy: it returns the observations that exist within the window rather than resolving a single date. Implementations backed by a remote feed may fetch on demand, which is why the method is asynchronous.

Exceptions

ArgumentNullException

Thrown if fromIsoCode or toIsoCode is null.

ArgumentException

Thrown if either ISO code is not a three-character uppercase ASCII code, or if endDate precedes startDate.

TryGetProvider(string, out IDatedRateProvider)

Attempts to resolve a grouped child by name, for callers that need a specific source rather than the routed result.

public bool TryGetProvider(string name, out IDatedRateProvider provider)

Parameters

name string

The child name.

provider IDatedRateProvider

When this method returns true, the matching child provider.

Returns

bool

true when a child with the supplied name exists; otherwise false.

Exceptions

ArgumentNullException

Thrown when name is null.

TryGetRate(string, string, DateOnly, RateLookupOptions?, out RateLookupResult)

Attempts to resolve the exchange rate from fromIsoCode to toIsoCode on date under options, returning a flag indicating whether a rate was found.

public bool TryGetRate(string fromIsoCode, string toIsoCode, DateOnly date, RateLookupOptions? options, out RateLookupResult result)

Parameters

fromIsoCode string

The source-currency ISO-style code.

toIsoCode string

The destination-currency ISO-style code.

date DateOnly

The calendar date for which a rate is required.

options RateLookupOptions

The lookup rules to apply, including date-resolution policy and tolerance. null is treated as Exact.

result RateLookupResult

When this method returns true, contains the resolved RateLookupResult; otherwise, contains default.

Returns

bool

true if a rate was resolved; otherwise false.

Exceptions

ArgumentNullException

Thrown if fromIsoCode or toIsoCode is null.

ArgumentException

Thrown if either ISO code is not a three-character uppercase ASCII code, or if options is invalid.

ArgumentOutOfRangeException

Thrown if options contains a negative tolerance or an undefined enum value.

Explicit Interface Implementations

IRateProvider.GetRate(string, string)

Returns the exchange rate that converts one unit of fromIsoCode to units of toIsoCode.

decimal IRateProvider.GetRate(string fromIsoCode, string toIsoCode)

Parameters

fromIsoCode string

The source currency's ISO 4217 code.

toIsoCode string

The destination currency's ISO 4217 code.

Returns

decimal

The rate.

Exceptions

KeyNotFoundException

No rate is available for the requested pair.

Applies to

ProductVersions
.NET8, 10