Provides the reference data (lookup table) framework: typed base classes, thread-safe collections, a hybrid-cache-backed orchestrator, and code-serialization support.
The canonical way to introduce reference data is through the *.CodeGen project (ref-data.yaml + CoreEx.CodeGen). This is the deterministic, preferred pattern used across all sample domains. Code generation produces:
| Generated output | Description |
|---|---|
*.g.cs — ReferenceData<TSelf> partial class |
The typed reference data contract |
*.g.cs — ReferenceDataCollection<TSelf> class |
Thread-safe, cache-friendly collection |
*.g.cs — IReferenceDataRepository |
Repository interface for loading from the database |
*.g.cs — ReferenceDataRepository |
EF Core implementation of the repository |
*.g.cs — IReferenceDataProvider / ReferenceDataService |
Orchestrator provider wiring all types together |
*.g.cs — ReferenceDataController |
API controller exposing all reference data types |
Declare every reference data type in ref-data.yaml inside the *.CodeGen project:
collectionSortOrder: Code
repository: EntityFramework
entities:
- name: MovementStatus
- name: UnitOfMeasure
plural: UnitsOfMeasure
properties:
- name: Scale
type: intRun the *.CodeGen project to regenerate all *.g.cs outputs. Never edit generated files directly.
After generation, add a hand-authored partial class alongside the generated one to declare the known code values as const string fields:
// MovementStatus.cs — hand-authored alongside MovementStatus.g.cs
public partial class MovementStatus
{
public const string Pending = "P";
public const string Confirmed = "C";
public const string Canceled = "X";
}Use these constants directly in business logic and validators — no runtime lookup required:
if (movement.Status == MovementStatus.Pending) { ... }Register the generated IReferenceDataProvider with the orchestrator, then dynamically register all generated services and repositories:
// Program.cs
builder.Services
.AddReferenceDataOrchestrator<ReferenceDataService>() // generated IReferenceDataProvider
...
// Dynamic registration discovers ReferenceDataService and ReferenceDataRepository via [ScopedService]
builder.Services.AddDynamicServicesUsing<ReferenceDataService, ReferenceDataRepository>();The orchestrator resolves IHybridCache from DI to cache loaded collections. Register FusionCache separately — see CoreEx.Caching.FusionCache.
AddReferenceDataOrchestrator also auto-registers ReferenceDataQuery.Default, enabling ReferenceDataOrchestrator.QueryAsync<TRef>(QueryArgs?, PagingArgs?, CancellationToken) on all ref-data endpoints out of the box. To customise filtering/ordering per type, pass a Func<Type, QueryArgsConfig?> selector:
// Custom config for one type; all others fall back to ReferenceDataQueryArgsConfig.Default.
builder.Services.AddReferenceDataOrchestrator(sp =>
{
var orch = new ReferenceDataOrchestrator(sp, sp.GetRequiredService<ILogger<ReferenceDataOrchestrator>>());
orch.RegisterQuery(new ReferenceDataQuery(type =>
type == typeof(Country) ? CountryQueryArgsConfig.Default : null));
return orch;
});For properties that serialise as a list of string codes on the wire but expose typed reference data objects in code:
public class Order : IIdentifier<Guid>
{
// Serialized as ["E","A"] on the wire; exposes ICollection<BasketStatus> in code
public ReferenceDataCodeCollection<BasketStatus> AllowedStatuses { get; set; } = [];
}Reference data items with StartsOn/EndsOn control IsValid at runtime. The validation date defaults to Runtime.UtcNow but can be overridden per-request by injecting IReferenceDataContext (registered as a scoped service) and setting its Date property.
// Override the validation date for the current request scope
public class MyService(IReferenceDataContext refDataContext)
{
public void SetValidationDate(DateTimeOffset date)
=> refDataContext.Date = date;
// Override for a specific type only
public void SetValidationDateForType(DateTimeOffset date)
=> refDataContext[typeof(DiscountCoupon)] = date;
}- Do not hand-write the
ReferenceData<TSelf>class, collection, repository, service, or controller — useref-data.yamland the*.CodeGenproject to generate them. - Do not edit
*.g.csfiles — they are overwritten on every generation run. - Do not load reference data collections on every request — the orchestrator caches via
IHybridCache; load functions are called only on a cache miss. - Do not access reference data in
staticconstructors — the orchestrator must be resolved from DI at runtime. - Do not use
ReferenceDataCodeCollection<T>for single-value fields — use a plainCodestring property on the contract instead.
- README — full
ReferenceData,ReferenceDataCollection,ReferenceDataHybridCache, andReferenceDataOrchestratorAPI reference. - CoreEx.Caching.FusionCache — recommended
IHybridCacheimplementation forReferenceDataHybridCache. - CoreEx — defines
IReferenceData,IReferenceDataCollection, andReferenceDataOrchestrator. - Contracts layer — how reference-data contracts are declared with
[ReferenceData]and consumed via code properties in entity contracts. - Infrastructure layer — reference-data repository implementation and cache registration in real sample code.
- Tooling — how
ref-data.yamland*.CodeGendrive generation of the full reference-data controller/service/repository layer.