A database-first, explicit-DDL ORM for .NET — runtime-emitted entity types, JOIN-free eager loading, CDC push notifications, and schema-driven GraphQL/OpenAPI generation.
If you've built anything with Sequelize.js in the Node.js world, you know the feeling: point it at a database, define (or reverse-engineer) a model, and you're querying with Model.findAll({ where: {...}, include: [...] }) within minutes. Associations, lifecycle hooks, read replicas — it's all there, and it stays close enough to SQL that you never feel like you're fighting an abstraction.
.NET's dominant ORM story is different. Entity Framework Core is excellent — but it's fundamentally code-first: you declare DbContext/entity classes, and migrations describe how the database should change to match your code. That's a great model when your application owns the schema. It's a much worse fit when the database already exists, is owned by another team (or another codebase, possibly in another language entirely), or when you simply don't want application code deciding what the schema should look like.
That gap is why I built SequelizeDotNet.
What it actually does
SequelizeDotNet is a database-first ORM for .NET across six relational databases — SQL Server, SQLite, PostgreSQL, MySQL/MariaDB, Oracle, and DB2 — built on three non-negotiable design decisions:
The database is the source of truth, always. Nothing infers what your code "should" look like from a schema you wrote. The model comes from the database.
Explicit DDL, never automatic diffing. Structural changes happen through clearly-named method calls — RenameColumnAsync, AddColumnAsync, DropTableAsync — that a developer invokes deliberately. Destructive operations require force: true and are locked in production unless explicitly overridden. There's no sync({ alter: true })-style "make the database match my model, however that requires" — because that's exactly the class of feature that makes automatic schema sync dangerous in production, and Sequelize's own docs discourage it for good reason.
Runtime types, not code generation. Here's where .NET gets to do something JavaScript can't: EntityTypeFactory emits a real CLR type per table via System.Reflection.Emit, cached per-table with a lazy, thread-safe, single-emission guarantee. Change a column in the database, and the next query against that table gets a freshly emitted type reflecting it — no rebuild, no restart, no generated .cs file to keep in sync.
A few things I got to build that Sequelize itself doesn't have
Because .NET's runtime and type system make them possible:
Nested and many-to-many eager loading, always JOIN-free. Include("Author.Publisher.Country") walks three foreign-key hops as three separate, cached, IN (...)-filtered queries. IncludeMany("Tags", "dbo", "PostTags", "FK_PostTags_Posts", "FK_PostTags_Tags") does the same through an explicitly-named junction table. Nothing is ever hidden behind an opaque generated JOIN plan.
Read/write routing that actually understands raw SQL. Sequelize's read replication support has had recurring reports of raw queries silently bypassing the replica pool. StatementIntentClassifier inspects a raw query's leading keyword specifically so that escape hatch stays correctly routed too.
Zero-restart schema hot-reload. SchemaWatcher polls, diffs, and swaps the runtime entity type for a changed table — atomically, versioned — without restarting the process.
SQL Server Change Data Capture as a subscribable event. Most ORMs have zero built-in CDC consumption. CdcChangeWatcher turns SQL Server's CDC change tables into a C# event you subscribe to once.
GraphQL and OpenAPI generation straight from the introspected schema. No separate modeling step — introspect once, generate a GraphQL SDL schema or an OpenAPI 3.0 document from what the database already knows.
Where it stands today — honestly
This is a young project (v0.1.0) and I want to be upfront about exactly how far it's been verified, not just what's implemented:
SQL Server and SQLite are exhaustively live-tested — 140+ tests between them, covering every data type, every query operator, transactions, hooks, schema drift, hot-reload, and bulk insert, run against real LocalDB and temp-file databases.
PostgreSQL, MySQL, Oracle, and DB2 are fully implemented behind the exact same IDialect contract, but only unit-tested at the query-translator level — I don't have live instances of these to verify end-to-end behavior against yet. If you do, contributions are very welcome — porting the existing SQL Server/SQLite live test suites to one of these engines is the single most valuable thing anyone could contribute right now.
223 automated tests total, 0 failures, as of this release.
If your codebase already owns its schema and you want compile-time-checked LINQ, EF Core is very likely still the better default choice today — it's mature, Microsoft-backed, and has a much larger ecosystem. SequelizeDotNet is for the specific case where the database is the source of truth and you'd rather work the way Sequelize taught a generation of developers to work.
Try it
dotnet add package SequelizeDotNet.Core
dotnet add package SequelizeDotNet.Providers.SqlServer
var engine = new QueryEngine(new SqlServerDialect(), new ModelRegistry());
var query = QueryEngine.Query("dbo", "Posts")
.Where("AuthorId", QueryOperator.Equal, 42)
.Include("FK_Posts_Authors");
var rows = await engine.ToListAsync(query, connection);
Full docs: Developer Guide · Source & issues: github.com/mirshahreza/SequelizeDotNet · License: MIT.
Huge thanks to everyone who's ever contributed to Sequelize.js — this project exists because that one spent over a decade proving this way of working with databases is worth having, in any language.
If you've been burned by a migration tool guessing wrong, or you're maintaining a .NET service against a database another team owns, I'd love your feedback — and if you have a PostgreSQL, MySQL, Oracle, or DB2 instance you can point a test suite at, I'd love that even more.
SequelizeDotNet · MIT License · github.com/mirshahreza/SequelizeDotNet
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.