rust-ef
An EF Core-inspired ORM for Rust — DbContext / DbSet<T> / IEntityType,
the linq! macro, change tracking, transactions, migrations, and multi-database
support, with first-class rust-dix 0.9
dependency-injection integration.
Full handbook: https://rf2026.github.io/rust-ef/
Install
[]
= "2.2"
= "2.2" # one provider: sqlite / postgres / mysql
= "0.10" # DI container
= { = "1", = ["full"] }
MSRV: Rust 1.95.
Define an entity
use *;
#[derive(EntityType)] registers the entity at compile time; DbContext::from_options()
discovers it automatically — no manual discover_entities() call.
Register a DbContext (rust-dix 0.10)
use Arc;
use *;
use *;
use *;
use DbContextOptionsBuilderExt as _;
let provider = new
.add_dbcontext
.build
.unwrap;
// Owned resolution (recommended): &mut self access, no locks.
// Allowed from the root provider: the instance is caller-owned and never cached.
let mut ctx: DbContext = provider.get_owned.unwrap;
// Shared resolution: Arc<DbContext>, &self access, one instance per scope.
// rust-dix 0.10 enables strict scope validation by default, so create a scope.
let scope = provider.scope;
let shared: = scope.get.unwrap;
Upgrading from rust-ef 2.0 / rust-dix 0.7? Root
get::<DbContext>()now returnsRdiError::ScopedFromRootProvider. Useprovider.scope()(or owned resolution). For a temporary transition, build withServiceCollection::build_with(ServiceProviderOptions::lenient()). See CHANGELOG.md.
More registration variants (2.1)
- Typed keys:
#[derive(DbContextKey)]generatesIDbContextKeyand the rust-dix 0.10From<Key> for ServiceKeycontract, so registration,DbContextResolverand#[inject(owned, key = Db::Read)]all use the same typed key. - Dynamic tenant/sharded contexts:
add_dbcontext_anykey(|key, options| ...)registers one fallback for any requested key, with bounded-LRU per-key provider/pool caching (add_dbcontext_anykey_with_capacity). Use the default entity model + query filters for isolation. - Async startup:
add_dbcontext_async/add_dbcontext_keyed_asyncdefer provider creation tobuild_async().awaitand return typedRdiErrorfailures; an optionaloptions.async_preflight(...)hook validates the pool asynchronously. TheDbContextstays a synchronous Scoped service, so owned injection is unchanged.
See the handbook DI chapter and docs/v2.1-migration-guide.md.
What's new in 2.2
- Composable predicates:
linq!(|b: Blog| ...)returns a typedPredicate<Blog>(and/or/not/and_opt/or_opt,always()/never()seeds) that you compose per request and apply withquery.filter(predicate);count()and the page query share one condition. The oldlinq!(filter ...)keyword and thefilter_column/or_wherechain API are gone — seedocs/v2.2-migration-guide.md. - Database-level constraints: composite / partial / expression indexes, composite
foreign keys,
CHECK, column defaults, MySQL table options, snapshot export andDbContext::migrate(dir)with provider-appropriate atomicity. - EFCore entity-configuration parity: column type / precision / fixed length /
collation / comments, computed columns, alternate keys, views, keyless entities,
schema-qualified tables,
INCLUDEcolumns and#[derive(DbEnum)].
Query and save
let mut ctx: DbContext = provider.get_owned?;
ctx.;
ctx.ensure_created.await?; // create tables (dev/test); migrations are preferred in production
ctx.;
ctx.save_changes.await?;
let expr = linq!;
let blogs = ctx..filter.to_list.await?;
Features
| Area | Highlights |
|---|---|
| Entity mapping | #[derive(EntityType)] with 20+ field attributes, navigation types, compile-time registration, #[derive(DbEnum)] |
| Query | linq! expression trees, composable Predicate<T> filters, join/group_by/aggregation, CTEs, window functions, lazy loading (opt-in) |
| Persistence | save_changes(), parameterized SQL, transactions and savepoints, cascade delete, bulk execute_update / execute_delete |
| Migrations | model diff, Up/Down SQL, migration history, composite/partial/expression indexes, composite FKs, CHECK, column defaults/comments/computed columns, MySQL table options, schema-qualified tables, views |
| DI | add_dbcontext / add_dbcontext_keyed / add_dbcontext_anykey / async variants on rust-dix 0.10 (Scoped, owned + shared resolution, typed keys, dynamic contexts) |
| Interceptors | before/after/failure hooks around save_changes() |
| Observability | optional tracing spans and slow-query warnings |
Crate layout
| Crate | Purpose |
|---|---|
rust-ef |
core: DbContext, DbSet, query builder, change tracking, migrations |
rust-ef-macros |
#[derive(EntityType)], linq!, column!, #[entity] |
rust-ef-sqlite / rust-ef-postgres / rust-ef-mysql |
database providers |
rust-ef-cli |
migration / scaffold CLI (not published) |
The core is provider- and DI-agnostic: providers inject a factory into
DbContextOptions, and crates/core/src/di.rs is the only rust-dix integration
surface.
Core modules
entity.rs IEntityType, IFromRow, IGetKeyValues, IEntitySnapshot
metadata.rs EntityTypeMeta, PropertyMeta, NavigationMeta
registration.rs compile-time entity / configuration registration (inventory)
provider/ IDatabaseProvider, ISqlGenerator, IAsyncConnection, DbValue
db_context/ DbContext, DbContextOptions, save pipeline
db_set.rs IDbSet<T>, DbSet<T>
query/ IQueryable<T>, QueryBuilder<T>, linq! internals
change_executor/ INSERT / UPDATE / DELETE execution
model_builder.rs ModelBuilder, IEntityTypeConfiguration<T>
tracking.rs ChangeTracker (property-level snapshots)
relations.rs BelongsTo, HasMany, HasOne, DeleteBehavior
navigation_loader.rs eager loading (Include / ThenInclude)
lazy.rs opt-in lazy navigation loading
cascade.rs cascade save / delete
transaction.rs context-scoped transactions and savepoints
interceptor.rs ISaveChangesInterceptor pipeline
migration/ MigrationEngine, diffing, history
di.rs rust-dix 0.10 integration
Cargo features
| Feature | Enables |
|---|---|
serde |
Serde impls for DbValue |
chrono |
DateTime<Utc> / NaiveDateTime / NaiveDate mapping |
uuid |
uuid::Uuid mapping |
decimal |
rust_decimal::Decimal mapping |
tracing |
query spans and slow-query warnings |
Documentation
- Handbook: https://rf2026.github.io/rust-ef/
- API docs: https://docs.rs/rust-ef
- Migration guides:
v2.2·v2.1 - Examples:
examples/blog,examples/soft_delete,examples/audit
License
MIT — see LICENSE.