The samples include two developer-time tooling projects per domain that are not part of the runtime stack. They run locally during development to generate code and manage the database schema. Neither is deployed or referenced at runtime.
Each domain has a *.CodeGen console project (e.g. Contoso.Products.CodeGen, Contoso.Shopping.CodeGen) that drives the CoreEx.CodeGen package. Its sole purpose is to eliminate the boilerplate associated with reference data: every reference-data entity follows the same deterministic pattern — contract class, API controller route, service method, repository interface, repository implementation, and EF Core mapper — so all of it is generated rather than hand-authored.
- A developer authors
ref-data.yamlalongsideProgram.cs, declaring each reference-data entity and any per-entity or per-property overrides. - The file is validated against the
coreex-refdata.jsonJSON Schema (IDE YAML language-server support provides validation and auto-complete). - Running
dotnet runinvokesCodeGenConsole, which hands off to OnRamp. OnRamp loadsref-data-script.yaml(embedded in the package), resolves the ordered generation steps, evaluates the embedded Handlebars templates against the typed configuration model, and writes.g.csfiles into the correct target projects. - Target project directories — Contracts, Api, Application, Infrastructure — are resolved automatically by convention from the CodeGen project's location; no path configuration is required.
| Artefact | Target layer | Description |
|---|---|---|
*.g.cs contract class |
Contracts | Typed reference-data entity contract. |
*.g.cs controller route |
API host | HTTP endpoint exposing the entity collection. |
*.g.cs service method |
Application | Service-layer method delegating to the repository. |
*.g.cs repository interface |
Application | Interface declaration for the entity repository. |
*.g.cs repository |
Infrastructure | EF Core repository implementation. |
*.g.cs mapper |
Infrastructure | Bidirectional EF Core mapper for the entity. |
All generated files carry the .g.cs suffix, clearly distinguishing them from hand-authored code and excluding them from manual maintenance.
collectionSortOrder: Code # sort order applied to all entity collections
repository: EntityFramework # repository implementation strategy
entities:
- name: Brand # simplest form — all defaults apply
- name: SubCategory
properties:
- name: CategoryCode
type: ^Category # ^ prefix = reference-data typed property
- name: UnitOfMeasure
plural: UnitsOfMeasure # override pluralization
properties:
- name: Scale
type: intThe full schema reference is available in docs/CodeGeneration.md, docs/Entity.md, and docs/Property.md.
Running dotnet run -- count invokes the Count command, which walks all solution output directories, classifies .g.cs vs hand-authored .cs files, and renders a formatted table showing total and generated file and line counts per directory. This gives an at-a-glance view of how much of the codebase is generated.
Each domain has a *.Database console project (e.g. Contoso.Products.Database, Contoso.Shopping.Database) that drives the DbEx database management framework. It is the primary tool for deploying and managing the domain database across all environments — local development, CI/CD pipelines, and production — providing a consistent, repeatable, command-driven lifecycle.
Products uses DbEx.Postgres (PostgreSQL); Shopping uses DbEx.SqlServer (SQL Server), demonstrating that the same tooling approach is database-agnostic. A MySQL variant (DbEx.MySql) is also available.
DbEx exposes a rich command set. A single run can execute one or more commands in order:
| Command | Description |
|---|---|
Drop |
Drops the database where it already exists. |
Create |
Creates the database where it does not already exist. |
Migrate |
Applies outstanding DbUp-style ordered migration scripts; tracks which have already run and applies only new ones. Scripts are immutable once applied. |
CodeGen |
Executes an optional code-generation step — used here to generate the Infrastructure-layer .g.cs files (persistence models and DbContext partial). |
Schema |
Drops and re-applies idempotent schema objects (stored procedures, functions, types) from the Schema/ folder on every run. |
Data |
Applies YAML/JSON-defined seed data — useful for reference data, master data, and test fixtures. Supports both INSERT and MERGE semantics. |
Reset |
Deletes all data from the database (exclusions configurable); designed for test environment resets. |
Execute |
Executes arbitrary SQL statements or script files directly. |
Script |
Scaffolds a new timestamped migration script file using the naming convention. |
Composite commands combine the above for common scenarios:
| Composite | Runs |
|---|---|
All |
Create → Migrate → CodeGen → Schema → Data |
Deploy |
Migrate → Schema |
DeployWithData |
Migrate → Schema → Data |
ResetAndAll |
Reset → All (test environments) |
In the samples, dotnet run without arguments defaults to All for local development, and Deploy (or DeployWithData) is the typical command for environment promotion pipelines.
Migration scripts live under Migrations/ as embedded resources and follow a timestamp-ordered naming convention:
20260101-000201-create-products-product.pgsql
20260101-000202-create-products-inventory.pgsql
Scripts are immutable once applied — subsequent schema changes require new scripts (e.g. ALTER TABLE). DbEx tracks applied scripts in a journal table, so only new scripts are run. Scripts can include moustache-style parameter substitution ({{Company}}) resolved from MigrationArgs.Parameters.
Objects under Schema/ (stored procedures, functions, user-defined table types) are dropped and re-created on every Schema run. This makes them safely idempotent and avoids the immutability constraint of migration scripts. The transactional outbox stored procedures / functions (e.g. spOutboxEnqueue, fn_outbox_batch_claim) are generated here and carry a .g.sql / .g.pgsql suffix.
Seed data is declared in Data/ref-data.yaml using a concise schema → table → rows YAML structure. DbEx infers column types from the live schema and supports:
- Insert (default) — straightforward row insertion.
- Merge (
$prefix on table name) — upsert semantics; idempotent on repeated runs. - Reference data resolution — foreign-key
Idcolumns can be specified by code; DbEx resolves the identifier at runtime via sub-query. - Identifier generation (
^prefix) — auto-generates GUIDs for primary keys.
products:
- $brand: # merge (upsert) — safe to re-run
- AVA: Avanade
- CON: Contoso
- $unit_of_measure:
- EA: Each
- KG: KilogramThe CodeGen command generates .g.cs files into the Infrastructure project based on the tables declared in dbex.yaml:
| Generated artefact | Location | Description |
|---|---|---|
<Entity>.g.cs (one per table) |
Infrastructure/Persistence/ |
Schema-aligned persistence model extending ModelBase<TId>, with optional marker interfaces such as ILogicallyDeleted. |
*DbContext.g.cs |
Infrastructure/Repositories/ |
Partial DbContext class exposing AddGeneratedModels(ModelBuilder) to register all persistence models with EF Core. |
These are the only .g.cs files produced by *.Database; all other generated C# across the solution comes from *.CodeGen.
dbex.yaml is the configuration entry point, declaring the schema, which tables to include in code generation, and whether to provision outbox infrastructure:
schema: products # schema (namespace) for all tables
outbox: true # generate transactional outbox tables + stored procs/functions
outboxName: outbox # prefix for outbox table and procedure names
tables:
- name: brand # reference-data tables (also seeded via Data/ref-data.yaml)
- name: category
- name: product # transactional tables
- name: inventoryWhen outbox: true is set, DbEx generates the full outbox infrastructure — tables (via a Migrate script) and stored procedures / functions (via Schema) — with no hand-authored DDL required.
The tooling projects feed the layer stack at design time but are invisible at runtime:
- CodeGen → generates
.g.csfiles that become part of the Contracts, API, Application, and Infrastructure layers. - Database → deploys the schema that the Infrastructure layer reads and writes, provisions the outbox tables consumed by the Outbox Relay host, and generates the Infrastructure-layer persistence models (
Persistence/*.g.cs) and EF CoreDbContextpartial (Repositories/*DbContext.g.cs).
See contracts-layer.md for how generated reference-data types are used, infrastructure-layer.md for the schema-aligned models, and hosts-layer.md for how the outbox relay consumes the deployed outbox infrastructure.