DBML turned out to be a perfect input for the tool. The generator came first a few years earlier, and DBML support is the new part.
In the first version, the source of that metadata (types, keys, nullability, relationships, enums) was an Entity Framework DbContext. The output is a generated ASP.NET Core REST API with a generic CRUD flow of controller, unit of work and repository, plus a parametrized frontend data grid and editor that consumes the API. It’s been in use on real projects and it does the job it was built for.
Now I have added DBML as a second source into the same generator, to drop the compiling code-first requirement and allow quick iteration on prototypes and demos before any code is written.
Example:
Enum LoanStatus {
Open
Returned
Overdue
}
Table Member {
id UUID [pk]
membership_no String [unique, not null]
full_name String [not null]
joined_on Date [not null]
}
Table MemberCard {
member_id UUID [pk, ref: - Member.id]
card_number String [unique, not null, note: "Printed on the plastic. Reissued on loss."]
}
Table Author {
id Int [pk, increment]
full_name String [not null]
}
Table Book {
id Int [pk, increment]
title String [not null]
isbn String [unique]
shelf_code String [note: "Where it physically lives."]
}
Table BookAuthor {
book_id Int [pk, ref: > Book.id]
author_id Int [pk, ref: > Author.id]
}
Table Loan {
id Int [pk, increment]
member_id UUID [not null, ref: > Member.id]
book_id Int [not null, ref: > Book.id]
status LoanStatus [not null]
due_on Date [not null, note: "Fines start the day after."]
approved_by_id UUID [ref: > Member.id]
}
records Member (id, membership_no, full_name, joined_on) {
"8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f", "M-00417", "Ada Whitfield", "2024-03-11"
}
records Author (id, full_name) {
1, "Ursula Fenwick"
}
Every line decides something:
not nullbecomes a required field in the editor.- enum
LoanStatusbecomes a dropdown. ref: >becomes a remote searchable API lookup that shows a member’s name rather than a UUID.note:becomes the tooltip on the column header, the field in the editor and the model title.- column names are humanized into labels.
recordsblocks seed the preview, so grids open with Ada and Ursula already in them instead of empty.- relationships are used to offer configurable master views with parent-child filtering:
Membercan be a master screen with the member’s loans filtered underneath it, plus aMemberCardform (a unique reference renders as a form, the same as a 1:1).BookAuthor, being a junction table, can be included as an assignment list onBookorAuthor.
- labels and titles are derived from the column and table names.
- preview data comes from
records, and you can add or edit rows, but those changes live only as long as the sandbox.
On top of all that there’s a small config layer for the calls a schema genuinely can’t make: field order, hiding something, layout, what shows in the nav.
What’s underneath:
- the DBML is parsed by
@dbml/coreitself, so syntax has exactly one source of truth and I’m not maintaining a second parser that drifts. That becomes an intermediate model, - then C# source emitted as Roslyn syntax trees: POCO entities, one EF Core
IEntityTypeConfigurationper entity, a DbContext. Roslyn compiles that in memory and loads it (this is where the two sources meet). - the original generator then reflects over the freshly built DbContext exactly as it always did over a hand-written one,
- the output is real ASP.NET Core with real EF Core queries over an in-memory database, behind a generic CRUD flow (controller, unit of work, repository), not a mock of one. The frontend is a parametrized grid and editor reading the same generated metadata, so there is no per-table UI code for it to fall out of sync with.
Live demos run without an account at https://studio.pangeacode.com
Short demo to watch: https://www.youtube.com/watch?v=sDqrM-fgtBI
This forum is full of people who have written far more DBML than I have, so I would appreciate any feedback. Does it behave the way you would expect it to?
Thank you!