Model migration
At startup the engine’s ModelMigrationService (an IHostedService registered by AddGenie) scans
a models directory and applies each file in dependency order — entities → views → SQL →
navbar — via a per-type strategy. This is what populates the runtime metadata stores the API
reads from; it is separate from the EF migration that creates physical tables.
What each file type does
Section titled “What each file type does” ┌─▶ *.entity.xml ──[EntityMigrationStrategy]─────────▶ Entity store + triggersModelMigrationService ─────┼─▶ *.view.xml ────[ObjectViewMigrationStrategy]─────▶ ViewStore (ObjectView JSON) scans models directory ├─▶ *.sql ─────────[StoredProcedureMigrationStrategy]─▶ Recognized DDL executed └─▶ *.navbar.xml ──[NavbarMigrationStrategy]──────────▶ Navigation store| File | Strategy | Result |
|---|---|---|
*.entity.xml |
EntityMigrationStrategy |
Upserted into the entity store (entity metadata); the per-entity sequence/search/audit triggers are (re)created here. |
*.view.xml |
ObjectViewMigrationStrategy |
Upserted into ViewStore — the polymorphic ObjectView JSON the UI renders as grid/form/view. |
*.sql |
StoredProcedureMigrationStrategy |
Recognized DDL (CREATE [OR ALTER] PROCEDURE/FUNCTION/VIEW/TRIGGER) is executed; plain SQL is only recorded. |
*.navbar.xml |
NavbarMigrationStrategy |
Synced into the navigation store. |
Each applied file is recorded by a content hash, so an unchanged file is skipped on the next boot — only changed files re-run.
Controlling it — the Genie:Migration config section
Section titled “Controlling it — the Genie:Migration config section”Model-migration behaviour binds from the Genie:Migration section (ModelMigrationOptions),
overridable in code via ConfigureMigrations:
"Genie": { "Migration": { "MigrationExecution": "Forced", "ModelsPath": "models", "FilePatterns": [ "*.entity.xml", "*.view.xml", "*.sql", "*.navbar.xml" ] }}FilePatterns chooses which kinds of file the sweep picks up; the stage order is fixed regardless
(entities → views → SQL → navbar → RBAC), because a view may reference an entity and a grant may
reference a view. Configuring it replaces the default, and an unsupported pattern is rejected at
startup rather than collected and never run.
MigrationExecution:
No— don’t run on startup.Yes— run, skipping files whose content hash is unchanged.Forced— re-apply every file regardless of hash.
builder.Services.AddGenie<YourContext>(genie => genie .LoadFromConfiguration(builder.Configuration) .ConfigureMigrations(m => m.MigrationExecution = MigrationExecutionMode.Yes));Locating the models directory
Section titled “Locating the models directory”Point Genie:Migration:ModelsPath at your model files:
- An absolute path is used as-is.
- A relative path is resolved against the application base directory (
bin/…) first, then the host content root — so"models"covers both a published app and running from the source tree. - A configured path that does not exist is logged as an error and migration is skipped. It deliberately does not fall back to the conventional locations: a typo that silently migrates some other folder — or nothing at all — is worse than a visible stop.
Leave ModelsPath unset to use the convention: <appBase>/models, then <appBase>/../models. If
neither exists the service logs a warning naming both probed paths and skips.
The simplest portable approach is to copy your model files into the build output. The Inventory
sample copies ../models/** into bin/.../models with a <None … CopyToOutputDirectory="PreserveNewest">
item in its csproj, and sets "ModelsPath": "models":
<ItemGroup> <None Include="..\models\**\*.entity.xml;..\models\**\*.view.xml;..\models\**\*.sql;..\models\**\*.navbar.xml" Link="models\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" /></ItemGroup>Match the directory layout to your deployment.
How this relates to dotnet ef migrations
Section titled “How this relates to dotnet ef migrations”Two distinct steps run against the same database, in a fixed order at boot:
dotnet ef migrations(design-time, in the host assembly) — creates the physical tables from the source-generated EF configurations. Applied by the host, typically viacontext.Database.Migrate()on startup. When you upgrade the engine and its model gains tables (e.g.Identity.ResourceCapabilities+Identity.PermissionCapabilitiesfor capabilities and grants), scaffold a new host migration (dotnet ef migrations add <Name>) so those tables exist — this works for SQL Server and PostgreSQL from the same model.- Engine SQL bootstrap (
EngineSqlBootstrapper) — deploys the engine’s shared SQL objects (sequence functions, company-scope helpers, password-encryption procs). Runs before the model migration. See Backend integration. - Model migration (
ModelMigrationService) — populates the metadata stores and creates the per-entity triggers described above.
┌──────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐│ dotnet ef migrations │ ─▶ │ EngineSqlBootstrapper │ ─▶ │ ModelMigrationService ││ physical tables │ │ shared SQL objects │ │ metadata + triggers │└──────────────────────┘ └───────────────────────┘ └───────────────────────┘So authoring a new entity is: write the *.entity.xml, dotnet ef migrations add (tables), then let
the runtime migration register its metadata + triggers on the next boot.