
Here's a tension I've watched play out on more than a few .NET teams. EF Core migrations are the default way a lot of shops manage their database schema -- it's what the tooling nudges you toward, it's what your junior developers already know, and it's what your CI pipeline is already wired up to run. Meanwhile, if you're also running Marten or Polecat, the Critter Stack is quietly managing its own schema for you through Weasel, our schema-management engine. Two worlds, two ways of tracking what your database is supposed to look like, and a slightly nervous feeling every time someone asks "so which one is the source of truth?"
Weasel can now answer that question by joining the two worlds together. It can generate EF Core migrations directly from a Weasel schema model. Under the covers it translates the Weasel model into EF Core MigrationOperation objects and emits real, standard-looking C# migration files -- the same kind you'd get from dotnet ef migrations add. If you're standardized on EF Core migrations but you also run Marten or Polecat, you can now fold the Critter Stack's schema straight into your existing migration pipeline instead of babysitting two schema tools. And if you just have an existing (or crusty, legacy) database that you'd like to bring under EF Core migrations, Weasel can baseline that too.
The friction, stated plainly
The Critter Stack has always managed its own schema. That's a feature -- Marten knows exactly what tables, indexes, functions, and sequences it needs to do its job, and it can apply or export those on demand. But if your organization has already decided that EF Core migrations are the one true schema deployment mechanism, Marten's independent schema management is one more thing that lives outside that pipeline. You end up reconciling two mental models, and reconciliation is where mistakes hide.
The obvious fix is to teach Weasel to speak EF Core's language. That's exactly what the EF Core migration generation feature does.
From Weasel model to C# migration file
At the center of the feature is a translation layer: it maps a Weasel schema model onto EF Core MigrationOperation instances. Once you're expressing your schema as migration operations, you're on EF Core's home turf. From there a C# migration file emitter writes out the actual migration classes, and a stub DbContext generator gives EF Core the context it expects to anchor those migrations to. The output is migration files that look and behave like the ones your team already reviews and deploys.
The CLI: db-ef-migration
All of this is driven by a new command line verb, db-ef-migration, with three sub-verbs:
# Generate an incremental migration from the current model vs. the last snapshot
dotnet run -- db-ef-migration add Initial
# Print the EF toolchain incantation for an idempotent SQL script
dotnet run -- db-ef-migration script
# Record already-generated migrations against an existing database
dotnet run -- db-ef-migration baselineadd generates an incremental migration by diffing your current schema model against the last recorded snapshot -- the same rhythm as dotnet ef migrations add, just sourced from Weasel's understanding of your schema. It takes a migration name, exactly like EF's own verb does. script doesn't emit the SQL itself; it prints the EF toolchain incantation that does (dotnet ef migrations script --idempotent), because once the migration files exist they're ordinary EF migrations and the EF tooling is already good at that. baseline records already-generated migrations against an existing database, so you can bring a legacy schema under migration control without hand-writing that first enormous migration.
I'm deliberately keeping the descriptions at the verb level here. The exact flags and options live in the migration generation docs, and I'd rather point you there than hard-code something in a blog post that drifts out of date the moment we add an option.
Incremental diffs, not just one-shot dumps
The add verb leans on a serialized schema snapshot plus a differ. Weasel writes out a snapshot of the schema after each run, and on the next run it compares the current model against that snapshot to compute what changed. That's what gives you proper add-migration-style incremental diffs between runs, rather than regenerating the entire world every time. Combined with baseline, the workflow is complete: capture where you are today, then let each subsequent add describe only what moved.
Parity broad enough to trust
Schema translation is one of those features where "it works" and "it works for my schema" are very different claims. The first round of coverage handled the bread and butter -- tables and indexes. A follow-up round widened that considerably to cover sequences, check constraints, computed columns, index methods, and drift detection.
A word on databases, because it's worth being precise rather than generous here. Weasel's EF Core mapping -- the direction where Weasel reads a DbContext and manages the schema itself -- reaches across all five providers, and that round extended its casing and computed-column parity to Oracle and MySQL. Migration generation, the feature this post is about, ships v1 for PostgreSQL and SQL Server. SQLite is deliberately excluded: its ALTER emulation rebuilds tables from the migration's target model, and these attribute-only migrations don't carry one. If you're on Oracle or MySQL and you want the Critter Stack schema in your database, use the mapping direction and let Weasel apply it -- generation isn't there yet.
Breadth is nice, but the part that lets me sleep at night is the validation harness. We built an inverted schema-comparison check: it takes the generated migration, applies the effect, and verifies that the result actually reproduces the schema you intended. In other words, it round-trips the migration and confirms the output matches the goal rather than just assuming the translation was faithful. That's the difference between "we generated a migration" and "we generated a migration that produces the right database."
When you need to bend it: the customization hook
No schema mapping survives contact with a sufficiently opinionated production database, so there's an escape hatch. The EfSchemaMappingCustomization hook lets you customize which tables get mapped and contribute additional schema objects into the EF Core migration path. If you have objects that Weasel doesn't model natively, or you want to shape exactly how a table lands in the migration, that's where you plug in.
Who should reach for this
Three groups, mainly:
- Teams running Marten or Polecat who want the Critter Stack's schema managed inside their existing EF Core migrations pipeline, so there's exactly one deployment mechanism and one source of truth.
- Teams who want to baseline an existing or legacy database into EF migrations without hand-authoring that painful first migration.
- Teams on PostgreSQL or SQL Server, which are the v1 providers for generation. (Oracle and MySQL users are well served by the mapping direction, where Weasel applies the schema itself.)
The migration generation docs carry the exact syntax, flags, and configuration details -- start there once you've decided this fits your workflow. The coexistence guide is the other one to read, because it covers the rule that actually bites: a table is owned by exactly one migration stream, never two.
This post is the middle of three. Meet Weasel covers the engine underneath all of this, and Clone, docker compose up, Go covers the opposite workflow -- letting Weasel apply your EF Core schema directly, which is what you probably want in local development and integration tests even if you generate migration files for production.
If you'd like a hand wiring this into an existing EF Core migrations pipeline -- or you just want a second set of eyes on a gnarly legacy schema before you baseline it -- that's exactly the kind of thing a JasperFX support plan covers, and you can always find us in Discord to talk it through.

